Bug 1387907: Store computed module data in the startup cache. r?mixedpuppy draft
authorKris Maglione <maglione.k@gmail.com>
Sun, 06 Aug 2017 18:04:28 -0700
changeset 641429 36e0186dbae57187eae72c844e21ddf4ea8758e5
parent 641403 2640f61d885fee9bed60e8c15b4df1b2cd4c6a31
child 641430 02cbf8020c6d333c71cc4b0ea351e20cf66754ce
push id72512
push usermaglione.k@gmail.com
push dateMon, 07 Aug 2017 01:04:48 +0000
reviewersmixedpuppy
bugs1387907
milestone57.0a1
Bug 1387907: Store computed module data in the startup cache. r?mixedpuppy MozReview-Commit-ID: 4PV4z870GTb
browser/components/extensions/ext-browser.js
browser/components/extensions/ext-browser.json
browser/components/extensions/extensions-browser.manifest
browser/components/extensions/jar.mn
toolkit/components/extensions/ExtensionCommon.jsm
toolkit/components/extensions/ExtensionParent.jsm
toolkit/components/extensions/ext-toolkit.js
toolkit/components/extensions/ext-toolkit.json
toolkit/components/extensions/extensions-toolkit.manifest
toolkit/components/extensions/jar.mn
--- a/browser/components/extensions/ext-browser.js
+++ b/browser/components/extensions/ext-browser.js
@@ -69,170 +69,8 @@ global.openOptionsPage = (extension) => 
     });
     return Promise.resolve();
   }
 
   let viewId = `addons://detail/${encodeURIComponent(extension.id)}/preferences`;
 
   return window.BrowserOpenAddonsMgr(viewId);
 };
