Bug 1257154 - Switch to getting DOM title updates via a frame script to work better with e10s. r=mikedeboer
authorMark Banner <standard8@mozilla.com>
Fri, 01 Apr 2016 13:54:08 +0100
changeset 292109 1725b460c3e0de97590cb8764df9ace9115b813e
parent 292108 163ec1ec0f25e482a9c5156581887744f1f3fbb7
child 292110 91fd28b61badff20e75856f09fc2eb84bb537570
push id74762
push usercbook@mozilla.com
push dateThu, 07 Apr 2016 09:56:20 +0000
treeherdermozilla-inbound@772253c53374 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmikedeboer
bugs1257154
milestone48.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1257154 - Switch to getting DOM title updates via a frame script to work better with e10s. r=mikedeboer
browser/extensions/loop/bootstrap.js
browser/extensions/loop/chrome/content/modules/tabFrame.js
browser/extensions/loop/chrome/test/mochitest/browser.ini
browser/extensions/loop/chrome/test/mochitest/browser_sharingTitleListeners.js
--- a/browser/extensions/loop/bootstrap.js
+++ b/browser/extensions/loop/bootstrap.js
@@ -8,16 +8,19 @@
 const { interfaces: Ci, utils: Cu, classes: Cc } = Components;
 
 const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 const kBrowserSharingNotificationId = "loop-sharing-notification";
 
 const CURSOR_MIN_DELTA = 3;
 const CURSOR_MIN_INTERVAL = 100;
 const CURSOR_CLICK_DELAY = 1000;
+// Due to bug 1051238 frame scripts are cached forever, so we can't update them
+// as a restartless add-on. The Math.random() is the work around for this.
+const FRAME_SCRIPT = "chrome://loop/content/modules/tabFrame.js?" + Math.random();
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/AppConstants.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
@@ -52,16 +55,17 @@ var WindowListener = {
    */
   setupBrowserUI: function(window) {
     let document = window.document;
     let gBrowser = window.gBrowser;
     let xhrClass = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"];
     let FileReader = window.FileReader;
     let menuItem = null;
     let isSlideshowOpen = false;
+    let titleChangedListener = null;
 
     // the "exported" symbols
     var LoopUI = {
       /**
        * @var {XULWidgetSingleWrapper} toolbarButton Getter for the Loop toolbarbutton
        *                                             instance for this window. This should
        *                                             not be used in the hidden window.
        */
@@ -110,16 +114,20 @@ var WindowListener = {
           }, result => {
             this._constants = result;
           });
         }
 
         return this._constants;
       },
 
