Bug 1451795 - Move lazy devtools initialization helper into a static method of the ExtensionAPI. r=aswan
authorLuca Greco <lgreco@mozilla.com>
Wed, 11 Apr 2018 18:36:05 +0200
changeset 794493 6e55646aad4a8e3ff48167d60beb7c5a8b41be38
parent 794492 39c076bf5d77fa66c37d37e6fd6e94a68c493756
child 794494 17ed43c1c76bb9ccf7c274370bed9ce1bce8d0cf
push id109697
push userbmo:sledru@mozilla.com
push dateSat, 12 May 2018 10:04:34 +0000
reviewersaswan
bugs1451795
milestone62.0a1
Bug 1451795 - Move lazy devtools initialization helper into a static method of the ExtensionAPI. r=aswan MozReview-Commit-ID: KOxQcgsKXoB
browser/components/extensions/parent/ext-devtools.js
--- a/browser/components/extensions/parent/ext-devtools.js
+++ b/browser/components/extensions/parent/ext-devtools.js
@@ -12,20 +12,20 @@ ChromeUtils.defineModuleGetter(this, "De
 
 ChromeUtils.import("resource://gre/modules/ExtensionParent.jsm");
 
 var {
   HiddenExtensionPage,
   watchExtensionProxyContextLoad,
 } = ExtensionParent;
 
-// Map[extension -> DevToolsPageDefinition]
-let devtoolsPageDefinitionMap = new Map();
-
-let initDevTools;
+// Get the devtools preference given the extension id.
+function getDevToolsPrefBranchName(extensionId) {
+  return `devtools.webextensions.${extensionId}`;
+}
 
 /**
  * Retrieve the devtools target for the devtools extension proxy context
  * (lazily cloned from the target of the toolbox associated to the context
  * the first time that it is accessed).
  *
  * @param {DevToolsExtensionPageContextParent} context
  *   A devtools extension proxy context.
@@ -217,18 +217,16 @@ class DevToolsPage extends HiddenExtensi
  *
  * @param {Extension} extension
  *   The extension that owns the devtools_page.
  * @param {string}    url
  *   The path to the devtools page html page relative to the extension base URL.
  */
 class DevToolsPageDefinition {
   constructor(extension, url) {
-    initDevTools();
-
     this.url = url;
     this.extension = extension;
 
     // Map[TabTarget -> DevToolsPage]
     this.devtoolsPageForTarget = new Map();
   }
 
   onThemeChanged(themeName) {
@@ -270,117 +268,145 @@ class DevToolsPageDefinition {
       }
     }
   }
 
   forgetForTarget(target) {
     this.devtoolsPageForTarget.delete(target);
   }
 
+  /**
+   * Build the devtools_page instances for all the existing toolboxes
+   * (if the toolbox target is supported).
+   */
+  build() {
+    // Iterate over the existing toolboxes and create the devtools page for them
+    // (if the toolbox target is supported).
+    for (let toolbox of DevToolsShim.getToolboxes()) {
+      if (!toolbox.target.isLocalTab) {
+        // Skip any non-local tab.
+        continue;
+      }
+
+      // Ensure that the WebExtension is listed in the toolbox options.
+      toolbox.registerWebExtension(this.extension.uuid, {
+        name: this.extension.name,
+        pref: `${getDevToolsPrefBranchName(this.extension.id)}.enabled`,
+      });
+
+      this.buildForToolbox(toolbox);
+    }
+  }
+
+  /**
+   * Shutdown all the devtools_page instances.
+   */
   shutdown() {
     for (let target of this.devtoolsPageForTarget.keys()) {
       this.shutdownForTarget(target);
     }
 
     if (this.devtoolsPageForTarget.size > 0) {
       throw new Error(
         `Leaked ${this.devtoolsPageForTarget.size} DevToolsPage instances in devtoolsPageForTarget Map`
       );
     }
   }
 }
 
