Bug 1302996 - Stop loading various useless SDK modules on early devtools startup. r=jryans draft
authorAlexandre Poirot <poirot.alex@gmail.com>
Mon, 29 Aug 2016 08:23:32 -0700
changeset 444739 7a89ecc8cf97951875cc96c45081c29e7073b0e3
parent 444478 05328d3102efd4d5fc0696489734d7771d24459f
child 444740 06f3d18b705b3bee4ac235be958e783b3ceb57b2
push id37346
push userbmo:poirot.alex@gmail.com
push dateMon, 28 Nov 2016 18:48:40 +0000
reviewersjryans
bugs1302996
milestone53.0a1
Bug 1302996 - Stop loading various useless SDK modules on early devtools startup. r=jryans MozReview-Commit-ID: 5Roi7rmmbes
devtools/client/definitions.js
devtools/client/framework/devtools-browser.js
devtools/client/framework/devtools.js
--- a/devtools/client/definitions.js
+++ b/devtools/client/definitions.js
@@ -7,16 +7,17 @@
 const Services = require("Services");
 const osString = Services.appinfo.OS;
 
 // Panels
 loader.lazyGetter(this, "OptionsPanel", () => require("devtools/client/framework/toolbox-options").OptionsPanel);
 loader.lazyGetter(this, "InspectorPanel", () => require("devtools/client/inspector/panel").InspectorPanel);
 loader.lazyGetter(this, "WebConsolePanel", () => require("devtools/client/webconsole/panel").WebConsolePanel);
 loader.lazyGetter(this, "DebuggerPanel", () => require("devtools/client/debugger/panel").DebuggerPanel);
+loader.lazyGetter(this, "NewDebuggerPanel", () => require("devtools/client/debugger/new/panel").DebuggerPanel);
 loader.lazyGetter(this, "StyleEditorPanel", () => require("devtools/client/styleeditor/styleeditor-panel").StyleEditorPanel);
 loader.lazyGetter(this, "ShaderEditorPanel", () => require("devtools/client/shadereditor/panel").ShaderEditorPanel);
 loader.lazyGetter(this, "CanvasDebuggerPanel", () => require("devtools/client/canvasdebugger/panel").CanvasDebuggerPanel);
 loader.lazyGetter(this, "WebAudioEditorPanel", () => require("devtools/client/webaudioeditor/panel").WebAudioEditorPanel);
 loader.lazyGetter(this, "MemoryPanel", () => require("devtools/client/memory/panel").MemoryPanel);
 loader.lazyGetter(this, "PerformancePanel", () => require("devtools/client/performance/panel").PerformancePanel);
 loader.lazyGetter(this, "NetMonitorPanel", () => require("devtools/client/netmonitor/panel").NetMonitorPanel);
 loader.lazyGetter(this, "StoragePanel", () => require("devtools/client/storage/panel").StoragePanel);
