Bug 1297535 - Register devtools menu and keys on DOMContentLoaded to happen before CustomizableUI. r=bgrins draft
authorAlexandre Poirot <poirot.alex@gmail.com>
Wed, 24 Aug 2016 07:26:32 -0700
changeset 418942 1d8afdb3395fee971c1d2966ed57de168d4c5291
parent 417914 66a77b9bfe5dcacd50eccf85de7c0e7e15ce0ffd
child 532458 b08893947ffd0d96df8c971d875c9e701566c589
push id30807
push userbmo:poirot.alex@gmail.com
push dateThu, 29 Sep 2016 10:10:46 +0000
reviewersbgrins
bugs1297535
milestone52.0a1
Bug 1297535 - Register devtools menu and keys on DOMContentLoaded to happen before CustomizableUI. r=bgrins MozReview-Commit-ID: ACy3yJTgzyA
devtools/client/framework/devtools-browser.js
--- a/devtools/client/framework/devtools-browser.js
+++ b/devtools/client/framework/devtools-browser.js
@@ -136,16 +136,20 @@ var gDevToolsBrowser = exports.gDevTools
         break;
       case "nsPref:changed":
         if (prefName.endsWith("enabled")) {
           for (let win of this._trackedBrowserWindows) {
             this.updateCommandAvailability(win);
           }
         }
         break;