-// Get the devtools preference given the extension id.
-function getDevToolsPrefBranchName(extensionId) {
-  return `devtools.webextensions.${extensionId}`;
-}
-
-let devToolsInitialized = false;
-initDevTools = function() {
-  if (devToolsInitialized) {
-    return;
-  }
-
-  /* eslint-disable mozilla/balanced-listeners */
-  // Create a devtools page context for a new opened toolbox,
-  // based on the registered devtools_page definitions.
-  DevToolsShim.on("toolbox-created", toolbox => {
-    if (!toolbox.target.isLocalTab) {
-      // Only local tabs are currently supported (See Bug 1304378 for additional details
-      // related to remote targets support).
-      let msg = `Ignoring DevTools Toolbox for target "${toolbox.target.toString()}": ` +
-                `"${toolbox.target.name}" ("${toolbox.target.url}"). ` +
-                "Only local tab are currently supported by the WebExtensions DevTools API.";
-      let scriptError = Cc["@mozilla.org/scripterror;1"].createInstance(Ci.nsIScriptError);
-      scriptError.init(msg, null, null, null, null, Ci.nsIScriptError.warningFlag, "content javascript");
-      Services.console.logMessage(scriptError);
-
-      return;
-    }
-
-    for (let [extension, devtoolsPage] of devtoolsPageDefinitionMap) {
-      // Ensure that the WebExtension is listed in the toolbox options.
-      toolbox.registerWebExtension(extension.uuid, {
-        name: extension.name,
-        pref: `${getDevToolsPrefBranchName(extension.id)}.enabled`,
-      });
+this.devtools = class extends ExtensionAPI {
+  constructor(extension) {
+    super(extension);
 
-      // Do not build the devtools page if the extension has been disabled
-      // (e.g. based on the devtools preference).
-      if (!toolbox.isWebExtensionEnabled(extension.uuid)) {
-        continue;
-      }
-
-      devtoolsPage.buildForToolbox(toolbox);
-    }
-  });
-
-  // Destroy a devtools page context for a destroyed toolbox,
-  // based on the registered devtools_page definitions.
-  DevToolsShim.on("toolbox-destroy", target => {
-    if (!target.isLocalTab) {
-      // Only local tabs are currently supported (See Bug 1304378 for additional details
-      // related to remote targets support).
-      return;
-    }
+    // DevToolsPageDefinition instance (created in onManifestEntry).
+    this.pageDefinition = null;
 
-    for (let devtoolsPageDefinition of devtoolsPageDefinitionMap.values()) {
-      devtoolsPageDefinition.shutdownForTarget(target);
-    }
-  });
-  /* eslint-enable mozilla/balanced-listeners */
-
-  devToolsInitialized = true;
-};
-
-this.devtools = class extends ExtensionAPI {
-  onManifestEntry(entryName) {
-    this.initDevToolsPref();
-    this.createDevToolsPageDefinition();
-  }
-
-  onShutdown(reason) {
-    this.destroyDevToolsPageDefinition();
-    this.uninitDevToolsPref();
+    this.onToolboxCreated = this.onToolboxCreated.bind(this);
+    this.onToolboxDestroy = this.onToolboxDestroy.bind(this);
   }
 
   static onUninstall(extensionId) {
     // Remove the preference branch on uninstall.
     const prefBranch = Services.prefs.getBranch(
       `${getDevToolsPrefBranchName(extensionId)}.`);
 
     prefBranch.deleteBranch("");
   }
 
+  onManifestEntry(entryName) {
+    const {extension} = this;
+
+    this.initDevToolsPref();
+
+    // Create the devtools_page definition.
+    this.pageDefinition = new DevToolsPageDefinition(
+      extension, extension.manifest.devtools_page);
+
+    // Build the extension devtools_page on all existing toolboxes (if the extension
+    // devtools_page is not disabled by the related preference).
+    if (!this.isDevToolsPageDisabled()) {
+      this.pageDefinition.build();
+    }
+
+    DevToolsShim.on("toolbox-created", this.onToolboxCreated);
+    DevToolsShim.on("toolbox-destroy", this.onToolboxDestroy);
+  }
+
+  onShutdown(reason) {
+    DevToolsShim.off("toolbox-created", this.onToolboxCreated);
+    DevToolsShim.off("toolbox-destroy", this.onToolboxDestroy);
+
+    // Shutdown the extension devtools_page from all existing toolboxes.
+    this.pageDefinition.shutdown();
+    this.pageDefinition = null;
+
+    // Iterate over the existing toolboxes and unlist the devtools webextension from them.
+    for (let toolbox of DevToolsShim.getToolboxes()) {
+      toolbox.unregisterWebExtension(this.extension.uuid);
+    }
+
+    this.uninitDevToolsPref();
+  }
+
   getAPI(context) {
     return {
       devtools: {},
     };
   }
 
+  onToolboxCreated(toolbox) {
+    if (!toolbox.target.isLocalTab) {
+      // Only local tabs are currently supported (See Bug 1304378 for additional details
+      // related to remote targets support).
+      return;
+    }
+
+    // Ensure that the WebExtension is listed in the toolbox options.
+    toolbox.registerWebExtension(this.extension.uuid, {
+      name: this.extension.name,
+      pref: `${getDevToolsPrefBranchName(this.extension.id)}.enabled`,
+    });
+
+    // Do not build the devtools page if the extension has been disabled
+    // (e.g. based on the devtools preference).
+    if (toolbox.isWebExtensionEnabled(this.extension.uuid)) {
+      this.pageDefinition.buildForToolbox(toolbox);
+    }
+  }
+
+  onToolboxDestroy(target) {
+    if (!target.isLocalTab) {
+      // Only local tabs are currently supported (See Bug 1304378 for additional details
+      // related to remote targets support).
+      return;
+    }
+
+    this.pageDefinition.shutdownForTarget(target);
+  }
+
   /**
    * Initialize the DevTools preferences branch for the extension and
    * start to observe it for changes on the "enabled" preference.
    */
   initDevToolsPref() {
     const prefBranch = Services.prefs.getBranch(
       `${getDevToolsPrefBranchName(this.extension.id)}.`);
 
@@ -422,95 +448,16 @@ this.devtools = class extends ExtensionA
    */
   observe(subject, topic, prefName) {
     // We are currently interested only in the "enabled" preference from the
     // WebExtension devtools preferences branch.
     if (subject !== this.devtoolsPrefBranch || prefName !== "enabled") {
       return;
     }
 
+    // Shutdown or build the devtools_page on any existing toolbox.
     if (this.isDevToolsPageDisabled()) {
-      this.shutdownDevToolsPages();
+      this.pageDefinition.shutdown();
     } else {
-      this.buildDevToolsPages();
-    }
-  }
-
-  /**
-   * Create the devtools_page definition for the extension and build the devtools_page
-   * for any existing toolbox that is supported as a target (currentl only toolbox with
-   * a local tab as a target).
-   */
-  createDevToolsPageDefinition() {
-    let {extension} = this;
-    let {manifest} = extension;
-
-    if (devtoolsPageDefinitionMap.has(extension)) {
-      throw new Error("Cannot create an extension devtools page multiple times");
-    }
-
-    // Create and register a new devtools_page definition as specified in the
-    // "devtools_page" property in the extension manifest.
-    let devtoolsPageDefinition = new DevToolsPageDefinition(extension, manifest.devtools_page);
-    devtoolsPageDefinitionMap.set(extension, devtoolsPageDefinition);
-
-    this.buildDevToolsPages();
-  }
-
-  /**
-   * Destroy the devtools_page definition for the extension, shutdown any built
-   * devtools_page from all the existing toolbox and ensure that the extension is unlisted
-   * from the toolbox options panel if the extension is being disabled or uninstalled.
-   *
-   */
-  destroyDevToolsPageDefinition() {
-    this.shutdownDevToolsPages();
-
-    // Destroy the registered devtools_page definition on extension shutdown.
-    devtoolsPageDefinitionMap.delete(this.extension);
-
-    // Iterate over the existing toolboxes and unlist the devtools webextension from them.
-    for (let toolbox of DevToolsShim.getToolboxes()) {
-      toolbox.unregisterWebExtension(this.extension.uuid);
-    }
-  }
-
-  /**
-   * Build the devtools_page instances for the existing toolboxes (if its definition has been
-   * created and the toolbox target is supported)/
-   */
-  buildDevToolsPages() {
-    const devtoolsPageDefinition = devtoolsPageDefinitionMap.get(this.extension);
-    if (!devtoolsPageDefinition) {
-      return;
-    }
-
-    // Iterate over the existing toolboxes and create the devtools page for them
-    // (if the toolbox target is supported).
-    for (let toolbox of DevToolsShim.getToolboxes()) {
-      if (!toolbox.target.isLocalTab) {
-        // Skip any non-local tab.
-        continue;
-      }
-
-      // Ensure that the WebExtension is listed in the toolbox options.
-      toolbox.registerWebExtension(this.extension.uuid, {
-        name: this.extension.name,
-        pref: `${getDevToolsPrefBranchName(this.extension.id)}.enabled`,
-      });
-
-      devtoolsPageDefinition.buildForToolbox(toolbox);
-    }
-  }
-
-  /**
-   * Shutdown any existing devtools_page instances from the existing toolboxes
-   * (without destroying its definition, so that the devtools_page can be rebuilt
-   * when it is re-enabled by toggling the related DevTools preference).
-   */
-  shutdownDevToolsPages() {
-    const devtoolsPageDefinition = devtoolsPageDefinitionMap.get(this.extension);
-
-    if (devtoolsPageDefinition) {
-      devtoolsPageDefinition.shutdown();
+      this.pageDefinition.build();
     }
   }
 };