@@ -150,18 +151,16 @@ Tools.jsdebugger = {
 
   build: function (iframeWindow, toolbox) {
     return new DebuggerPanel(iframeWindow, toolbox);
   }
 };
 
 function switchDebugger() {
   if (Services.prefs.getBoolPref("devtools.debugger.new-debugger-frontend")) {
-    const NewDebuggerPanel = require("devtools/client/debugger/new/panel").DebuggerPanel;
-
     Tools.jsdebugger.url = "chrome://devtools/content/debugger/new/index.html";
     Tools.jsdebugger.build = function (iframeWindow, toolbox) {
       return new NewDebuggerPanel(iframeWindow, toolbox);
     };
   } else {
     Tools.jsdebugger.url = "chrome://devtools/content/debugger/debugger.xul";
     Tools.jsdebugger.build = function (iframeWindow, toolbox) {
       return new DebuggerPanel(iframeWindow, toolbox);
--- a/devtools/client/framework/devtools-browser.js
+++ b/devtools/client/framework/devtools-browser.js
@@ -13,17 +13,16 @@
  **/
 
 const {Cc, Ci, Cu} = require("chrome");
 const Services = require("Services");
 const promise = require("promise");
 const defer = require("devtools/shared/defer");
 const Telemetry = require("devtools/client/shared/telemetry");
 const { gDevTools } = require("./devtools");
-const { when: unload } = require("sdk/system/unload");
 
 // Load target and toolbox lazily as they need gDevTools to be fully initialized
 loader.lazyRequireGetter(this, "TargetFactory", "devtools/client/framework/target", true);
 loader.lazyRequireGetter(this, "Toolbox", "devtools/client/framework/toolbox", true);
 loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);
 loader.lazyRequireGetter(this, "DebuggerClient", "devtools/shared/client/main", true);
 loader.lazyRequireGetter(this, "BrowserMenus", "devtools/client/framework/browser-menus");
 loader.lazyRequireGetter(this, "findCssSelector", "devtools/shared/inspector/css-logic", true);
@@ -137,16 +136,26 @@ var gDevToolsBrowser = exports.gDevTools
         break;
       case "nsPref:changed":
         if (prefName.endsWith("enabled")) {
           for (let win of this._trackedBrowserWindows) {
             this.updateCommandAvailability(win);
           }
         }
         break;
+      case "quit-application":
+        gDevToolsBrowser.destroy({ shuttingDown: true });
+        break;
+      case "sdk:loader:destroy":
+        // This event is fired when the devtools loader unloads, which happens
+        // only when the add-on workflow ask devtools to be reloaded.
+        if (subject.wrappedJSObject == require('@loader/unload')) {
+          gDevToolsBrowser.destroy({ shuttingDown: false });
+        }
+        break;
     }
   },
 
   _prefObserverRegistered: false,
 
   ensurePrefObserver: function () {
     if (!this._prefObserverRegistered) {
       this._prefObserverRegistered = true;
@@ -725,29 +734,38 @@ var gDevToolsBrowser = exports.gDevTools
     let tabStats = gDevToolsBrowser._tabStats;
     this._telemetry.log(TABS_OPEN_PEAK_HISTOGRAM, tabStats.peakOpen);
     this._telemetry.log(TABS_OPEN_AVG_HISTOGRAM, mean(tabStats.histOpen));
     this._telemetry.log(TABS_PINNED_PEAK_HISTOGRAM, tabStats.peakPinned);
     this._telemetry.log(TABS_PINNED_AVG_HISTOGRAM, mean(tabStats.histPinned));
   },
 
   /**
-   * All browser windows have been closed, tidy up remaining objects.
+   * Either the SDK Loader has been destroyed by the add-on contribution
+   * workflow, or firefox is shutting down.
+
+   * @param {boolean} shuttingDown
+   *        True if firefox is currently shutting down. We may prevent doing
+   *        some cleanups to speed it up. Otherwise everything need to be
+   *        cleaned up in order to be able to load devtools again.
    */
-  destroy: function () {
+  destroy: function ({ shuttingDown }) {
     Services.prefs.removeObserver("devtools.", gDevToolsBrowser);
     Services.obs.removeObserver(gDevToolsBrowser, "browser-delayed-startup-finished");
-    Services.obs.removeObserver(gDevToolsBrowser.destroy, "quit-application");
+    Services.obs.removeObserver(gDevToolsBrowser, "quit-application");
+    Services.obs.removeObserver(gDevToolsBrowser, "sdk:loader:destroy");
 
     gDevToolsBrowser._pingTelemetry();
     gDevToolsBrowser._telemetry = null;
 
     for (let win of gDevToolsBrowser._trackedBrowserWindows) {
       gDevToolsBrowser._forgetBrowserWindow(win);
     }
+
+    gDevTools.destroy({ shuttingDown });
   },
 };
 
 // Handle all already registered tools,
 gDevTools.getToolDefinitionArray()
          .forEach(def => gDevToolsBrowser._addToolToWindows(def));
 // and the new ones.
 gDevTools.on("tool-registered", function (ev, toolId) {
@@ -761,25 +779,22 @@ gDevTools.on("tool-registered", function
 
 gDevTools.on("tool-unregistered", function (ev, toolId) {
   gDevToolsBrowser._removeToolFromWindows(toolId);
 });
 
 gDevTools.on("toolbox-ready", gDevToolsBrowser._updateMenuCheckbox);
 gDevTools.on("toolbox-destroyed", gDevToolsBrowser._updateMenuCheckbox);
 
-Services.obs.addObserver(gDevToolsBrowser.destroy, "quit-application", false);
+Services.obs.addObserver(gDevToolsBrowser, "quit-application", false);
 Services.obs.addObserver(gDevToolsBrowser, "browser-delayed-startup-finished", false);
+// Watch for module loader unload. Fires when the tools are reloaded.
+Services.obs.addObserver(gDevToolsBrowser, "sdk:loader:destroy", 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._registerBrowserWindow(win);
   }
 }
-
-// Watch for module loader unload. Fires when the tools are reloaded.
-unload(function () {
-  gDevToolsBrowser.destroy();
-});
--- a/devtools/client/framework/devtools.js
+++ b/devtools/client/framework/devtools.js
@@ -13,43 +13,37 @@ loader.lazyRequireGetter(this, "Toolbox"
 loader.lazyRequireGetter(this, "ToolboxHostManager", "devtools/client/framework/toolbox-host-manager", true);
 loader.lazyRequireGetter(this, "gDevToolsBrowser", "devtools/client/framework/devtools-browser", true);
 
 const {defaultTools: DefaultTools, defaultThemes: DefaultThemes} =
   require("devtools/client/definitions");
 const EventEmitter = require("devtools/shared/event-emitter");
 const {JsonView} = require("devtools/client/jsonview/main");
 const AboutDevTools = require("devtools/client/framework/about-devtools-toolbox");
-const {when: unload} = require("sdk/system/unload");
 const {Task} = require("devtools/shared/task");
 
 const FORBIDDEN_IDS = new Set(["toolbox", ""]);
 const MAX_ORDINAL = 99;
 
 /**
  * DevTools is a class that represents a set of developer tools, it holds a
  * set of tools and keeps track of open toolboxes in the browser.
  */
 this.DevTools = function DevTools() {
   this._tools = new Map();     // Map<toolId, tool>
   this._themes = new Map();    // Map<themeId, theme>
   this._toolboxes = new Map(); // Map<target, toolbox>
 
-  // destroy() is an observer's handler so we need to preserve context.
-  this.destroy = this.destroy.bind(this);
-
   // JSON Viewer for 'application/json' documents.
   JsonView.initialize();
 
   AboutDevTools.register();
 
   EventEmitter.decorate(this);
 
-  Services.obs.addObserver(this.destroy, "quit-application", false);
-
   // This is important step in initialization codepath where we are going to
   // start registering all default tools and themes: create menuitems, keys, emit
   // related events.
   this.registerDefaults();
 };
 
 DevTools.prototype = {
   // The windowtype of the main window, used in various tools. This may be set
@@ -465,30 +459,33 @@ DevTools.prototype = {
     let toolbox = this._toolboxes.get(target);
     if (toolbox == null) {
       return promise.resolve(false);
     }
     return toolbox.destroy().then(() => true);
   },
 
   /**
-   * Called to tear down a tools provider.
+   * Either the SDK Loader has been destroyed by the add-on contribution
+   * workflow, or firefox is shutting down.
+
+   * @param {boolean} shuttingDown
+   *        True if firefox is currently shutting down. We may prevent doing
+   *        some cleanups to speed it up. Otherwise everything need to be
+   *        cleaned up in order to be able to load devtools again.
    */
-  _teardown: function DT_teardown() {
-    for (let [target, toolbox] of this._toolboxes) {
-      toolbox.destroy();
+  destroy: function ({ shuttingDown }) {
+    // Do not cleanup everything during firefox shutdown, but only when
+    // devtools are reloaded via the add-on contribution workflow.
+    if (!shuttingDown) {
+      for (let [target, toolbox] of this._toolboxes) {
+        toolbox.destroy();
+      }
+      AboutDevTools.unregister();
     }
-    AboutDevTools.unregister();
-  },
-
-  /**
-   * All browser windows have been closed, tidy up remaining objects.
-   */
-  destroy: function () {
-    Services.obs.removeObserver(this.destroy, "quit-application");
 
     for (let [key, tool] of this.getToolDefinitionMap()) {
       this.unregisterTool(key, true);
     }
 
     JsonView.destroy();
 
     gDevTools.unregisterDefaults();
@@ -504,13 +501,8 @@ DevTools.prototype = {
   *[Symbol.iterator ]() {
     for (let toolbox of this._toolboxes) {
       yield toolbox;
     }
   }
 };
 
 const gDevTools = exports.gDevTools = new DevTools();
-
-// Watch for module loader unload. Fires when the tools are reloaded.
-unload(function () {
-  gDevTools._teardown();
-});