-
-extensions.registerModules({
-  bookmarks: {
-    url: "chrome://browser/content/ext-bookmarks.js",
-    schema: "chrome://browser/content/schemas/bookmarks.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["bookmarks"],
-    ],
-  },
-  browserAction: {
-    url: "chrome://browser/content/ext-browserAction.js",
-    schema: "chrome://browser/content/schemas/browser_action.json",
-    scopes: ["addon_parent"],
-    manifest: ["browser_action"],
-    paths: [
-      ["browserAction"],
-    ],
-  },
-  browsingData: {
-    url: "chrome://browser/content/ext-browsingData.js",
-    schema: "chrome://browser/content/schemas/browsing_data.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["browsingData"],
-    ],
-  },
-  chrome_settings_overrides: {
-    url: "chrome://browser/content/ext-chrome-settings-overrides.js",
-    scopes: [],
-    schema: "chrome://browser/content/schemas/chrome_settings_overrides.json",
-    manifest: ["chrome_settings_overrides"],
-  },
-  commands: {
-    url: "chrome://browser/content/ext-commands.js",
-    schema: "chrome://browser/content/schemas/commands.json",
-    scopes: ["addon_parent"],
-    manifest: ["commands"],
-    paths: [
-      ["commands"],
-    ],
-  },
-  devtools: {
-    url: "chrome://browser/content/ext-devtools.js",
-    schema: "chrome://browser/content/schemas/devtools.json",
-    scopes: ["devtools_parent"],
-    manifest: ["devtools_page"],
-    paths: [
-      ["devtools"],
-    ],
-  },
-  devtools_inspectedWindow: {
-    url: "chrome://browser/content/ext-devtools-inspectedWindow.js",
-    schema: "chrome://browser/content/schemas/devtools_inspected_window.json",
-    scopes: ["devtools_parent"],
-    paths: [
-      ["devtools", "inspectedWindow"],
-    ],
-  },
-  devtools_network: {
-    url: "chrome://browser/content/ext-devtools-network.js",
-    schema: "chrome://browser/content/schemas/devtools_network.json",
-    scopes: ["devtools_parent"],
-    paths: [
-      ["devtools", "network"],
-    ],
-  },
-  devtools_panels: {
-    url: "chrome://browser/content/ext-devtools-panels.js",
-    schema: "chrome://browser/content/schemas/devtools_panels.json",
-    scopes: ["devtools_parent"],
-    paths: [
-      ["devtools", "panels"],
-    ],
-  },
-  history: {
-    url: "chrome://browser/content/ext-history.js",
-    schema: "chrome://browser/content/schemas/history.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["history"],
-    ],
-  },
-  // This module supports the "menus" and "contextMenus" namespaces,
-  // and because of permissions, the module name must differ from both.
-  menusInternal: {
-    url: "chrome://browser/content/ext-menus.js",
-    schema: "chrome://browser/content/schemas/menus.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["menusInternal"],
-    ],
-  },
-  omnibox: {
-    url: "chrome://browser/content/ext-omnibox.js",
-    schema: "chrome://browser/content/schemas/omnibox.json",
-    scopes: ["addon_parent"],
-    manifest: ["omnibox"],
-    paths: [
-      ["omnibox"],
-    ],
-  },
-  pageAction: {
-    url: "chrome://browser/content/ext-pageAction.js",
-    schema: "chrome://browser/content/schemas/page_action.json",
-    scopes: ["addon_parent"],
-    manifest: ["page_action"],
-    paths: [
-      ["pageAction"],
-    ],
-  },
-  geckoProfiler: {
-    url: "chrome://browser/content/ext-geckoProfiler.js",
-    schema: "chrome://browser/content/schemas/geckoProfiler.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["geckoProfiler"],
-    ],
-  },
-  sessions: {
-    url: "chrome://browser/content/ext-sessions.js",
-    schema: "chrome://browser/content/schemas/sessions.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["sessions"],
-    ],
-  },
-  sidebarAction: {
-    url: "chrome://browser/content/ext-sidebarAction.js",
-    schema: "chrome://browser/content/schemas/sidebar_action.json",
-    scopes: ["addon_parent"],
-    manifest: ["sidebar_action"],
-    paths: [
-      ["sidebarAction"],
-    ],
-  },
-  tabs: {
-    url: "chrome://browser/content/ext-tabs.js",
-    schema: "chrome://browser/content/schemas/tabs.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["tabs"],
-    ],
-  },
-  urlOverrides: {
-    url: "chrome://browser/content/ext-url-overrides.js",
-    schema: "chrome://browser/content/schemas/url_overrides.json",
-    scopes: ["addon_parent"],
-    manifest: ["chrome_url_overrides"],
-    paths: [
-      ["urlOverrides"],
-    ],
-  },
-  windows: {
-    url: "chrome://browser/content/ext-windows.js",
-    schema: "chrome://browser/content/schemas/windows.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["windows"],
-    ],
-  },
-});
copy from browser/components/extensions/ext-browser.js
copy to browser/components/extensions/ext-browser.json
--- a/browser/components/extensions/ext-browser.js
+++ b/browser/components/extensions/ext-browser.json
@@ -1,238 +1,159 @@
-"use strict";
-
-// The ext-* files are imported into the same scopes.
-/* import-globals-from ext-utils.js */
-
-global.EventEmitter = ExtensionUtils.EventEmitter;
-
-// This function is pretty tightly tied to Extension.jsm.
-// Its job is to fill in the |tab| property of the sender.
-const getSender = (extension, target, sender) => {
-  let tabId;
-  if ("tabId" in sender) {
-    // The message came from a privileged extension page running in a tab. In
-    // that case, it should include a tabId property (which is filled in by the
-    // page-open listener below).
-    tabId = sender.tabId;
-    delete sender.tabId;
-  } else if (target instanceof Ci.nsIDOMXULElement) {
-    tabId = tabTracker.getBrowserData(target).tabId;
-  }
-
-  if (tabId) {
-    let tab = extension.tabManager.get(tabId, null);
-    if (tab) {
-      sender.tab = tab.convert();
-    }
-  }
-};
-
-// Used by Extension.jsm
-global.tabGetSender = getSender;
-
-/* eslint-disable mozilla/balanced-listeners */
-extensions.on("uninstall", (msg, extension) => {
-  if (extension.uninstallURL) {
-    let browser = windowTracker.topWindow.gBrowser;
-    browser.addTab(extension.uninstallURL, {relatedToCurrent: true});
-  }
-});
-
-extensions.on("page-shutdown", (type, context) => {
-  if (context.viewType == "tab") {
-    if (context.extension.id !== context.xulBrowser.contentPrincipal.addonId) {
-      // Only close extension tabs.
-      // This check prevents about:addons from closing when it contains a
-      // WebExtension as an embedded inline options page.
-      return;
-    }
-    let {gBrowser} = context.xulBrowser.ownerGlobal;
-    if (gBrowser) {
-      let nativeTab = gBrowser.getTabForBrowser(context.xulBrowser);
-      if (nativeTab) {
-        gBrowser.removeTab(nativeTab);
-      }
-    }
-  }
-});
-/* eslint-enable mozilla/balanced-listeners */
-
-global.openOptionsPage = (extension) => {
-  let window = windowTracker.topWindow;
-  if (!window) {
-    return Promise.reject({message: "No browser window available"});
-  }
-
-  if (extension.manifest.options_ui.open_in_tab) {
-    window.switchToTabHavingURI(extension.manifest.options_ui.page, true, {
-      triggeringPrincipal: extension.principal,
-    });
-    return Promise.resolve();
-  }
-
-  let viewId = `addons://detail/${encodeURIComponent(extension.id)}/preferences`;
-
-  return window.BrowserOpenAddonsMgr(viewId);
-};
-
-extensions.registerModules({
-  bookmarks: {
-    url: "chrome://browser/content/ext-bookmarks.js",
-    schema: "chrome://browser/content/schemas/bookmarks.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["bookmarks"],
-    ],
+{
+  "bookmarks": {
+    "url": "chrome://browser/content/ext-bookmarks.js",
+    "schema": "chrome://browser/content/schemas/bookmarks.json",
+    "scopes": ["addon_parent"],
+    "paths": [
+      ["bookmarks"]
+    ]
+  },
+  "browserAction": {
+    "url": "chrome://browser/content/ext-browserAction.js",
+    "schema": "chrome://browser/content/schemas/browser_action.json",
+    "scopes": ["addon_parent"],
+    "manifest": ["browser_action"],
+    "paths": [
+      ["browserAction"]
+    ]
+  },
+  "browsingData": {
+    "url": "chrome://browser/content/ext-browsingData.js",
+    "schema": "chrome://browser/content/schemas/browsing_data.json",
+    "scopes": ["addon_parent"],
+    "paths": [
+      ["browsingData"]
+    ]
+  },
+  "chrome_settings_overrides": {
+    "url": "chrome://browser/content/ext-chrome-settings-overrides.js",
+    "scopes": [],
+    "schema": "chrome://browser/content/schemas/chrome_settings_overrides.json",
+    "manifest": ["chrome_settings_overrides"]
   },
-  browserAction: {
-    url: "chrome://browser/content/ext-browserAction.js",
-    schema: "chrome://browser/content/schemas/browser_action.json",
-    scopes: ["addon_parent"],
-    manifest: ["browser_action"],
-    paths: [
-      ["browserAction"],
-    ],
+  "commands": {
+    "url": "chrome://browser/content/ext-commands.js",
+    "schema": "chrome://browser/content/schemas/commands.json",
+    "scopes": ["addon_parent"],
+    "manifest": ["commands"],
+    "paths": [
+      ["commands"]
+    ]
+  },
+  "devtools": {
+    "url": "chrome://browser/content/ext-devtools.js",
+    "schema": "chrome://browser/content/schemas/devtools.json",
+    "scopes": ["devtools_parent"],
+    "manifest": ["devtools_page"],
+    "paths": [
+      ["devtools"]
+    ]
   },
-  browsingData: {
-    url: "chrome://browser/content/ext-browsingData.js",
-    schema: "chrome://browser/content/schemas/browsing_data.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["browsingData"],
-    ],
+  "devtools_inspectedWindow": {
+    "url": "chrome://browser/content/ext-devtools-inspectedWindow.js",
+    "schema": "chrome://browser/content/schemas/devtools_inspected_window.json",
+    "scopes": ["devtools_parent"],
+    "paths": [
+      ["devtools", "inspectedWindow"]
+    ]
   },
-  chrome_settings_overrides: {
-    url: "chrome://browser/content/ext-chrome-settings-overrides.js",
-    scopes: [],
-    schema: "chrome://browser/content/schemas/chrome_settings_overrides.json",
-    manifest: ["chrome_settings_overrides"],
+  "devtools_network": {
+    "url": "chrome://browser/content/ext-devtools-network.js",
+    "schema": "chrome://browser/content/schemas/devtools_network.json",
+    "scopes": ["devtools_parent"],
+    "paths": [
+      ["devtools", "network"]
+    ]
   },
-  commands: {
-    url: "chrome://browser/content/ext-commands.js",
-    schema: "chrome://browser/content/schemas/commands.json",
-    scopes: ["addon_parent"],
-    manifest: ["commands"],
-    paths: [
-      ["commands"],
-    ],
+  "devtools_panels": {
+    "url": "chrome://browser/content/ext-devtools-panels.js",
+    "schema": "chrome://browser/content/schemas/devtools_panels.json",
+    "scopes": ["devtools_parent"],
+    "paths": [
+      ["devtools", "panels"]
+    ]
   },
-  devtools: {
-    url: "chrome://browser/content/ext-devtools.js",
-    schema: "chrome://browser/content/schemas/devtools.json",
-    scopes: ["devtools_parent"],
-    manifest: ["devtools_page"],
-    paths: [
-      ["devtools"],
-    ],
+  "history": {
+    "url": "chrome://browser/content/ext-history.js",
+    "schema": "chrome://browser/content/schemas/history.json",
+    "scopes": ["addon_parent"],
+    "paths": [
+      ["history"]
+    ]
   },
-  devtools_inspectedWindow: {
-    url: "chrome://browser/content/ext-devtools-inspectedWindow.js",
-    schema: "chrome://browser/content/schemas/devtools_inspected_window.json",
-    scopes: ["devtools_parent"],
-    paths: [
-      ["devtools", "inspectedWindow"],
-    ],
-  },
-  devtools_network: {
-    url: "chrome://browser/content/ext-devtools-network.js",
-    schema: "chrome://browser/content/schemas/devtools_network.json",
-    scopes: ["devtools_parent"],
-    paths: [
-      ["devtools", "network"],
-    ],
+  "menusInternal": {
+    "url": "chrome://browser/content/ext-menus.js",
+    "schema": "chrome://browser/content/schemas/menus.json",
+    "scopes": ["addon_parent"],
+    "paths": [
+      ["menusInternal"]
+    ]
   },
-  devtools_panels: {
-    url: "chrome://browser/content/ext-devtools-panels.js",
-    schema: "chrome://browser/content/schemas/devtools_panels.json",
-    scopes: ["devtools_parent"],
-    paths: [
-      ["devtools", "panels"],
-    ],
-  },
-  history: {
-    url: "chrome://browser/content/ext-history.js",
-    schema: "chrome://browser/content/schemas/history.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["history"],
-    ],
+  "omnibox": {
+    "url": "chrome://browser/content/ext-omnibox.js",
+    "schema": "chrome://browser/content/schemas/omnibox.json",
+    "scopes": ["addon_parent"],
+    "manifest": ["omnibox"],
+    "paths": [
+      ["omnibox"]
+    ]
   },
-  // This module supports the "menus" and "contextMenus" namespaces,
-  // and because of permissions, the module name must differ from both.
-  menusInternal: {
-    url: "chrome://browser/content/ext-menus.js",
-    schema: "chrome://browser/content/schemas/menus.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["menusInternal"],
-    ],
+  "pageAction": {
+    "url": "chrome://browser/content/ext-pageAction.js",
+    "schema": "chrome://browser/content/schemas/page_action.json",
+    "scopes": ["addon_parent"],
+    "manifest": ["page_action"],
+    "paths": [
+      ["pageAction"]
+    ]
   },
-  omnibox: {
-    url: "chrome://browser/content/ext-omnibox.js",
-    schema: "chrome://browser/content/schemas/omnibox.json",
-    scopes: ["addon_parent"],
-    manifest: ["omnibox"],
-    paths: [
-      ["omnibox"],
-    ],
+  "geckoProfiler": {
+    "url": "chrome://browser/content/ext-geckoProfiler.js",
+    "schema": "chrome://browser/content/schemas/geckoProfiler.json",
+    "scopes": ["addon_parent"],
+    "paths": [
+      ["geckoProfiler"]
+    ]
   },
-  pageAction: {
-    url: "chrome://browser/content/ext-pageAction.js",
-    schema: "chrome://browser/content/schemas/page_action.json",
-    scopes: ["addon_parent"],
-    manifest: ["page_action"],
-    paths: [
-      ["pageAction"],
-    ],
+  "sessions": {
+    "url": "chrome://browser/content/ext-sessions.js",
+    "schema": "chrome://browser/content/schemas/sessions.json",
+    "scopes": ["addon_parent"],
+    "paths": [
+      ["sessions"]
+    ]
   },
-  geckoProfiler: {
-    url: "chrome://browser/content/ext-geckoProfiler.js",
-    schema: "chrome://browser/content/schemas/geckoProfiler.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["geckoProfiler"],
-    ],
-  },
-  sessions: {
-    url: "chrome://browser/content/ext-sessions.js",
-    schema: "chrome://browser/content/schemas/sessions.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["sessions"],
-    ],
+  "sidebarAction": {
+    "url": "chrome://browser/content/ext-sidebarAction.js",
+    "schema": "chrome://browser/content/schemas/sidebar_action.json",
+    "scopes": ["addon_parent"],
+    "manifest": ["sidebar_action"],
+    "paths": [
+      ["sidebarAction"]
+    ]
   },
-  sidebarAction: {
-    url: "chrome://browser/content/ext-sidebarAction.js",
-    schema: "chrome://browser/content/schemas/sidebar_action.json",
-    scopes: ["addon_parent"],
-    manifest: ["sidebar_action"],
-    paths: [
-      ["sidebarAction"],
-    ],
-  },
-  tabs: {
-    url: "chrome://browser/content/ext-tabs.js",
-    schema: "chrome://browser/content/schemas/tabs.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["tabs"],
-    ],
+  "tabs": {
+    "url": "chrome://browser/content/ext-tabs.js",
+    "schema": "chrome://browser/content/schemas/tabs.json",
+    "scopes": ["addon_parent"],
+    "paths": [
+      ["tabs"]
+    ]
   },
-  urlOverrides: {
-    url: "chrome://browser/content/ext-url-overrides.js",
-    schema: "chrome://browser/content/schemas/url_overrides.json",
-    scopes: ["addon_parent"],
-    manifest: ["chrome_url_overrides"],
-    paths: [
-      ["urlOverrides"],
-    ],
+  "urlOverrides": {
+    "url": "chrome://browser/content/ext-url-overrides.js",
+    "schema": "chrome://browser/content/schemas/url_overrides.json",
+    "scopes": ["addon_parent"],
+    "manifest": ["chrome_url_overrides"],
+    "paths": [
+      ["urlOverrides"]
+    ]
   },
-  windows: {
-    url: "chrome://browser/content/ext-windows.js",
-    schema: "chrome://browser/content/schemas/windows.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["windows"],
-    ],
-  },
-});
+  "windows": {
+    "url": "chrome://browser/content/ext-windows.js",
+    "schema": "chrome://browser/content/schemas/windows.json",
+    "scopes": ["addon_parent"],
+    "paths": [
+      ["windows"]
+    ]
+  }
+}
--- a/browser/components/extensions/extensions-browser.manifest
+++ b/browser/components/extensions/extensions-browser.manifest
@@ -1,6 +1,8 @@
+category webextension-modules browser chrome://browser/content/ext-browser.json
+
 category webextension-scripts browser chrome://browser/content/ext-browser.js
 category webextension-scripts utils chrome://browser/content/ext-utils.js
 category webextension-scripts-devtools browser chrome://browser/content/ext-c-browser.js
 category webextension-scripts-addon browser chrome://browser/content/ext-c-browser.js
 
 category webextension-schemas menus_internal chrome://browser/content/schemas/menus_internal.json
--- a/browser/components/extensions/jar.mn
+++ b/browser/components/extensions/jar.mn
@@ -9,16 +9,17 @@ browser.jar:
     content/browser/extension-mac-panel.css
 #endif
 #ifdef XP_WIN
     content/browser/extension-win-panel.css
 #endif
     content/browser/extension.svg
     content/browser/ext-bookmarks.js
     content/browser/ext-browser.js
+    content/browser/ext-browser.json
     content/browser/ext-browserAction.js
     content/browser/ext-browsingData.js
     content/browser/ext-chrome-settings-overrides.js
     content/browser/ext-commands.js
     content/browser/ext-devtools.js
     content/browser/ext-devtools-inspectedWindow.js
     content/browser/ext-devtools-network.js
     content/browser/ext-devtools-panels.js
--- a/toolkit/components/extensions/ExtensionCommon.jsm
+++ b/toolkit/components/extensions/ExtensionCommon.jsm
@@ -10,16 +10,18 @@
  */
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 /* exported ExtensionCommon */
 
 this.EXPORTED_SYMBOLS = ["ExtensionCommon"];
 
+Cu.importGlobalProperties(["fetch"]);
+
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "MessageChannel",
                                   "resource://gre/modules/MessageChannel.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
                                   "resource://gre/modules/Preferences.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
@@ -613,16 +615,36 @@ function deepCopy(dest, source) {
       }
       deepCopy(dest[prop], source[prop]);
     } else {
       Object.defineProperty(dest, prop, desc);
     }
   }
 }
 
+function getChild(map, key) {
+  let child = map.children.get(key);
+  if (!child) {
+    child = {
+      modules: new Set(),
+      children: new Map(),
+    };
+
+    map.children.set(key, child);
+  }
+  return child;
+}
+
+function getPath(map, path) {
+  for (let key of path) {
+    map = getChild(map, key);
+  }
+  return map;
+}
+
 /**
  * Manages loading and accessing a set of APIs for a specific extension
  * context.
  *
  * @param {BaseContext} context
  *        The context to manage APIs for.
  * @param {SchemaAPIManager} apiManager
  *        The API manager holding the APIs to manage.
@@ -707,17 +729,17 @@ class CanOfAPIs {
 
     let obj = this.root;
     let modules = this.apiManager.modulePaths;
 
     for (let key of path.split(".")) {
       if (!obj) {
         return;
       }
-      modules = modules.get(key);
+      modules = getChild(modules, key);
 
       for (let name of modules.modules) {
         if (!this.apis.has(name)) {
           this.loadAPI(name);
         }
       }
 
       obj = obj[key];
@@ -743,17 +765,17 @@ class CanOfAPIs {
 
     let obj = this.root;
     let modules = this.apiManager.modulePaths;
 
     for (let key of path.split(".")) {
       if (!obj) {
         return;
       }
-      modules = modules.get(key);
+      modules = getChild(modules, key);
 
       for (let name of modules.modules) {
         if (!this.apis.has(name)) {
           await this.asyncLoadAPI(name);
         }
       }
 
       if (typeof obj[key] === "function") {
@@ -763,28 +785,16 @@ class CanOfAPIs {
       }
     }
 
     this.apiPaths.set(path, obj);
     return obj;
   }
 }
 
-class DeepMap extends DefaultMap {
-  constructor() {
-    super(() => new DeepMap());
-
-    this.modules = new Set();
-  }
-
-  getPath(path) {
-    return path.reduce((map, key) => map.get(key), this);
-  }
-}
-
 /**
  * @class APIModule
  * @abstract
  *
  * @property {string} url
  *       The URL of the script which contains the module's
  *       implementation. This script must define a global property
  *       matching the modules name, which must be a class constructor
@@ -827,27 +837,64 @@ class SchemaAPIManager extends EventEmit
    *     "proxy" - A proxy script process.
    */
   constructor(processType) {
     super();
     this.processType = processType;
     this.global = this._createExtGlobal();
 
     this.modules = new Map();
-    this.modulePaths = new DeepMap();
+    this.modulePaths = {children: new Map(), modules: new Set()};
     this.manifestKeys = new Map();
     this.eventModules = new DefaultMap(() => new Set());
 
+    this._modulesJSONLoaded = false;
+
     this.schemaURLs = new Set();
 
     this.apis = new DefaultWeakMap(() => new Map());
 
     this._scriptScopes = [];
   }
 
+  async loadModuleJSON(urls) {
+    function fetchJSON(url) {
+      return fetch(url).then(resp => resp.json());
+    }
+
+    for (let json of await Promise.all(urls.map(fetchJSON))) {
+      this.registerModules(json);
+    }
+
+    this._modulesJSONLoaded = true;
+
+    return new StructuredCloneHolder({
+      modules: this.modules,
+      modulePaths: this.modulePaths,
+      manifestKeys: this.manifestKeys,
+      eventModules: this.eventModules,
+      schemaURLs: this.schemaURLs,
+    });
+  }
+
+  initModuleData(moduleData) {
+    if (!this._modulesJSONLoaded) {
+      let data = moduleData.deserialize({});
+
+      this.modules = data.modules;
+      this.modulePaths = data.modulePaths;
+      this.manifestKeys = data.manifestKeys;
+      this.eventModules = new DefaultMap(() => new Set(),
+                                         data.eventModules);
+      this.schemaURLs = data.schemaURLs;
+    }
+
+    this._modulesJSONLoaded = true;
+  }
+
   /**
    * Registers a set of ExtensionAPI modules to be lazily loaded and
    * managed by this manager.
    *
    * @param {object} obj
    *        An object containing property for eacy API module to be
    *        registered. Each value should be an object implementing the
    *        APIModule interface.
@@ -873,17 +920,17 @@ class SchemaAPIManager extends EventEmit
         if (this.manifestKeys.has(key)) {
           throw new Error(`Manifest key '${key}' already registered by '${this.manifestKeys.get(key)}'`);
         }
 
         this.manifestKeys.set(key, name);
       }
 
       for (let path of details.paths || []) {
-        this.modulePaths.getPath(path).modules.add(name);
+        getPath(this.modulePaths, path).modules.add(name);
       }
     }
   }
 
   /**
    * Emits an `onManifestEntry` event for the top-level manifest entry
    * on all relevant {@link ExtensionAPI} instances for the given
    * extension.
--- a/toolkit/components/extensions/ExtensionParent.jsm
+++ b/toolkit/components/extensions/ExtensionParent.jsm
@@ -59,26 +59,28 @@ var {
   defineLazyGetter,
   promiseDocumentLoaded,
   promiseEvent,
   promiseFileContents,
   promiseObserved,
 } = ExtensionUtils;
 
 const BASE_SCHEMA = "chrome://extensions/content/schemas/manifest.json";
+const CATEGORY_EXTENSION_MODULES = "webextension-modules";
 const CATEGORY_EXTENSION_SCHEMAS = "webextension-schemas";
 const CATEGORY_EXTENSION_SCRIPTS = "webextension-scripts";
 
 let schemaURLs = new Set();
 
 schemaURLs.add("chrome://extensions/content/schemas/experiments.json");
 
 let GlobalManager;
 let ParentAPIManager;
 let ProxyMessenger;
+let StartupCache;
 
 // This object loads the ext-*.js scripts that define the extension API.
 let apiManager = new class extends SchemaAPIManager {
   constructor() {
     super("main");
     this.initialized = null;
 
     this.on("startup", (event, extension) => { // eslint-disable-line mozilla/balanced-listeners
@@ -88,28 +90,41 @@ let apiManager = new class extends Schem
           api.onStartup(extension.startupReason);
         }));
       }
 
       return Promise.all(promises);
     });
   }
 
+  getModuleJSONURLs() {
+    return Array.from(XPCOMUtils.enumerateCategoryEntries(CATEGORY_EXTENSION_MODULES),
+                      ([name, url]) => url);
+  }
+
   // Loads all the ext-*.js scripts currently registered.
   lazyInit() {
     if (this.initialized) {
       return this.initialized;
     }
 
-    let scripts = [];
+    let modulesPromise = StartupCache.other.get(
+      ["parentModules"],
+      () => this.loadModuleJSON(this.getModuleJSONURLs()));
+
+    let scriptURLs = [];
     for (let [/* name */, value] of XPCOMUtils.enumerateCategoryEntries(CATEGORY_EXTENSION_SCRIPTS)) {
-      scripts.push(value);
+      scriptURLs.push(value);
     }
 
-    let promise = Promise.all(scripts.map(url => ChromeUtils.compileScript(url))).then(scripts => {
+    let promise = (async () => {
+      let scripts = await Promise.all(scriptURLs.map(url => ChromeUtils.compileScript(url)));
+
+      this.initModuleData(await modulesPromise);
+
       for (let script of scripts) {
         script.executeInGlobal(this.global);
       }
 
       // Load order matters here. The base manifest defines types which are
       // extended by other schemas, so needs to be loaded first.
       return Schemas.load(BASE_SCHEMA).then(() => {
         let promises = [];
@@ -119,17 +134,17 @@ let apiManager = new class extends Schem
         for (let url of this.schemaURLs) {
           promises.push(Schemas.load(url));
         }
         for (let url of schemaURLs) {
           promises.push(Schemas.load(url));
         }
         return Promise.all(promises);
       });
-    });
+    })();
 
     /* eslint-disable mozilla/balanced-listeners */
     Services.mm.addMessageListener("Extension:GetTabAndWindowId", this);
     /* eslint-enable mozilla/balanced-listeners */
 
     this.initialized = promise;
     return this.initialized;
   }