+      get mm() {
+        return window.getGroupMessageManager("browsers");
+      },
+
       /**
        * @return {Promise}
        */
       promiseDocumentVisible(aDocument) {
         if (!aDocument.hidden) {
           return Promise.resolve(aDocument);
         }
 
@@ -308,16 +316,20 @@ var WindowListener = {
         this.addMenuItem();
 
         // Don't do the rest if this is for the hidden window - we don't
         // have a toolbar there.
         if (window == Services.appShell.hiddenDOMWindow) {
           return;
         }
 
+        // Load the frame script into any tab, plus any that get created in the
+        // future.
+        this.mm.loadFrameScript(FRAME_SCRIPT, true);
+
         // Cleanup when the window unloads.
         window.addEventListener("unload", () => {
           Services.obs.removeObserver(this, "loop-status-changed");
         });
 
         Services.obs.addObserver(this, "loop-status-changed", false);
 
         this.updateToolbarState();
@@ -517,19 +529,23 @@ var WindowListener = {
        * Push message parameters:
        * - {Integer} windowId  The new windowId for the browser.
        */
       startBrowserSharing: function() {
         if (!this._listeningToTabSelect) {
           gBrowser.tabContainer.addEventListener("TabSelect", this);
           this._listeningToTabSelect = true;
 
+          titleChangedListener = this.handleDOMTitleChanged.bind(this);
+
           // Watch for title changes as opposed to location changes as more
           // metadata about the page is available when this event fires.
-          gBrowser.addEventListener("DOMTitleChanged", this);
+          this.mm.addMessageListener("loop@mozilla.org:DOMTitleChanged",
+            titleChangedListener);
+
           this._browserSharePaused = false;
 
           // Add this event to the parent gBrowser to avoid adding and removing
           // it for each individual tab's browsers.
           gBrowser.addEventListener("mousemove", this);
           gBrowser.addEventListener("click", this);
         }
 
@@ -556,17 +572,22 @@ var WindowListener = {
        */
       stopBrowserSharing: function() {
         if (!this._listeningToTabSelect) {
           return;
         }
 
         this._hideBrowserSharingInfoBar();
         gBrowser.tabContainer.removeEventListener("TabSelect", this);
-        gBrowser.removeEventListener("DOMTitleChanged", this);
+
+        if (titleChangedListener) {
+          this.mm.removeMessageListener("loop@mozilla.org:DOMTitleChanged",
+            titleChangedListener);
+          titleChangedListener = null;
+        }
 
         // Remove shared pointers related events
         gBrowser.removeEventListener("mousemove", this);
         gBrowser.removeEventListener("click", this);
         this.removeRemoteCursor();
 
         this._listeningToTabSelect = false;
         this._browserSharePaused = false;
@@ -795,24 +816,36 @@ var WindowListener = {
        */
       _notifyBrowserSwitch: function() {
          // Get the first window Id for the listener.
         this.LoopAPI.broadcastPushMessage("BrowserSwitch",
           gBrowser.selectedBrowser.outerWindowID);
       },
 
       /**
+       * Handles events from the frame script.
+       *
+       * @param {Object} message The message received from the frame script.
+       */
+      handleDOMTitleChanged: function(message) {
+        if (!this._listeningToTabSelect || this._browserSharePaused) {
+          return;
+        }
+
+        if (gBrowser.selectedBrowser == message.target) {
+          // Get the new title of the shared tab
+          this._notifyBrowserSwitch();
+        }
+      },
+
+      /**
        * Handles events from gBrowser.
        */
       handleEvent: function(event) {
         switch (event.type) {
-          case "DOMTitleChanged":
-            // Get the new title of the shared tab
-            this._notifyBrowserSwitch();
-            break;
           case "TabSelect":
             let wasVisible = false;
             // Hide the infobar from the previous tab.
             if (event.detail.previousTab) {
               wasVisible = this._hideBrowserSharingInfoBar(
                             event.detail.previousTab.linkedBrowser);
               // And remove the cursor.
               this.removeRemoteCursor();
@@ -945,16 +978,20 @@ var WindowListener = {
    * document.getElementById() etc. will work here.
    *
    * @param {Object} window The window to remove the integration from.
    */
   tearDownBrowserUI: function(window) {
     if (window.LoopUI) {
       window.LoopUI.removeMenuItem();
 
+      // This stops the frame script being loaded to new tabs, but doesn't
+      // remove it from existing tabs (there's no way to do that).
+      window.LoopUI.mm.removeDelayedFrameScript(FRAME_SCRIPT);
+
       // XXX Bug 1229352 - Add in tear-down of the panel.
     }
   },
 
   // nsIWindowMediatorListener functions.
   onOpenWindow: function(xulWindow) {
     // A new window has opened.
     let domWindow = xulWindow.QueryInterface(Ci.nsIInterfaceRequestor)
new file mode 100644
--- /dev/null
+++ b/browser/extensions/loop/chrome/content/modules/tabFrame.js
@@ -0,0 +1,22 @@
+/* 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/. */
+
+"use strict";
+
+/* global sendAsyncMessage */
+
+/**
+ * This script runs in the content process and is attached to browsers when
+ * they are created.
+ */
+
+// Listen for when the title is changed and send a message back to the chrome
+// process.
+addEventListener("DOMTitleChanged", ({ target }) => {
+  sendAsyncMessage("loop@mozilla.org:DOMTitleChanged", {
+    details: "titleChanged"
+  }, {
+     target: target
+  });
+});
--- a/browser/extensions/loop/chrome/test/mochitest/browser.ini
+++ b/browser/extensions/loop/chrome/test/mochitest/browser.ini
@@ -10,9 +10,10 @@ support-files =
 [browser_LoopRooms_channel.js]
 [browser_menuitem.js]
 [browser_mozLoop_appVersionInfo.js]
 [browser_mozLoop_chat.js]
 [browser_mozLoop_context.js]
 [browser_mozLoop_socialShare.js]
 [browser_mozLoop_sharingListeners.js]
 [browser_mozLoop_telemetry.js]
+[browser_sharingTitleListeners.js]
 [browser_toolbarbutton.js]
new file mode 100644
--- /dev/null
+++ b/browser/extensions/loop/chrome/test/mochitest/browser_sharingTitleListeners.js
@@ -0,0 +1,51 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * This file contains tests for the browser sharing document title listeners.
+ */
+"use strict";
+
+var [, gHandlers] = LoopAPI.inspect();
+
+function promiseBrowserSwitch() {
+  return new Promise(resolve => {
+    LoopAPI.stub([{
+      sendAsyncMessage: function(messageName, data) {
+        if (data[0] == "BrowserSwitch") {
+          LoopAPI.restore();
+          resolve();
+        }
+      }
+    }]);
+  });
+}
+
+add_task(function* setup() {
+  Services.prefs.setBoolPref("loop.remote.autostart", true);
+
+  gHandlers.AddBrowserSharingListener({ data: [42] }, () => {});
+
+  let newTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank", true);
+
+  registerCleanupFunction(function* () {
+    // Remove the listener.
+    gHandlers.RemoveBrowserSharingListener({ data: [42] }, function() {});
+
+    yield BrowserTestUtils.removeTab(newTab);
+
+    Services.prefs.clearUserPref("loop.remote.autostart");
+  });
+});
+
+add_task(function* test_notifyOnTitleChanged() {
+  // Hook up the async listener and wait for the BrowserSwitch to happen.
+  let browserSwitchPromise = promiseBrowserSwitch();
+
+  BrowserTestUtils.loadURI(gBrowser.selectedBrowser, "about:mozilla");
+
+  // Now check we get the notification of the browser switch.
+  yield browserSwitchPromise;
+
+  Assert.ok(true, "We got notification of the browser switch");
+});