Commit 0232a50a authored by Julian Descottes's avatar Julian Descottes
Browse files

Bug 1823670 - [messagehandler] Destroy MessageHandlers when pages move to...

Bug 1823670 - [messagehandler] Destroy MessageHandlers when pages move to BFCache r=webdriver-reviewers,whimboo

Differential Revision: https://phabricator.services.mozilla.com/D173132
parent ebb16c58
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@ support-files =
prefs =
  remote.messagehandler.modulecache.useBrowserTestRoot=true

[browser_bfcache.js]
[browser_events_dispatcher.js]
[browser_events_handler.js]
[browser_events_module.js]
+98 −0
Original line number Diff line number Diff line
/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

const { RootMessageHandler } = ChromeUtils.importESModule(
  "chrome://remote/content/shared/messagehandler/RootMessageHandler.sys.mjs"
);

const TEST_PREF = "remote.messagehandler.test.pref";

// Check that pages in bfcache no longer have message handlers attached to them,
// and that they will not emit unexpected events.
add_task(async function test_bfcache_broadcast() {
  const tab = await addTab("https://example.com/document-builder.sjs?html=tab");
  const rootMessageHandler = createRootMessageHandler("session-id-bfcache");

  try {
    const browsingContext = tab.linkedBrowser.browsingContext;
    const contextDescriptor = {
      type: ContextDescriptorType.TopBrowsingContext,
      id: browsingContext.browserId,
    };

    // Whenever a "preference-changed" event from the eventonprefchange module
    // will be received on the root MessageHandler, increment a counter.
    let preferenceChangeEventCount = 0;
    const onEvent = (evtName, wrappedEvt) => {
      if (wrappedEvt.name === "preference-changed") {
        preferenceChangeEventCount++;
      }
    };
    rootMessageHandler.on("message-handler-event", onEvent);

    // Initialize the preference, no eventonprefchange module should be created
    // yet so preferenceChangeEventCount is not expected to be updated.
    Services.prefs.setIntPref(TEST_PREF, 0);
    await TestUtils.waitForCondition(() => preferenceChangeEventCount >= 0);
    is(preferenceChangeEventCount, 0);

    // Broadcast a "ping" command to force the creation of the eventonprefchange
    // module
    let values = await sendPingCommand(rootMessageHandler, contextDescriptor);
    is(values.length, 1, "Broadcast returned a single value");

    Services.prefs.setIntPref(TEST_PREF, 1);
    await TestUtils.waitForCondition(() => preferenceChangeEventCount >= 1);
    is(preferenceChangeEventCount, 1);

    info("Navigate to another page");
    await loadURL(
      tab.linkedBrowser,
      "https://example.com/document-builder.sjs?html=othertab"
    );

    values = await sendPingCommand(rootMessageHandler, contextDescriptor);
    is(values.length, 1, "Broadcast returned a single value after navigation");

    info("Update the preference and check we only receive 1 event");
    Services.prefs.setIntPref(TEST_PREF, 2);
    await TestUtils.waitForCondition(() => preferenceChangeEventCount >= 2);
    is(preferenceChangeEventCount, 2);

    info("Navigate to another origin");
    await loadURL(
      tab.linkedBrowser,
      "https://example.org/document-builder.sjs?html=otherorigin"
    );

    values = await sendPingCommand(rootMessageHandler, contextDescriptor);
    is(
      values.length,
      1,
      "Broadcast returned a single value after cross origin navigation"
    );

    info("Update the preference and check again that we only receive 1 event");
    Services.prefs.setIntPref(TEST_PREF, 3);
    await TestUtils.waitForCondition(() => preferenceChangeEventCount >= 3);
    is(preferenceChangeEventCount, 3);
  } finally {
    rootMessageHandler.destroy();
    gBrowser.removeTab(tab);
    Services.prefs.clearUserPref(TEST_PREF);
  }
});

function sendPingCommand(rootMessageHandler, contextDescriptor) {
  return rootMessageHandler.handleCommand({
    moduleName: "eventonprefchange",
    commandName: "ping",
    params: {},
    destination: {
      contextDescriptor,
      type: WindowGlobalMessageHandler.type,
    },
  });
}
+33 −0
Original line number Diff line number Diff line
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import { Module } from "chrome://remote/content/shared/messagehandler/Module.sys.mjs";

const TEST_PREF = "remote.messagehandler.test.pref";

class EventOnPrefChangeModule extends Module {
  constructor(messageHandler) {
    super(messageHandler);
    Services.prefs.addObserver(TEST_PREF, this.#onPreferenceUpdated);
  }

  destroy() {
    Services.prefs.removeObserver(TEST_PREF, this.#onPreferenceUpdated);
  }

  #onPreferenceUpdated = () => {
    this.emitEvent("preference-changed");
  };

  /**
   * Commands
   */

  ping() {
    // We only use this command to force creating the module.
    return 1;
  }
}

export const eventonprefchange = EventOnPrefChangeModule;
+2 −0
Original line number Diff line number Diff line
@@ -24,6 +24,8 @@ const FRAME_ACTOR_CONFIG = {
      "chrome://remote/content/shared/messagehandler/transports/js-window-actors/MessageHandlerFrameChild.sys.mjs",
    events: {
      DOMWindowCreated: {},
      pagehide: {},
      pageshow: {},
    },
  },
  allFrames: true,
+8 −2
Original line number Diff line number Diff line
@@ -33,11 +33,17 @@ export class MessageHandlerFrameChild extends JSWindowActorChild {
    this._registry.on("message-handler-registry-event", this._onRegistryEvent);
  }

  handleEvent({ type }) {
    if (type == "DOMWindowCreated") {
  handleEvent({ persisted, type }) {
    if (type == "DOMWindowCreated" || (type == "pageshow" && persisted)) {
      // When the window is created or is retrieved from BFCache, instantiate
      // a MessageHandler for all sessions which might need it.
      if (lazy.isBrowsingContextCompatible(this.manager.browsingContext)) {
        this._registry.createAllMessageHandlers();
      }
    } else if (type == "pagehide" && persisted) {
      // When the page is moved to BFCache, all the currently created message
      // handlers should be destroyed.
      this._registry.destroy();
    }
  }