+      case "domwindowopened":
+        let win = subject.QueryInterface(Ci.nsIDOMEventTarget);
+        win.addEventListener("DOMContentLoaded", this, { once: true });
+        break;
     }
   },
 
   _prefObserverRegistered: false,
 
   ensurePrefObserver: function () {
     if (!this._prefObserverRegistered) {
       this._prefObserverRegistered = true;
@@ -397,42 +401,59 @@ var gDevToolsBrowser = exports.gDevTools
    * Move WebIDE widget to the navbar
    */
    // Used by webide.js
   moveWebIDEWidgetInNavbar: function () {
     CustomizableUI.addWidgetToArea("webide-button", CustomizableUI.AREA_NAVBAR);
   },
 
   /**
+   * Starts setting up devtools on a given browser window. This method is
+   * called on DOMContentLoaded, so earlier than registerBrowserWindow which
+   * is called after delayed-startup notification. This method should only do
+   * what has to be done early. Otherwise devtools should be initialized lazily
+   * to prevent overloading Firefox startup.
+   *
+   * @param {ChromeWindow} window
+   *        The window to which devtools should be hooked to.
+   */
+  _onBrowserWindowLoaded: function (win) {
+    // This method is called for all top level window, only consider firefox
+    // windows
+    if (!win.gBrowser || !win.location.href.endsWith("browser.xul")) {
+      return;
+    }
+    BrowserMenus.addMenus(win.document);
+    win.addEventListener("unload", this);
+  },
+
+  /**
    * Add this DevTools's presence to a browser window's document
    *
-   * @param {XULDocument} doc
-   *        The document to which devtools should be hooked to.
+   * @param {ChromeWindow} win
+   *        The window to which devtools should be hooked to.
    */
   _registerBrowserWindow: function (win) {
     if (gDevToolsBrowser._trackedBrowserWindows.has(win)) {
       return;
     }
     gDevToolsBrowser._trackedBrowserWindows.add(win);
 
-    BrowserMenus.addMenus(win.document);
-
     // Register the Developer widget in the Hamburger menu or navbar
     // only once menus are registered as it depends on it.
     gDevToolsBrowser.installDeveloperWidget();
 
     // Inject lazily DeveloperToolbar on the chrome window
     loader.lazyGetter(win, "DeveloperToolbar", function () {
       let { DeveloperToolbar } = require("devtools/client/shared/developer-toolbar");
       return new DeveloperToolbar(win);
     });
 
     this.updateCommandAvailability(win);
     this.ensurePrefObserver();
-    win.addEventListener("unload", this);
 
     let tabContainer = win.gBrowser.tabContainer;
     tabContainer.addEventListener("TabSelect", this, false);
     tabContainer.addEventListener("TabOpen", this, false);
     tabContainer.addEventListener("TabClose", this, false);
     tabContainer.addEventListener("TabPinned", this, false);
     tabContainer.addEventListener("TabUnpinned", this, false);
   },
@@ -619,23 +640,26 @@ var gDevToolsBrowser = exports.gDevTools
   /**
    * Called on browser unload to remove menu entries, toolboxes and event
    * listeners from the closed browser window.
    *
    * @param  {XULWindow} win
    *         The window containing the menu entry
    */
   _forgetBrowserWindow: function (win) {
+    // _forgetBrowserWindow can only be called once for each window, but
+    // _registerBrowserWindow may not have been called. Instead, only
+    // _onBrowserWindowLoaded was and we only need to revert that.
+    win.removeEventListener("unload", this);
+    BrowserMenus.removeMenus(win.document);
+
     if (!gDevToolsBrowser._trackedBrowserWindows.has(win)) {
       return;
     }
     gDevToolsBrowser._trackedBrowserWindows.delete(win);
-    win.removeEventListener("unload", this);
-
-    BrowserMenus.removeMenus(win.document);
 
     // Destroy toolboxes for closed window
     for (let [target, toolbox] of gDevTools._toolboxes) {
       if (toolbox.win.top == win) {
         toolbox.destroy();
       }
     }
 
@@ -674,16 +698,19 @@ var gDevToolsBrowser = exports.gDevTools
         this._tabStats.histOpen.push(open);
         this._tabStats.histPinned.push(pinned);
         this._tabStats.peakOpen = Math.max(open, this._tabStats.peakOpen);
         this._tabStats.peakPinned = Math.max(pinned, this._tabStats.peakPinned);
         break;
       case "TabSelect":
         gDevToolsBrowser._updateMenuCheckbox();
         break;
+      case "DOMContentLoaded":
+        gDevToolsBrowser._onBrowserWindowLoaded(event.target.defaultView);
+        break;
       case "unload":
         // top-level browser window unload
         gDevToolsBrowser._forgetBrowserWindow(event.target.defaultView);
         break;
     }
   },
 
   _pingTelemetry: function () {
@@ -703,16 +730,17 @@ var gDevToolsBrowser = exports.gDevTools
     this._telemetry.log(TABS_PINNED_AVG_HISTOGRAM, mean(tabStats.histPinned));
   },
 
   /**
    * All browser windows have been closed, tidy up remaining objects.
    */
   destroy: function () {
     Services.prefs.removeObserver("devtools.", gDevToolsBrowser);
+    Services.ww.unregisterNotification(gDevToolsBrowser);
     Services.obs.removeObserver(gDevToolsBrowser, "browser-delayed-startup-finished");
     Services.obs.removeObserver(gDevToolsBrowser.destroy, "quit-application");
 
     gDevToolsBrowser._pingTelemetry();
     gDevToolsBrowser._telemetry = null;
 
     for (let win of gDevToolsBrowser._trackedBrowserWindows) {
       gDevToolsBrowser._forgetBrowserWindow(win);
@@ -735,24 +763,26 @@ gDevTools.on("tool-unregistered", functi
   }
   gDevToolsBrowser._removeToolFromWindows(toolId);
 });
 
 gDevTools.on("toolbox-ready", gDevToolsBrowser._updateMenuCheckbox);
 gDevTools.on("toolbox-destroyed", gDevToolsBrowser._updateMenuCheckbox);
 
 Services.obs.addObserver(gDevToolsBrowser.destroy, "quit-application", false);
+Services.ww.registerNotification(gDevToolsBrowser);
 Services.obs.addObserver(gDevToolsBrowser, "browser-delayed-startup-finished", false);
 
 // Fake end of browser window load event for all already opened windows
 // that is already fully loaded.
 let enumerator = Services.wm.getEnumerator(gDevTools.chromeWindowType);
 while (enumerator.hasMoreElements()) {
   let win = enumerator.getNext();
   if (win.gBrowserInit && win.gBrowserInit.delayedStartupFinished) {
+    gDevToolsBrowser._onBrowserWindowLoaded(win);
     gDevToolsBrowser._registerBrowserWindow(win);
   }
 }
 
 // Watch for module loader unload. Fires when the tools are reloaded.
 unload(function () {
   gDevToolsBrowser.destroy();
 });