@@ -1356,20 +1371,20 @@ let IconDetails = {
   // These URLs should already be properly escaped, but make doubly sure CSS
   // string escape characters are escaped here, since they could lead to a
   // sandbox break.
   escapeUrl(url) {
     return url.replace(/[\\\s"]/g, encodeURIComponent);
   },
 };
 
-let StartupCache = {
+StartupCache = {
   DB_NAME: "ExtensionStartupCache",
 
-  STORE_NAMES: Object.freeze(["general", "locales", "manifests", "permissions", "schemas"]),
+  STORE_NAMES: Object.freeze(["general", "locales", "manifests", "other", "permissions", "schemas"]),
 
   get file() {
     return FileUtils.getFile("ProfLD", ["startupCache", "webext.sc.lz4"]);
   },
 
   get saver() {
     if (!this._saver) {
       this._saver = new DeferredSave(this.file.path,
--- a/toolkit/components/extensions/ext-toolkit.js
+++ b/toolkit/components/extensions/ext-toolkit.js
@@ -73,195 +73,16 @@ global.getContainerForCookieStoreId = fu
 };
 
 global.isValidCookieStoreId = function(storeId) {
   return isDefaultCookieStoreId(storeId) ||
          isPrivateCookieStoreId(storeId) ||
          isContainerCookieStoreId(storeId);
 };
 
-extensions.registerModules({
-  manifest: {
-    schema: "chrome://extensions/content/schemas/extension_types.json",
-    scopes: [],
-  },
-  alarms: {
-    url: "chrome://extensions/content/ext-alarms.js",
-    schema: "chrome://extensions/content/schemas/alarms.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["alarms"],
-    ],
-  },
-  backgroundPage: {
-    url: "chrome://extensions/content/ext-backgroundPage.js",
-    scopes: ["addon_parent"],
-    manifest: ["background"],
-  },
-  browserSettings: {
-    url: "chrome://extensions/content/ext-browserSettings.js",
-    schema: "chrome://extensions/content/schemas/browser_settings.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["browserSettings"],
-    ],
-  },
-  contextualIdentities: {
-    url: "chrome://extensions/content/ext-contextualIdentities.js",
-    schema: "chrome://extensions/content/schemas/contextual_identities.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["contextualIdentities"],
-    ],
-  },
-  cookies: {
-    url: "chrome://extensions/content/ext-cookies.js",
-    schema: "chrome://extensions/content/schemas/cookies.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["cookies"],
-    ],
-  },
-  downloads: {
-    url: "chrome://extensions/content/ext-downloads.js",
-    schema: "chrome://extensions/content/schemas/downloads.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["downloads"],
-    ],
-  },
-  extension: {
-    url: "chrome://extensions/content/ext-extension.js",
-    schema: "chrome://extensions/content/schemas/extension.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["extension"],
-    ],
-  },
-  geolocation: {
-    url: "chrome://extensions/content/ext-geolocation.js",
-    events: ["startup"],
-  },
-  i18n: {
-    url: "chrome://extensions/content/ext-i18n.js",
-    schema: "chrome://extensions/content/schemas/i18n.json",
-    scopes: ["addon_parent", "content_child", "devtools_child"],
-    paths: [
-      ["i18n"],
-    ],
-  },
-  idle: {
-    url: "chrome://extensions/content/ext-idle.js",
-    schema: "chrome://extensions/content/schemas/idle.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["idle"],
-    ],
-  },
-  management: {
-    url: "chrome://extensions/content/ext-management.js",
-    schema: "chrome://extensions/content/schemas/management.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["management"],
-    ],
-  },
-  notifications: {
-    url: "chrome://extensions/content/ext-notifications.js",
-    schema: "chrome://extensions/content/schemas/notifications.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["notifications"],
-    ],
-  },
-  permissions: {
-    url: "chrome://extensions/content/ext-permissions.js",
-    schema: "chrome://extensions/content/schemas/permissions.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["permissions"],
-    ],
-  },
-  privacy: {
-    url: "chrome://extensions/content/ext-privacy.js",
-    schema: "chrome://extensions/content/schemas/privacy.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["privacy"],
-    ],
-  },
-  protocolHandlers: {
-    url: "chrome://extensions/content/ext-protocolHandlers.js",
-    schema: "chrome://extensions/content/schemas/extension_protocol_handlers.json",
-    scopes: ["addon_parent"],
-    manifest: ["protocol_handlers"],
-  },
-  proxy: {
-    url: "chrome://extensions/content/ext-proxy.js",
-    schema: "chrome://extensions/content/schemas/proxy.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["proxy"],
-    ],
-  },
-  runtime: {
-    url: "chrome://extensions/content/ext-runtime.js",
-    schema: "chrome://extensions/content/schemas/runtime.json",
-    scopes: ["addon_parent", "content_parent", "devtools_parent"],
-    paths: [
-      ["runtime"],
-    ],
-  },
-  storage: {
-    url: "chrome://extensions/content/ext-storage.js",
-    schema: "chrome://extensions/content/schemas/storage.json",
-    scopes: ["addon_parent", "content_parent", "devtools_parent"],
-    paths: [
-      ["storage"],
-    ],
-  },
-  test: {
-    schema: "chrome://extensions/content/schemas/test.json",
-    scopes: [],
-  },
-  theme: {
-    url: "chrome://extensions/content/ext-theme.js",
-    schema: "chrome://extensions/content/schemas/theme.json",
-    scopes: ["addon_parent"],
-    manifest: ["theme"],
-    paths: [
-      ["theme"],
-    ],
-  },
-  topSites: {
-    url: "chrome://extensions/content/ext-topSites.js",
-    schema: "chrome://extensions/content/schemas/top_sites.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["topSites"],
-    ],
-  },
-  webNavigation: {
-    url: "chrome://extensions/content/ext-webNavigation.js",
-    schema: "chrome://extensions/content/schemas/web_navigation.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["webNavigation"],
-    ],
-  },
-  webRequest: {
-    url: "chrome://extensions/content/ext-webRequest.js",
-    schema: "chrome://extensions/content/schemas/web_request.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["webRequest"],
-    ],
-  },
-});
-
 if (AppConstants.MOZ_BUILD_APP === "browser") {
   extensions.registerModules({
     identity: {
       schema: "chrome://extensions/content/schemas/identity.json",
       scopes: ["addon_parent"],
     },
   });
 }
copy from toolkit/components/extensions/ext-toolkit.js
copy to toolkit/components/extensions/ext-toolkit.json
--- a/toolkit/components/extensions/ext-toolkit.js
+++ b/toolkit/components/extensions/ext-toolkit.json
@@ -1,267 +1,178 @@
-"use strict";
-
-// These are defined on "global" which is used for the same scopes as the other
-// ext-*.js files.
-/* exported getCookieStoreIdForTab, getCookieStoreIdForContainer,
-            getContainerForCookieStoreId,
-            isValidCookieStoreId, isContainerCookieStoreId,
-            EventManager, InputEventManager */
-/* global getCookieStoreIdForTab:false, getCookieStoreIdForContainer:false,
-          getContainerForCookieStoreId: false,
-          isValidCookieStoreId:false, isContainerCookieStoreId:false,
-          isDefaultCookieStoreId: false, isPrivateCookieStoreId:false,
-          EventManager: false, InputEventManager: false */
-
-XPCOMUtils.defineLazyModuleGetter(this, "ContextualIdentityService",
-                                  "resource://gre/modules/ContextualIdentityService.jsm");
-
-Cu.import("resource://gre/modules/ExtensionCommon.jsm");
-
-global.EventManager = ExtensionCommon.EventManager;
-global.InputEventManager = class extends EventManager {
-  constructor(...args) {
-    super(...args);
-    this.inputHandling = true;
-  }
-};
-
-/* globals DEFAULT_STORE, PRIVATE_STORE, CONTAINER_STORE */
-
-global.DEFAULT_STORE = "firefox-default";
-global.PRIVATE_STORE = "firefox-private";
-global.CONTAINER_STORE = "firefox-container-";
-
-global.getCookieStoreIdForTab = function(data, tab) {
-  if (data.incognito) {
-    return PRIVATE_STORE;
-  }
-
-  if (tab.userContextId) {
-    return getCookieStoreIdForContainer(tab.userContextId);
-  }
-
-  return DEFAULT_STORE;
-};
-
-global.isPrivateCookieStoreId = function(storeId) {
-  return storeId == PRIVATE_STORE;
-};
-
-global.isDefaultCookieStoreId = function(storeId) {
-  return storeId == DEFAULT_STORE;
-};
-
-global.isContainerCookieStoreId = function(storeId) {
-  return storeId !== null && storeId.startsWith(CONTAINER_STORE);
-};
-
-global.getCookieStoreIdForContainer = function(containerId) {
-  return CONTAINER_STORE + containerId;
-};
-
-global.getContainerForCookieStoreId = function(storeId) {
-  if (!isContainerCookieStoreId(storeId)) {
-    return null;
-  }
-
-  let containerId = storeId.substring(CONTAINER_STORE.length);
-  if (ContextualIdentityService.getPublicIdentityFromId(containerId)) {
-    return parseInt(containerId, 10);
-  }
-
-  return null;
-};
-
-global.isValidCookieStoreId = function(storeId) {
-  return isDefaultCookieStoreId(storeId) ||
-         isPrivateCookieStoreId(storeId) ||
-         isContainerCookieStoreId(storeId);
-};
-
-extensions.registerModules({
-  manifest: {
-    schema: "chrome://extensions/content/schemas/extension_types.json",
-    scopes: [],
+{
+  "manifest": {
+    "schema": "chrome://extensions/content/schemas/extension_types.json",
+    "scopes": []
+  },
+  "alarms": {
+    "url": "chrome://extensions/content/ext-alarms.js",
+    "schema": "chrome://extensions/content/schemas/alarms.json",
+    "scopes": ["addon_parent"],
+    "paths": [
+      ["alarms"]
+    ]
+  },
+  "backgroundPage": {
+    "url": "chrome://extensions/content/ext-backgroundPage.js",
+    "scopes": ["addon_parent"],
+    "manifest": ["background"]
+  },
+  "browserSettings": {
+    "url": "chrome://extensions/content/ext-browserSettings.js",
+    "schema": "chrome://extensions/content/schemas/browser_settings.json",
+    "scopes": ["addon_parent"],
+    "paths": [
+      ["browserSettings"]
+    ]
+  },
+  "contextualIdentities": {
+    "url": "chrome://extensions/content/ext-contextualIdentities.js",
+    "schema": "chrome://extensions/content/schemas/contextual_identities.json",
+    "scopes": ["addon_parent"],
+    "paths": [
+      ["contextualIdentities"]
+    ]
+  },
+  "cookies": {
+    "url": "chrome://extensions/content/ext-cookies.js",
+    "schema": "chrome://extensions/content/schemas/cookies.json",
+    "scopes": ["addon_parent"],
+    "paths": [
+      ["cookies"]
+    ]
   },
-  alarms: {
-    url: "chrome://extensions/content/ext-alarms.js",
-    schema: "chrome://extensions/content/schemas/alarms.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["alarms"],
-    ],
+  "downloads": {
+    "url": "chrome://extensions/content/ext-downloads.js",
+    "schema": "chrome://extensions/content/schemas/downloads.json",
+    "scopes": ["addon_parent"],
+    "paths": [
+      ["downloads"]
+    ]
   },
-  backgroundPage: {
-    url: "chrome://extensions/content/ext-backgroundPage.js",
-    scopes: ["addon_parent"],
-    manifest: ["background"],
+  "extension": {
+    "url": "chrome://extensions/content/ext-extension.js",
+    "schema": "chrome://extensions/content/schemas/extension.json",
+    "scopes": ["addon_parent"],
+    "paths": [
+      ["extension"]
+    ]
   },
-  browserSettings: {
-    url: "chrome://extensions/content/ext-browserSettings.js",
-    schema: "chrome://extensions/content/schemas/browser_settings.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["browserSettings"],
-    ],
+  "geolocation": {
+    "url": "chrome://extensions/content/ext-geolocation.js",
+    "events": ["startup"]
   },
-  contextualIdentities: {
-    url: "chrome://extensions/content/ext-contextualIdentities.js",
-    schema: "chrome://extensions/content/schemas/contextual_identities.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["contextualIdentities"],
-    ],
+  "i18n": {
+    "url": "chrome://extensions/content/ext-i18n.js",
+    "schema": "chrome://extensions/content/schemas/i18n.json",
+    "scopes": ["addon_parent", "content_child", "devtools_child"],
+    "paths": [
+      ["i18n"]
+    ]
   },
-  cookies: {
-    url: "chrome://extensions/content/ext-cookies.js",
-    schema: "chrome://extensions/content/schemas/cookies.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["cookies"],
-    ],
+  "idle": {
+    "url": "chrome://extensions/content/ext-idle.js",
+    "schema": "chrome://extensions/content/schemas/idle.json",
+    "scopes": ["addon_parent"],
+    "paths": [
+      ["idle"]
+    ]
   },
-  downloads: {
-    url: "chrome://extensions/content/ext-downloads.js",
-    schema: "chrome://extensions/content/schemas/downloads.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["downloads"],
-    ],
+  "management": {
+    "url": "chrome://extensions/content/ext-management.js",
+    "schema": "chrome://extensions/content/schemas/management.json",
+    "scopes": ["addon_parent"],
+    "paths": [
+      ["management"]
+    ]
   },
-  extension: {
-    url: "chrome://extensions/content/ext-extension.js",
-    schema: "chrome://extensions/content/schemas/extension.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["extension"],
-    ],
-  },
-  geolocation: {
-    url: "chrome://extensions/content/ext-geolocation.js",
-    events: ["startup"],
+  "notifications": {
+    "url": "chrome://extensions/content/ext-notifications.js",
+    "schema": "chrome://extensions/content/schemas/notifications.json",
+    "scopes": ["addon_parent"],
+    "paths": [
+      ["notifications"]
+    ]
   },
-  i18n: {
-    url: "chrome://extensions/content/ext-i18n.js",
-    schema: "chrome://extensions/content/schemas/i18n.json",
-    scopes: ["addon_parent", "content_child", "devtools_child"],
-    paths: [
-      ["i18n"],
-    ],
-  },
-  idle: {
-    url: "chrome://extensions/content/ext-idle.js",
-    schema: "chrome://extensions/content/schemas/idle.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["idle"],
-    ],
+  "permissions": {
+    "url": "chrome://extensions/content/ext-permissions.js",
+    "schema": "chrome://extensions/content/schemas/permissions.json",
+    "scopes": ["addon_parent"],
+    "paths": [
+      ["permissions"]
+    ]
   },
-  management: {
-    url: "chrome://extensions/content/ext-management.js",
-    schema: "chrome://extensions/content/schemas/management.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["management"],
-    ],
+  "privacy": {
+    "url": "chrome://extensions/content/ext-privacy.js",
+    "schema": "chrome://extensions/content/schemas/privacy.json",
+    "scopes": ["addon_parent"],
+    "paths": [
+      ["privacy"]
+    ]
   },
-  notifications: {
-    url: "chrome://extensions/content/ext-notifications.js",
-    schema: "chrome://extensions/content/schemas/notifications.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["notifications"],
-    ],
+  "protocolHandlers": {
+    "url": "chrome://extensions/content/ext-protocolHandlers.js",
+    "schema": "chrome://extensions/content/schemas/extension_protocol_handlers.json",
+    "scopes": ["addon_parent"],
+    "manifest": ["protocol_handlers"]
   },
-  permissions: {
-    url: "chrome://extensions/content/ext-permissions.js",
-    schema: "chrome://extensions/content/schemas/permissions.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["permissions"],
-    ],
+  "proxy": {
+    "url": "chrome://extensions/content/ext-proxy.js",
+    "schema": "chrome://extensions/content/schemas/proxy.json",
+    "scopes": ["addon_parent"],
+    "paths": [
+      ["proxy"]
+    ]
   },
-  privacy: {
-    url: "chrome://extensions/content/ext-privacy.js",
-    schema: "chrome://extensions/content/schemas/privacy.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["privacy"],
-    ],
-  },
-  protocolHandlers: {
-    url: "chrome://extensions/content/ext-protocolHandlers.js",
-    schema: "chrome://extensions/content/schemas/extension_protocol_handlers.json",
-    scopes: ["addon_parent"],
-    manifest: ["protocol_handlers"],
+  "runtime": {
+    "url": "chrome://extensions/content/ext-runtime.js",
+    "schema": "chrome://extensions/content/schemas/runtime.json",
+    "scopes": ["addon_parent", "content_parent", "devtools_parent"],
+    "paths": [
+      ["runtime"]
+    ]
   },
-  proxy: {
-    url: "chrome://extensions/content/ext-proxy.js",
-    schema: "chrome://extensions/content/schemas/proxy.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["proxy"],
-    ],
+  "storage": {
+    "url": "chrome://extensions/content/ext-storage.js",
+    "schema": "chrome://extensions/content/schemas/storage.json",
+    "scopes": ["addon_parent", "content_parent", "devtools_parent"],
+    "paths": [
+      ["storage"]
+    ]
   },
-  runtime: {
-    url: "chrome://extensions/content/ext-runtime.js",
-    schema: "chrome://extensions/content/schemas/runtime.json",
-    scopes: ["addon_parent", "content_parent", "devtools_parent"],
-    paths: [
-      ["runtime"],
-    ],
+  "test": {
+    "schema": "chrome://extensions/content/schemas/test.json",
+    "scopes": []
   },
-  storage: {
-    url: "chrome://extensions/content/ext-storage.js",
-    schema: "chrome://extensions/content/schemas/storage.json",
-    scopes: ["addon_parent", "content_parent", "devtools_parent"],
-    paths: [
-      ["storage"],
-    ],
-  },
-  test: {
-    schema: "chrome://extensions/content/schemas/test.json",
-    scopes: [],
+  "theme": {
+    "url": "chrome://extensions/content/ext-theme.js",
+    "schema": "chrome://extensions/content/schemas/theme.json",
+    "scopes": ["addon_parent"],
+    "manifest": ["theme"],
+    "paths": [
+      ["theme"]
+    ]
   },
-  theme: {
-    url: "chrome://extensions/content/ext-theme.js",
-    schema: "chrome://extensions/content/schemas/theme.json",
-    scopes: ["addon_parent"],
-    manifest: ["theme"],
-    paths: [
-      ["theme"],
-    ],
-  },
-  topSites: {
-    url: "chrome://extensions/content/ext-topSites.js",
-    schema: "chrome://extensions/content/schemas/top_sites.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["topSites"],
-    ],
+  "topSites": {
+    "url": "chrome://extensions/content/ext-topSites.js",
+    "schema": "chrome://extensions/content/schemas/top_sites.json",
+    "scopes": ["addon_parent"],
+    "paths": [
+      ["topSites"]
+    ]
   },
-  webNavigation: {
-    url: "chrome://extensions/content/ext-webNavigation.js",
-    schema: "chrome://extensions/content/schemas/web_navigation.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["webNavigation"],
-    ],
+  "webNavigation": {
+    "url": "chrome://extensions/content/ext-webNavigation.js",
+    "schema": "chrome://extensions/content/schemas/web_navigation.json",
+    "scopes": ["addon_parent"],
+    "paths": [
+      ["webNavigation"]
+    ]
   },
-  webRequest: {
-    url: "chrome://extensions/content/ext-webRequest.js",
-    schema: "chrome://extensions/content/schemas/web_request.json",
-    scopes: ["addon_parent"],
-    paths: [
-      ["webRequest"],
-    ],
-  },
-});
-
-if (AppConstants.MOZ_BUILD_APP === "browser") {
-  extensions.registerModules({
-    identity: {
-      schema: "chrome://extensions/content/schemas/identity.json",
-      scopes: ["addon_parent"],
-    },
-  });
+  "webRequest": {
+    "url": "chrome://extensions/content/ext-webRequest.js",
+    "schema": "chrome://extensions/content/schemas/web_request.json",
+    "scopes": ["addon_parent"],
+    "paths": [
+      ["webRequest"]
+    ]
+  }
 }
--- a/toolkit/components/extensions/extensions-toolkit.manifest
+++ b/toolkit/components/extensions/extensions-toolkit.manifest
@@ -1,9 +1,11 @@
 # scripts
+category webextension-modules toolkit chrome://extensions/content/ext-toolkit.json
+
 category webextension-scripts tabs-base chrome://extensions/content/ext-tabs-base.js
 category webextension-scripts toolkit chrome://extensions/content/ext-toolkit.js
 category webextension-scripts-content toolkit chrome://extensions/content/ext-c-toolkit.js
 category webextension-scripts-devtools toolkit chrome://extensions/content/ext-c-toolkit.js
 category webextension-scripts-addon toolkit chrome://extensions/content/ext-c-toolkit.js
 
 category webextension-schemas events chrome://extensions/content/schemas/events.json
 category webextension-schemas native_host_manifest chrome://extensions/content/schemas/native_host_manifest.json
--- a/toolkit/components/extensions/jar.mn
+++ b/toolkit/components/extensions/jar.mn
@@ -22,16 +22,17 @@ toolkit.jar:
     content/extensions/ext-privacy.js
     content/extensions/ext-protocolHandlers.js
     content/extensions/ext-proxy.js
     content/extensions/ext-runtime.js
     content/extensions/ext-storage.js
     content/extensions/ext-tabs-base.js
     content/extensions/ext-theme.js
     content/extensions/ext-toolkit.js
+    content/extensions/ext-toolkit.json
     content/extensions/ext-topSites.js
     content/extensions/ext-webRequest.js
     content/extensions/ext-webNavigation.js
     # Below is a separate group using the naming convention ext-c-*.js that run
     # in the child process.
     content/extensions/ext-c-backgroundPage.js
     content/extensions/ext-c-extension.js
 #ifndef ANDROID