Merge inbound to mozilla-central. a=merge
authorOana Pop Rus <opoprus@mozilla.com>
Thu, 21 Feb 2019 11:31:00 +0200
changeset 460199 2acb22602c60a2380cdd978d6bbdc55ba314604b
parent 460198 5fa1f6433408d804a98025029a3ed38260a81dce (current diff)
parent 460146 60252879d73cfa378fd7348afdf6ef64bae0915f (diff)
child 460200 9d2f9b494284ecab54baa59df62c4a8cb6690acb
child 460202 989d16d0f995a6f4fe510d714b6f22b1adbf35b0
child 460244 a671f97c000602a78429fc1b4fbbf4b8bc5ce44b
push id112059
push useropoprus@mozilla.com
push dateThu, 21 Feb 2019 09:45:58 +0000
treeherdermozilla-inbound@9d2f9b494284 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone67.0a1
first release with
nightly linux32
2acb22602c60 / 67.0a1 / 20190221093143 / files
nightly linux64
2acb22602c60 / 67.0a1 / 20190221093143 / files
nightly mac
2acb22602c60 / 67.0a1 / 20190221093143 / files
nightly win32
2acb22602c60 / 67.0a1 / 20190221093143 / files
nightly win64
2acb22602c60 / 67.0a1 / 20190221093143 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
dom/media/webspeech/synth/speechd/SpeechDispatcherModule.cpp
dom/media/webspeech/synth/test/FakeSynthModule.cpp
dom/plugins/base/nsPluginModule.cpp
dom/presentation/provider/PresentationDeviceProviderModule.cpp
extensions/permissions/nsModuleFactory.cpp
extensions/pref/autoconfig/src/nsConfigFactory.cpp
extensions/spellcheck/src/mozSpellCheckerFactory.cpp
gfx/src/nsThebesGfxFactory.cpp
netwerk/dns/mdns/libmdns/nsMulticastDNSModule.cpp
security/manager/pki/nsPKIModule.cpp
storage/build/mozStorageModule.cpp
toolkit/components/resistfingerprinting/LanguagePrompt.jsm
tools/profiler/gecko/nsProfilerFactory.cpp
xpfe/appshell/nsAppShellFactory.cpp
new file mode 100644
--- /dev/null
+++ b/browser/actors/RFPHelperChild.jsm
@@ -0,0 +1,22 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* 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/. */
+
+var EXPORTED_SYMBOLS = ["RFPHelperChild"];
+
+const {ActorChild} = ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
+const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const kPrefLetterboxing = "privacy.resistFingerprinting.letterboxing";
+
+XPCOMUtils.defineLazyPreferenceGetter(this, "isLetterboxingEnabled",
+  kPrefLetterboxing, false);
+
+class RFPHelperChild extends ActorChild {
+  handleEvent() {
+    if (isLetterboxingEnabled) {
+      this.mm.sendAsyncMessage("Letterboxing:ContentSizeUpdated");
+    }
+  }
+}
--- a/browser/actors/moz.build
+++ b/browser/actors/moz.build
@@ -37,12 +37,13 @@ FINAL_TARGET_FILES.actors += [
     'LightweightThemeChild.jsm',
     'LightWeightThemeInstallChild.jsm',
     'LinkHandlerChild.jsm',
     'NetErrorChild.jsm',
     'OfflineAppsChild.jsm',
     'PageInfoChild.jsm',
     'PageStyleChild.jsm',
     'PluginChild.jsm',
+    'RFPHelperChild.jsm',
     'SearchTelemetryChild.jsm',
     'URIFixupChild.jsm',
     'WebRTCChild.jsm',
 ]
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -23,17 +23,16 @@ XPCOMUtils.defineLazyModuleGetters(this,
   ContextualIdentityService: "resource://gre/modules/ContextualIdentityService.jsm",
   CustomizableUI: "resource:///modules/CustomizableUI.jsm",
   Deprecated: "resource://gre/modules/Deprecated.jsm",
   DownloadsCommon: "resource:///modules/DownloadsCommon.jsm",
   DownloadUtils: "resource://gre/modules/DownloadUtils.jsm",
   E10SUtils: "resource://gre/modules/E10SUtils.jsm",
   ExtensionsUI: "resource:///modules/ExtensionsUI.jsm",
   FormValidationHandler: "resource:///modules/FormValidationHandler.jsm",
-  LanguagePrompt: "resource://gre/modules/LanguagePrompt.jsm",
   HomePage: "resource:///modules/HomePage.jsm",
   LightweightThemeConsumer: "resource://gre/modules/LightweightThemeConsumer.jsm",
   LightweightThemeManager: "resource://gre/modules/LightweightThemeManager.jsm",
   Log: "resource://gre/modules/Log.jsm",
   LoginManagerParent: "resource://gre/modules/LoginManagerParent.jsm",
   NetUtil: "resource://gre/modules/NetUtil.jsm",
   NewTabUtils: "resource://gre/modules/NewTabUtils.jsm",
   OpenInTabsUtils: "resource:///modules/OpenInTabsUtils.jsm",
@@ -45,16 +44,17 @@ XPCOMUtils.defineLazyModuleGetters(this,
   PlacesUIUtils: "resource:///modules/PlacesUIUtils.jsm",
   PlacesTransactions: "resource://gre/modules/PlacesTransactions.jsm",
   PluralForm: "resource://gre/modules/PluralForm.jsm",
   Pocket: "chrome://pocket/content/Pocket.jsm",
   PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
   ProcessHangMonitor: "resource:///modules/ProcessHangMonitor.jsm",
   PromiseUtils: "resource://gre/modules/PromiseUtils.jsm",
   ReaderParent: "resource:///modules/ReaderParent.jsm",
+  RFPHelper: "resource://gre/modules/RFPHelper.jsm",
   SafeBrowsing: "resource://gre/modules/SafeBrowsing.jsm",
   Sanitizer: "resource:///modules/Sanitizer.jsm",
   SessionStartup: "resource:///modules/sessionstore/SessionStartup.jsm",
   SessionStore: "resource:///modules/sessionstore/SessionStore.jsm",
   ShortcutUtils: "resource://gre/modules/ShortcutUtils.jsm",
   SimpleServiceDiscovery: "resource://gre/modules/SimpleServiceDiscovery.jsm",
   SiteDataManager: "resource:///modules/SiteDataManager.jsm",
   SitePermissions: "resource:///modules/SitePermissions.jsm",
@@ -1943,18 +1943,16 @@ var gBrowserInit = {
     gAccessibilityServiceIndicator.uninit();
 
     AccessibilityRefreshBlocker.uninit();
 
     if (gToolbarKeyNavEnabled) {
       ToolbarKeyboardNavigator.uninit();
     }
 
-    LanguagePrompt.uninit();
-
     BrowserSearch.uninit();
 
     // Now either cancel delayedStartup, or clean up the services initialized from
     // it.
     if (this._boundDelayedStartup) {
       this._cancelDelayedStartup();
     } else {
       if (Win7Features) {
--- a/browser/components/BrowserGlue.jsm
+++ b/browser/components/BrowserGlue.jsm
@@ -239,16 +239,26 @@ let ACTORS = {
       ],
 
       observers: [
         "decoder-doctor-notification",
       ],
     },
   },
 
+  RFPHelper: {
+    child: {
+      module: "resource:///actors/RFPHelperChild.jsm",
+      group: "browsers",
+      events: {
+        "resize": {},
+      },
+    },
+  },
+
   SearchTelemetry: {
     child: {
       module: "resource:///actors/SearchTelemetryChild.jsm",
       events: {
         DOMContentLoaded: {},
         "pageshow": {mozSystemGroup: true},
       },
     },
@@ -392,17 +402,16 @@ XPCOMUtils.defineLazyModuleGetters(this,
   Discovery: "resource:///modules/Discovery.jsm",
   ExtensionsUI: "resource:///modules/ExtensionsUI.jsm",
   FileSource: "resource://gre/modules/L10nRegistry.jsm",
   FxAccounts: "resource://gre/modules/FxAccounts.jsm",
   HomePage: "resource:///modules/HomePage.jsm",
   HybridContentTelemetry: "resource://gre/modules/HybridContentTelemetry.jsm",
   Integration: "resource://gre/modules/Integration.jsm",
   L10nRegistry: "resource://gre/modules/L10nRegistry.jsm",
-  LanguagePrompt: "resource://gre/modules/LanguagePrompt.jsm",
   LightweightThemeManager: "resource://gre/modules/LightweightThemeManager.jsm",
   LiveBookmarkMigrator: "resource:///modules/LiveBookmarkMigrator.jsm",
   NewTabUtils: "resource://gre/modules/NewTabUtils.jsm",
   Normandy: "resource://normandy/Normandy.jsm",
   ObjectUtils: "resource://gre/modules/ObjectUtils.jsm",
   OS: "resource://gre/modules/osfile.jsm",
   PageActions: "resource:///modules/PageActions.jsm",
   PageThumbs: "resource://gre/modules/PageThumbs.jsm",
@@ -410,16 +419,17 @@ XPCOMUtils.defineLazyModuleGetters(this,
   PermissionUI: "resource:///modules/PermissionUI.jsm",
   PingCentre: "resource:///modules/PingCentre.jsm",
   PlacesBackups: "resource://gre/modules/PlacesBackups.jsm",
   PlacesUtils: "resource://gre/modules/PlacesUtils.jsm",
   PluralForm: "resource://gre/modules/PluralForm.jsm",
   PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
   ProcessHangMonitor: "resource:///modules/ProcessHangMonitor.jsm",
   RemoteSettings: "resource://services-settings/remote-settings.js",
+  RFPHelper: "resource://gre/modules/RFPHelper.jsm",
   SafeBrowsing: "resource://gre/modules/SafeBrowsing.jsm",
   Sanitizer: "resource:///modules/Sanitizer.jsm",
   SaveToPocket: "chrome://pocket/content/SaveToPocket.jsm",
   SearchTelemetry: "resource:///modules/SearchTelemetry.jsm",
   SessionStartup: "resource:///modules/sessionstore/SessionStartup.jsm",
   SessionStore: "resource:///modules/sessionstore/SessionStore.jsm",
   ShellService: "resource:///modules/ShellService.jsm",
   TabCrashHandler: "resource:///modules/ContentCrashHandlers.jsm",
@@ -1456,16 +1466,17 @@ BrowserGlue.prototype = {
 
     PageThumbs.uninit();
     NewTabUtils.uninit();
     AboutPrivateBrowsingHandler.uninit();
     AutoCompletePopup.uninit();
     DateTimePickerParent.uninit();
 
     Normandy.uninit();
+    RFPHelper.uninit();
   },
 
   // Set up a listener to enable/disable the screenshots extension
   // based on its preference.
   _monitorScreenshotsPref() {
     const PREF = "extensions.screenshots.disabled";
     const ID = "screenshots@mozilla.org";
     const _checkScreenshotsPref = async () => {
@@ -1669,17 +1680,17 @@ BrowserGlue.prototype = {
 
     if (AppConstants.platform == "win") {
       Services.tm.idleDispatchToMainThread(() => {
         JawsScreenReaderVersionCheck.onWindowsRestored();
       });
     }
 
     Services.tm.idleDispatchToMainThread(() => {
-      LanguagePrompt.init();
+      RFPHelper.init();
     });
 
     Services.tm.idleDispatchToMainThread(() => {
       Blocklist.loadBlocklistAsync();
     });
 
     if (Services.prefs.getIntPref("browser.livebookmarks.migrationAttemptsLeft", 0) > 0) {
       Services.tm.idleDispatchToMainThread(() => {
--- a/browser/components/resistfingerprinting/test/browser/browser.ini
+++ b/browser/components/resistfingerprinting/test/browser/browser.ini
@@ -6,16 +6,17 @@ support-files =
   file_keyBoardEvent.sjs
   file_navigator.html
   file_navigatorWorker.js
   file_workerNetInfo.js
   file_workerPerformance.js
   head.js
 
 [browser_block_mozAddonManager.js]
+[browser_dynamical_window_rounding.js]
 [browser_navigator.js]
 [browser_netInfo.js]
 [browser_performanceAPI.js]
 [browser_roundedWindow_dialogWindow.js]
 [browser_roundedWindow_newWindow.js]
 [browser_roundedWindow_open_max_inner.js]
 [browser_roundedWindow_open_max_outer.js]
 [browser_roundedWindow_open_mid_inner.js]
--- a/browser/components/resistfingerprinting/test/browser/browser_bug1369357_site_specific_zoom_level.js
+++ b/browser/components/resistfingerprinting/test/browser/browser_bug1369357_site_specific_zoom_level.js
@@ -20,12 +20,14 @@ add_task(async function() {
     ],
   });
 
   tab3 = await BrowserTestUtils.openNewForegroundTab(gBrowser, testPage);
   tab3Zoom = ZoomManager.getZoomForBrowser(tab3.linkedBrowser);
 
   isnot(tab3Zoom, tab1Zoom, "privacy.resistFingerprinting is true, site-specific zoom level should be disabled");
 
+  await FullZoom.reset();
+
   BrowserTestUtils.removeTab(tab1);
   BrowserTestUtils.removeTab(tab2);
   BrowserTestUtils.removeTab(tab3);
 });
new file mode 100644
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/browser/browser_dynamical_window_rounding.js
@@ -0,0 +1,281 @@
+/* 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/.
+ *
+ * Bug 1407366 - A test case for reassuring the size of the content viewport is
+ *   rounded if the window is resized when letterboxing is enabled.
+ *
+ * A helpful note: if this test starts randomly failing; it may be because the
+ * zoom level was not reset by an earlier-run test. See Bug 1407366 for an
+ * example.
+ */
+
+const TEST_PATH = "http://example.net/browser/browser/components/resistfingerprinting/test/browser/";
+
+const DEFAULT_ROUNDED_WIDTH_STEP  = 200;
+const DEFAULT_ROUNDED_HEIGHT_STEP = 100;
+
+// A set of test cases which defines the width and the height of the outer window.
+const TEST_CASES = [
+  {width: 1250, height: 1000},
+  {width: 1500, height: 1050},
+  {width: 1120, height: 760},
+  {width: 800,  height: 600},
+  {width: 640,  height: 400},
+  {width: 500,  height: 350},
+  {width: 300,  height: 170},
+];
+
+function getPlatform() {
+  const {OS} = Services.appinfo;
+  if (OS == "WINNT") {
+    return "win";
+  } else if (OS == "Darwin") {
+    return "mac";
+  }
+  return "linux";
+}
+
+function handleOSFuzziness(aContent, aTarget) {
+  /*
+   * On Windows, we observed off-by-one pixel differences that
+   * couldn't be expained. When manually setting the window size
+   * to try to reproduce it; it did not occur.
+   */
+  if (getPlatform() == "win") {
+    return Math.abs(aContent - aTarget) <= 1;
+  }
+  return aContent == aTarget;
+}
+
+function checkForDefaultSetting(
+  aContentWidth, aContentHeight, aRealWidth, aRealHeight) {
+  // The default behavior for rounding is to round window with 200x100 stepping.
+  // So, we can get the rounded size by subtracting the remainder.
+  let targetWidth = aRealWidth - (aRealWidth % DEFAULT_ROUNDED_WIDTH_STEP);
+  let targetHeight = aRealHeight - (aRealHeight % DEFAULT_ROUNDED_HEIGHT_STEP);
+
+  // This platform-specific code is explained in the large comment below.
+  if (getPlatform() != "linux") {
+    ok(handleOSFuzziness(aContentWidth, targetWidth),
+      `Default Dimensions: The content window width is correctly rounded into. ${aRealWidth}px -> ${aContentWidth}px should equal ${targetWidth}px`);
+
+    ok(handleOSFuzziness(aContentHeight, targetHeight),
+      `Default Dimensions: The content window height is correctly rounded into. ${aRealHeight}px -> ${aContentHeight}px should equal ${targetHeight}px`);
+
+    // Using ok() above will cause Win/Mac to fail on even the first test, we don't need to repeat it, return true so waitForCondition ends
+    return true;
+  }
+  // Returning true or false depending on if the test succeeded will cause Linux to repeat until it succeeds.
+  return handleOSFuzziness(aContentWidth, targetWidth) && handleOSFuzziness(aContentHeight, targetHeight);
+}
+
+async function test_dynamical_window_rounding(aWindow, aCheckFunc) {
+  // We need to wait for the updating the margins for the newly opened tab, or
+  // it will affect the following tests.
+  let promiseForTheFirstRounding =
+    TestUtils.topicObserved("test:letterboxing:update-margin-finish");
+
+  info("Open a content tab for testing.");
+  let tab = await BrowserTestUtils.openNewForegroundTab(
+    aWindow.gBrowser, TEST_PATH + "file_dummy.html");
+
+  info("Wait until the margins are applied for the opened tab.");
+  await promiseForTheFirstRounding;
+
+  let getContainerSize = (aTab) => {
+    let browserContainer = aWindow.gBrowser
+                                  .getBrowserContainer(aTab.linkedBrowser);
+    return {
+      containerWidth: browserContainer.clientWidth,
+      containerHeight: browserContainer.clientHeight,
+    };
+  };
+
+  for (let {width, height} of TEST_CASES) {
+    let caseString = "Case " + width + "x" + height + ": ";
+    // Create a promise for waiting for the margin update.
+    let promiseRounding =
+      TestUtils.topicObserved("test:letterboxing:update-margin-finish");
+
+    let {containerWidth, containerHeight} = getContainerSize(tab);
+
+    info(caseString + "Resize the window and wait until resize event happened (currently " +
+      containerWidth + "x" + containerHeight + ")");
+    await new Promise(resolve => {
+      ({containerWidth, containerHeight} = getContainerSize(tab));
+      info(caseString + "Resizing (currently " + containerWidth + "x" + containerHeight + ")");
+
+      aWindow.onresize = () => {
+        ({containerWidth, containerHeight} = getContainerSize(tab));
+        info(caseString + "Resized (currently " + containerWidth + "x" + containerHeight + ")");
+        if (getPlatform() == "linux" && containerWidth != width) {
+          /*
+           * We observed frequent test failures that resulted from receiving an onresize
+           * event where the browser was resized to an earlier requested dimension. This
+           * resize event happens on Linux only, and is an artifact of the asynchronous
+           * resizing. (See more discussion on 1407366#53)
+           *
+           * We cope with this problem in two ways.
+           *
+           * 1: If we detect that the browser was resized to the wrong value; we
+           *    redo the resize. (This is the lines of code immediately following this
+           *    comment)
+           * 2: We repeat the test until it works using waitForCondition(). But we still
+           *    test Win/Mac more thoroughly: they do not loop in waitForCondition more
+           *    than once, and can fail the test on the first attempt (because their
+           *    check() functions use ok() while on Linux, we do not all ok() and instead
+           *    rely on waitForCondition to fail).
+           *
+           * The logging statements in this test, and RFPHelper.jsm, help narrow down and
+           * illustrate the issue.
+           */
+          info(caseString + "We hit the weird resize bug. Resize it again.");
+          aWindow.resizeTo(width, height);
+        } else {
+          resolve();
+        }
+      };
+      aWindow.resizeTo(width, height);
+    });
+
+    ({containerWidth, containerHeight} = getContainerSize(tab));
+    info(caseString + "Waiting until margin has been updated on browser element. (currently " +
+      containerWidth + "x" + containerHeight + ")");
+    await promiseRounding;
+
+    info(caseString + "Get innerWidth/Height from the content.");
+    await BrowserTestUtils.waitForCondition(async () => {
+      let {contentWidth, contentHeight} = await ContentTask.spawn(
+        tab.linkedBrowser, null, () => {
+          return {
+            contentWidth: content.innerWidth,
+            contentHeight: content.innerHeight,
+          };
+        });
+
+      info(caseString + "Check the result.");
+      return aCheckFunc(contentWidth, contentHeight, containerWidth, containerHeight);
+    }, "Default Dimensions: The content window width is correctly rounded into.");
+  }
+
+  BrowserTestUtils.removeTab(tab);
+}
+
+async function test_customize_width_and_height(aWindow) {
+  const test_dimensions = `120x80, 200x143, 335x255, 600x312, 742x447, 813x558,
+                           990x672, 1200x733, 1470x858`;
+
+  await SpecialPowers.pushPrefEnv({"set":
+    [
+      ["privacy.resistFingerprinting.letterboxing.dimensions", test_dimensions],
+    ],
+  });
+
+  let dimensions_set = test_dimensions.split(",").map(item => {
+    let sizes = item.split("x").map(size => parseInt(size, 10));
+
+    return {
+      width: sizes[0],
+      height: sizes[1],
+    };
+  });
+
+  let checkDimension =
+    (aContentWidth, aContentHeight, aRealWidth, aRealHeight) => {
+      let matchingArea = aRealWidth * aRealHeight;
+      let minWaste = Number.MAX_SAFE_INTEGER;
+      let targetDimensions = undefined;
+
+      // Find the dimensions which waste the least content area.
+      for (let dim of dimensions_set) {
+        if (dim.width > aRealWidth || dim.height > aRealHeight) {
+          continue;
+        }
+
+        let waste = matchingArea - dim.width * dim.height;
+
+        if (waste >= 0 && waste < minWaste) {
+          targetDimensions = dim;
+          minWaste = waste;
+        }
+      }
+
+      // This platform-specific code is explained in the large comment above.
+      if (getPlatform() != "linux") {
+        ok(handleOSFuzziness(aContentWidth, targetDimensions.width),
+          `Custom Dimension: The content window width is correctly rounded into. ${aRealWidth}px -> ${aContentWidth}px should equal ${targetDimensions.width}`);
+
+        ok(handleOSFuzziness(aContentHeight, targetDimensions.height),
+          `Custom Dimension: The content window height is correctly rounded into. ${aRealHeight}px -> ${aContentHeight}px should equal ${targetDimensions.height}`);
+
+        // Using ok() above will cause Win/Mac to fail on even the first test, we don't need to repeat it, return true so waitForCondition ends
+        return true;
+      }
+      // Returning true or false depending on if the test succeeded will cause Linux to repeat until it succeeds.
+      return handleOSFuzziness(aContentWidth, targetDimensions.width) && handleOSFuzziness(aContentHeight, targetDimensions.height);
+    };
+
+  await test_dynamical_window_rounding(aWindow, checkDimension);
+
+  await SpecialPowers.popPrefEnv();
+}
+
+async function test_no_rounding_for_chrome(aWindow) {
+  // First, resize the window to a size with is not rounded.
+  await new Promise(resolve => {
+    aWindow.onresize = () => resolve();
+    aWindow.resizeTo(700, 450);
+  });
+
+  // open a chrome privilege tab, like about:config.
+  let tab = await BrowserTestUtils.openNewForegroundTab(
+    aWindow.gBrowser, "about:config");
+
+  // Check that the browser element should not have a margin.
+  is(tab.linkedBrowser.style.margin, "", "There is no margin around chrome tab.");
+
+  BrowserTestUtils.removeTab(tab);
+}
+
+add_task(async function setup() {
+  await SpecialPowers.pushPrefEnv({"set":
+    [
+      ["privacy.resistFingerprinting.letterboxing", true],
+      ["privacy.resistFingerprinting.letterboxing.testing", true],
+    ],
+  });
+});
+
+add_task(async function do_tests() {
+  // Store the original window size before testing.
+  let originalOuterWidth = window.outerWidth;
+  let originalOuterHeight = window.outerHeight;
+
+  info("Run test for the default window rounding.");
+  await test_dynamical_window_rounding(window, checkForDefaultSetting);
+
+  info("Run test for the window rounding with customized dimensions.");
+  await test_customize_width_and_height(window);
+
+  info("Run test for no margin around tab with the chrome privilege.");
+  await test_no_rounding_for_chrome(window);
+
+  // Restore the original window size.
+  window.outerWidth = originalOuterWidth;
+  window.outerHeight = originalOuterHeight;
+
+  // Testing that whether the dynamical rounding works for new windows.
+  let win = await BrowserTestUtils.openNewBrowserWindow();
+
+  info("Run test for the default window rounding in new window.");
+  await test_dynamical_window_rounding(win, checkForDefaultSetting);
+
+  info("Run test for the window rounding with customized dimensions in new window.");
+  await test_customize_width_and_height(win);
+
+  info("Run test for no margin around tab with the chrome privilege in new window.");
+  await test_no_rounding_for_chrome(win);
+
+  await BrowserTestUtils.closeWindow(win);
+});
new file mode 100644
--- /dev/null
+++ b/devtools/platform/components.conf
@@ -0,0 +1,14 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Classes = [
+    {
+        'cid': '{ec5aa99c-7abb-4142-ac5f-aab2419e38e2}',
+        'contract_ids': ['@mozilla.org/jsinspector;1'],
+        'type': 'mozilla::jsinspector::nsJSInspector',
+        'headers': ['/devtools/platform/nsJSInspector.h'],
+    },
+]
--- a/devtools/platform/moz.build
+++ b/devtools/platform/moz.build
@@ -11,9 +11,13 @@ XPIDL_SOURCES += [
 ]
 
 XPIDL_MODULE = 'jsinspector'
 
 SOURCES += [
     'nsJSInspector.cpp',
 ]
 
+XPCOM_MANIFESTS += [
+    'components.conf',
+]
+
 FINAL_LIBRARY = 'xul'
--- a/devtools/platform/nsJSInspector.cpp
+++ b/devtools/platform/nsJSInspector.cpp
@@ -3,17 +3,16 @@
  * 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/. */
 
 #include "nsJSInspector.h"
 #include "nsIXPConnect.h"
 #include "nsThreadUtils.h"
 #include "jsfriendapi.h"
 #include "mozilla/HoldDropJSObjects.h"
-#include "mozilla/ModuleUtils.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "nsServiceManagerUtils.h"
 #include "nsMemory.h"
 #include "nsArray.h"
 #include "nsTArray.h"
 
 #define JSINSPECTOR_CONTRACTID "@mozilla.org/jsinspector;1"
 
@@ -22,18 +21,16 @@
     0xec5aa99c, 0x7abb, 0x4142, {                    \
       0xac, 0x5f, 0xaa, 0xb2, 0x41, 0x9e, 0x38, 0xe2 \
     }                                                \
   }
 
 namespace mozilla {
 namespace jsinspector {
 
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsJSInspector)
-
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSInspector)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_INTERFACE_MAP_ENTRY(nsIJSInspector)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSInspector)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSInspector)
@@ -116,23 +113,8 @@ nsJSInspector::GetEventLoopNestLevel(uin
 NS_IMETHODIMP
 nsJSInspector::GetLastNestRequestor(JS::MutableHandle<JS::Value> out) {
   out.set(mLastRequestor);
   return NS_OK;
 }
 
 }  // namespace jsinspector
 }  // namespace mozilla
-
-NS_DEFINE_NAMED_CID(JSINSPECTOR_CID);
-
-static const mozilla::Module::CIDEntry kJSInspectorCIDs[] = {
-    {&kJSINSPECTOR_CID, false, nullptr,
-     mozilla::jsinspector::nsJSInspectorConstructor},
-    {nullptr}};
-
-static const mozilla::Module::ContractIDEntry kJSInspectorContracts[] = {
-    {JSINSPECTOR_CONTRACTID, &kJSINSPECTOR_CID}, {nullptr}};
-
-static const mozilla::Module kJSInspectorModule = {
-    mozilla::Module::kVersion, kJSInspectorCIDs, kJSInspectorContracts};
-
-NSMODULE_DEFN(jsinspector) = &kJSInspectorModule;
--- a/dom/base/Timeout.h
+++ b/dom/base/Timeout.h
@@ -84,17 +84,17 @@ class Timeout final : public LinkedListE
 
   // Returned as value of setTimeout()
   uint32_t mTimeoutId;
 
   // Identifies which firing level this Timeout is being processed in
   // when sync loops trigger nested firing.
   uint32_t mFiringId;
 
-#ifdef DEBUG
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
   int64_t mFiringIndex;
 #endif
 
   // The popup state at timeout creation time if not created from
   // another timeout
   PopupBlocker::PopupControlState mPopupState;
 
   // Used to allow several reasons for setting a timeout, where each
--- a/dom/base/TimeoutManager.cpp
+++ b/dom/base/TimeoutManager.cpp
@@ -449,17 +449,17 @@ int32_t gDisableOpenClickDelay;
 TimeoutManager::TimeoutManager(nsGlobalWindowInner& aWindow,
                                uint32_t aMaxIdleDeferMS)
     : mWindow(aWindow),
       mExecutor(new TimeoutExecutor(this, false, 0)),
       mIdleExecutor(new TimeoutExecutor(this, true, aMaxIdleDeferMS)),
       mTimeouts(*this),
       mTimeoutIdCounter(1),
       mNextFiringId(InvalidFiringId + 1),
-#ifdef DEBUG
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
       mFiringIndex(0),
       mLastFiringIndex(-1),
 #endif
       mRunningTimeout(nullptr),
       mIdleTimeouts(*this),
       mIdleCallbackTimeoutCounter(1),
       mLastBudgetUpdate(TimeStamp::Now()),
       mExecutionBudget(GetMaxBudget(mWindow.IsBackgroundInternal())),
@@ -558,17 +558,17 @@ nsresult TimeoutManager::SetTimeout(nsIT
   // code can handle. (Note: we already forced |interval| to be non-negative,
   // so the uint32_t cast (to avoid compiler warnings) is ok.)
   uint32_t maxTimeoutMs = PR_IntervalToMilliseconds(DOM_MAX_TIMEOUT_VALUE);
   if (static_cast<uint32_t>(interval) > maxTimeoutMs) {
     interval = maxTimeoutMs;
   }
 
   RefPtr<Timeout> timeout = new Timeout();
-#ifdef DEBUG
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
   timeout->mFiringIndex = -1;
 #endif
   timeout->mWindow = &mWindow;
   timeout->mIsInterval = aIsInterval;
   timeout->mInterval = TimeDuration::FromMilliseconds(interval);
   timeout->mScriptHandler = aHandler;
   timeout->mReason = aReason;
 
@@ -855,24 +855,40 @@ void TimeoutManager::RunTimeout(const Ti
 
     for (RefPtr<Timeout> timeout = timeouts.GetFirst(); timeout != nullptr;
          timeout = next) {
       next = timeout->getNext();
       // We should only execute callbacks for the set of expired Timeout
       // objects we computed above.
       if (timeout->mFiringId != firingId) {
         // If the FiringId does not match, but is still valid, then this is
-        // a Timeout for another RunTimeout() on the call stack.  Just
-        // skip it.
+        // a Timeout for another RunTimeout() on the call stack (such as in
+        // the case of nested event loops, for alert() or more likely XHR).
+        // Just skip it.
         if (IsValidFiringId(timeout->mFiringId)) {
           MOZ_LOG(gTimeoutLog, LogLevel::Debug,
                   ("Skipping Run%s(TimeoutManager=%p, timeout=%p) since "
-                   "firingId %d is valid (processing firingId %d)",
+                   "firingId %d is valid (processing firingId %d) - "
+                   "FiringIndex %" PRId64 " (mLastFiringIndex %" PRId64 ")",
                    timeout->mIsInterval ? "Interval" : "Timeout", this,
-                   timeout.get(), timeout->mFiringId, firingId));
+                   timeout.get(), timeout->mFiringId, firingId,
+                   timeout->mFiringIndex, mFiringIndex));
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+          // The old FiringIndex assumed no recursion; recursion can cause
+          // other timers to get fired "in the middle" of a sequence we've
+          // already assigned firingindexes to.  Since we're not going to
+          // run this timeout now, remove any FiringIndex that was already
+          // set.
+
+          // Since all timers that have FiringIndexes set *must* be ready
+          // to run and have valid FiringIds, all of them will be 'skipped'
+          // and reset if we recurse - we don't have to look through the
+          // list past where we'll stop on the first InvalidFiringId.
+          timeout->mFiringIndex = -1;
+#endif
           continue;
         }
 
         // If, however, the FiringId is invalid then we have reached Timeout
         // objects beyond the list we calculated above.  This can happen
         // if the Timeout just beyond our last expired Timeout is cancelled
         // by one of the callbacks we've just executed.  In this case we
         // should just stop iterating.  We're done.
@@ -894,17 +910,17 @@ void TimeoutManager::RunTimeout(const Ti
       // retain compliance with the spec language
       // (https://html.spec.whatwg.org/#dom-settimeout) specifically items
       // 15 ("If method context is a Window object, wait until the Document
       // associated with method context has been fully active for a further
       // timeout milliseconds (not necessarily consecutively)") and item 16
       // ("Wait until any invocations of this algorithm that had the same
       // method context, that started before this one, and whose timeout is
       // equal to or less than this one's, have completed.").
-#ifdef DEBUG
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
       if (timeout->mFiringIndex == -1) {
         timeout->mFiringIndex = mFiringIndex++;
       }
 #endif
 
       if (mIsLoading && !aProcessIdle) {
         // Any timeouts that would fire during a load will be deferred
         // until the load event occurs, but if there's an idle time,
@@ -934,18 +950,28 @@ void TimeoutManager::RunTimeout(const Ti
         if (!scx) {
           // No context means this window was closed or never properly
           // initialized for this language.  This timer will never fire
           // so just remove it.
           timeout->remove();
           continue;
         }
 
-#ifdef DEBUG
-        MOZ_ASSERT(timeout->mFiringIndex > mLastFiringIndex);
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+        if (timeout->mFiringIndex <= mLastFiringIndex) {
+          MOZ_LOG(gTimeoutLog, LogLevel::Debug,
+                  ("Incorrect firing index for Run%s(TimeoutManager=%p, "
+                   "timeout=%p) with "
+                   "firingId %d - FiringIndex %" PRId64
+                   " (mLastFiringIndex %" PRId64 ")",
+                   timeout->mIsInterval ? "Interval" : "Timeout", this,
+                   timeout.get(), timeout->mFiringId, timeout->mFiringIndex,
+                   mFiringIndex));
+        }
+        MOZ_DIAGNOSTIC_ASSERT(timeout->mFiringIndex > mLastFiringIndex);
         mLastFiringIndex = timeout->mFiringIndex;
 #endif
         // This timeout is good to run
         bool timeout_was_cleared = mWindow.RunTimeoutHandler(timeout, scx);
 #if MOZ_GECKO_PROFILER
         if (profiler_is_active()) {
           TimeDuration elapsed = now - timeout->SubmitTime();
           TimeDuration target = timeout->When() - timeout->SubmitTime();
@@ -1063,17 +1089,17 @@ bool TimeoutManager::RescheduleTimeout(T
 
   // Compute time to next timeout for interval timer.
   // Make sure nextInterval is at least CalculateDelay().
   TimeDuration nextInterval = CalculateDelay(aTimeout);
 
   TimeStamp firingTime = aLastCallbackTime + nextInterval;
   TimeDuration delay = firingTime - aCurrentNow;
 
-#ifdef DEBUG
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
   aTimeout->mFiringIndex = -1;
 #endif
   // And make sure delay is nonnegative; that might happen if the timer
   // thread is firing our timers somewhat early or if they're taking a long
   // time to run the callback.
   if (delay < TimeDuration(0)) {
     delay = TimeDuration(0);
   }
--- a/dom/base/TimeoutManager.h
+++ b/dom/base/TimeoutManager.h
@@ -198,17 +198,17 @@ class TimeoutManager final {
   // it must be a separate ref-counted object.
   RefPtr<TimeoutExecutor> mExecutor;
   // For timeouts run off the idle queue
   RefPtr<TimeoutExecutor> mIdleExecutor;
   // The list of timeouts coming from non-tracking scripts.
   Timeouts mTimeouts;
   uint32_t mTimeoutIdCounter;
   uint32_t mNextFiringId;
-#ifdef DEBUG
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
   int64_t mFiringIndex;
   int64_t mLastFiringIndex;
 #endif
   AutoTArray<uint32_t, 2> mFiringIdStack;
   mozilla::dom::Timeout* mRunningTimeout;
 
   // Timeouts that would have fired but are being deferred until MainThread
   // is idle (because we're loading)
--- a/dom/file/uri/BlobURLProtocolHandler.cpp
+++ b/dom/file/uri/BlobURLProtocolHandler.cpp
@@ -12,17 +12,16 @@
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/Exceptions.h"
 #include "mozilla/dom/BlobImpl.h"
 #include "mozilla/dom/IPCBlobUtils.h"
 #include "mozilla/dom/MediaSource.h"
 #include "mozilla/ipc/IPCStreamUtils.h"
 #include "mozilla/LoadInfo.h"
-#include "mozilla/ModuleUtils.h"
 #include "mozilla/NullPrincipal.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/SystemGroup.h"
 #include "nsClassHashtable.h"
 #include "nsContentUtils.h"
 #include "nsError.h"
 #include "nsIAsyncShutdown.h"
 #include "nsIException.h"  // for nsIStackFrame
@@ -898,43 +897,16 @@ nsresult NS_GetSourceForMediaSourceURI(n
   RefPtr<MediaSource> mediaSource = info->mMediaSource;
   mediaSource.forget(aSource);
   return NS_OK;
 }
 
 namespace mozilla {
 namespace dom {
 
-#define NS_BLOBPROTOCOLHANDLER_CID                   \
-  {                                                  \
-    0xb43964aa, 0xa078, 0x44b2, {                    \
-      0xb0, 0x6b, 0xfd, 0x4d, 0x1b, 0x17, 0x2e, 0x66 \
-    }                                                \
-  }
-
-NS_GENERIC_FACTORY_CONSTRUCTOR(BlobURLProtocolHandler)
-
-NS_DEFINE_NAMED_CID(NS_BLOBPROTOCOLHANDLER_CID);
-
-static const Module::CIDEntry kBlobURLProtocolHandlerCIDs[] = {
-    {&kNS_BLOBPROTOCOLHANDLER_CID, false, nullptr,
-     BlobURLProtocolHandlerConstructor},
-    {nullptr}};
-
-static const Module::ContractIDEntry kBlobURLProtocolHandlerContracts[] = {
-    {NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX BLOBURI_SCHEME,
-     &kNS_BLOBPROTOCOLHANDLER_CID},
-    {nullptr}};
-
-static const Module kBlobURLProtocolHandlerModule = {
-    Module::kVersion, kBlobURLProtocolHandlerCIDs,
-    kBlobURLProtocolHandlerContracts};
-
-NSMODULE_DEFN(BlobURLProtocolHandler) = &kBlobURLProtocolHandlerModule;
-
 bool IsType(nsIURI* aUri, DataInfo::ObjectType aType) {
   DataInfo* info = GetDataInfoFromURI(aUri);
   if (!info) {
     return false;
   }
 
   return info->mObjectType == aType;
 }
--- a/dom/file/uri/FontTableURIProtocolHandler.cpp
+++ b/dom/file/uri/FontTableURIProtocolHandler.cpp
@@ -1,17 +1,19 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #include "FontTableURIProtocolHandler.h"
-#include "mozilla/ModuleUtils.h"
+#include "nsIURIMutator.h"
+#include "nsIUUIDGenerator.h"
 #include "nsNetUtil.h"
+#include "nsSimpleURI.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 /* static */ nsresult FontTableURIProtocolHandler::GenerateURIString(
     nsACString &aUri) {
   nsresult rv;
   nsCOMPtr<nsIUUIDGenerator> uuidgen =
@@ -102,36 +104,8 @@ FontTableURIProtocolHandler::NewURI(cons
   if (NS_FAILED(uri->SchemeIs(FONTTABLEURI_SCHEME, &schemeIs)) || !schemeIs) {
     NS_WARNING("Non-fonttable spec in FontTableURIProtocolHandler");
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   uri.forget(aResult);
   return NS_OK;
 }
-
-#define NS_FONTTABLEPROTOCOLHANDLER_CID              \
-  {                                                  \
-    0x3fc8f04e, 0xd719, 0x43ca, {                    \
-      0x9a, 0xd0, 0x18, 0xee, 0x32, 0x02, 0x11, 0xf2 \
-    }                                                \
-  }
-
-NS_GENERIC_FACTORY_CONSTRUCTOR(FontTableURIProtocolHandler)
-
-NS_DEFINE_NAMED_CID(NS_FONTTABLEPROTOCOLHANDLER_CID);
-
-static const mozilla::Module::CIDEntry FontTableURIProtocolHandlerCIDs[] = {
-    {&kNS_FONTTABLEPROTOCOLHANDLER_CID, false, nullptr,
-     FontTableURIProtocolHandlerConstructor},
-    {nullptr}};
-
-static const mozilla::Module::ContractIDEntry
-    FontTableURIProtocolHandlerContracts[] = {
-        {NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX FONTTABLEURI_SCHEME,
-         &kNS_FONTTABLEPROTOCOLHANDLER_CID},
-        {nullptr}};
-
-static const mozilla::Module FontTableURIProtocolHandlerModule = {
-    mozilla::Module::kVersion, FontTableURIProtocolHandlerCIDs,
-    FontTableURIProtocolHandlerContracts};
-
-NSMODULE_DEFN(FontTableURIProtocolHandler) = &FontTableURIProtocolHandlerModule;
new file mode 100644
--- /dev/null
+++ b/dom/file/uri/components.conf
@@ -0,0 +1,20 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Classes = [
+    {
+        'cid': '{3fc8f04e-d719-43ca-9ad0-18ee320211f2}',
+        'contract_ids': ['@mozilla.org/network/protocol;1?name=moz-fonttable'],
+        'type': 'mozilla::dom::FontTableURIProtocolHandler',
+        'headers': ['mozilla/dom/FontTableURIProtocolHandler.h'],
+    },
+    {
+        'cid': '{b43964aa-a078-44b2-b06b-fd4d1b172e66}',
+        'contract_ids': ['@mozilla.org/network/protocol;1?name=blob'],
+        'type': 'mozilla::dom::BlobURLProtocolHandler',
+        'headers': ['mozilla/dom/BlobURLProtocolHandler.h'],
+    },
+]
--- a/dom/file/uri/moz.build
+++ b/dom/file/uri/moz.build
@@ -15,16 +15,20 @@ EXPORTS.mozilla.dom += [
 
 UNIFIED_SOURCES += [
     'BlobURL.cpp',
     'BlobURLChannel.cpp',
     'BlobURLProtocolHandler.cpp',
     'FontTableURIProtocolHandler.cpp',
 ]
 
+XPCOM_MANIFESTS += [
+    'components.conf',
+]
+
 LOCAL_INCLUDES += [
     '/dom/file',
     '/netwerk/base',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
--- a/dom/media/webspeech/synth/moz.build
+++ b/dom/media/webspeech/synth/moz.build
@@ -33,20 +33,23 @@ if CONFIG['MOZ_WEBSPEECH']:
         'nsSynthVoiceRegistry.cpp',
         'SpeechSynthesis.cpp',
         'SpeechSynthesisUtterance.cpp',
         'SpeechSynthesisVoice.cpp',
     ]
 
     if CONFIG['MOZ_WEBSPEECH_TEST_BACKEND']:
         UNIFIED_SOURCES += [
-            'test/FakeSynthModule.cpp',
             'test/nsFakeSynthServices.cpp'
         ]
 
+        XPCOM_MANIFESTS += [
+            'test/components.conf',
+        ]
+
     if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
         DIRS += ['windows']
 
     if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
         DIRS += ['cocoa']
 
     if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
         DIRS += ['android']
deleted file mode 100644
--- a/dom/media/webspeech/synth/speechd/SpeechDispatcherModule.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* 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/. */
-
-#include "mozilla/ModuleUtils.h"
-#include "nsIClassInfoImpl.h"
-#include "SpeechDispatcherService.h"
-
-using namespace mozilla::dom;
-
-#define SPEECHDISPATCHERSERVICE_CID                  \
-  {                                                  \
-    0x8817b1cf, 0x5ada, 0x43bf, {                    \
-      0xbd, 0x73, 0x60, 0x76, 0x57, 0x70, 0x3d, 0x0d \
-    }                                                \
-  }
-
-#define SPEECHDISPATCHERSERVICE_CONTRACTID \
-  "@mozilla.org/synthspeechdispatcher;1"
-
-// Defines SpeechDispatcherServiceConstructor
-NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(
-    SpeechDispatcherService, SpeechDispatcherService::GetInstanceForService)
-
-// Defines kSPEECHDISPATCHERSERVICE_CID
-NS_DEFINE_NAMED_CID(SPEECHDISPATCHERSERVICE_CID);
-
-static const mozilla::Module::CIDEntry kCIDs[] = {
-    {&kSPEECHDISPATCHERSERVICE_CID, true, nullptr,
-     SpeechDispatcherServiceConstructor},
-    {nullptr}};
-
-static const mozilla::Module::ContractIDEntry kContracts[] = {
-    {SPEECHDISPATCHERSERVICE_CONTRACTID, &kSPEECHDISPATCHERSERVICE_CID},
-    {nullptr}};
-
-static const mozilla::Module::CategoryEntry kCategories[] = {
-    {"speech-synth-started", "SpeechDispatcher Speech Synth",
-     SPEECHDISPATCHERSERVICE_CONTRACTID},
-    {nullptr}};
-
-static const mozilla::Module kModule = {
-    mozilla::Module::kVersion,
-    kCIDs,
-    kContracts,
-    kCategories,
-    nullptr,
-    nullptr,
-    nullptr,
-};
-
-NSMODULE_DEFN(synthspeechdispatcher) = &kModule;
new file mode 100644
--- /dev/null
+++ b/dom/media/webspeech/synth/speechd/components.conf
@@ -0,0 +1,17 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Classes = [
+    {
+        'cid': '{8817b1cf-5ada-43bf-bd73-607657703d0d}',
+        'contract_ids': ['@mozilla.org/synthspeechdispatcher;1'],
+        'singleton': True,
+        'type': 'mozilla::dom::SpeechDispatcherService',
+        'headers': ['/dom/media/webspeech/synth/speechd/SpeechDispatcherService.h'],
+        'constructor': 'mozilla::dom::SpeechDispatcherService::GetInstanceForService',
+        'categories': {"speech-synth-started": 'SpeechDispatcher Speech Synth'},
+    },
+]
--- a/dom/media/webspeech/synth/speechd/moz.build
+++ b/dom/media/webspeech/synth/speechd/moz.build
@@ -1,13 +1,17 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 UNIFIED_SOURCES += [
-  'SpeechDispatcherModule.cpp',
   'SpeechDispatcherService.cpp'
 ]
+
+XPCOM_MANIFESTS += [
+    'components.conf',
+]
+
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
deleted file mode 100644
--- a/dom/media/webspeech/synth/test/FakeSynthModule.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-/* 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/. */
-
-#include "mozilla/ModuleUtils.h"
-#include "nsIClassInfoImpl.h"
-
-#include "nsFakeSynthServices.h"
-
-using namespace mozilla::dom;
-
-#define FAKESYNTHSERVICE_CID                         \
-  {                                                  \
-    0xe7d52d9e, 0xc148, 0x47d8, {                    \
-      0xab, 0x2a, 0x95, 0xd7, 0xf4, 0x0e, 0xa5, 0x3d \
-    }                                                \
-  }
-
-#define FAKESYNTHSERVICE_CONTRACTID "@mozilla.org/fakesynth;1"
-
-// Defines nsFakeSynthServicesConstructor
-NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(
-    nsFakeSynthServices, nsFakeSynthServices::GetInstanceForService)
-
-// Defines kFAKESYNTHSERVICE_CID
-NS_DEFINE_NAMED_CID(FAKESYNTHSERVICE_CID);
-
-static const mozilla::Module::CIDEntry kCIDs[] = {
-    {&kFAKESYNTHSERVICE_CID, true, nullptr, nsFakeSynthServicesConstructor},
-    {nullptr}};
-
-static const mozilla::Module::ContractIDEntry kContracts[] = {
-    {FAKESYNTHSERVICE_CONTRACTID, &kFAKESYNTHSERVICE_CID}, {nullptr}};
-
-static const mozilla::Module::CategoryEntry kCategories[] = {
-    {"speech-synth-started", "Fake Speech Synth", FAKESYNTHSERVICE_CONTRACTID},
-    {nullptr}};
-
-static void UnloadFakeSynthmodule() { nsFakeSynthServices::Shutdown(); }
-
-static const mozilla::Module kModule = {
-    mozilla::Module::kVersion, kCIDs, kContracts, kCategories, nullptr, nullptr,
-    UnloadFakeSynthmodule};
-
-NSMODULE_DEFN(fakesynth) = &kModule;
new file mode 100644
--- /dev/null
+++ b/dom/media/webspeech/synth/test/components.conf
@@ -0,0 +1,17 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Classes = [
+    {
+        'cid': '{e7d52d9e-c148-47d8-ab2a-95d7f40ea53d}',
+        'contract_ids': ['@mozilla.org/fakesynth;1'],
+        'singleton': True,
+        'type': 'mozilla::dom::nsFakeSynthServices',
+        'headers': ['/dom/media/webspeech/synth/test/nsFakeSynthServices.h'],
+        'constructor': 'mozilla::dom::nsFakeSynthServices::GetInstanceForService',
+        'categories': {'speech-synth-started': 'Fake Speech Synth'},
+    },
+]
--- a/dom/media/webspeech/synth/test/nsFakeSynthServices.cpp
+++ b/dom/media/webspeech/synth/test/nsFakeSynthServices.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.h"
 #include "nsFakeSynthServices.h"
 #include "nsPrintfCString.h"
 #include "SharedBuffer.h"
 #include "nsISimpleEnumerator.h"
 
+#include "mozilla/ClearOnShutdown.h"
 #include "mozilla/dom/nsSynthVoiceRegistry.h"
 #include "mozilla/dom/nsSpeechTask.h"
 
 #include "nsThreadUtils.h"
 #include "prenv.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/DebugOnly.h"
 
@@ -268,29 +269,22 @@ nsFakeSynthServices* nsFakeSynthServices
   if (!XRE_IsParentProcess()) {
     MOZ_ASSERT(false,
                "nsFakeSynthServices can only be started on main gecko process");
     return nullptr;
   }
 
   if (!sSingleton) {
     sSingleton = new nsFakeSynthServices();
+    ClearOnShutdown(&sSingleton);
   }
 
   return sSingleton;
 }
 
 already_AddRefed<nsFakeSynthServices>
 nsFakeSynthServices::GetInstanceForService() {
   RefPtr<nsFakeSynthServices> picoService = GetInstance();
   return picoService.forget();
 }
 
-void nsFakeSynthServices::Shutdown() {
-  if (!sSingleton) {
-    return;
-  }
-
-  sSingleton = nullptr;
-}
-
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/media/webspeech/synth/test/nsFakeSynthServices.h
+++ b/dom/media/webspeech/synth/test/nsFakeSynthServices.h
@@ -24,18 +24,16 @@ class nsFakeSynthServices : public nsIOb
   NS_DECL_NSIOBSERVER
 
   nsFakeSynthServices() = default;
 
   static nsFakeSynthServices* GetInstance();
 
   static already_AddRefed<nsFakeSynthServices> GetInstanceForService();
 
-  static void Shutdown();
-
  private:
   virtual ~nsFakeSynthServices() = default;
 
   void Init();
 
   nsCOMPtr<nsISpeechService> mSynthService;
 
   static StaticRefPtr<nsFakeSynthServices> sSingleton;
new file mode 100644
--- /dev/null
+++ b/dom/plugins/base/components.conf
@@ -0,0 +1,16 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Classes = [
+    {
+        'cid': '{23e8fd98-a625-4b08-be1a-f7cc18a5b106}',
+        'contract_ids': ['@mozilla.org/plugin/host;1'],
+        'singleton': True,
+        'type': 'nsPluginHost',
+        'headers': ['nsPluginHost.h'],
+        'constructor': 'nsPluginHost::GetInst',
+    },
+]
--- a/dom/plugins/base/moz.build
+++ b/dom/plugins/base/moz.build
@@ -33,17 +33,16 @@ EXPORTS += [
     'nsPluginTags.h',
 ]
 
 UNIFIED_SOURCES += [
     'nsJSNPRuntime.cpp',
     'nsNPAPIPluginInstance.cpp',
     'nsNPAPIPluginStreamListener.cpp',
     'nsPluginInstanceOwner.cpp',
-    'nsPluginModule.cpp',
     'nsPluginStreamListenerPeer.cpp',
     'nsPluginTags.cpp',
 ]
 
 SOURCES += [
     'nsNPAPIPlugin.cpp', # Conflict with X11 headers
     'nsPluginHost.cpp',  # Conflict with NS_NPAPIPLUGIN_CALLBACK
 ]
@@ -62,16 +61,20 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'co
         'nsPluginsDirDarwin.cpp', # conflict with mozilla::EventPriority
     ]
 else:
     UNIFIED_SOURCES += [
         'nsPluginNativeWindow.cpp',
         'nsPluginsDirUnix.cpp',
     ]
 
+XPCOM_MANIFESTS += [
+    'components.conf',
+]
+
 LOCAL_INCLUDES += [
     '/dom/base',
     '/dom/plugins/ipc',
     '/layout/generic',
     '/layout/xul',
     '/netwerk/base',
     '/widget',
     '/widget/cocoa',
deleted file mode 100644
--- a/dom/plugins/base/nsPluginModule.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-#include "mozilla/ModuleUtils.h"
-#include "nsPluginHost.h"
-#include "nsPluginsCID.h"
-
-NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsPluginHost, nsPluginHost::GetInst)
-NS_DEFINE_NAMED_CID(NS_PLUGIN_HOST_CID);
-
-static const mozilla::Module::CIDEntry kPluginCIDs[] = {
-    {&kNS_PLUGIN_HOST_CID, false, nullptr, nsPluginHostConstructor}, {nullptr}};
-
-static const mozilla::Module::ContractIDEntry kPluginContracts[] = {
-    {MOZ_PLUGIN_HOST_CONTRACTID, &kNS_PLUGIN_HOST_CID}, {nullptr}};
-
-static const mozilla::Module kPluginModule = {mozilla::Module::kVersion,
-                                              kPluginCIDs, kPluginContracts};
-
-NSMODULE_DEFN(nsPluginModule) = &kPluginModule;
deleted file mode 100644
--- a/dom/presentation/provider/PresentationDeviceProviderModule.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* 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/. */
-
-#include "MulticastDNSDeviceProvider.h"
-#include "mozilla/ModuleUtils.h"
-
-#define MULTICAST_DNS_PROVIDER_CID                   \
-  {                                                  \
-    0x814f947a, 0x52f7, 0x41c9, {                    \
-      0x94, 0xa1, 0x36, 0x84, 0x79, 0x72, 0x84, 0xac \
-    }                                                \
-  }
-
-#define MULTICAST_DNS_PROVIDER_CONTRACT_ID \
-  "@mozilla.org/presentation-device/multicastdns-provider;1"
-
-using mozilla::dom::presentation::MulticastDNSDeviceProvider;
-
-NS_GENERIC_FACTORY_CONSTRUCTOR(MulticastDNSDeviceProvider)
-NS_DEFINE_NAMED_CID(MULTICAST_DNS_PROVIDER_CID);
-
-static const mozilla::Module::CIDEntry kPresentationDeviceProviderCIDs[] = {
-    {&kMULTICAST_DNS_PROVIDER_CID, false, nullptr,
-     MulticastDNSDeviceProviderConstructor},
-    {nullptr}};
-
-static const mozilla::Module::ContractIDEntry
-    kPresentationDeviceProviderContracts[] = {
-        {MULTICAST_DNS_PROVIDER_CONTRACT_ID, &kMULTICAST_DNS_PROVIDER_CID},
-        {nullptr}};
-
-static const mozilla::Module::CategoryEntry
-    kPresentationDeviceProviderCategories[] = {
-#if defined(MOZ_WIDGET_COCOA) || defined(MOZ_WIDGET_ANDROID)
-        {PRESENTATION_DEVICE_PROVIDER_CATEGORY, "MulticastDNSDeviceProvider",
-         MULTICAST_DNS_PROVIDER_CONTRACT_ID},
-#endif
-        {nullptr}};
-
-static const mozilla::Module kPresentationDeviceProviderModule = {
-    mozilla::Module::kVersion, kPresentationDeviceProviderCIDs,
-    kPresentationDeviceProviderContracts,
-    kPresentationDeviceProviderCategories};
-
-NSMODULE_DEFN(PresentationDeviceProviderModule) =
-    &kPresentationDeviceProviderModule;
--- a/dom/presentation/provider/components.conf
+++ b/dom/presentation/provider/components.conf
@@ -1,21 +1,33 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
+categories = {}
+
+if buildconfig.substs['MOZ_WIDGET_TOOLKIT'] in ('cocoa', 'android'):
+    categories["presentation-device-provider"] = "MulticastDNSDeviceProvider"
+
 Classes = [
     {
         'cid': '{f4079b8b-ede5-4b90-a112-5b415a931deb}',
         'contract_ids': ['@mozilla.org/presentation/control-service;1'],
         'jsm': 'resource://gre/modules/PresentationControlService.jsm',
         'constructor': 'PresentationControlService',
     },
+    {
+        'cid': '{814f947a-52f7-41c9-94a1-3684797284ac}',
+        'contract_ids': ['@mozilla.org/presentation-device/multicastdns-provider;1'],
+        'type': 'mozilla::dom::presentation::MulticastDNSDeviceProvider',
+        'headers': ['/dom/presentation/provider/MulticastDNSDeviceProvider.h'],
+        'categories': categories,
+    },
 ]
 
 if buildconfig.substs['MOZ_WIDGET_TOOLKIT'] == 'android':
     Classes += [
         {
             'cid': '{7394f24c-dbc3-48c8-8a47-cd10169b7c6b}',
             'contract_ids': ['@mozilla.org/presentation-device/android-cast-device-provider;1'],
             'jsm': 'resource://gre/modules/AndroidCastDeviceProvider.jsm',
--- a/dom/presentation/provider/moz.build
+++ b/dom/presentation/provider/moz.build
@@ -6,17 +6,20 @@
 
 EXTRA_JS_MODULES += [
     'PresentationControlService.jsm'
 ]
 
 UNIFIED_SOURCES += [
     'DeviceProviderHelpers.cpp',
     'MulticastDNSDeviceProvider.cpp',
-    'PresentationDeviceProviderModule.cpp',
+]
+
+XPCOM_MANIFESTS += [
+    'components.conf',
 ]
 
 XPCOM_MANIFESTS += [
     'components.conf',
 ]
 
 EXTRA_JS_MODULES.presentation += [
     'ControllerStateMachine.jsm',
new file mode 100644
--- /dev/null
+++ b/extensions/cookie/components.conf
@@ -0,0 +1,22 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Headers = [
+    '/extensions/cookie/nsCookieModule.h',
+]
+
+UnloadFunc = 'mozilla::CookieModuleDtor'
+
+Classes = [
+    {
+        'cid': '{4f6b5e00-0c36-11d5-a535-0010a401eb10}',
+        'contract_ids': ['@mozilla.org/permissionmanager;1'],
+        'singleton': True,
+        'type': 'nsIPermissionManager',
+        'constructor': 'nsPermissionManager::GetXPCOMSingleton',
+        'headers': ['/extensions/cookie/nsPermissionManager.h'],
+    },
+]
--- a/extensions/cookie/moz.build
+++ b/extensions/cookie/moz.build
@@ -12,16 +12,20 @@ EXPORTS += [
 
 UNIFIED_SOURCES += [
     'nsCookieModule.cpp',
     'nsCookiePermission.cpp',
     'nsPermission.cpp',
     'nsPermissionManager.cpp',
 ]
 
+XPCOM_MANIFESTS += [
+    'components.conf',
+]
+
 LOCAL_INCLUDES += [
     '/caps',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
--- a/extensions/cookie/nsCookieModule.cpp
+++ b/extensions/cookie/nsCookieModule.cpp
@@ -1,37 +1,12 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/. */
 
-#include "mozilla/ModuleUtils.h"
-#include "nsIServiceManager.h"
-#include "nsPermissionManager.h"
-#include "nsICategoryManager.h"
 #include "nsCookiePermission.h"
-#include "nsString.h"
 
-// Define the constructor function for the objects
-NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIPermissionManager,
-                                         nsPermissionManager::GetXPCOMSingleton)
-
-NS_DEFINE_NAMED_CID(NS_PERMISSIONMANAGER_CID);
+namespace mozilla {
 
-static const mozilla::Module::CIDEntry kCookieCIDs[] = {
-    {&kNS_PERMISSIONMANAGER_CID, false, nullptr,
-     nsIPermissionManagerConstructor},
-    {nullptr}};
-
-static const mozilla::Module::ContractIDEntry kCookieContracts[] = {
-    {NS_PERMISSIONMANAGER_CONTRACTID, &kNS_PERMISSIONMANAGER_CID}, {nullptr}};
-
-static void CookieModuleDtor() { nsCookiePermission::Shutdown(); }
+void CookieModuleDtor() { nsCookiePermission::Shutdown(); }
 
-static const mozilla::Module kCookieModule = {mozilla::Module::kVersion,
-                                              kCookieCIDs,
-                                              kCookieContracts,
-                                              nullptr,
-                                              nullptr,
-                                              nullptr,
-                                              CookieModuleDtor};
-
-NSMODULE_DEFN(nsCookieModule) = &kCookieModule;
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/extensions/cookie/nsCookieModule.h
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef nsCookieModule_h
+#define nsCookieModule_h
+
+#include "nscore.h"
+
+namespace mozilla {
+
+void CookieModuleDtor();
+
+}  // namespace mozilla
+
+#endif
+
new file mode 100644
--- /dev/null
+++ b/extensions/permissions/components.conf
@@ -0,0 +1,16 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Classes = [
+    {
+        'cid': '{4ca6b67b-5cc7-4e71-a98a-97af1c134862}',
+        'contract_ids': ['@mozilla.org/permissions/contentblocker;1'],
+        'type': 'nsContentBlocker',
+        'headers': ['/extensions/permissions/nsContentBlocker.h'],
+        'init_method': 'Init',
+        'categories': {'content-policy': '@mozilla.org/permissions/contentblocker;1'},
+    },
+]
--- a/extensions/permissions/moz.build
+++ b/extensions/permissions/moz.build
@@ -1,15 +1,18 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 UNIFIED_SOURCES += [
     'nsContentBlocker.cpp',
-    'nsModuleFactory.cpp',
+]
+
+XPCOM_MANIFESTS += [
+    'components.conf',
 ]
 
 FINAL_LIBRARY = 'xul'
 
 with Files('**'):
     BUG_COMPONENT = ('Core', 'DOM')
deleted file mode 100644
--- a/extensions/permissions/nsModuleFactory.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-/* 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/. */
-
-#include "mozilla/ModuleUtils.h"
-#include "nsIServiceManager.h"
-#include "nsContentBlocker.h"
-#include "nsString.h"
-
-// Define the constructor function for the objects
-NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsContentBlocker, Init)
-
-NS_DEFINE_NAMED_CID(NS_CONTENTBLOCKER_CID);
-
-static const mozilla::Module::CIDEntry kPermissionsCIDs[] = {
-    {&kNS_CONTENTBLOCKER_CID, false, nullptr, nsContentBlockerConstructor},
-    {nullptr}};
-
-static const mozilla::Module::ContractIDEntry kPermissionsContracts[] = {
-    {NS_CONTENTBLOCKER_CONTRACTID, &kNS_CONTENTBLOCKER_CID}, {nullptr}};
-
-static const mozilla::Module::CategoryEntry kPermissionsCategories[] = {
-    {"content-policy", NS_CONTENTBLOCKER_CONTRACTID,
-     NS_CONTENTBLOCKER_CONTRACTID},
-    {nullptr}};
-
-static const mozilla::Module kPermissionsModule = {
-    mozilla::Module::kVersion, kPermissionsCIDs, kPermissionsContracts,
-    kPermissionsCategories};
-
-NSMODULE_DEFN(nsPermissionsModule) = &kPermissionsModule;
new file mode 100644
--- /dev/null
+++ b/extensions/pref/autoconfig/src/components.conf
@@ -0,0 +1,16 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Classes = [
+    {
+        'cid': '{ba5bc4c6-1dd1-11b2-bb89-b844c6ec0339}',
+        'contract_ids': ['@mozilla.org/readconfig;1'],
+        'type': 'nsReadConfig',
+        'headers': ['/extensions/pref/autoconfig/src/nsReadConfig.h'],
+        'init_method': 'Init',
+        'categories': {'pref-config-startup': 'ReadConfig Module'},
+    },
+]
--- a/extensions/pref/autoconfig/src/moz.build
+++ b/extensions/pref/autoconfig/src/moz.build
@@ -1,18 +1,21 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 UNIFIED_SOURCES += [
     'nsAutoConfig.cpp',
-    'nsConfigFactory.cpp',
     'nsJSConfigTriggers.cpp',
     'nsReadConfig.cpp',
 ]
 
+XPCOM_MANIFESTS += [
+    'components.conf',
+]
+
 FINAL_LIBRARY = 'xul'
 
 FINAL_TARGET_FILES.defaults.autoconfig += [
     'prefcalls.js',
 ]
deleted file mode 100644
--- a/extensions/pref/autoconfig/src/nsConfigFactory.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-#include "mozilla/ModuleUtils.h"
-#include "nsReadConfig.h"
-#include "nsIAppStartupNotifier.h"
-
-#define NS_READCONFIG_CID                            \
-  {                                                  \
-    0xba5bc4c6, 0x1dd1, 0x11b2, {                    \
-      0xbb, 0x89, 0xb8, 0x44, 0xc6, 0xec, 0x03, 0x39 \
-    }                                                \
-  }
-
-#define NS_READCONFIG_CONTRACTID "@mozilla.org/readconfig;1"
-
-NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsReadConfig, Init)
-
-NS_DEFINE_NAMED_CID(NS_READCONFIG_CID);
-
-static const mozilla::Module::CIDEntry kAutoConfigCIDs[] = {
-    {&kNS_READCONFIG_CID, false, nullptr, nsReadConfigConstructor}, {nullptr}};
-
-static const mozilla::Module::ContractIDEntry kAutoConfigContracts[] = {
-    {NS_READCONFIG_CONTRACTID, &kNS_READCONFIG_CID}, {nullptr}};
-
-static const mozilla::Module::CategoryEntry kAutoConfigCategories[] = {
-    {"pref-config-startup", "ReadConfig Module", NS_READCONFIG_CONTRACTID},
-    {nullptr}};
-
-static const mozilla::Module kAutoConfigModule = {
-    mozilla::Module::kVersion, kAutoConfigCIDs, kAutoConfigContracts,
-    kAutoConfigCategories};
-
-NSMODULE_DEFN(nsAutoConfigModule) = &kAutoConfigModule;
--- a/extensions/spellcheck/hunspell/glue/mozHunspell.cpp
+++ b/extensions/spellcheck/hunspell/glue/mozHunspell.cpp
@@ -67,16 +67,17 @@
 #include "nsUnicharUtils.h"
 #include "nsCRT.h"
 #include "mozInlineSpellChecker.h"
 #include <stdlib.h>
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 #include "nsNetUtil.h"
 #include "mozilla/dom/ContentParent.h"
+#include "mozilla/Components.h"
 
 using mozilla::dom::ContentParent;
 using namespace mozilla;
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(mozHunspell)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(mozHunspell)
 
 NS_INTERFACE_MAP_BEGIN(mozHunspell)
@@ -85,16 +86,24 @@ NS_INTERFACE_MAP_BEGIN(mozHunspell)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   NS_INTERFACE_MAP_ENTRY(nsIMemoryReporter)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozISpellCheckingEngine)
   NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(mozHunspell)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTION(mozHunspell, mPersonalDictionary)
 
+NS_IMPL_COMPONENT_FACTORY(mozHunspell) {
+  auto hunspell = MakeRefPtr<mozHunspell>();
+  if (NS_SUCCEEDED(hunspell->Init())) {
+    return hunspell.forget().downcast<mozISpellCheckingEngine>();
+  }
+  return nullptr;
+}
+
 template <>
 mozilla::CountingAllocatorBase<HunspellAllocator>::AmountType
     mozilla::CountingAllocatorBase<HunspellAllocator>::sAmount(0);
 
 mozHunspell::mozHunspell() : mHunspell(nullptr) {
 #ifdef DEBUG
   // There must be only one instance of this class: it reports memory based on
   // a single static count in HunspellAllocator.
new file mode 100644
--- /dev/null
+++ b/extensions/spellcheck/src/components.conf
@@ -0,0 +1,20 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Classes = [
+    {
+        'cid': '{56c778e4-1bee-45f3-a689-886692a97fe7}',
+        'contract_ids': ['@mozilla.org/spellchecker/engine;1'],
+        'type': 'mozHunspell',
+    },
+    {
+        'cid': '{7ef52eaf-b7e1-462b-87e2-5d1dbaca9048}',
+        'contract_ids': ['@mozilla.org/spellchecker/personaldictionary;1'],
+        'type': 'mozPersonalDictionary',
+        'headers': ['/extensions/spellcheck/src/mozPersonalDictionary.h'],
+        'init_method': 'Init',
+    },
+]
--- a/extensions/spellcheck/src/moz.build
+++ b/extensions/spellcheck/src/moz.build
@@ -6,17 +6,20 @@
 
 include('/ipc/chromium/chromium-config.mozbuild')
 UNIFIED_SOURCES += [
     'mozEnglishWordUtils.cpp',
     'mozInlineSpellChecker.cpp',
     'mozInlineSpellWordUtil.cpp',
     'mozPersonalDictionary.cpp',
     'mozSpellChecker.cpp',
-    'mozSpellCheckerFactory.cpp',
+]
+
+XPCOM_MANIFESTS += [
+    'components.conf',
 ]
 
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '../hunspell/glue',
     '../hunspell/src',
     '/dom/base',
deleted file mode 100644
--- a/extensions/spellcheck/src/mozSpellCheckerFactory.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-#include "mozilla/ModuleUtils.h"
-#include "mozHunspell.h"
-#include "mozPersonalDictionary.h"
-#include "nsIFile.h"
-
-NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(mozHunspell, Init)
-NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(mozPersonalDictionary, Init)
-
-NS_DEFINE_NAMED_CID(MOZ_HUNSPELL_CID);
-NS_DEFINE_NAMED_CID(MOZ_PERSONALDICTIONARY_CID);
-
-static const mozilla::Module::CIDEntry kSpellcheckCIDs[] = {
-    {&kMOZ_HUNSPELL_CID, false, nullptr, mozHunspellConstructor},
-    {&kMOZ_PERSONALDICTIONARY_CID, false, nullptr,
-     mozPersonalDictionaryConstructor},
-    {nullptr}};
-
-static const mozilla::Module::ContractIDEntry kSpellcheckContracts[] = {
-    {MOZ_HUNSPELL_CONTRACTID, &kMOZ_HUNSPELL_CID},
-    {MOZ_PERSONALDICTIONARY_CONTRACTID, &kMOZ_PERSONALDICTIONARY_CID},
-    {nullptr}};
-
-const mozilla::Module kSpellcheckModule = {
-    mozilla::Module::kVersion, kSpellcheckCIDs, kSpellcheckContracts, nullptr};
-
-NSMODULE_DEFN(mozSpellCheckerModule) = &kSpellcheckModule;
--- a/gfx/2d/BufferUnrotate.cpp
+++ b/gfx/2d/BufferUnrotate.cpp
@@ -1,14 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
+#include "BufferUnrotate.h"
+
 #include <algorithm>  // min & max
 #include <cstdlib>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 namespace mozilla {
--- a/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -187,24 +187,26 @@ static bool PatternIsCompatible(const Pa
     }
     default:
       return true;
   }
 }
 
 static cairo_user_data_key_t surfaceDataKey;
 
-void ReleaseData(void* aData) {
+static void ReleaseData(void* aData) {
   DataSourceSurface* data = static_cast<DataSourceSurface*>(aData);
   data->Unmap();
   data->Release();
 }
 
-cairo_surface_t* CopyToImageSurface(unsigned char* aData, const IntRect& aRect,
-                                    int32_t aStride, SurfaceFormat aFormat) {
+static cairo_surface_t* CopyToImageSurface(unsigned char* aData,
+                                           const IntRect& aRect,
+                                           int32_t aStride,
+                                           SurfaceFormat aFormat) {
   MOZ_ASSERT(aData);
 
   auto aRectWidth = aRect.Width();
   auto aRectHeight = aRect.Height();
 
   cairo_surface_t* surf = cairo_image_surface_create(
       GfxFormatToCairoFormat(aFormat), aRectWidth, aRectHeight);
   // In certain scenarios, requesting larger than 8k image fails.  Bug 803568
@@ -231,31 +233,31 @@ cairo_surface_t* CopyToImageSurface(unsi
   return surf;
 }
 
 /**
  * If aSurface can be represented as a surface of type
  * CAIRO_SURFACE_TYPE_IMAGE then returns that surface. Does
  * not add a reference.
  */
-cairo_surface_t* GetAsImageSurface(cairo_surface_t* aSurface) {
+static cairo_surface_t* GetAsImageSurface(cairo_surface_t* aSurface) {
   if (cairo_surface_get_type(aSurface) == CAIRO_SURFACE_TYPE_IMAGE) {
     return aSurface;
 #ifdef CAIRO_HAS_WIN32_SURFACE
   } else if (cairo_surface_get_type(aSurface) == CAIRO_SURFACE_TYPE_WIN32) {
     return cairo_win32_surface_get_image(aSurface);
 #endif
   }
 
   return nullptr;
 }
 
-cairo_surface_t* CreateSubImageForData(unsigned char* aData,
-                                       const IntRect& aRect, int aStride,
-                                       SurfaceFormat aFormat) {
+static cairo_surface_t* CreateSubImageForData(unsigned char* aData,
+                                              const IntRect& aRect, int aStride,
+                                              SurfaceFormat aFormat) {
   if (!aData) {
     gfxWarning() << "DrawTargetCairo.CreateSubImageForData null aData";
     return nullptr;
   }
   unsigned char* data =
       aData + aRect.Y() * aStride + aRect.X() * BytesPerPixel(aFormat);
 
   cairo_surface_t* image = cairo_image_surface_create_for_data(
@@ -264,19 +266,19 @@ cairo_surface_t* CreateSubImageForData(u
   cairo_surface_set_device_offset(image, -aRect.X(), -aRect.Y());
   return image;
 }
 
 /**
  * Returns a referenced cairo_surface_t representing the
  * sub-image specified by aSubImage.
  */
-cairo_surface_t* ExtractSubImage(cairo_surface_t* aSurface,
-                                 const IntRect& aSubImage,
-                                 SurfaceFormat aFormat) {
+static cairo_surface_t* ExtractSubImage(cairo_surface_t* aSurface,
+                                        const IntRect& aSubImage,
+                                        SurfaceFormat aFormat) {
   // No need to worry about retaining a reference to the original
   // surface since the only caller of this function guarantees
   // that aSurface will stay alive as long as the result
 
   cairo_surface_t* image = GetAsImageSurface(aSurface);
   if (image) {
     image =
         CreateSubImageForData(cairo_image_surface_get_data(image), aSubImage,
@@ -300,17 +302,17 @@ cairo_surface_t* ExtractSubImage(cairo_s
 
 /**
  * Returns cairo surface for the given SourceSurface.
  * If possible, it will use the cairo_surface associated with aSurface,
  * otherwise, it will create a new cairo_surface.
  * In either case, the caller must call cairo_surface_destroy on the
  * result when it is done with it.
  */
-cairo_surface_t* GetCairoSurfaceForSourceSurface(
+static cairo_surface_t* GetCairoSurfaceForSourceSurface(
     SourceSurface* aSurface, bool aExistingOnly = false,
     const IntRect& aSubImage = IntRect()) {
   if (!aSurface) {
     return nullptr;
   }
 
   IntRect subimage = IntRect(IntPoint(), aSurface->GetSize());
   if (!aSubImage.IsEmpty()) {
--- a/gfx/2d/DrawTargetRecording.cpp
+++ b/gfx/2d/DrawTargetRecording.cpp
@@ -18,17 +18,17 @@
 namespace mozilla {
 namespace gfx {
 
 struct RecordingSourceSurfaceUserData {
   void *refPtr;
   RefPtr<DrawEventRecorderPrivate> recorder;
 };
 
-void RecordingSourceSurfaceUserDataFunc(void *aUserData) {
+static void RecordingSourceSurfaceUserDataFunc(void *aUserData) {
   RecordingSourceSurfaceUserData *userData =
       static_cast<RecordingSourceSurfaceUserData *>(aUserData);
 
   userData->recorder->RemoveSourceSurface((SourceSurface *)userData->refPtr);
   userData->recorder->RemoveStoredObject(userData->refPtr);
   userData->recorder->RecordEvent(
       RecordedSourceSurfaceDestruction(ReferencePtr(userData->refPtr)));
 
@@ -264,17 +264,17 @@ void DrawTargetRecording::Fill(const Pat
   mRecorder->RecordEvent(RecordedFill(this, pathRecording, aPattern, aOptions));
 }
 
 struct RecordingFontUserData {
   void *refPtr;
   RefPtr<DrawEventRecorderPrivate> recorder;
 };
 
-void RecordingFontUserDataDestroyFunc(void *aUserData) {
+static void RecordingFontUserDataDestroyFunc(void *aUserData) {
   RecordingFontUserData *userData =
       static_cast<RecordingFontUserData *>(aUserData);
 
   userData->recorder->RecordEvent(
       RecordedScaledFontDestruction(ReferencePtr(userData->refPtr)));
   userData->recorder->RemoveScaledFont((ScaledFont *)userData->refPtr);
   delete userData;
 }
--- a/gfx/2d/DrawTargetWrapAndRecord.cpp
+++ b/gfx/2d/DrawTargetWrapAndRecord.cpp
@@ -18,17 +18,17 @@
 namespace mozilla {
 namespace gfx {
 
 struct WrapAndRecordSourceSurfaceUserData {
   void *refPtr;
   RefPtr<DrawEventRecorderPrivate> recorder;
 };
 
-void WrapAndRecordSourceSurfaceUserDataFunc(void *aUserData) {
+static void WrapAndRecordSourceSurfaceUserDataFunc(void *aUserData) {
   WrapAndRecordSourceSurfaceUserData *userData =
       static_cast<WrapAndRecordSourceSurfaceUserData *>(aUserData);
 
   userData->recorder->RemoveSourceSurface((SourceSurface *)userData->refPtr);
   userData->recorder->RemoveStoredObject(userData->refPtr);
   userData->recorder->RecordEvent(
       RecordedSourceSurfaceDestruction(ReferencePtr(userData->refPtr)));
 
@@ -350,17 +350,17 @@ void DrawTargetWrapAndRecord::Fill(const
                  aOptions);
 }
 
 struct WrapAndRecordFontUserData {
   void *refPtr;
   RefPtr<DrawEventRecorderPrivate> recorder;
 };
 
-void WrapAndRecordFontUserDataDestroyFunc(void *aUserData) {
+static void WrapAndRecordFontUserDataDestroyFunc(void *aUserData) {
   WrapAndRecordFontUserData *userData =
       static_cast<WrapAndRecordFontUserData *>(aUserData);
 
   userData->recorder->RecordEvent(
       RecordedScaledFontDestruction(ReferencePtr(userData->refPtr)));
   userData->recorder->RemoveScaledFont((ScaledFont *)userData->refPtr);
   delete userData;
 }
--- a/gfx/2d/FilterNodeSoftware.cpp
+++ b/gfx/2d/FilterNodeSoftware.cpp
@@ -175,17 +175,18 @@ class SpecularLightingSoftware {
 
 }  // unnamed namespace
 
 // from xpcom/ds/nsMathUtils.h
 static int32_t NS_lround(double x) {
   return x >= 0.0 ? int32_t(x + 0.5) : int32_t(x - 0.5);
 }
 
-already_AddRefed<DataSourceSurface> CloneAligned(DataSourceSurface *aSource) {
+static already_AddRefed<DataSourceSurface> CloneAligned(
+    DataSourceSurface *aSource) {
   return CreateDataSourceSurfaceByCloning(aSource);
 }
 
 static void FillRectWithPixel(DataSourceSurface *aSurface,
                               const IntRect &aFillRect, IntPoint aPixelPos) {
   MOZ_ASSERT(!aFillRect.Overflows());
   MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect),
              "aFillRect needs to be completely inside the surface");
@@ -1808,17 +1809,17 @@ static void TransferComponents(
     }
 
     // Zero padding to keep valgrind happy.
     PodZero(&targetData[y * targetStride + size.width * BytesPerPixel],
             targetStride - size.width * BytesPerPixel);
   }
 }
 
-bool IsAllZero(uint8_t aLookupTable[256]) {
+static bool IsAllZero(const uint8_t aLookupTable[256]) {
   for (int32_t i = 0; i < 256; i++) {
     if (aLookupTable[i] != 0) {
       return false;
     }
   }
   return true;
 }
 
--- a/gfx/2d/MacIOSurface.cpp
+++ b/gfx/2d/MacIOSurface.cpp
@@ -434,17 +434,17 @@ void MacIOSurface::Unlock(bool aReadOnly
   MacIOSurfaceLib::IOSurfaceUnlock(mIOSurfacePtr, aReadOnly ? READ_ONLY : 0,
                                    nullptr);
 }
 
 using mozilla::gfx::IntSize;
 using mozilla::gfx::SourceSurface;
 using mozilla::gfx::SurfaceFormat;
 
-void MacIOSurfaceBufferDeallocator(void* aClosure) {
+static void MacIOSurfaceBufferDeallocator(void* aClosure) {
   MOZ_ASSERT(aClosure);
 
   delete[] static_cast<unsigned char*>(aClosure);
 }
 
 already_AddRefed<SourceSurface> MacIOSurface::GetAsSurface() {
   Lock();
   size_t bytesPerRow = GetBytesPerRow();
--- a/gfx/2d/QuartzSupport.mm
+++ b/gfx/2d/QuartzSupport.mm
@@ -30,17 +30,17 @@ CGColorSpaceRef CreateSystemColorSpace()
   if (!cspace) {
     cspace = ::CGColorSpaceCreateDeviceRGB();
   }
   return cspace;
 }
 
 nsCARenderer::~nsCARenderer() { Destroy(); }
 
-void cgdata_release_callback(void* aCGData, const void* data, size_t size) {
+static void cgdata_release_callback(void* aCGData, const void* data, size_t size) {
   if (aCGData) {
     free(aCGData);
   }
 }
 
 void nsCARenderer::Destroy() {
   if (mCARenderer) {
     CARenderer* caRenderer = (CARenderer*)mCARenderer;
--- a/gfx/2d/ScaledFontMac.cpp
+++ b/gfx/2d/ScaledFontMac.cpp
@@ -183,18 +183,18 @@ SkTypeface* ScaledFontMac::CreateSkTypef
 // Note: cairo dlsyms it. We could do that but maybe it's
 // safe just to use?
 
 already_AddRefed<Path> ScaledFontMac::GetPathForGlyphs(
     const GlyphBuffer& aBuffer, const DrawTarget* aTarget) {
   return ScaledFontBase::GetPathForGlyphs(aBuffer, aTarget);
 }
 
-uint32_t CalcTableChecksum(const uint32_t* tableStart, uint32_t length,
-                           bool skipChecksumAdjust = false) {
+static uint32_t CalcTableChecksum(const uint32_t* tableStart, uint32_t length,
+                                  bool skipChecksumAdjust = false) {
   uint32_t sum = 0L;
   const uint32_t* table = tableStart;
   const uint32_t* end = table + length / sizeof(uint32_t);
   while (table < end) {
     if (skipChecksumAdjust && (table - tableStart) == 2) {
       table++;
     } else {
       sum += CFSwapInt32BigToHost(*table++);
@@ -216,17 +216,17 @@ uint32_t CalcTableChecksum(const uint32_
 struct TableRecord {
   uint32_t tag;
   uint32_t checkSum;
   uint32_t offset;
   uint32_t length;
   CFDataRef data;
 };
 
-int maxPow2LessThan(int a) {
+static int maxPow2LessThan(int a) {
   int x = 1;
   int shift = 0;
   while ((x << (shift + 1)) < a) {
     shift++;
   }
   return shift;
 }
 
--- a/gfx/2d/unittest/TestCairo.cpp
+++ b/gfx/2d/unittest/TestCairo.cpp
@@ -4,17 +4,17 @@
 
 #include "cairo.h"
 
 #include "gtest/gtest.h"
 
 namespace mozilla {
 namespace layers {
 
-void TryCircle(double centerX, double centerY, double radius) {
+static void TryCircle(double centerX, double centerY, double radius) {
   printf("TestCairo:TryArcs centerY %f, radius %f\n", centerY, radius);
 
   cairo_surface_t *surf =
       cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 8, 21);
   ASSERT_TRUE(surf != nullptr);
 
   cairo_t *cairo = cairo_create(surf);
   ASSERT_TRUE(cairo != nullptr);
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -50,18 +50,16 @@
 #include <list>
 #include <set>
 
 uint8_t gLayerManagerLayerBuilder;
 
 namespace mozilla {
 namespace layers {
 
-FILE* FILEOrDefault(FILE* aFile) { return aFile ? aFile : stderr; }
-
 typedef ScrollableLayerGuid::ViewID ViewID;
 
 using namespace mozilla::gfx;
 using namespace mozilla::Compression;
 
 //--------------------------------------------------
 // LayerManager
 
--- a/gfx/layers/basic/BasicLayersImpl.cpp
+++ b/gfx/layers/basic/BasicLayersImpl.cpp
@@ -209,23 +209,10 @@ gfx::CompositionOp GetEffectiveOperator(
 
   if (op != CompositionOp::OP_OVER) {
     return op;
   }
 
   return ToData(aLayer)->GetOperator();
 }
 
-ShadowableLayer* ToShadowable(Layer* aLayer) {
-  return aLayer->AsShadowableLayer();
-}
-
-bool ShouldShadow(Layer* aLayer) {
-  if (!ToShadowable(aLayer)) {
-    MOZ_ASSERT(aLayer->GetType() == Layer::TYPE_READBACK,
-               "Only expect not to shadow ReadbackLayers");
-    return false;
-  }
-  return true;
-}
-
 }  // namespace layers
 }  // namespace mozilla
--- a/gfx/layers/client/ClientPaintedLayer.cpp
+++ b/gfx/layers/client/ClientPaintedLayer.cpp
@@ -142,20 +142,30 @@ void ClientPaintedLayer::RenderLayerWith
              mContentClient->BorrowDrawTargetForPainting(state, &iter)) {
     SetAntialiasingFlags(this, target);
 
     RefPtr<gfxContext> ctx =
         gfxContext::CreatePreservingTransformOrNull(target);
     MOZ_ASSERT(ctx);  // already checked the target above
 
     if (!gfxEnv::SkipRasterization()) {
-      ClientManager()->GetPaintedLayerCallback()(
-          this, ctx, iter.mDrawRegion, iter.mDrawRegion, state.mClip,
-          state.mRegionToInvalidate,
-          ClientManager()->GetPaintedLayerCallbackData());
+      if (!target->IsCaptureDT()) {
+        target->ClearRect(Rect());
+        if (target->IsValid()) {
+          ClientManager()->GetPaintedLayerCallback()(
+              this, ctx, iter.mDrawRegion, iter.mDrawRegion, state.mClip,
+              state.mRegionToInvalidate,
+              ClientManager()->GetPaintedLayerCallbackData());
+        }
+      } else {
+        ClientManager()->GetPaintedLayerCallback()(
+            this, ctx, iter.mDrawRegion, iter.mDrawRegion, state.mClip,
+            state.mRegionToInvalidate,
+            ClientManager()->GetPaintedLayerCallbackData());
+      }
     }
 
     ctx = nullptr;
     mContentClient->ReturnDrawTarget(target);
     didUpdate = true;
   }
 
   mContentClient->EndPaint(state, &readbackUpdates);
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -19,30 +19,23 @@
 #include "mozilla/gfx/Rect.h"         // for Rect
 #include "mozilla/gfx/Tools.h"        // for BytesPerPixel
 #include "mozilla/layers/CompositableForwarder.h"
 #include "mozilla/layers/CompositorBridgeChild.h"  // for CompositorBridgeChild
 #include "mozilla/layers/LayerMetricsWrapper.h"
 #include "mozilla/layers/ShadowLayers.h"  // for ShadowLayerForwarder
 #include "mozilla/layers/PaintThread.h"   // for PaintThread
 #include "TextureClientPool.h"
-#include "nsDebug.h"              // for NS_ASSERTION
 #include "nsISupportsImpl.h"      // for gfxContext::AddRef, etc
 #include "nsExpirationTracker.h"  // for nsExpirationTracker
 #include "nsMathUtils.h"          // for NS_lroundf
 #include "LayersLogging.h"
 #include "UnitTransforms.h"  // for TransformTo
 #include "mozilla/UniquePtr.h"
 
-// This is the minimum area that we deem reasonable to copy from the front
-// buffer to the back buffer on tile updates. If the valid region is smaller
-// than this, we just redraw it and save on the copy (and requisite
-// surface-locking involved).
-#define MINIMUM_TILE_COPY_AREA (1.f / 16.f)
-
 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
 #  include "cairo.h"
 #  include <sstream>
 using mozilla::layers::Layer;
 static void DrawDebugOverlay(mozilla::gfx::DrawTarget* dt, int x, int y,
                              int width, int height) {
   gfxContext c(dt);
 
--- a/gfx/layers/ipc/CompositorThread.cpp
+++ b/gfx/layers/ipc/CompositorThread.cpp
@@ -18,20 +18,16 @@ namespace gfx {
 void ReleaseVRManagerParentSingleton();
 }  // namespace gfx
 
 namespace layers {
 
 static StaticRefPtr<CompositorThreadHolder> sCompositorThreadHolder;
 static bool sFinishedCompositorShutDown = false;
 
-CompositorThreadHolder* GetCompositorThreadHolder() {
-  return sCompositorThreadHolder;
-}
-
 base::Thread* CompositorThread() {
   return sCompositorThreadHolder
              ? sCompositorThreadHolder->GetCompositorThread()
              : nullptr;
 }
 
 /* static */ MessageLoop* CompositorThreadHolder::Loop() {
   return CompositorThread() ? CompositorThread()->message_loop() : nullptr;
--- a/gfx/layers/ipc/ImageBridgeParent.cpp
+++ b/gfx/layers/ipc/ImageBridgeParent.cpp
@@ -43,19 +43,16 @@ using namespace mozilla::gfx;
 using namespace mozilla::media;
 
 ImageBridgeParent::ImageBridgeMap ImageBridgeParent::sImageBridges;
 
 StaticAutoPtr<mozilla::Monitor> sImageBridgesLock;
 
 static StaticRefPtr<ImageBridgeParent> sImageBridgeParentSingleton;
 
-// defined in CompositorBridgeParent.cpp
-CompositorThreadHolder* GetCompositorThreadHolder();
-
 /* static */ void ImageBridgeParent::Setup() {
   MOZ_ASSERT(NS_IsMainThread());
   if (!sImageBridgesLock) {
     sImageBridgesLock = new Monitor("ImageBridges");
     mozilla::ClearOnShutdown(&sImageBridgesLock);
   }
 }
 
new file mode 100644
--- /dev/null
+++ b/gfx/src/components.conf
@@ -0,0 +1,14 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Classes = [
+    {
+        'cid': '{a6cf9115-15b3-11d2-932e-00805f8add32}',
+        'contract_ids': ['@mozilla.org/gfx/fontenumerator;1'],
+        'type': 'nsThebesFontEnumerator',
+        'headers': ['/gfx/src/nsThebesFontEnumerator.h'],
+    },
+]
--- a/gfx/src/moz.build
+++ b/gfx/src/moz.build
@@ -63,26 +63,29 @@ UNIFIED_SOURCES += [
     'gfxCrashReporterUtils.cpp',
     'gfxTelemetry.cpp',
     'nsColor.cpp',
     'nsFont.cpp',
     'nsFontMetrics.cpp',
     'nsRect.cpp',
     'nsRegion.cpp',
     'nsThebesFontEnumerator.cpp',
-    'nsThebesGfxFactory.cpp',
     'nsTransform2D.cpp',
     'TiledRegion.cpp',
 ]
 
 # nsDeviceContext.cpp cannot be built in unified mode because it pulls in OS X system headers.
 SOURCES += [
     'nsDeviceContext.cpp',
 ]
 
+XPCOM_MANIFESTS += [
+    'components.conf',
+]
+
 include('/ipc/chromium/chromium-config.mozbuild')
 
 LOCAL_INCLUDES += [
     '/dom/ipc',  # for ContentChild.h
 ]
 
 FINAL_LIBRARY = 'xul'
 
deleted file mode 100644
--- a/gfx/src/nsThebesGfxFactory.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* 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/. */
-
-#include "gfxPlatform.h"         // for gfxPlatform
-#include "mozilla/Assertions.h"  // for MOZ_ASSERT_HELPER2
-#include "mozilla/Attributes.h"  // for final
-#include "mozilla/Module.h"      // for Module, Module::CIDEntry, etc
-#include "mozilla/ModuleUtils.h"
-#include "mozilla/mozalloc.h"        // for operator new
-#include "nsCOMPtr.h"                // for nsCOMPtr
-#include "nsError.h"                 // for NS_ERROR_NO_AGGREGATION, etc
-#include "nsGfxCIID.h"               // for NS_FONT_ENUMERATOR_CID, etc
-#include "nsID.h"                    // for NS_DEFINE_NAMED_CID, etc
-#include "nsISupports.h"             // for NS_DECL_ISUPPORTS, etc
-#include "nsThebesFontEnumerator.h"  // for nsThebesFontEnumerator
-
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsThebesFontEnumerator)
-
-NS_DEFINE_NAMED_CID(NS_FONT_ENUMERATOR_CID);
-
-static const mozilla::Module::CIDEntry kThebesCIDs[] = {
-    {&kNS_FONT_ENUMERATOR_CID, false, nullptr,
-     nsThebesFontEnumeratorConstructor},
-    {nullptr}};
-
-static const mozilla::Module::ContractIDEntry kThebesContracts[] = {
-    {"@mozilla.org/gfx/fontenumerator;1", &kNS_FONT_ENUMERATOR_CID}, {nullptr}};
-
-static const mozilla::Module kThebesModule = {mozilla::Module::kVersion,
-                                              kThebesCIDs,
-                                              kThebesContracts,
-                                              nullptr,
-                                              nullptr,
-                                              nullptr,
-                                              nullptr};
-
-NSMODULE_DEFN(nsGfxModule) = &kThebesModule;
--- a/gfx/tests/gtest/TestTreeTraversal.cpp
+++ b/gfx/tests/gtest/TestTreeTraversal.cpp
@@ -1191,20 +1191,16 @@ struct AssignSearchNodeTypesWithLastLeaf
   void operator()(SearchTestNodeForward* aNode) {
     aNode->SetType(SearchNodeType::Hay);
     if (aNode->IsLeaf()) {
       node = aNode;
     }
   }
 };
 
-bool FindNeedle(SearchTestNode* aNode) {
-  return aNode->GetType() == SearchNodeType::Needle;
-}
-
 struct AssignSearchNodeTypesAllHay {
   void operator()(SearchTestNode* aNode) {
     aNode->SetType(SearchNodeType::Hay);
   }
 };
 
 struct AssignSearchNodeTypesWithFirstLeafAsNeedle {
   RefPtr<SearchTestNodeReverse>& needleNode;
@@ -1244,18 +1240,16 @@ struct AllocateUnitRegionsToLeavesOnly {
       int x = squareCount % xWrap;
       int y = squareCount / xWrap;
       aNode->SetRegion(nsRegion(nsRect(x, y, 1, 1)));
       squareCount++;
     }
   }
 };
 
-void ForEachNodeDoNothing(ForEachTestNode* aNode) {}
-
 template <typename Node>
 static RefPtr<Node> DepthFirstSearchForwardRecursive(RefPtr<Node> aNode) {
   if (aNode->GetType() == SearchNodeType::Needle) {
     return aNode;
   }
   for (RefPtr<Node> node = aNode->GetFirstChild(); node != nullptr;
        node = node->GetNextSibling()) {
     if (RefPtr<Node> foundNode = DepthFirstSearchForwardRecursive(node)) {
--- a/ipc/mscom/InterceptorLog.cpp
+++ b/ipc/mscom/InterceptorLog.cpp
@@ -430,17 +430,17 @@ void Logger::Flush() {
     MutexAutoLock lock(mMutex);
     linesToWrite.SwapElements(mEntries);
   }
 
   for (uint32_t i = 0, len = linesToWrite.Length(); i < len; ++i) {
     uint32_t bytesWritten;
     nsCString& line = linesToWrite[i];
     nsresult rv = mLogFile->Write(line.get(), line.Length(), &bytesWritten);
-    NS_WARN_IF(NS_FAILED(rv));
+    Unused << NS_WARN_IF(NS_FAILED(rv));
   }
 }
 
 StaticAutoPtr<Logger> sLogger;
 
 NS_IMETHODIMP
 ShutdownEvent::Observe(nsISupports* aSubject, const char* aTopic,
                        const char16_t* aData) {
--- a/ipc/mscom/Registration.cpp
+++ b/ipc/mscom/Registration.cpp
@@ -410,17 +410,17 @@ static CRITICAL_SECTION* GetMutex() {
 #if !defined(MOZILLA_INTERNAL_API)
     // sRegistry allocation is fallible outside of Mozilla processes
     if (!sRegistry) {
       return;
     }
 #endif
   }
 
-  sRegistry->emplaceBack(aProxy);
+  MOZ_ALWAYS_TRUE(sRegistry->emplaceBack(aProxy));
 }
 
 /* static */ void RegisteredProxy::DeleteFromRegistry(RegisteredProxy* aProxy) {
   MOZ_ASSERT(aProxy);
 
   AutoCriticalSection lock(GetMutex());
 
   MOZ_ASSERT(sRegistry && !sRegistry->empty());
@@ -444,17 +444,17 @@ static StaticAutoPtr<Vector<Pair<const A
 void RegisterArrayData(const ArrayData* aArrayData, size_t aLength) {
   AutoCriticalSection lock(GetMutex());
 
   if (!sArrayData) {
     sArrayData = new Vector<Pair<const ArrayData*, size_t>>();
     ClearOnShutdown(&sArrayData, ShutdownPhase::ShutdownThreads);
   }
 
-  sArrayData->emplaceBack(MakePair(aArrayData, aLength));
+  MOZ_ALWAYS_TRUE(sArrayData->emplaceBack(MakePair(aArrayData, aLength)));
 }
 
 const ArrayData* FindArrayData(REFIID aIid, ULONG aMethodIndex) {
   AutoCriticalSection lock(GetMutex());
 
   if (!sArrayData) {
     return nullptr;
   }
--- a/ipc/mscom/SpinEvent.cpp
+++ b/ipc/mscom/SpinEvent.cpp
@@ -23,17 +23,17 @@ bool SpinEvent::sIsMulticore = false;
   SYSTEM_INFO sysInfo;
   ::GetSystemInfo(&sysInfo);
   sIsMulticore = sysInfo.dwNumberOfProcessors > 1;
   return true;
 }
 
 SpinEvent::SpinEvent() : mDone(false) {
   static const bool gotStatics = InitStatics();
-  MOZ_ASSERT(gotStatics);
+  MOZ_ALWAYS_TRUE(gotStatics);
 
   mDoneEvent.own(::CreateEventW(nullptr, FALSE, FALSE, nullptr));
   MOZ_ASSERT(mDoneEvent);
 }
 
 bool SpinEvent::Wait(HANDLE aTargetThread) {
   MOZ_ASSERT(aTargetThread);
   if (!aTargetThread) {
--- a/ipc/mscom/moz.build
+++ b/ipc/mscom/moz.build
@@ -83,11 +83,8 @@ LOCAL_INCLUDES += [
 DEFINES['MOZ_MSCOM_REMARSHAL_NO_HANDLER'] = True
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 with Files("**"):
     BUG_COMPONENT = ("Core", "IPC: MSCOM")
-
-if CONFIG['CC_TYPE'] == 'clang-cl':
-    AllowCompilerWarnings()  # workaround for bug 1090497
--- a/ipc/mscom/oop/moz.build
+++ b/ipc/mscom/oop/moz.build
@@ -32,11 +32,8 @@ LIBRARY_DEFINES['MOZ_MSCOM_REMARSHAL_NO_
 
 DisableStlWrapping()
 NO_EXPAND_LIBS = True
 FORCE_STATIC_LIB = True
 
 # This DLL may be loaded into other processes, so we need static libs for
 # Windows 7 and Windows 8.
 USE_STATIC_LIBS = True
-
-if CONFIG['CC_TYPE'] == 'clang-cl':
-    AllowCompilerWarnings()  # workaround for bug 1090497
--- a/js/ductwork/debugger/JSDebugger.cpp
+++ b/js/ductwork/debugger/JSDebugger.cpp
@@ -5,34 +5,31 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "JSDebugger.h"
 #include "nsIXPConnect.h"
 #include "nsThreadUtils.h"
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "js/Wrapper.h"
-#include "mozilla/ModuleUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsMemory.h"
 
 #define JSDEBUGGER_CONTRACTID "@mozilla.org/jsdebugger;1"
 
 #define JSDEBUGGER_CID                               \
   {                                                  \
     0x0365cbd5, 0xd46e, 0x4e94, {                    \
       0xa3, 0x9f, 0x83, 0xb6, 0x3c, 0xd1, 0xa9, 0x63 \
     }                                                \
   }
 
 namespace mozilla {
 namespace jsdebugger {
 
-NS_GENERIC_FACTORY_CONSTRUCTOR(JSDebugger)
-
 NS_IMPL_ISUPPORTS(JSDebugger, IJSDebugger)
 
 JSDebugger::JSDebugger() {}
 
 JSDebugger::~JSDebugger() {}
 
 NS_IMETHODIMP
 JSDebugger::AddClass(JS::Handle<JS::Value> global, JSContext* cx) {
@@ -70,23 +67,8 @@ JSDebugger::AddClass(JS::Handle<JS::Valu
     }
   }
 
   return NS_OK;
 }
 
 }  // namespace jsdebugger
 }  // namespace mozilla
-
-NS_DEFINE_NAMED_CID(JSDEBUGGER_CID);
-
-static const mozilla::Module::CIDEntry kJSDebuggerCIDs[] = {
-    {&kJSDEBUGGER_CID, false, nullptr,
-     mozilla::jsdebugger::JSDebuggerConstructor},
-    {nullptr}};
-
-static const mozilla::Module::ContractIDEntry kJSDebuggerContracts[] = {
-    {JSDEBUGGER_CONTRACTID, &kJSDEBUGGER_CID}, {nullptr}};
-
-static const mozilla::Module kJSDebuggerModule = {
-    mozilla::Module::kVersion, kJSDebuggerCIDs, kJSDebuggerContracts};
-
-NSMODULE_DEFN(jsdebugger) = &kJSDebuggerModule;
new file mode 100644
--- /dev/null
+++ b/js/ductwork/debugger/components.conf
@@ -0,0 +1,14 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Classes = [
+    {
+        'cid': '{0365cbd5-d46e-4e94-a39f-83b63cd1a963}',
+        'contract_ids': ['@mozilla.org/jsdebugger;1'],
+        'type': 'mozilla::jsdebugger::JSDebugger',
+        'headers': ['/js/ductwork/debugger/JSDebugger.h'],
+    },
+]
--- a/js/ductwork/debugger/moz.build
+++ b/js/ductwork/debugger/moz.build
@@ -14,13 +14,17 @@ XPIDL_SOURCES += [
 XPIDL_MODULE = 'jsdebugger'
 
 XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell.ini']
 
 SOURCES += [
     'JSDebugger.cpp',
 ]
 
+XPCOM_MANIFESTS += [
+    'components.conf',
+]
+
 EXTRA_JS_MODULES += [
     'jsdebugger.jsm',
 ]
 
 FINAL_LIBRARY = 'xul'
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -1485,33 +1485,16 @@ bool TypedObject::isAttached() const {
   JSObject& owner = as<OutlineTypedObject>().owner();
   if (owner.is<ArrayBufferObject>() &&
       owner.as<ArrayBufferObject>().isDetached()) {
     return false;
   }
   return true;
 }
 
-/* static */ bool TypedObject::GetBuffer(JSContext* cx, unsigned argc,
-                                         Value* vp) {
-  CallArgs args = CallArgsFromVp(argc, vp);
-  JSObject& obj = args[0].toObject();
-  ArrayBufferObject* buffer;
-  if (obj.is<OutlineTransparentTypedObject>()) {
-    buffer = obj.as<OutlineTransparentTypedObject>().getOrCreateBuffer(cx);
-  } else {
-    buffer = obj.as<InlineTransparentTypedObject>().getOrCreateBuffer(cx);
-  }
-  if (!buffer) {
-    return false;
-  }
-  args.rval().setObject(*buffer);
-  return true;
-}
-
 /* static */ bool TypedObject::GetByteOffset(JSContext* cx, unsigned argc,
                                              Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
   args.rval().setInt32(
       AssertedCast<int32_t>(args[0].toObject().as<TypedObject>().offset()));
   return true;
 }
 
@@ -1526,21 +1509,16 @@ bool TypedObject::isAttached() const {
                                      descr, heap);
   } else {
     return createUnattachedWithClass(cx, &OutlineTransparentTypedObject::class_,
                                      descr, heap);
   }
 }
 
 void OutlineTypedObject::setOwnerAndData(JSObject* owner, uint8_t* data) {
-  // Make sure we don't associate with array buffers whose data is from an
-  // inline typed object, see obj_trace.
-  MOZ_ASSERT_IF(owner && owner->is<ArrayBufferObject>(),
-                !owner->as<ArrayBufferObject>().forInlineTypedObject());
-
   // Typed objects cannot move from one owner to another, so don't worry
   // about pre barriers during this initialization.
   owner_ = owner;
   data_ = data;
 
   if (owner) {
     if (!IsInsideNursery(this) && IsInsideNursery(owner)) {
       // Trigger a post barrier when attaching an object outside the nursery to
@@ -1589,24 +1567,16 @@ void OutlineTypedObject::setOwnerAndData
 }
 
 void OutlineTypedObject::attach(JSContext* cx, ArrayBufferObject& buffer,
                                 uint32_t offset) {
   MOZ_ASSERT(!isAttached());
   MOZ_ASSERT(offset <= buffer.byteLength());
   MOZ_ASSERT(size() <= buffer.byteLength() - offset);
 
-  // If the owner's data is from an inline typed object, associate this with
-  // the inline typed object instead, to simplify tracing.
-  if (buffer.forInlineTypedObject()) {
-    InlineTypedObject& realOwner = buffer.firstView()->as<InlineTypedObject>();
-    attach(cx, realOwner, offset);
-    return;
-  }
-
   buffer.setHasTypedObjectViews();
 
   {
     AutoEnterOOMUnsafeRegion oomUnsafe;
     if (!buffer.addView(cx, this)) {
       oomUnsafe.crash("TypedObject::attach");
     }
   }
@@ -1706,21 +1676,17 @@ void OutlineTypedObject::attach(JSContex
   JSObject* oldOwner = typedObj.owner_;
   TraceManuallyBarrieredEdge(trc, &typedObj.owner_, "typed object owner");
   JSObject* owner = typedObj.owner_;
 
   uint8_t* oldData = typedObj.outOfLineTypedMem();
   uint8_t* newData = oldData;
 
   // Update the data pointer if the owner moved and the owner's data is
-  // inline with it. Note that an array buffer pointing to data in an inline
-  // typed object will never be used as an owner for another outline typed
-  // object. In such cases, the owner will be the inline typed object itself.
-  MOZ_ASSERT_IF(owner->is<ArrayBufferObject>(),
-                !owner->as<ArrayBufferObject>().forInlineTypedObject());
+  // inline with it.
   if (owner != oldOwner && (owner->is<InlineTypedObject>() ||
                             owner->as<ArrayBufferObject>().hasInlineData())) {
     newData += reinterpret_cast<uint8_t*>(owner) -
                reinterpret_cast<uint8_t*>(oldOwner);
     typedObj.setData(newData);
 
     if (trc->isTenuringTracer()) {
       Nursery& nursery = trc->runtime()->gc.nursery();
@@ -2221,80 +2187,16 @@ void OutlineTypedObject::notifyBufferDet
     auto& nursery = dst->runtimeFromMainThread()->gc.nursery();
     bool direct = descr.size() >= sizeof(uintptr_t);
     nursery.setForwardingPointerWhileTenuring(oldData, newData, direct);
   }
 
   return 0;
 }
 
-ArrayBufferObject* InlineTransparentTypedObject::getOrCreateBuffer(
-    JSContext* cx) {
-  ObjectRealm& realm = ObjectRealm::get(this);
-  if (!realm.lazyArrayBuffers) {
-    auto table = cx->make_unique<ObjectWeakMap>(cx);
-    if (!table) {
-      return nullptr;
-    }
-
-    realm.lazyArrayBuffers = std::move(table);
-  }
-
-  ObjectWeakMap* table = realm.lazyArrayBuffers.get();
-
-  JSObject* obj = table->lookup(this);
-  if (obj) {
-    return &obj->as<ArrayBufferObject>();
-  }
-
-  ArrayBufferObject::BufferContents contents =
-      ArrayBufferObject::BufferContents::createPlain(inlineTypedMem());
-  size_t nbytes = typeDescr().size();
-
-  // Prevent GC under ArrayBufferObject::create, which might move this object
-  // and its contents.
-  gc::AutoSuppressGC suppress(cx);
-
-  ArrayBufferObject* buffer = ArrayBufferObject::create(
-      cx, nbytes, contents, ArrayBufferObject::DoesntOwnData);
-  if (!buffer) {
-    return nullptr;
-  }
-
-  // The owning object must always be the array buffer's first view. This
-  // both prevents the memory from disappearing out from under the buffer
-  // (the first view is held strongly by the buffer) and is used by the
-  // buffer marking code to detect whether its data pointer needs to be
-  // relocated.
-  MOZ_ALWAYS_TRUE(buffer->addView(cx, this));
-
-  buffer->setForInlineTypedObject();
-  buffer->setHasTypedObjectViews();
-
-  if (!table->add(cx, this, buffer)) {
-    return nullptr;
-  }
-
-  if (IsInsideNursery(this)) {
-    // Make sure the buffer is traced by the next generational collection,
-    // so that its data pointer is updated after this typed object moves.
-    storeBuffer()->putWholeCell(buffer);
-  }
-
-  return buffer;
-}
-
-ArrayBufferObject* OutlineTransparentTypedObject::getOrCreateBuffer(
-    JSContext* cx) {
-  if (owner().is<ArrayBufferObject>()) {
-    return &owner().as<ArrayBufferObject>();
-  }
-  return owner().as<InlineTransparentTypedObject>().getOrCreateBuffer(cx);
-}
-
 /******************************************************************************
  * Typed object classes
  */
 
 const ObjectOps TypedObject::objectOps_ = {
     TypedObject::obj_lookupProperty,
     TypedObject::obj_defineProperty,
     TypedObject::obj_hasProperty,
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -614,17 +614,16 @@ class TypedObject : public ShapedObject 
   static TypedObject* createZeroed(JSContext* cx, HandleTypeDescr typeObj,
                                    gc::InitialHeap heap = gc::DefaultHeap);
 
   // User-accessible constructor (`new TypeDescriptor(...)`). Note that the
   // callee here is the type descriptor.
   static MOZ_MUST_USE bool construct(JSContext* cx, unsigned argc, Value* vp);
 
   /* Accessors for self hosted code. */
-  static MOZ_MUST_USE bool GetBuffer(JSContext* cx, unsigned argc, Value* vp);
   static MOZ_MUST_USE bool GetByteOffset(JSContext* cx, unsigned argc,
                                          Value* vp);
 
   Shape** addressOfShapeFromGC() {
     return shapeRef().unsafeUnbarrieredForTracing();
   }
 };
 
@@ -696,18 +695,16 @@ class OutlineTypedObject : public TypedO
 
   static void obj_trace(JSTracer* trace, JSObject* object);
 };
 
 // Class for a transparent typed object whose owner is an array buffer.
 class OutlineTransparentTypedObject : public OutlineTypedObject {
  public:
   static const Class class_;
-
-  ArrayBufferObject* getOrCreateBuffer(JSContext* cx);
 };
 
 // Class for an opaque typed object whose owner may be either an array buffer
 // or an opaque inlined typed object.
 class OutlineOpaqueTypedObject : public OutlineTypedObject {
  public:
   static const Class class_;
 };
@@ -755,18 +752,16 @@ class InlineTypedObject : public TypedOb
 };
 
 // Class for a transparent typed object with inline data, which may have a
 // lazily allocated array buffer.
 class InlineTransparentTypedObject : public InlineTypedObject {
  public:
   static const Class class_;
 
-  ArrayBufferObject* getOrCreateBuffer(JSContext* cx);
-
   uint8_t* inlineTypedMem() const {
     return InlineTypedObject::inlineTypedMem();
   }
 };
 
 // Class for an opaque typed object with inline data and no array buffer.
 class InlineOpaqueTypedObject : public InlineTypedObject {
  public:
--- a/js/src/builtin/TypedObjectConstants.h
+++ b/js/src/builtin/TypedObjectConstants.h
@@ -23,17 +23,17 @@
 #define JS_TYPEDARRAYLAYOUT_LENGTH_SLOT 1
 #define JS_TYPEDARRAYLAYOUT_BYTEOFFSET_SLOT 2
 
 ///////////////////////////////////////////////////////////////////////////
 // Slots and flags for ArrayBuffer objects
 
 #define JS_ARRAYBUFFER_FLAGS_SLOT 3
 
-#define JS_ARRAYBUFFER_DETACHED_FLAG 0x4
+#define JS_ARRAYBUFFER_DETACHED_FLAG 0x8
 
 ///////////////////////////////////////////////////////////////////////////
 // Slots for typed prototypes
 
 #define JS_TYPROTO_SLOTS 0
 
 ///////////////////////////////////////////////////////////////////////////
 // Slots for type objects
--- a/js/src/gc/Memory.cpp
+++ b/js/src/gc/Memory.cpp
@@ -153,22 +153,21 @@ template <bool AlwaysGetNew = true>
 static bool TryToAlignChunk(void** aRegion, void** aRetainedRegion,
                             size_t length, size_t alignment);
 
 static void* MapAlignedPagesSlow(size_t length, size_t alignment);
 static void* MapAlignedPagesLastDitch(size_t length, size_t alignment);
 
 #ifdef JS_64BIT
 static void* MapAlignedPagesRandom(size_t length, size_t alignment);
-void* TestMapAlignedPagesLastDitch(size_t, size_t) { return nullptr; }
-#else
+#endif
+
 void* TestMapAlignedPagesLastDitch(size_t length, size_t alignment) {
   return MapAlignedPagesLastDitch(length, alignment);
 }
-#endif
 
 /*
  * We can only decommit unused pages if the hardcoded Arena
  * size matches the page size for the running process.
  */
 static inline bool DecommitEnabled() { return pageSize == ArenaSize; }
 
 /* Returns the offset from the nearest aligned address at or below |region|. */
@@ -420,38 +419,55 @@ void* MapAlignedPages(size_t length, siz
 
     MOZ_RELEASE_ASSERT(!IsInvalidRegion(region, length));
     MOZ_ASSERT(OffsetFromAligned(region, alignment) == 0);
 
     return region;
   }
 #endif
 
+  // Try to allocate the region. If the returned address is aligned,
+  // either we OOMed (region is nullptr) or we're done.
   void* region = MapMemory(length);
   if (OffsetFromAligned(region, alignment) == 0) {
     return region;
   }
 
+  // Try to align the region. On success, TryToAlignChunk() returns
+  // true and we can return the aligned region immediately.
   void* retainedRegion;
-  TryToAlignChunk(&region, &retainedRegion, length, alignment);
+  if (TryToAlignChunk(&region, &retainedRegion, length, alignment)) {
+    MOZ_ASSERT(region && OffsetFromAligned(region, alignment) == 0);
+    MOZ_ASSERT(!retainedRegion);
+    return region;
+  }
+
+  // On failure, the unaligned region is retained unless we OOMed. We don't
+  // use the retained region on this path (see the last ditch allocator).
   if (retainedRegion) {
     UnmapInternal(retainedRegion, length);
   }
+
+  // If it fails to align the given region, TryToAlignChunk() returns the
+  // next valid region that we might be able to align (unless we OOMed).
   if (region) {
-    if (OffsetFromAligned(region, alignment) == 0) {
-      return region;
-    }
+    MOZ_ASSERT(OffsetFromAligned(region, alignment) != 0);
     UnmapInternal(region, length);
   }
 
+  // Since we couldn't align the first region, fall back to allocating a
+  // region large enough that we can definitely align it.
   region = MapAlignedPagesSlow(length, alignment);
   if (!region) {
+    // If there wasn't enough contiguous address space left for that,
+    // try to find an alignable region using the last ditch allocator.
     region = MapAlignedPagesLastDitch(length, alignment);
   }
 
+  // At this point we should either have an aligned region or nullptr.
   MOZ_ASSERT(OffsetFromAligned(region, alignment) == 0);
   return region;
 }
 
 #ifdef JS_64BIT
 
 /*
  * This allocator takes advantage of the large address range on some 64-bit
@@ -505,16 +521,17 @@ static void* MapAlignedPagesRandom(size_
       UnmapInternal(region, length);
       continue;
     }
     if (OffsetFromAligned(region, alignment) == 0) {
       return region;
     }
     void* retainedRegion = nullptr;
     if (TryToAlignChunk<false>(&region, &retainedRegion, length, alignment)) {
+      MOZ_ASSERT(region && OffsetFromAligned(region, alignment) == 0);
       MOZ_ASSERT(!retainedRegion);
       return region;
     }
     MOZ_ASSERT(region && !retainedRegion);
     UnmapInternal(region, length);
   }
 
   if (numAddressBits < 48) {
@@ -585,16 +602,17 @@ static void* MapAlignedPagesLastDitch(si
   void* tempMaps[MaxLastDitchAttempts];
   int attempt = 0;
   void* region = MapMemory(length);
   if (OffsetFromAligned(region, alignment) == 0) {
     return region;
   }
   for (; attempt < MaxLastDitchAttempts; ++attempt) {
     if (TryToAlignChunk(&region, tempMaps + attempt, length, alignment)) {
+      MOZ_ASSERT(region && OffsetFromAligned(region, alignment) == 0);
       MOZ_ASSERT(!tempMaps[attempt]);
       break;  // Success!
     }
     if (!region || !tempMaps[attempt]) {
       break;  // We ran out of memory, so give up.
     }
   }
   if (OffsetFromAligned(region, alignment)) {
@@ -700,16 +718,22 @@ static bool TryToAlignChunk(void** aRegi
   }
 
   void* retainedRegion = nullptr;
   bool result = OffsetFromAligned(regionStart, alignment) == 0;
   if (AlwaysGetNew && !result) {
     // If our current chunk cannot be aligned, just get a new one.
     retainedRegion = regionStart;
     regionStart = MapMemory(length);
+    // Our new region might happen to already be aligned.
+    result = OffsetFromAligned(regionStart, alignment) == 0;
+    if (result) {
+      UnmapInternal(retainedRegion, length);
+      retainedRegion = nullptr;
+    }
   }
 
   *aRegion = regionStart;
   *aRetainedRegion = retainedRegion;
   return regionStart && result;
 }
 
 #endif
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -216,16 +216,17 @@ void js::Nursery::disable() {
 
   freeChunksFrom(0);
   capacity_ = 0;
 
   // We must reset currentEnd_ so that there is no space for anything in the
   // nursery.  JIT'd code uses this even if the nursery is disabled.
   currentEnd_ = 0;
   currentStringEnd_ = 0;
+  position_ = 0;
   runtime()->gc.storeBuffer().disable();
 }
 
 void js::Nursery::enableStrings() {
   MOZ_ASSERT(isEmpty());
   canAllocateStrings_ = true;
   currentStringEnd_ = currentEnd_;
 }
--- a/js/src/gc/Nursery.h
+++ b/js/src/gc/Nursery.h
@@ -357,16 +357,17 @@ class Nursery {
   // Used and free space, not counting chunk trailers.
   //
   // usedSpace() + freeSpace() == capacity()
   //
   MOZ_ALWAYS_INLINE size_t usedSpace() const {
     return capacity() - freeSpace();
   }
   MOZ_ALWAYS_INLINE size_t freeSpace() const {
+    MOZ_ASSERT(isEnabled());
     MOZ_ASSERT(currentEnd_ - position_ <= NurseryChunkUsableSize);
     MOZ_ASSERT(currentChunk_ < maxChunkCount());
     return (currentEnd_ - position_) +
            (maxChunkCount() - currentChunk_ - 1) * NurseryChunkUsableSize;
   }
 
 #ifdef JS_GC_ZEAL
   void enterZealMode();
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -3424,20 +3424,16 @@ void MacroAssembler::wasmInterruptCheck(
   branch32(Assembler::Equal, Address(tls, offsetof(wasm::TlsData, interrupt)),
            Imm32(0), &ok);
   wasmTrap(wasm::Trap::CheckInterrupt, bytecodeOffset);
   bind(&ok);
 }
 
 void MacroAssembler::wasmReserveStackChecked(uint32_t amount,
                                              wasm::BytecodeOffset trapOffset) {
-  if (!amount) {
-    return;
-  }
-
   // If the frame is large, don't bump sp until after the stack limit check so
   // that the trap handler isn't called with a wild sp.
 
   if (amount > MAX_UNCHECKED_LEAF_FRAME_SIZE) {
     Label ok;
     Register scratch = ABINonArgReg0;
     moveStackPtrTo(scratch);
     subPtr(Address(WasmTlsReg, offsetof(wasm::TlsData, stackLimit)), scratch);
--- a/js/src/jsapi-tests/testGCAllocator.cpp
+++ b/js/src/jsapi-tests/testGCAllocator.cpp
@@ -130,16 +130,19 @@ bool testGCAllocatorUp(const size_t Page
   CHECK(positionIsCorrect("x--xx--xoo--xxx-", stagingArea, chunkPool,
                           tempChunks));
   // Check that we also fall back after an unalignable and an alignable chunk.
   CHECK(positionIsCorrect("x--xx---x-oo--x-", stagingArea, chunkPool,
                           tempChunks));
   // Check that the last ditch allocator works as expected.
   CHECK(positionIsCorrect("x--xx--xx-oox---", stagingArea, chunkPool,
                           tempChunks, UseLastDitchAllocator));
+  // Check that the last ditch allocator can deal with naturally aligned chunks.
+  CHECK(positionIsCorrect("x--xx--xoo------", stagingArea, chunkPool,
+                          tempChunks, UseLastDitchAllocator));
 
   // Clean up.
   while (--tempChunks >= 0) {
     unmapPages(chunkPool[tempChunks], 2 * Chunk);
   }
   return true;
 }
 
@@ -180,16 +183,19 @@ bool testGCAllocatorDown(const size_t Pa
   CHECK(positionIsCorrect("-xxx--oox--xx--x", stagingArea, chunkPool,
                           tempChunks));
   // Check that we also fall back after an unalignable and an alignable chunk.
   CHECK(positionIsCorrect("-x--oo-x---xx--x", stagingArea, chunkPool,
                           tempChunks));
   // Check that the last ditch allocator works as expected.
   CHECK(positionIsCorrect("---xoo-xx--xx--x", stagingArea, chunkPool,
                           tempChunks, UseLastDitchAllocator));
+  // Check that the last ditch allocator can deal with naturally aligned chunks.
+  CHECK(positionIsCorrect("------oox--xx--x", stagingArea, chunkPool,
+                          tempChunks, UseLastDitchAllocator));
 
   // Clean up.
   while (--tempChunks >= 0) {
     unmapPages(chunkPool[tempChunks], 2 * Chunk);
   }
   return true;
 }
 
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2017,17 +2017,17 @@ extern JS_PUBLIC_API bool IsSetObject(JS
  * Assign 'undefined' to all of the object's non-reserved slots. Note: this is
  * done for all slots, regardless of the associated property descriptor.
  */
 JS_PUBLIC_API void JS_SetAllNonReservedSlotsToUndefined(JSContext* cx,
                                                         JSObject* objArg);
 
 /**
  * Create a new array buffer with the given contents. It must be legal to pass
- * these contents to free(). On success, the ownership is transferred to the
+ * these contents to JS_free(). On success, the ownership is transferred to the
  * new array buffer.
  */
 extern JS_PUBLIC_API JSObject* JS_NewArrayBufferWithContents(JSContext* cx,
                                                              size_t nbytes,
                                                              void* contents);
 
 namespace JS {
 
@@ -2062,18 +2062,18 @@ using BufferContentsFreeFunc = void (*)(
  * freed with some function other than free().
  */
 extern JS_PUBLIC_API JSObject* JS_NewExternalArrayBuffer(
     JSContext* cx, size_t nbytes, void* contents,
     JS::BufferContentsFreeFunc freeFunc, void* freeUserData = nullptr);
 
 /**
  * Create a new array buffer with the given contents.  The array buffer does not
- * take ownership of contents, and JS_DetachArrayBuffer must be called before
- * the contents are disposed of.
+ * take ownership of contents.  JS_DetachArrayBuffer must be called before
+ * the contents are disposed of by the user; this call will always succeed.
  */
 extern JS_PUBLIC_API JSObject* JS_NewArrayBufferWithExternalContents(
     JSContext* cx, size_t nbytes, void* contents);
 
 /**
  * Steal the contents of the given array buffer. The array buffer has its
  * length set to 0 and its contents array cleared. The caller takes ownership
  * of the return value and must free it or transfer ownership via
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -1938,17 +1938,19 @@ extern JS_FRIEND_API void* JS_GetArrayBu
  */
 extern JS_FRIEND_API JSObject* JS_GetArrayBufferViewBuffer(
     JSContext* cx, JS::HandleObject obj, bool* isSharedMemory);
 
 /**
  * Detach an ArrayBuffer, causing all associated views to no longer refer to
  * the ArrayBuffer's original attached memory.
  *
- * The |changeData| argument is obsolete and ignored.
+ * This function throws only if it is provided a non-ArrayBuffer object or if
+ * the provided ArrayBuffer is a WASM-backed ArrayBuffer or an ArrayBuffer used
+ * in asm.js code.
  */
 extern JS_FRIEND_API bool JS_DetachArrayBuffer(JSContext* cx,
                                                JS::HandleObject obj);
 
 /**
  * Check whether the obj is a detached ArrayBufferObject. Note that this may
  * return false if a security wrapper is encountered that denies the
  * unwrapping.
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -1911,19 +1911,19 @@ static uint8_t* CacheEntry_getBytecode(J
   *length = arrayBuffer->byteLength();
   return arrayBuffer->dataPointer();
 }
 
 static bool CacheEntry_setBytecode(JSContext* cx, HandleObject cache,
                                    uint8_t* buffer, uint32_t length) {
   MOZ_ASSERT(CacheEntry_isCacheEntry(cache));
 
-  ArrayBufferObject::BufferContents contents =
-      ArrayBufferObject::BufferContents::create<ArrayBufferObject::PLAIN>(
-          buffer);
+  using BufferContents = ArrayBufferObject::BufferContents;
+
+  BufferContents contents = BufferContents::createPlainData(buffer);
   Rooted<ArrayBufferObject*> arrayBuffer(
       cx, ArrayBufferObject::create(cx, length, contents));
   if (!arrayBuffer) {
     return false;
   }
 
   SetReservedSlot(cache, CacheEntry_BYTECODE, ObjectValue(*arrayBuffer));
   return true;
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -288,17 +288,17 @@ static const ClassOps ArrayBufferObjectC
     nullptr, /* enumerate */
     nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     ArrayBufferObject::finalize,
     nullptr, /* call        */
     nullptr, /* hasInstance */
     nullptr, /* construct   */
-    ArrayBufferObject::trace,
+    nullptr, /* trace */
 };
 
 static const JSFunctionSpec arraybuffer_functions[] = {
     JS_FN("isView", ArrayBufferObject::fun_isView, 1, 0), JS_FS_END};
 
 static const JSPropertySpec arraybuffer_properties[] = {
     JS_SELF_HOSTED_SYM_GET(species, "ArrayBufferSpecies", 0), JS_PS_END};
 
@@ -440,17 +440,17 @@ bool ArrayBufferObject::class_constructo
   args.rval().setObject(*bufobj);
   return true;
 }
 
 static ArrayBufferObject::BufferContents AllocateArrayBufferContents(
     JSContext* cx, uint32_t nbytes) {
   uint8_t* p =
       cx->pod_callocCanGC<uint8_t>(nbytes, js::ArrayBufferContentsArena);
-  return ArrayBufferObject::BufferContents::create<ArrayBufferObject::PLAIN>(p);
+  return ArrayBufferObject::BufferContents::createPlainData(p);
 }
 
 static void NoteViewBufferWasDetached(
     ArrayBufferViewObject* view, ArrayBufferObject::BufferContents newContents,
     JSContext* cx) {
   MOZ_ASSERT(!view->isSharedMemory());
 
   view->notifyBufferDetached(newContents.data());
@@ -463,18 +463,16 @@ static void NoteViewBufferWasDetached(
                                             Handle<ArrayBufferObject*> buffer,
                                             BufferContents newContents) {
   cx->check(buffer);
   MOZ_ASSERT(!buffer->isPreparedForAsmJS());
 
   // When detaching buffers where we don't know all views, the new data must
   // match the old data. All missing views are typed objects, which do not
   // expect their data to ever change.
-  MOZ_ASSERT_IF(buffer->forInlineTypedObject(),
-                newContents.data() == buffer->dataPointer());
 
   // When detaching a buffer with typed object views, any jitcode accessing
   // such views must be deoptimized so that detachment checks are performed.
   // This is done by setting a zone-wide flag indicating that buffers with
   // typed object views have been detached.
   if (buffer->hasTypedObjectViews()) {
     // Make sure the global object's group has been instantiated, so the
     // flag change will be observed.
@@ -498,18 +496,16 @@ static void NoteViewBufferWasDetached(
     for (size_t i = 0; i < views->length(); i++) {
       JSObject* view = (*views)[i];
       NoteViewBufferWasDetached(&view->as<ArrayBufferViewObject>(), newContents,
                                 cx);
     }
     innerViews.removeViews(buffer);
   }
   if (JSObject* view = buffer->firstView()) {
-    MOZ_ASSERT(!buffer->forInlineTypedObject(),
-               "Typed object buffers cannot be detached");
     NoteViewBufferWasDetached(&view->as<ArrayBufferViewObject>(), newContents,
                               cx);
     buffer->setFirstView(nullptr);
   }
 
   if (newContents.data() != buffer->dataPointer()) {
     buffer->setNewData(cx->runtime()->defaultFreeOp(), newContents, OwnsData);
   }
@@ -556,17 +552,16 @@ void ArrayBufferObject::changeViewConten
 
 // BufferContents is specific to ArrayBuffer, hence it will not represent shared
 // memory.
 
 void ArrayBufferObject::changeContents(JSContext* cx,
                                        BufferContents newContents,
                                        OwnsState ownsState) {
   MOZ_RELEASE_ASSERT(!isWasm());
-  MOZ_ASSERT(!forInlineTypedObject());
 
   // Change buffer contents.
   uint8_t* oldDataPointer = dataPointer();
   setNewData(cx->runtime()->defaultFreeOp(), newContents, ownsState);
 
   // Update all views.
   auto& innerViews = ObjectRealm::get(this).innerViews.get();
   if (InnerViewTable::ViewVector* views =
@@ -926,27 +921,39 @@ bool js::CreateWasmBuffer(JSContext* cx,
 // Returning false without throwing means that asm.js linking will fail which
 // will recompile as non-asm.js.
 /* static */ bool ArrayBufferObject::prepareForAsmJS(
     JSContext* cx, Handle<ArrayBufferObject*> buffer) {
   MOZ_ASSERT(buffer->byteLength() % wasm::PageSize == 0);
   // Don't assert cx->wasmHaveSignalHandlers because (1) they aren't needed
   // for asm.js, (2) they are only installed for WebAssembly, not asm.js.
 
-  if (buffer->forInlineTypedObject()) {
+  // wasm buffers can be detached at any time.
+  if (buffer->isWasm()) {
+    MOZ_ASSERT(!buffer->isPreparedForAsmJS());
     return false;
   }
 
-  if (!buffer->isWasm() && buffer->isPreparedForAsmJS()) {
-    return true;
+  // asm.js code and associated buffers are potentially long-lived.  Yet if
+  // |buffer->hasUserOwnedData()|, |buffer| *must* be detached by the user
+  // before the user-provided data is disposed.  Eliminate the complexity of
+  // this edge case by not allowing buffers with user-provided content to be
+  // used with asm.js, as no callers exist that want to use such buffer with
+  // asm.js.
+  if (buffer->hasUserOwnedData()) {
+    MOZ_ASSERT(!buffer->isPreparedForAsmJS());
+    return false;
   }
 
-  // Non-prepared-for-asm.js wasm buffers can be detached at any time.
-  if (buffer->isWasm()) {
-    return false;
+  MOZ_ASSERT(buffer->isPlainData() || buffer->isMapped() ||
+             buffer->isExternal());
+
+  // Buffers already prepared for asm.js need no further work.
+  if (buffer->isPreparedForAsmJS()) {
+    return true;
   }
 
   if (!buffer->ownsData()) {
     BufferContents contents =
         AllocateArrayBufferContents(cx, buffer->byteLength());
     if (!contents) {
       return false;
     }
@@ -981,35 +988,43 @@ ArrayBufferObject::FreeInfo* ArrayBuffer
   MOZ_ASSERT(isExternal());
   return reinterpret_cast<FreeInfo*>(inlineDataPointer());
 }
 
 void ArrayBufferObject::releaseData(FreeOp* fop) {
   MOZ_ASSERT(ownsData());
 
   switch (bufferKind()) {
-    case PLAIN:
+    case PLAIN_DATA:
       fop->free_(dataPointer());
       break;
+    case USER_OWNED:
+      MOZ_ASSERT_UNREACHABLE("user-owned data should never be owned by this");
+      break;
     case MAPPED:
       gc::DeallocateMappedContent(dataPointer(), byteLength());
       break;
     case WASM:
       WasmArrayRawBuffer::Release(dataPointer());
       break;
     case EXTERNAL:
       if (freeInfo()->freeFunc) {
         // The analyzer can't know for sure whether the embedder-supplied
         // free function will GC. We give the analyzer a hint here.
         // (Doing a GC in the free function is considered a programmer
         // error.)
         JS::AutoSuppressGCAnalysis nogc;
         freeInfo()->freeFunc(dataPointer(), freeInfo()->freeUserData);
       }
       break;
+    case BAD1:
+    case BAD2:
+    case BAD3:
+      MOZ_CRASH("invalid BufferKind encountered");
+      break;
   }
 }
 
 void ArrayBufferObject::setDataPointer(BufferContents contents,
                                        OwnsState ownsData) {
   setFixedSlot(DATA_SLOT, PrivateValue(contents.data()));
   setOwnsData(ownsData);
   setFlags((flags() & ~KIND_MASK) | contents.kind());
@@ -1118,17 +1133,18 @@ Maybe<uint32_t> js::WasmArrayBufferMaxSi
   if (!newRawBuf) {
     return false;
   }
   BufferContents contents =
       BufferContents::create<WASM>(newRawBuf->dataPointer());
   newBuf->initialize(newSize, contents, OwnsData);
 
   memcpy(newBuf->dataPointer(), oldBuf->dataPointer(), oldBuf->byteLength());
-  ArrayBufferObject::detach(cx, oldBuf, BufferContents::createPlain(nullptr));
+  ArrayBufferObject::detach(cx, oldBuf,
+                            BufferContents::createPlainData(nullptr));
   return true;
 }
 
 uint32_t ArrayBufferObject::wasmBoundsCheckLimit() const {
   if (isWasm()) {
     return contents().wasmBuffer()->boundsCheckLimit();
   }
   return byteLength();
@@ -1202,17 +1218,17 @@ ArrayBufferObject* ArrayBufferObject::cr
     }
   } else {
     MOZ_ASSERT(ownsState == OwnsData);
     size_t usableSlots = NativeObject::MAX_FIXED_SLOTS - reservedSlots;
     if (nbytes <= usableSlots * sizeof(Value)) {
       int newSlots = JS_HOWMANY(nbytes, sizeof(Value));
       MOZ_ASSERT(int(nbytes) <= newSlots * int(sizeof(Value)));
       nslots = reservedSlots + newSlots;
-      contents = BufferContents::createPlain(nullptr);
+      contents = BufferContents::createPlainData(nullptr);
     } else {
       contents = AllocateArrayBufferContents(cx, nbytes);
       if (!contents) {
         ReportOutOfMemory(cx);
         return nullptr;
       }
       allocated = true;
     }
@@ -1232,41 +1248,42 @@ ArrayBufferObject* ArrayBufferObject::cr
   }
 
   MOZ_ASSERT(obj->getClass() == &class_);
   MOZ_ASSERT(!gc::IsInsideNursery(obj));
 
   if (!contents) {
     void* data = obj->inlineDataPointer();
     memset(data, 0, nbytes);
-    obj->initialize(nbytes, BufferContents::createPlain(data), DoesntOwnData);
+    obj->initialize(nbytes, BufferContents::createPlainData(data),
+                    DoesntOwnData);
   } else {
     obj->initialize(nbytes, contents, ownsState);
   }
 
   return obj;
 }
 
 ArrayBufferObject* ArrayBufferObject::create(
     JSContext* cx, uint32_t nbytes, HandleObject proto /* = nullptr */) {
-  return create(cx, nbytes, BufferContents::createPlain(nullptr),
+  return create(cx, nbytes, BufferContents::createPlainData(nullptr),
                 OwnsState::OwnsData, proto);
 }
 
 ArrayBufferObject* ArrayBufferObject::createEmpty(JSContext* cx) {
   AutoSetNewObjectMetadata metadata(cx);
   ArrayBufferObject* obj = NewBuiltinClassInstance<ArrayBufferObject>(cx);
   if (!obj) {
     return nullptr;
   }
 
   obj->setByteLength(0);
   obj->setFlags(0);
   obj->setFirstView(nullptr);
-  obj->setDataPointer(BufferContents::createPlain(nullptr), DoesntOwnData);
+  obj->setDataPointer(BufferContents::createPlainData(nullptr), DoesntOwnData);
 
   return obj;
 }
 
 ArrayBufferObject* ArrayBufferObject::createFromNewRawBuffer(
     JSContext* cx, WasmArrayRawBuffer* buffer, uint32_t initialSize) {
   AutoSetNewObjectMetadata metadata(cx);
   ArrayBufferObject* obj = NewBuiltinClassInstance<ArrayBufferObject>(cx);
@@ -1286,33 +1303,34 @@ ArrayBufferObject* ArrayBufferObject::cr
 
   return obj;
 }
 
 /* static */ ArrayBufferObject::BufferContents
 ArrayBufferObject::externalizeContents(JSContext* cx,
                                        Handle<ArrayBufferObject*> buffer,
                                        bool hasStealableContents) {
-  MOZ_ASSERT(buffer->isPlain(), "Only support doing this on plain ABOs");
+  MOZ_ASSERT(buffer->isPlainData(),
+             "only support doing this on ABOs containing plain data");
   MOZ_ASSERT(!buffer->isDetached(), "must have contents to externalize");
   MOZ_ASSERT_IF(hasStealableContents, buffer->hasStealableContents());
 
   BufferContents contents = buffer->contents();
 
   if (hasStealableContents) {
     buffer->setOwnsData(DoesntOwnData);
     return contents;
   }
 
   // Create a new chunk of memory to return since we cannot steal the
   // existing contents away from the buffer.
   BufferContents newContents =
       AllocateArrayBufferContents(cx, buffer->byteLength());
   if (!newContents) {
-    return BufferContents::createPlain(nullptr);
+    return BufferContents::createFailed();
   }
   memcpy(newContents.data(), contents.data(), buffer->byteLength());
   buffer->changeContents(cx, newContents, DoesntOwnData);
 
   return newContents;
 }
 
 /* static */ ArrayBufferObject::BufferContents ArrayBufferObject::stealContents(
@@ -1325,29 +1343,29 @@ ArrayBufferObject::externalizeContents(J
                     (buffer->isWasm() && !buffer->isPreparedForAsmJS()));
   cx->check(buffer);
 
   BufferContents oldContents = buffer->contents();
 
   if (hasStealableContents) {
     // Return the old contents and reset the detached buffer's data
     // pointer. This pointer should never be accessed.
-    auto newContents = BufferContents::createPlain(nullptr);
+    auto newContents = BufferContents::createPlainData(nullptr);
     buffer->setOwnsData(DoesntOwnData);  // Do not free the stolen data.
     ArrayBufferObject::detach(cx, buffer, newContents);
     buffer->setOwnsData(DoesntOwnData);  // Do not free the nullptr.
     return oldContents;
   }
 
   // Create a new chunk of memory to return since we cannot steal the
   // existing contents away from the buffer.
   BufferContents contentsCopy =
       AllocateArrayBufferContents(cx, buffer->byteLength());
   if (!contentsCopy) {
-    return BufferContents::createPlain(nullptr);
+    return BufferContents::createFailed();
   }
 
   if (buffer->byteLength() > 0) {
     memcpy(contentsCopy.data(), oldContents.data(), buffer->byteLength());
   }
   ArrayBufferObject::detach(cx, buffer, oldContents);
   return contentsCopy;
 }
@@ -1356,34 +1374,44 @@ ArrayBufferObject::externalizeContents(J
     JSObject* obj, mozilla::MallocSizeOf mallocSizeOf, JS::ClassInfo* info) {
   ArrayBufferObject& buffer = AsArrayBuffer(obj);
 
   if (!buffer.ownsData()) {
     return;
   }
 
   switch (buffer.bufferKind()) {
-    case PLAIN:
+    case PLAIN_DATA:
       if (buffer.isPreparedForAsmJS()) {
         info->objectsMallocHeapElementsAsmJS +=
             mallocSizeOf(buffer.dataPointer());
       } else {
         info->objectsMallocHeapElementsNormal +=
             mallocSizeOf(buffer.dataPointer());
       }
       break;
+    case USER_OWNED:
+      MOZ_ASSERT_UNREACHABLE(
+          "user-owned data should never be owned by this, and such memory "
+          "should be accounted for by the code that provided it");
+      break;
     case MAPPED:
       info->objectsNonHeapElementsNormal += buffer.byteLength();
       break;
     case WASM:
       info->objectsNonHeapElementsWasm += buffer.byteLength();
       MOZ_ASSERT(buffer.wasmMappedSize() >= buffer.byteLength());
       info->wasmGuardPages += buffer.wasmMappedSize() - buffer.byteLength();
       break;
-    case KIND_MASK:
+    case EXTERNAL:
+      MOZ_CRASH("external buffers not currently supported");
+      break;
+    case BAD1:
+    case BAD2:
+    case BAD3:
       MOZ_CRASH("bad bufferKind()");
   }
 }
 
 /* static */ void ArrayBufferObject::finalize(FreeOp* fop, JSObject* obj) {
   ArrayBufferObject& buffer = obj->as<ArrayBufferObject>();
 
   if (buffer.ownsData()) {
@@ -1398,35 +1426,16 @@ ArrayBufferObject::externalizeContents(J
   MOZ_ASSERT(toBuffer->byteLength() >= toIndex + count);
   MOZ_ASSERT(fromBuffer->byteLength() >= fromIndex);
   MOZ_ASSERT(fromBuffer->byteLength() >= fromIndex + count);
 
   memcpy(toBuffer->dataPointer() + toIndex,
          fromBuffer->dataPointer() + fromIndex, count);
 }
 
-/* static */ void ArrayBufferObject::trace(JSTracer* trc, JSObject* obj) {
-  // If this buffer is associated with an inline typed object,
-  // fix up the data pointer if the typed object was moved.
-  ArrayBufferObject& buf = obj->as<ArrayBufferObject>();
-
-  if (!buf.forInlineTypedObject()) {
-    return;
-  }
-
-  JSObject* view = MaybeForwarded(buf.firstView());
-  MOZ_ASSERT(view && view->is<InlineTransparentTypedObject>());
-
-  TraceManuallyBarrieredEdge(trc, &view,
-                             "array buffer inline typed object owner");
-  buf.setFixedSlot(
-      DATA_SLOT,
-      PrivateValue(view->as<InlineTransparentTypedObject>().inlineTypedMem()));
-}
-
 /* static */ size_t ArrayBufferObject::objectMoved(JSObject* obj,
                                                    JSObject* old) {
   ArrayBufferObject& dst = obj->as<ArrayBufferObject>();
   const ArrayBufferObject& src = old->as<ArrayBufferObject>();
 
   // Fix up possible inline data pointer.
   if (src.hasInlineData()) {
     dst.setFixedSlot(DATA_SLOT, PrivateValue(dst.inlineDataPointer()));
@@ -1629,17 +1638,17 @@ JS_FRIEND_API bool JS_DetachArrayBuffer(
   if (buffer->isWasm() || buffer->isPreparedForAsmJS()) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_WASM_NO_TRANSFER);
     return false;
   }
 
   ArrayBufferObject::BufferContents newContents =
       buffer->hasStealableContents()
-          ? ArrayBufferObject::BufferContents::createPlain(nullptr)
+          ? ArrayBufferObject::BufferContents::createPlainData(nullptr)
           : buffer->contents();
 
   ArrayBufferObject::detach(cx, buffer, newContents);
 
   return true;
 }
 
 JS_FRIEND_API bool JS_IsDetachedArrayBufferObject(JSObject* obj) {
@@ -1660,18 +1669,19 @@ JS_FRIEND_API JSObject* JS_NewArrayBuffe
 
 JS_PUBLIC_API JSObject* JS_NewArrayBufferWithContents(JSContext* cx,
                                                       size_t nbytes,
                                                       void* data) {
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
   MOZ_ASSERT_IF(!data, nbytes == 0);
 
-  ArrayBufferObject::BufferContents contents =
-      ArrayBufferObject::BufferContents::create<ArrayBufferObject::PLAIN>(data);
+  using BufferContents = ArrayBufferObject::BufferContents;
+
+  BufferContents contents = BufferContents::createPlainData(data);
   return ArrayBufferObject::create(cx, nbytes, contents,
                                    ArrayBufferObject::OwnsData,
                                    /* proto = */ nullptr, TenuredObject);
 }
 
 JS_PUBLIC_API JSObject* JS_NewExternalArrayBuffer(
     JSContext* cx, size_t nbytes, void* data,
     JS::BufferContentsFreeFunc freeFunc, void* freeUserData) {
@@ -1690,18 +1700,20 @@ JS_PUBLIC_API JSObject* JS_NewExternalAr
 }
 
 JS_PUBLIC_API JSObject* JS_NewArrayBufferWithExternalContents(JSContext* cx,
                                                               size_t nbytes,
                                                               void* data) {
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
   MOZ_ASSERT_IF(!data, nbytes == 0);
-  ArrayBufferObject::BufferContents contents =
-      ArrayBufferObject::BufferContents::create<ArrayBufferObject::PLAIN>(data);
+
+  using BufferContents = ArrayBufferObject::BufferContents;
+
+  BufferContents contents = BufferContents::createUserOwned(data);
   return ArrayBufferObject::create(cx, nbytes, contents,
                                    ArrayBufferObject::DoesntOwnData,
                                    /* proto = */ nullptr, TenuredObject);
 }
 
 JS_FRIEND_API bool JS_IsArrayBufferObject(JSObject* obj) {
   return obj->canUnwrapAs<ArrayBufferObject>();
 }
@@ -1726,18 +1738,19 @@ JS_PUBLIC_API void* JS_ExternalizeArrayB
 
   if (!obj->is<ArrayBufferObject>()) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_TYPED_ARRAY_BAD_ARGS);
     return nullptr;
   }
 
   Handle<ArrayBufferObject*> buffer = obj.as<ArrayBufferObject>();
-  if (!buffer->isPlain()) {
-    // This operation isn't supported on mapped or wsm ArrayBufferObjects.
+  if (!buffer->isPlainData()) {
+    // This operation isn't supported on mapped or wasm ArrayBufferObjects, or
+    // on ArrayBufferObjects with user-provided data.
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_TYPED_ARRAY_BAD_ARGS);
     return nullptr;
   }
   if (buffer->isDetached()) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_TYPED_ARRAY_DETACHED);
     return nullptr;
@@ -1780,22 +1793,26 @@ JS_PUBLIC_API void* JS_StealArrayBufferC
   }
 
   if (buffer->isWasm() || buffer->isPreparedForAsmJS()) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_WASM_NO_TRANSFER);
     return nullptr;
   }
 
-  // The caller assumes that a plain malloc'd buffer is returned.
-  // hasStealableContents is true for mapped buffers, so we must additionally
-  // require that the buffer is plain. In the future, we could consider
-  // returning something that handles releasing the memory.
+  // The caller assumes that a plain malloc'd buffer is returned.  To steal
+  // actual contents, then, we must have |hasStealableContents()| *and* the
+  // contents must be |isPlainData()|.  (Mapped data would not be malloc'd;
+  // user-provided data we flat-out know nothing about at all -- although it
+  // *should* have not passed the |hasStealableContents()| check anyway.)
+  //
+  // In the future, we could consider returning something that handles
+  // releasing the memory, in the mapped-data case.
   bool hasStealableContents =
-      buffer->hasStealableContents() && buffer->isPlain();
+      buffer->hasStealableContents() && buffer->isPlainData();
 
   AutoRealm ar(cx, buffer);
   return ArrayBufferObject::stealContents(cx, buffer, hasStealableContents)
       .data();
 }
 
 JS_PUBLIC_API JSObject* JS_NewMappedArrayBufferWithContents(JSContext* cx,
                                                             size_t nbytes,
--- a/js/src/vm/ArrayBufferObject.h
+++ b/js/src/vm/ArrayBufferObject.h
@@ -170,54 +170,64 @@ class ArrayBufferObject : public ArrayBu
 
  public:
   enum OwnsState {
     DoesntOwnData = 0,
     OwnsData = 1,
   };
 
   enum BufferKind {
-    PLAIN = 0,  // malloced or inline data
-    WASM = 1,
-    MAPPED = 2,
-    EXTERNAL = 3,
+    /** Malloced or inline data. */
+    PLAIN_DATA = 0b000,
+
+    /**
+     * User-owned memory.  The associated buffer must be manually detached
+     * before the user invalidates (deallocates, reuses the storage of, &c.)
+     * the user-owned memory.
+     */
+    USER_OWNED = 0b001,
 
-    KIND_MASK = 0x3
+    WASM = 0b010,
+    MAPPED = 0b011,
+    EXTERNAL = 0b100,
+
+    // These kind-values are currently invalid.  We intend to expand valid
+    // BufferKinds in the future to either partly or fully use these values.
+    BAD1 = 0b101,
+    BAD2 = 0b110,
+    BAD3 = 0b111,
+
+    KIND_MASK = 0b111
   };
 
  protected:
   enum ArrayBufferFlags {
     // The flags also store the BufferKind
     BUFFER_KIND_MASK = BufferKind::KIND_MASK,
 
-    DETACHED = 0x4,
+    DETACHED = 0b1000,
 
     // The dataPointer() is owned by this buffer and should be released
     // when no longer in use. Releasing the pointer may be done by freeing,
     // invoking a free callback function, or unmapping, as determined by the
     // buffer's other flags.
     //
     // Array buffers which do not own their data include buffers that
     // allocate their data inline, and buffers that are created lazily for
     // typed objects with inline storage, in which case the buffer points
     // directly to the typed object's storage.
-    OWNS_DATA = 0x8,
-
-    // This array buffer was created lazily for a typed object with inline
-    // data. This implies both that the typed object owns the buffer's data
-    // and that the list of views sharing this buffer's data might be
-    // incomplete. Any missing views will be typed objects.
-    FOR_INLINE_TYPED_OBJECT = 0x10,
+    OWNS_DATA = 0b1'0000,
 
     // Views of this buffer might include typed objects.
-    TYPED_OBJECT_VIEWS = 0x20,
+    TYPED_OBJECT_VIEWS = 0b10'0000,
 
-    // This PLAIN or WASM buffer has been prepared for asm.js and cannot
-    // henceforth be transferred/detached.
-    FOR_ASMJS = 0x40
+    // This PLAIN_DATA, MAPPED, or EXTERNAL buffer (only WASM and USER_OWNED
+    // are excluded) has been prepared for asm.js and cannot henceforth be
+    // transferred/detached.
+    FOR_ASMJS = 0b100'0000,
   };
 
   static_assert(JS_ARRAYBUFFER_DETACHED_FLAG == DETACHED,
                 "self-hosted code with burned-in constants must use the "
                 "correct DETACHED bit value");
 
  public:
   class BufferContents {
@@ -243,27 +253,35 @@ class ArrayBufferObject : public ArrayBu
     }
 
    public:
     template <BufferKind Kind>
     static BufferContents create(void* data) {
       return BufferContents(static_cast<uint8_t*>(data), Kind);
     }
 
-    static BufferContents createPlain(void* data) {
-      return BufferContents(static_cast<uint8_t*>(data), PLAIN);
+    static BufferContents createPlainData(void* data) {
+      return BufferContents(static_cast<uint8_t*>(data), PLAIN_DATA);
+    }
+
+    static BufferContents createUserOwned(void* data) {
+      return BufferContents(static_cast<uint8_t*>(data), USER_OWNED);
     }
 
     static BufferContents createExternal(void* data,
                                          JS::BufferContentsFreeFunc freeFunc,
                                          void* freeUserData = nullptr) {
       return BufferContents(static_cast<uint8_t*>(data), EXTERNAL, freeFunc,
                             freeUserData);
     }
 
+    static BufferContents createFailed() {
+      return BufferContents(nullptr, PLAIN_DATA);
+    }
+
     uint8_t* data() const { return data_; }
     BufferKind kind() const { return kind_; }
     JS::BufferContentsFreeFunc freeFunc() const { return free_; }
     void* freeUserData() const { return freeUserData_; }
 
     explicit operator bool() const { return data_ != nullptr; }
     WasmArrayRawBuffer* wasmBuffer() const;
   };
@@ -299,17 +317,16 @@ class ArrayBufferObject : public ArrayBu
   static ArrayBufferObject* createFromNewRawBuffer(JSContext* cx,
                                                    WasmArrayRawBuffer* buffer,
                                                    uint32_t initialSize);
 
   static void copyData(Handle<ArrayBufferObject*> toBuffer, uint32_t toIndex,
                        Handle<ArrayBufferObject*> fromBuffer,
                        uint32_t fromIndex, uint32_t count);
 
-  static void trace(JSTracer* trc, JSObject* obj);
   static size_t objectMoved(JSObject* obj, JSObject* old);
 
   static BufferContents externalizeContents(JSContext* cx,
                                             Handle<ArrayBufferObject*> buffer,
                                             bool hasStealableContents);
   static BufferContents stealContents(JSContext* cx,
                                       Handle<ArrayBufferObject*> buffer,
                                       bool hasStealableContents);
@@ -374,17 +391,20 @@ class ArrayBufferObject : public ArrayBu
    * Check if the arrayBuffer contains any data. This will return false for
    * ArrayBuffer.prototype and detached ArrayBuffers.
    */
   bool hasData() const { return getClass() == &class_; }
 
   BufferKind bufferKind() const {
     return BufferKind(flags() & BUFFER_KIND_MASK);
   }
-  bool isPlain() const { return bufferKind() == PLAIN; }
+
+  bool isPlainData() const { return bufferKind() == PLAIN_DATA; }
+  bool hasUserOwnedData() const { return bufferKind() == USER_OWNED; }
+
   bool isWasm() const { return bufferKind() == WASM; }
   bool isMapped() const { return bufferKind() == MAPPED; }
   bool isExternal() const { return bufferKind() == EXTERNAL; }
   bool isDetached() const { return flags() & DETACHED; }
   bool isPreparedForAsmJS() const { return flags() & FOR_ASMJS; }
 
   // WebAssembly support:
   static MOZ_MUST_USE bool prepareForAsmJS(JSContext* cx,
@@ -404,41 +424,39 @@ class ArrayBufferObject : public ArrayBu
   static void finalize(FreeOp* fop, JSObject* obj);
 
   static BufferContents createMappedContents(int fd, size_t offset,
                                              size_t length);
 
   static size_t offsetOfFlagsSlot() { return getFixedSlotOffset(FLAGS_SLOT); }
   static size_t offsetOfDataSlot() { return getFixedSlotOffset(DATA_SLOT); }
 
-  void setForInlineTypedObject() {
-    setFlags(flags() | FOR_INLINE_TYPED_OBJECT);
-  }
   void setHasTypedObjectViews() { setFlags(flags() | TYPED_OBJECT_VIEWS); }
 
-  bool forInlineTypedObject() const {
-    return flags() & FOR_INLINE_TYPED_OBJECT;
-  }
-
  protected:
   void setDataPointer(BufferContents contents, OwnsState ownsState);
   void setByteLength(uint32_t length);
 
   uint32_t flags() const;
   void setFlags(uint32_t flags);
 
   bool ownsData() const { return flags() & OWNS_DATA; }
   void setOwnsData(OwnsState owns) {
     setFlags(owns ? (flags() | OWNS_DATA) : (flags() & ~OWNS_DATA));
   }
 
   bool hasTypedObjectViews() const { return flags() & TYPED_OBJECT_VIEWS; }
 
   void setIsDetached() { setFlags(flags() | DETACHED); }
-  void setIsPreparedForAsmJS() { setFlags(flags() | FOR_ASMJS); }
+  void setIsPreparedForAsmJS() {
+    MOZ_ASSERT(!isWasm());
+    MOZ_ASSERT(!hasUserOwnedData());
+    MOZ_ASSERT(isPlainData() || isMapped() || isExternal());
+    setFlags(flags() | FOR_ASMJS);
+  }
 
   void initialize(size_t byteLength, BufferContents contents,
                   OwnsState ownsState) {
     setByteLength(byteLength);
     setFlags(0);
     setFirstView(nullptr);
     setDataPointer(contents, ownsState);
   }
--- a/js/src/vm/ArrayBufferViewObject.cpp
+++ b/js/src/vm/ArrayBufferViewObject.cpp
@@ -31,22 +31,16 @@ using namespace js;
   // Update obj's data pointer if it moved.
   if (bufSlot.isObject()) {
     if (IsArrayBuffer(&bufSlot.toObject())) {
       ArrayBufferObject& buf =
           AsArrayBuffer(MaybeForwarded(&bufSlot.toObject()));
       uint32_t offset = uint32_t(obj->getFixedSlot(BYTEOFFSET_SLOT).toInt32());
       MOZ_ASSERT(offset <= INT32_MAX);
 
-      // We don't expose the underlying ArrayBuffer for typed objects,
-      // and we don't allow constructing a TypedObject from an arbitrary
-      // ArrayBuffer, so we should never have a TypedArray/DataView with
-      // a buffer that has TypedObject views.
-      MOZ_RELEASE_ASSERT(!buf.forInlineTypedObject());
-
       MOZ_ASSERT_IF(buf.dataPointer() == nullptr, offset == 0);
 
       // The data may or may not be inline with the buffer. The buffer
       // can only move during a compacting GC, in which case its
       // objectMoved hook has already updated the buffer's data pointer.
       size_t nfixed = obj->numFixedSlotsMaybeForwarded();
       obj->setPrivateUnbarriered(nfixed, buf.dataPointer() + offset);
     }
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -2649,17 +2649,16 @@ static const JSFunctionSpec intrinsic_fu
     JS_INLINABLE_FN("GuardToSetObject", intrinsic_GuardToBuiltin<SetObject>, 1,
                     0, IntrinsicGuardToSetObject),
     JS_FN("CallSetMethodIfWrapped",
           CallNonGenericSelfhostedMethod<Is<SetObject>>, 2, 0),
 
     // See builtin/TypedObject.h for descriptors of the typedobj functions.
     JS_FN("NewOpaqueTypedObject", js::NewOpaqueTypedObject, 1, 0),
     JS_FN("NewDerivedTypedObject", js::NewDerivedTypedObject, 3, 0),
-    JS_FN("TypedObjectBuffer", TypedObject::GetBuffer, 1, 0),
     JS_FN("TypedObjectByteOffset", TypedObject::GetByteOffset, 1, 0),
     JS_FN("AttachTypedObject", js::AttachTypedObject, 3, 0),
     JS_FN("TypedObjectIsAttached", js::TypedObjectIsAttached, 1, 0),
     JS_FN("TypedObjectTypeDescr", js::TypedObjectTypeDescr, 1, 0),
     JS_FN("ClampToUint8", js::ClampToUint8, 1, 0),
     JS_FN("GetTypedObjectModule", js::GetTypedObjectModule, 0, 0),
 
     JS_INLINABLE_FN("ObjectIsTypeDescr", js::ObjectIsTypeDescr, 1, 0,
--- a/modules/libpref/Preferences.cpp
+++ b/modules/libpref/Preferences.cpp
@@ -12,23 +12,23 @@
 
 #include "base/basictypes.h"
 #include "GeckoProfiler.h"
 #include "MainThreadUtils.h"
 #include "mozilla/ArenaAllocatorExtensions.h"
 #include "mozilla/ArenaAllocator.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/Components.h"
 #include "mozilla/dom/PContent.h"
 #include "mozilla/HashFunctions.h"
 #include "mozilla/HashTable.h"
 #include "mozilla/Logging.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/MemoryReporting.h"
-#include "mozilla/ModuleUtils.h"
 #include "mozilla/Omnijar.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ResultExtensions.h"
 #include "mozilla/ScopeExit.h"
 #include "mozilla/Services.h"
 #include "mozilla/ServoStyleSet.h"
 #include "mozilla/StaticPrefs.h"
 #include "mozilla/SyncRunnable.h"
@@ -5404,40 +5404,21 @@ static void InitVarCachePref(const nsACS
 }  // namespace mozilla
 
 #undef ENSURE_PARENT_PROCESS
 
 //===========================================================================
 // Module and factory stuff
 //===========================================================================
 
-NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(Preferences,
-                                         Preferences::GetInstanceForService)
-NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrefLocalizedString, Init)
-
-static NS_DEFINE_CID(kPrefServiceCID, NS_PREFSERVICE_CID);
-static NS_DEFINE_CID(kPrefLocalizedStringCID, NS_PREFLOCALIZEDSTRING_CID);
-
-static mozilla::Module::CIDEntry kPrefCIDs[] = {
-    {&kPrefServiceCID, true, nullptr, PreferencesConstructor,
-     Module::ALLOW_IN_SOCKET_PROCESS},
-    {&kPrefLocalizedStringCID, false, nullptr,
-     nsPrefLocalizedStringConstructor},
-    {nullptr}};
-
-static mozilla::Module::ContractIDEntry kPrefContracts[] = {
-    {NS_PREFSERVICE_CONTRACTID, &kPrefServiceCID,
-     Module::ALLOW_IN_SOCKET_PROCESS},
-    {NS_PREFLOCALIZEDSTRING_CONTRACTID, &kPrefLocalizedStringCID},
-    {nullptr}};
-
-static void UnloadPrefsModule() { Preferences::Shutdown(); }
-
-static const mozilla::Module kPrefModule = {mozilla::Module::kVersion,
-                                            kPrefCIDs,
-                                            kPrefContracts,
-                                            nullptr,
-                                            nullptr,
-                                            nullptr,
-                                            UnloadPrefsModule,
-                                            Module::ALLOW_IN_SOCKET_PROCESS};
-
-NSMODULE_DEFN(nsPrefModule) = &kPrefModule;
+NS_IMPL_COMPONENT_FACTORY(nsPrefLocalizedString) {
+  auto str = MakeRefPtr<nsPrefLocalizedString>();
+  if (NS_SUCCEEDED(str->Init())) {
+    return str.forget().downcast<nsISupports>();
+  }
+  return nullptr;
+}
+
+namespace mozilla {
+
+void UnloadPrefsModule() { Preferences::Shutdown(); }
+
+}
--- a/modules/libpref/Preferences.h
+++ b/modules/libpref/Preferences.h
@@ -28,16 +28,18 @@ class nsIFile;
 // The callback function will get passed the pref name which triggered the call
 // and the void* data which was passed to the registered callback function.
 typedef void (*PrefChangedFunc)(const char* aPref, void* aData);
 
 class nsPrefBranch;
 
 namespace mozilla {
 
+void UnloadPrefsModule();
+
 // A typesafe version of PrefChangeFunc, with its data argument type deduced
 // from the type of the argument passed to RegisterCallback.
 //
 // Note: We specify this as a dependent type TypedPrefChangeFunc<T>::SelfType so
 // that it does not participate in argument type deduction. This allows us to
 // use its implicit conversion constructor, and also allows our Register and
 // Unregister methods to accept non-capturing lambdas (which will not match
 // void(*)(const char*, T*) when used in type deduction) as callback functions.
new file mode 100644
--- /dev/null
+++ b/modules/libpref/components.conf
@@ -0,0 +1,27 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Headers = [
+    'mozilla/Preferences.h',
+]
+
+UnloadFunc = 'mozilla::UnloadPrefsModule'
+
+Classes = [
+    {
+        'cid': '{91ca2441-050f-4f7c-9df8-75b40ea40156}',
+        'contract_ids': ['@mozilla.org/preferences-service;1'],
+        'singleton': True,
+        'type': 'mozilla::Preferences',
+        'headers': ['mozilla/Preferences.h'],
+        'constructor': 'mozilla::Preferences::GetInstanceForService',
+    },
+    {
+        'cid': '{064d9cee-1dd2-11b2-83e3-d25ab0193c26}',
+        'contract_ids': ['@mozilla.org/pref-localizedstring;1'],
+        'type': 'nsPrefLocalizedString',
+    },
+]
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1437,16 +1437,19 @@ pref("privacy.firstparty.isolate",      
 // This pref is effective only when "privacy.firstparty.isolate" is true.
 pref("privacy.firstparty.isolate.restrict_opener_access", true);
 // We automatically decline canvas permission requests if they are not initiated
 // from user input. Just in case that breaks something, we allow the user to revert
 // this behavior with this obscure pref. We do not intend to support this long term.
 // If you do set it, to work around some broken website, please file a bug with
 // information so we can understand why it is needed.
 pref("privacy.resistFingerprinting.autoDeclineNoUserInputCanvasPrompts", true);
+// The log level for browser console messages logged in RFPHelper.jsm
+// Change to 'All' and restart to see the messages
+pref("privacy.resistFingerprinting.jsmloglevel", "Warn");
 // A subset of Resist Fingerprinting protections focused specifically on timers for testing
 // This affects the Animation API, the performance APIs, Date.getTime, Event.timestamp,
 //   File.lastModified, audioContext.currentTime, canvas.captureStream.currentTime
 pref("privacy.reduceTimerPrecision", true);
 // Dynamically tune the resolution of the timer reduction for both of the two above prefs
 pref("privacy.resistFingerprinting.reduceTimerPrecision.microseconds", 1000);
 // Enable jittering the clock one precision value forward
 pref("privacy.resistFingerprinting.reduceTimerPrecision.jitter", true);
--- a/modules/libpref/moz.build
+++ b/modules/libpref/moz.build
@@ -31,16 +31,20 @@ EXPORTS.mozilla += [
     'StaticPrefs.h',
 ]
 
 UNIFIED_SOURCES += [
     'Preferences.cpp',
     'SharedPrefMap.cpp',
 ]
 
+XPCOM_MANIFESTS += [
+    'components.conf',
+]
+
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 DEFINES['OS_ARCH'] = CONFIG['OS_ARCH']
 DEFINES['MOZ_WIDGET_TOOLKIT'] = CONFIG['MOZ_WIDGET_TOOLKIT']
 if CONFIG['MOZ_ENABLE_WEBRENDER']:
     DEFINES['MOZ_ENABLE_WEBRENDER'] = True
--- a/netwerk/dns/mdns/libmdns/components.conf
+++ b/netwerk/dns/mdns/libmdns/components.conf
@@ -1,15 +1,22 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
-Classes = []
+Classes = [
+    {
+        'cid': '{14a50f2b-7ff6-48a5-88e3-615fd111f5d3}',
+        'contract_ids': ['@mozilla.org/toolkit/components/mdnsresponder/dns-info;1'],
+        'type': 'mozilla::net::nsDNSServiceInfo',
+        'headers': ['/netwerk/dns/mdns/libmdns/nsDNSServiceInfo.h'],
+    },
+]
 
 if buildconfig.substs['MOZ_WIDGET_TOOLKIT'] != 'cocoa':
     Classes += [
         {
             'cid': '{f9346d98-f27a-4e89-b744-493843416480}',
             'contract_ids': ['@mozilla.org/toolkit/components/mdnsresponder/dns-sd;1'],
             'jsm': 'resource://gre/modules/DNSServiceDiscovery.jsm',
             'constructor': 'nsDNSServiceDiscovery',
--- a/netwerk/dns/mdns/libmdns/moz.build
+++ b/netwerk/dns/mdns/libmdns/moz.build
@@ -29,17 +29,20 @@ else:
 
     if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
         EXTRA_JS_MODULES += [
             'MulticastDNSAndroid.jsm',
         ]
 
 UNIFIED_SOURCES += [
     'nsDNSServiceInfo.cpp',
-    'nsMulticastDNSModule.cpp',
+]
+
+XPCOM_MANIFESTS += [
+    'components.conf',
 ]
 
 XPCOM_MANIFESTS += [
     'components.conf',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 FINAL_LIBRARY = 'xul'
deleted file mode 100644
--- a/netwerk/dns/mdns/libmdns/nsMulticastDNSModule.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-#if defined(MOZ_WIDGET_COCOA)
-#  define ENABLE_DNS_SERVICE_DISCOVERY
-#endif
-
-#include "mozilla/ModuleUtils.h"
-
-#ifdef ENABLE_DNS_SERVICE_DISCOVERY
-#  include "nsDNSServiceDiscovery.h"
-#endif
-
-#include "nsDNSServiceInfo.h"
-
-#ifdef ENABLE_DNS_SERVICE_DISCOVERY
-using mozilla::net::nsDNSServiceDiscovery;
-#  define DNSSERVICEDISCOVERY_CID                      \
-    {                                                  \
-      0x8df43d23, 0xd3f9, 0x4dd5, {                    \
-        0xb9, 0x65, 0xde, 0x2c, 0xa3, 0xf6, 0xa4, 0x2c \
-      }                                                \
-    }
-NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsDNSServiceDiscovery, Init)
-NS_DEFINE_NAMED_CID(DNSSERVICEDISCOVERY_CID);
-#endif  // ENABLE_DNS_SERVICE_DISCOVERY
-
-using mozilla::net::nsDNSServiceInfo;
-#define DNSSERVICEINFO_CID                           \
-  {                                                  \
-    0x14a50f2b, 0x7ff6, 0x48a5, {                    \
-      0x88, 0xe3, 0x61, 0x5f, 0xd1, 0x11, 0xf5, 0xd3 \
-    }                                                \
-  }
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsDNSServiceInfo)
-NS_DEFINE_NAMED_CID(DNSSERVICEINFO_CID);
-
-static const mozilla::Module::CIDEntry knsDNSServiceDiscoveryCIDs[] = {
-#ifdef ENABLE_DNS_SERVICE_DISCOVERY
-    {&kDNSSERVICEDISCOVERY_CID, false, nullptr,
-     nsDNSServiceDiscoveryConstructor},
-#endif
-    {&kDNSSERVICEINFO_CID, false, nullptr, nsDNSServiceInfoConstructor},
-    {nullptr}};
-
-static const mozilla::Module::ContractIDEntry
-    knsDNSServiceDiscoveryContracts[] = {
-#ifdef ENABLE_DNS_SERVICE_DISCOVERY
-        {DNSSERVICEDISCOVERY_CONTRACT_ID, &kDNSSERVICEDISCOVERY_CID},
-#endif
-        {DNSSERVICEINFO_CONTRACT_ID, &kDNSSERVICEINFO_CID},
-        {nullptr}};
-
-static const mozilla::Module::CategoryEntry knsDNSServiceDiscoveryCategories[] =
-    {{nullptr}};
-
-static const mozilla::Module knsDNSServiceDiscoveryModule = {
-    mozilla::Module::kVersion, knsDNSServiceDiscoveryCIDs,
-    knsDNSServiceDiscoveryContracts, knsDNSServiceDiscoveryCategories};
-
-NSMODULE_DEFN(nsDNSServiceDiscoveryModule) = &knsDNSServiceDiscoveryModule;
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/gio/components.conf
@@ -0,0 +1,13 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Classes = [
+    {
+        'cid': '{ee706783-3af8-4d19-9e84-e2ebfe213480}',
+        'contract_ids': ['@mozilla.org/network/protocol;1?name=moz-gio'],
+        'type': 'nsGIOProtocolHandler',
+    },
+]
--- a/netwerk/protocol/gio/moz.build
+++ b/netwerk/protocol/gio/moz.build
@@ -3,15 +3,19 @@
 # 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/.
 
 SOURCES += [
     'nsGIOProtocolHandler.cpp',
 ]
 
+XPCOM_MANIFESTS += [
+    'components.conf',
+]
+
 FINAL_LIBRARY = 'xul'
 
 CXXFLAGS += CONFIG['TK_CFLAGS']
 
 with Files('**'):
     BUG_COMPONENT = ('Core', 'Widget: Gtk')
 
--- a/netwerk/protocol/gio/nsGIOProtocolHandler.cpp
+++ b/netwerk/protocol/gio/nsGIOProtocolHandler.cpp
@@ -2,17 +2,17 @@
 /* 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/. */
 
 /*
  * This code is based on original Mozilla gnome-vfs extension. It implements
  * input stream provided by GVFS/GIO.
  */
-#include "mozilla/ModuleUtils.h"
+#include "mozilla/Components.h"
 #include "mozilla/NullPrincipal.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 #include "nsIObserver.h"
 #include "nsThreadUtils.h"
 #include "nsProxyRelease.h"
 #include "nsIStringBundle.h"
 #include "nsIStandardURL.h"
@@ -27,16 +27,18 @@
 #include "nsIInputStream.h"
 #include "nsIProtocolHandler.h"
 #include "mozilla/Monitor.h"
 #include "plstr.h"
 #include "prtime.h"
 #include <gio/gio.h>
 #include <algorithm>
 
+using namespace mozilla;
+
 #define MOZ_GIO_SCHEME "moz-gio"
 #define MOZ_GIO_SUPPORTED_PROTOCOLS "network.gio.supported-protocols"
 
 //-----------------------------------------------------------------------------
 
 // NSPR_LOG_MODULES=gio:5
 static mozilla::LazyLogModule sGIOLog("gio");
 #define LOG(args) MOZ_LOG(sGIOLog, mozilla::LogLevel::Debug, args)
@@ -840,16 +842,24 @@ class nsGIOProtocolHandler final : publi
   void InitSupportedProtocolsPref(nsIPrefBranch *prefs);
   bool IsSupportedProtocol(const nsCString &spec);
 
   nsCString mSupportedProtocols;
 };
 
 NS_IMPL_ISUPPORTS(nsGIOProtocolHandler, nsIProtocolHandler, nsIObserver)
 
+NS_IMPL_COMPONENT_FACTORY(nsGIOProtocolHandler) {
+  auto inst = MakeRefPtr<nsGIOProtocolHandler>();
+  if (NS_SUCCEEDED(inst->Init())) {
+    return inst.forget().downcast<nsIProtocolHandler>();
+  }
+  return nullptr;
+}
+
 nsresult nsGIOProtocolHandler::Init() {
   nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
   if (prefs) {
     InitSupportedProtocolsPref(prefs);
     prefs->AddObserver(MOZ_GIO_SUPPORTED_PROTOCOLS, this, false);
   }
 
   return NS_OK;
@@ -1003,35 +1013,8 @@ NS_IMETHODIMP
 nsGIOProtocolHandler::Observe(nsISupports *aSubject, const char *aTopic,
                               const char16_t *aData) {
   if (strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) {
     nsCOMPtr<nsIPrefBranch> prefs = do_QueryInterface(aSubject);
     InitSupportedProtocolsPref(prefs);
   }
   return NS_OK;
 }
-
-//-----------------------------------------------------------------------------
-
-#define NS_GIOPROTOCOLHANDLER_CID                    \
-  { /* ee706783-3af8-4d19-9e84-e2ebfe213480 */       \
-    0xee706783, 0x3af8, 0x4d19, {                    \
-      0x9e, 0x84, 0xe2, 0xeb, 0xfe, 0x21, 0x34, 0x80 \
-    }                                                \
-  }
-
-NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsGIOProtocolHandler, Init)
-NS_DEFINE_NAMED_CID(NS_GIOPROTOCOLHANDLER_CID);
-
-static const mozilla::Module::CIDEntry kVFSCIDs[] = {
-    {&kNS_GIOPROTOCOLHANDLER_CID, false, nullptr,
-     nsGIOProtocolHandlerConstructor},
-    {nullptr}};
-
-static const mozilla::Module::ContractIDEntry kVFSContracts[] = {
-    {NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX MOZ_GIO_SCHEME,
-     &kNS_GIOPROTOCOLHANDLER_CID},
-    {nullptr}};
-
-static const mozilla::Module kVFSModule = {mozilla::Module::kVersion, kVFSCIDs,
-                                           kVFSContracts};
-
-NSMODULE_DEFN(nsGIOModule) = &kVFSModule;
new file mode 100644
--- /dev/null
+++ b/parser/htmlparser/components.conf
@@ -0,0 +1,20 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Headers = [
+    'nsHTMLTags.h',
+]
+
+UnloadFunc = 'nsHTMLTags::ReleaseTable'
+
+Classes = [
+    {
+        'cid': '{2ce606b0-bee6-11d1-aad9-00805f8a3e14}',
+        'contract_ids': [],
+        'type': 'nsParser',
+        'headers': ['/parser/htmlparser/nsParser.h'],
+    },
+]
--- a/parser/htmlparser/moz.build
+++ b/parser/htmlparser/moz.build
@@ -41,12 +41,16 @@ UNIFIED_SOURCES += [
     'nsHTMLTokenizer.cpp',
     'nsParser.cpp',
     'nsParserModule.cpp',
     'nsParserMsgUtils.cpp',
     'nsScanner.cpp',
     'nsScannerString.cpp',
 ]
 
+XPCOM_MANIFESTS += [
+    'components.conf',
+]
+
 FINAL_LIBRARY = 'xul'
 
 if CONFIG['CC_TYPE'] in ('clang', 'gcc'):
     CXXFLAGS += ['-Wno-error=shadow']
new file mode 100644
--- /dev/null
+++ b/security/manager/pki/components.conf
@@ -0,0 +1,27 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Classes = [
+    {
+        'cid': '{4bfaa9f0-1dd2-11b2-afae-a82cbaa0b606}',
+        'contract_ids': ['@mozilla.org/security/nsASN1Tree;1'],
+        'type': 'nsNSSASN1Tree',
+        'headers': ['/security/manager/pki/nsASN1Tree.h'],
+    },
+    {
+        'cid': '{518e071f-1dd2-11b2-937e-c45f14def778}',
+        'contract_ids': [
+            '@mozilla.org/nsCertificateDialogs;1',
+            '@mozilla.org/nsClientAuthDialogs;1',
+            '@mozilla.org/nsGeneratingKeypairInfoDialogs;1',
+            '@mozilla.org/nsTokenDialogs;1',
+            '@mozilla.org/nsTokenPasswordDialogs;1',
+        ],
+        'type': 'nsNSSDialogs',
+        'headers': ['/security/manager/pki/nsNSSDialogs.h'],
+        'init_method': 'Init',
+    },
+]
--- a/security/manager/pki/moz.build
+++ b/security/manager/pki/moz.build
@@ -11,17 +11,20 @@ XPIDL_SOURCES += [
 ]
 
 XPIDL_MODULE = 'pippki'
 
 UNIFIED_SOURCES += [
     'nsASN1Tree.cpp',
     'nsNSSDialogHelper.cpp',
     'nsNSSDialogs.cpp',
-    'nsPKIModule.cpp',
+]
+
+XPCOM_MANIFESTS += [
+    'components.conf',
 ]
 
 LOCAL_INCLUDES += [
     '!/dist/public/nss',
 ]
 
 FINAL_LIBRARY = 'xul'
 
deleted file mode 100644
--- a/security/manager/pki/nsPKIModule.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- *
- * 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/. */
-
-#include "mozilla/ModuleUtils.h"
-#include "nsASN1Tree.h"
-#include "nsNSSDialogs.h"
-
-NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsNSSDialogs, Init)
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsNSSASN1Tree)
-
-NS_DEFINE_NAMED_CID(NS_NSSDIALOGS_CID);
-NS_DEFINE_NAMED_CID(NS_NSSASN1OUTINER_CID);
-
-static const mozilla::Module::CIDEntry kPKICIDs[] = {
-    {&kNS_NSSDIALOGS_CID, false, nullptr, nsNSSDialogsConstructor},
-    {&kNS_NSSASN1OUTINER_CID, false, nullptr, nsNSSASN1TreeConstructor},
-    {nullptr}};
-
-static const mozilla::Module::ContractIDEntry kPKIContracts[] = {
-    {NS_TOKENPASSWORDSDIALOG_CONTRACTID, &kNS_NSSDIALOGS_CID},
-    {NS_CERTIFICATEDIALOGS_CONTRACTID, &kNS_NSSDIALOGS_CID},
-    {NS_CLIENTAUTHDIALOGS_CONTRACTID, &kNS_NSSDIALOGS_CID},
-    {NS_TOKENDIALOGS_CONTRACTID, &kNS_NSSDIALOGS_CID},
-    {NS_GENERATINGKEYPAIRINFODIALOGS_CONTRACTID, &kNS_NSSDIALOGS_CID},
-    {NS_ASN1TREE_CONTRACTID, &kNS_NSSASN1OUTINER_CID},
-    {nullptr}};
-
-static const mozilla::Module kPKIModule = {mozilla::Module::kVersion, kPKICIDs,
-                                           kPKIContracts};
-
-NSMODULE_DEFN(PKI) = &kPKIModule;
--- a/security/sandbox/common/SandboxSettings.cpp
+++ b/security/sandbox/common/SandboxSettings.cpp
@@ -1,21 +1,23 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #include "mozISandboxSettings.h"
 
-#include "mozilla/ModuleUtils.h"
+#include "mozilla/Components.h"
 #include "mozilla/Preferences.h"
 
 #include "prenv.h"
 
+using namespace mozilla;
+
 namespace mozilla {
 
 int GetEffectiveContentSandboxLevel() {
   if (PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX")) {
     return 0;
   }
   int level = Preferences::GetInt("security.sandbox.content.level");
 // On Windows and macOS, enforce a minimum content sandbox level of 1 (except on
@@ -67,25 +69,14 @@ class SandboxSettings final : public moz
 NS_IMPL_ISUPPORTS(SandboxSettings, mozISandboxSettings)
 
 NS_IMETHODIMP SandboxSettings::GetEffectiveContentSandboxLevel(
     int32_t *aRetVal) {
   *aRetVal = mozilla::GetEffectiveContentSandboxLevel();
   return NS_OK;
 }
 
-NS_GENERIC_FACTORY_CONSTRUCTOR(SandboxSettings)
-
-NS_DEFINE_NAMED_CID(MOZ_SANDBOX_SETTINGS_CID);
+}  // namespace mozilla
 
-static const mozilla::Module::CIDEntry kSandboxSettingsCIDs[] = {
-    {&kMOZ_SANDBOX_SETTINGS_CID, false, nullptr, SandboxSettingsConstructor},
-    {nullptr}};
 
-static const mozilla::Module::ContractIDEntry kSandboxSettingsContracts[] = {
-    {MOZ_SANDBOX_SETTINGS_CONTRACTID, &kMOZ_SANDBOX_SETTINGS_CID}, {nullptr}};
-
-static const mozilla::Module kSandboxSettingsModule = {
-    mozilla::Module::kVersion, kSandboxSettingsCIDs, kSandboxSettingsContracts};
-
-NSMODULE_DEFN(SandboxSettingsModule) = &kSandboxSettingsModule;
-
-}  // namespace mozilla
+NS_IMPL_COMPONENT_FACTORY(mozISandboxSettings) {
+  return MakeAndAddRef<SandboxSettings>().downcast<nsISupports>();
+}
new file mode 100644
--- /dev/null
+++ b/security/sandbox/common/components.conf
@@ -0,0 +1,13 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Classes = [
+    {
+        'cid': '{5516303d-9007-45a0-94b9-940ef134a6e2}',
+        'contract_ids': ['@mozilla.org/sandbox/sandbox-settings;1'],
+        'type': 'mozISandboxSettings',
+    },
+]
--- a/security/sandbox/common/moz.build
+++ b/security/sandbox/common/moz.build
@@ -4,16 +4,20 @@
 # 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/.
 
 with Files('**'):
     BUG_COMPONENT = ('Core', 'Security: Process Sandboxing')
 
 UNIFIED_SOURCES += ['SandboxSettings.cpp']
 
+XPCOM_MANIFESTS += [
+    'components.conf',
+]
+
 XPIDL_SOURCES += [
     'mozISandboxSettings.idl',
 ]
 
 XPIDL_MODULE = 'sandbox'
 
 FINAL_LIBRARY = 'xul'
 
--- a/security/sandbox/linux/reporter/SandboxReporterWrappers.cpp
+++ b/security/sandbox/linux/reporter/SandboxReporterWrappers.cpp
@@ -5,22 +5,24 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozISandboxReporter.h"
 #include "SandboxReporter.h"
 
 #include <time.h>
 
 #include "mozilla/Assertions.h"
-#include "mozilla/ModuleUtils.h"
+#include "mozilla/Components.h"
 #include "nsCOMPtr.h"
 #include "nsPrintfCString.h"
 #include "nsTArray.h"
 #include "nsXULAppAPI.h"
 
+using namespace mozilla;
+
 namespace mozilla {
 
 class SandboxReportWrapper final : public mozISandboxReport {
  public:
   NS_DECL_ISUPPORTS
   NS_DECL_MOZISANDBOXREPORT
 
   explicit SandboxReportWrapper(const SandboxReport& aReport)
@@ -176,26 +178,13 @@ NS_IMETHODIMP SandboxReporterWrapper::Sn
   }
 
   nsCOMPtr<mozISandboxReportArray> wrapper =
       new SandboxReportArray(SandboxReporter::Singleton()->GetSnapshot());
   wrapper.forget(aRetval);
   return NS_OK;
 }
 
-NS_GENERIC_FACTORY_CONSTRUCTOR(SandboxReporterWrapper)
-
-NS_DEFINE_NAMED_CID(MOZ_SANDBOX_REPORTER_CID);
-
-static const mozilla::Module::CIDEntry kSandboxReporterCIDs[] = {
-    {&kMOZ_SANDBOX_REPORTER_CID, false, nullptr,
-     SandboxReporterWrapperConstructor},
-    {nullptr}};
+}  // namespace mozilla
 
-static const mozilla::Module::ContractIDEntry kSandboxReporterContracts[] = {
-    {MOZ_SANDBOX_REPORTER_CONTRACTID, &kMOZ_SANDBOX_REPORTER_CID}, {nullptr}};
-
-static const mozilla::Module kSandboxReporterModule = {
-    mozilla::Module::kVersion, kSandboxReporterCIDs, kSandboxReporterContracts};
-
-NSMODULE_DEFN(SandboxReporterModule) = &kSandboxReporterModule;
-
-}  // namespace mozilla
+NS_IMPL_COMPONENT_FACTORY(mozISandboxReporter) {
+  return MakeAndAddRef<SandboxReporterWrapper>().downcast<nsISupports>();
+}
new file mode 100644
--- /dev/null
+++ b/security/sandbox/linux/reporter/components.conf
@@ -0,0 +1,13 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Classes = [
+    {
+        'cid': '{5118a6f9-2493-4f97-9552-620663e03cb3}',
+        'contract_ids': ['@mozilla.org/sandbox/syscall-reporter;1'],
+        'type': 'mozISandboxReporter',
+    },
+]
--- a/security/sandbox/linux/reporter/moz.build
+++ b/security/sandbox/linux/reporter/moz.build
@@ -9,16 +9,20 @@ EXPORTS.mozilla += [
     'SandboxReporterCommon.h',
 ]
 
 SOURCES += [
     'SandboxReporter.cpp',
     'SandboxReporterWrappers.cpp',
 ]
 
+XPCOM_MANIFESTS += [
+    'components.conf',
+]
+
 LOCAL_INCLUDES += [
     '/security/sandbox/linux', # SandboxLogging.h
 ]
 
 # Need this for base::PlatformThread
 include('/ipc/chromium/chromium-config.mozbuild')
 
 # Need this for safe_sprintf.h used by SandboxLogging.h,
--- a/services/crypto/component/IdentityCryptoService.cpp
+++ b/services/crypto/component/IdentityCryptoService.cpp
@@ -1,24 +1,24 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #include "nsIIdentityCryptoService.h"
-#include "mozilla/ModuleUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIThread.h"
 #include "nsThreadUtils.h"
 #include "nsCOMPtr.h"
 #include "nsProxyRelease.h"
 #include "nsString.h"
 #include "mozilla/ArrayUtils.h"  // ArrayLength
 #include "mozilla/Base64.h"
+#include "mozilla/Components.h"
 #include "ScopedNSSTypes.h"
 #include "NSSErrorsService.h"
 
 #include "nss.h"
 #include "pk11pub.h"
 #include "secmod.h"
 #include "secerr.h"
 #include "keyhi.h"
@@ -444,36 +444,19 @@ SignRunnable::Run() {
     NS_DispatchToMainThread(this);
   } else {
     // Back on Main Thread
     (void)mCallback->SignFinished(mRv, mSignature);
   }
 
   return NS_OK;
 }
+}  // unnamed namespace
 
 // XPCOM module registration
 
-NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(IdentityCryptoService, Init)
-
-#define NS_IDENTITYCRYPTOSERVICE_CID                 \
-  {                                                  \
-    0xbea13a3a, 0x44e8, 0x4d7f, {                    \
-      0xa0, 0xa2, 0x2c, 0x67, 0xf8, 0x4e, 0x3a, 0x97 \
-    }                                                \
+NS_IMPL_COMPONENT_FACTORY(nsIIdentityCryptoService) {
+  auto inst = MakeRefPtr<IdentityCryptoService>();
+  if (NS_SUCCEEDED(inst->Init())) {
+    return inst.forget().downcast<nsIIdentityCryptoService>();
   }
-
-NS_DEFINE_NAMED_CID(NS_IDENTITYCRYPTOSERVICE_CID);
-
-const mozilla::Module::CIDEntry kCIDs[] = {
-    {&kNS_IDENTITYCRYPTOSERVICE_CID, false, nullptr,
-     IdentityCryptoServiceConstructor},
-    {nullptr}};
-
-const mozilla::Module::ContractIDEntry kContracts[] = {
-    {"@mozilla.org/identity/crypto-service;1", &kNS_IDENTITYCRYPTOSERVICE_CID},
-    {nullptr}};
-
-const mozilla::Module kModule = {mozilla::Module::kVersion, kCIDs, kContracts};
-
-}  // unnamed namespace
-
-NSMODULE_DEFN(identity) = &kModule;
+  return nullptr;
+}
new file mode 100644
--- /dev/null
+++ b/services/crypto/component/components.conf
@@ -0,0 +1,13 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Classes = [
+    {
+        'cid': '{bea13a3a-44e8-4d7f-a0a2-2c67f84e3a97}',
+        'contract_ids': ['@mozilla.org/identity/crypto-service;1'],
+        'type': 'nsIIdentityCryptoService',
+    },
+]
--- a/services/crypto/component/moz.build
+++ b/services/crypto/component/moz.build
@@ -9,9 +9,13 @@ XPIDL_SOURCES += [
 ]
 
 XPIDL_MODULE = 'services-crypto-component'
 
 SOURCES += [
     'IdentityCryptoService.cpp',
 ]
 
+XPCOM_MANIFESTS += [
+    'components.conf',
+]
+
 FINAL_LIBRARY = 'xul'
new file mode 100644
--- /dev/null
+++ b/storage/build/components.conf
@@ -0,0 +1,25 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Classes = [
+    {
+        'cid': '{bbbb1d61-438f-4436-92ed-8308e5830fb0}',
+        'contract_ids': ['@mozilla.org/storage/service;1'],
+        'singleton': True,
+        'type': 'mozilla::storage::Service',
+        'headers': ['/storage/mozStorageService.h'],
+        'constructor': 'mozilla::storage::Service::getSingleton',
+    },
+    {
+        'cid': '{3b667ee0-d2da-4ccc-9c3d-95f2ca6a8b4c}',
+        'contract_ids': ['@mozilla.org/storage/vacuum;1'],
+        'singleton': True,
+        'type': 'mozilla::storage::VacuumManager',
+        'headers': ['/storage/VacuumManager.h'],
+        'constructor': 'mozilla::storage::VacuumManager::getSingleton',
+        'categories': {'idle-daily': 'MozStorage Vacuum Manager'},
+    },
+]
--- a/storage/build/moz.build
+++ b/storage/build/moz.build
@@ -3,19 +3,15 @@
 # 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/.
 
 EXPORTS += [
     'mozStorageCID.h',
 ]
 
-SOURCES += [
-    'mozStorageModule.cpp',
+XPCOM_MANIFESTS += [
+    'components.conf',
 ]
 
 FINAL_LIBRARY = 'xul'
 
-LOCAL_INCLUDES += [
-    '..',
-]
-
 CXXFLAGS += CONFIG['SQLITE_CFLAGS']
deleted file mode 100644
--- a/storage/build/mozStorageModule.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-#include "nsCOMPtr.h"
-#include "mozilla/ModuleUtils.h"
-
-#include "mozStorageService.h"
-#include "mozStorageConnection.h"
-#include "VacuumManager.h"
-
-#include "mozStorageCID.h"
-
-namespace mozilla {
-namespace storage {
-
-NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(Service, Service::getSingleton)
-NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(VacuumManager,
-                                         VacuumManager::getSingleton)
-
-}  // namespace storage
-}  // namespace mozilla
-
-NS_DEFINE_NAMED_CID(MOZ_STORAGE_SERVICE_CID);
-NS_DEFINE_NAMED_CID(VACUUMMANAGER_CID);
-
-static const mozilla::Module::CIDEntry kStorageCIDs[] = {
-    {&kMOZ_STORAGE_SERVICE_CID, false, nullptr,
-     mozilla::storage::ServiceConstructor},
-    {&kVACUUMMANAGER_CID, false, nullptr,
-     mozilla::storage::VacuumManagerConstructor},
-    {nullptr}};
-
-static const mozilla::Module::ContractIDEntry kStorageContracts[] = {
-    {MOZ_STORAGE_SERVICE_CONTRACTID, &kMOZ_STORAGE_SERVICE_CID},
-    {VACUUMMANAGER_CONTRACTID, &kVACUUMMANAGER_CID},
-    {nullptr}};
-
-static const mozilla::Module::CategoryEntry kStorageCategories[] = {
-    {"idle-daily", "MozStorage Vacuum Manager", VACUUMMANAGER_CONTRACTID},
-    {nullptr}};
-
-static const mozilla::Module kStorageModule = {mozilla::Module::kVersion,
-                                               kStorageCIDs, kStorageContracts,
-                                               kStorageCategories};
-
-NSMODULE_DEFN(mozStorageModule) = &kStorageModule;
--- a/testing/xpcshell/head.js
+++ b/testing/xpcshell/head.js
@@ -226,16 +226,21 @@ function _do_main() {
   tm.spinEventLoopUntilEmpty();
 }
 
 function _do_quit() {
   _testLogger.info("exiting test");
   _quit = true;
 }
 
+// This is useless, except to the extent that it has the side-effect of
+// initializing the widget module, which some tests unfortunately
+// accidentally rely on.
+void Cc["@mozilla.org/widget/transferable;1"].createInstance();
+
 /**
  * Overrides idleService with a mock.  Idle is commonly used for maintenance
  * tasks, thus if a test uses a service that requires the idle service, it will
  * start handling them.
  * This behaviour would cause random failures and slowdown tests execution,
  * for example by running database vacuum or cleanups for each test.
  *
  * @note Idle service is overridden by default.  If a test requires it, it will
@@ -247,22 +252,16 @@ var _fakeIdleService = {
     return this.registrar =
       Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
   },
   contractID: "@mozilla.org/widget/idleservice;1",
   CID: Components.ID("{9163a4ae-70c2-446c-9ac1-bbe4ab93004e}"),
 
   activate: function FIS_activate() {
     if (!this.originalCID) {
-      // This is useless, except to the extent that it has the
-      // side-effect of initializing the widget module, which some
-      // callers unfortunately accidentally rely on.
-      void Components.manager.getClassObject(Cc[this.contractID],
-                                             Ci.nsIFactory);
-
       this.originalCID = this.registrar.contractIDToCID(this.contractID);
       // Replace with the mock.
       this.registrar.registerFactory(this.CID, "Fake Idle Service",
                                      this.contractID, this.factory
       );
     }
   },
 
--- a/toolkit/components/mozintl/components.conf
+++ b/toolkit/components/mozintl/components.conf
@@ -6,15 +6,16 @@
 
 Classes = [
     {
         'cid': '{b43c96be-2b3a-4dc4-90e9-b06d34219b68}',
         'contract_ids': ['@mozilla.org/mozintlhelper;1'],
         'type': 'mozilla::MozIntlHelper',
         'headers': ['/toolkit/components/mozintl/MozIntlHelper.h'],
     },
+
     {
         'cid': '{35ec195a-e8d0-4300-83af-c8a2cc84b4a3}',
         'contract_ids': ['@mozilla.org/mozintl;1'],
         'jsm': 'resource://gre/modules/mozIntl.jsm',
         'constructor': 'MozIntl',
     },
 ]
--- a/toolkit/components/mozintl/moz.build
+++ b/toolkit/components/mozintl/moz.build
@@ -23,9 +23,13 @@ SOURCES += [
 EXTRA_JS_MODULES += [
     'mozIntl.jsm',
 ]
 
 XPCOM_MANIFESTS += [
     'components.conf',
 ]
 
+XPCOM_MANIFESTS += [
+    'components.conf',
+]
+
 FINAL_LIBRARY = 'xul'
--- a/toolkit/components/places/moz.build
+++ b/toolkit/components/places/moz.build
@@ -78,16 +78,20 @@ if CONFIG['MOZ_PLACES']:
         'TaggingService.jsm',
         'UnifiedComplete.jsm',
     ]
 
     XPCOM_MANIFESTS += [
         'components.conf',
     ]
 
+    XPCOM_MANIFESTS += [
+        'components.conf',
+    ]
+
     FINAL_LIBRARY = 'xul'
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 with Files('**'):
     BUG_COMPONENT = ('Toolkit', 'Places')
 
 if CONFIG['CC_TYPE'] in ('clang', 'gcc'):
rename from toolkit/components/resistfingerprinting/LanguagePrompt.jsm
rename to toolkit/components/resistfingerprinting/RFPHelper.jsm
--- a/toolkit/components/resistfingerprinting/LanguagePrompt.jsm
+++ b/toolkit/components/resistfingerprinting/RFPHelper.jsm
@@ -1,99 +1,173 @@
 // -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
 /* 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";
 
-var EXPORTED_SYMBOLS = ["LanguagePrompt"];
+var EXPORTED_SYMBOLS = ["RFPHelper"];
 
 const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const kPrefResistFingerprinting = "privacy.resistFingerprinting";
 const kPrefSpoofEnglish = "privacy.spoof_english";
 const kTopicHttpOnModifyRequest = "http-on-modify-request";
 
-class _LanguagePrompt {
+const kPrefLetterboxing = "privacy.resistFingerprinting.letterboxing";
+const kPrefLetterboxingDimensions =
+  "privacy.resistFingerprinting.letterboxing.dimensions";
+const kPrefLetterboxingTesting =
+  "privacy.resistFingerprinting.letterboxing.testing";
+const kTopicDOMWindowOpened = "domwindowopened";
+const kEventLetterboxingSizeUpdate = "Letterboxing:ContentSizeUpdated";
+
+const kDefaultWidthStepping = 200;
+const kDefaultHeightStepping = 100;
+
+var logConsole;
+function log(msg) {
+  if (!logConsole) {
+    logConsole = console.createInstance({
+      prefix: "RFPHelper.jsm",
+      maxLogLevelPref: "privacy.resistFingerprinting.jsmloglevel",
+    });
+  }
+
+  logConsole.log(msg);
+}
+
+class _RFPHelper {
+  // ============================================================================
+  // Shared Setup
+  // ============================================================================
   constructor() {
     this._initialized = false;
   }
 
   init() {
     if (this._initialized) {
       return;
     }
     this._initialized = true;
 
+    // Add unconditional observers
     Services.prefs.addObserver(kPrefResistFingerprinting, this);
+    Services.prefs.addObserver(kPrefLetterboxing, this);
+    XPCOMUtils.defineLazyPreferenceGetter(this, "_letterboxingDimensions",
+      kPrefLetterboxingDimensions, "", null, this._parseLetterboxingDimensions);
+    XPCOMUtils.defineLazyPreferenceGetter(this, "_isLetterboxingTesting",
+      kPrefLetterboxingTesting, false);
+
+    // Add RFP and Letterboxing observers if prefs are enabled
     this._handleResistFingerprintingChanged();
+    this._handleLetterboxingPrefChanged();
   }
 
   uninit() {
     if (!this._initialized) {
       return;
     }
     this._initialized = false;
 
+    // Remove unconditional observers
     Services.prefs.removeObserver(kPrefResistFingerprinting, this);
-    this._removeObservers();
+    Services.prefs.removeObserver(kPrefLetterboxing, this);
+    // Remove the RFP observers, swallowing exceptions if they weren't present
+    this._removeRFPObservers();
   }
 
   observe(subject, topic, data) {
     switch (topic) {
       case "nsPref:changed":
         this._handlePrefChanged(data);
         break;
       case kTopicHttpOnModifyRequest:
         this._handleHttpOnModifyRequest(subject, data);
         break;
+      case kTopicDOMWindowOpened:
+        // We attach to the newly created window by adding tabsProgressListener
+        // and event listener on it. We listen for new tabs being added or
+        // the change of the content principal and apply margins accordingly.
+        this._handleDOMWindowOpened(subject);
+        break;
+      default:
+        break;
+    }
+  }
+
+  handleEvent(aMessage) {
+    switch (aMessage.type) {
+      case "TabOpen":
+      {
+        let tab = aMessage.target;
+        this._addOrClearContentMargin(tab.linkedBrowser);
+        break;
+      }
       default:
         break;
     }
   }
 
-  _removeObservers() {
+  receiveMessage(aMessage) {
+    switch (aMessage.name) {
+      case kEventLetterboxingSizeUpdate:
+        let win = aMessage.target.ownerGlobal;
+        this._updateMarginsForTabsInWindow(win);
+        break;
+      default:
+        break;
+    }
+  }
+
+  _handlePrefChanged(data) {
+    switch (data) {
+      case kPrefResistFingerprinting:
+        this._handleResistFingerprintingChanged();
+        break;
+      case kPrefSpoofEnglish:
+        this._handleSpoofEnglishChanged();
+        break;
+      case kPrefLetterboxing:
+        this._handleLetterboxingPrefChanged();
+        break;
+      default:
+        break;
+    }
+  }
+
+  // ============================================================================
+  // Language Prompt
+  // ============================================================================
+  _addRFPObservers() {
+    Services.prefs.addObserver(kPrefSpoofEnglish, this);
+    if (this._shouldPromptForLanguagePref()) {
+      Services.obs.addObserver(this, kTopicHttpOnModifyRequest);
+    }
+  }
+
+  _removeRFPObservers() {
     try {
       Services.pref.removeObserver(kPrefSpoofEnglish, this);
     } catch (e) {
       // do nothing
     }
     try {
       Services.obs.removeObserver(this, kTopicHttpOnModifyRequest);
     } catch (e) {
       // do nothing
     }
   }
 
-  _shouldPromptForLanguagePref() {
-    return (Services.locale.appLocaleAsLangTag.substr(0, 2) !== "en")
-      && (Services.prefs.getIntPref(kPrefSpoofEnglish) === 0);
-  }
-
-  _handlePrefChanged(data) {
-    switch (data) {
-      case kPrefResistFingerprinting:
-        this._handleResistFingerprintingChanged();
-        break;
-      case kPrefSpoofEnglish:
-        this._handleSpoofEnglishChanged();
-        break;
-      default:
-        break;
-    }
-  }
-
   _handleResistFingerprintingChanged() {
     if (Services.prefs.getBoolPref(kPrefResistFingerprinting)) {
-      Services.prefs.addObserver(kPrefSpoofEnglish, this);
-      if (this._shouldPromptForLanguagePref()) {
-        Services.obs.addObserver(this, kTopicHttpOnModifyRequest);
-      }
+      this._addRFPObservers();
     } else {
-      this._removeObservers();
+      this._removeRFPObservers();
     }
   }
 
   _handleSpoofEnglishChanged() {
     switch (Services.prefs.getIntPref(kPrefSpoofEnglish)) {
       case 0: // will prompt
         // This should only happen when turning privacy.resistFingerprinting off.
         // Works like disabling accept-language spoofing.
@@ -109,16 +183,21 @@ class _LanguagePrompt {
         Services.prefs.setCharPref("intl.accept_languages", "en-US, en");
         Services.prefs.setBoolPref("javascript.use_us_english_locale", true);
         break;
       default:
         break;
     }
   }
 
+  _shouldPromptForLanguagePref() {
+    return (Services.locale.appLocaleAsLangTag.substr(0, 2) !== "en")
+      && (Services.prefs.getIntPref(kPrefSpoofEnglish) === 0);
+  }
+
   _handleHttpOnModifyRequest(subject, data) {
     // If we are loading an HTTP page from content, show the
     // "request English language web pages?" prompt.
     let httpChannel;
     try {
       httpChannel = subject.QueryInterface(Ci.nsIHttpChannel);
     } catch (e) {
       return;
@@ -191,11 +270,262 @@ class _LanguagePrompt {
     let httpChannel;
     try {
       httpChannel = channel.QueryInterface(Ci.nsIHttpChannel);
     } catch (e) {
       return null;
     }
     return httpChannel.getRequestHeader("Accept-Language");
   }
+
+  // ==============================================================================
+  // Letterboxing
+  // ============================================================================
+  /**
+   * We use the TabsProgressListener to catch the change of the content
+   * principal. We would clear the margins around the content viewport if
+   * it is the system principal.
+   */
+  onLocationChange(aBrowser) {
+    this._addOrClearContentMargin(aBrowser);
+  }
+
+  _handleLetterboxingPrefChanged() {
+    if (Services.prefs.getBoolPref(kPrefLetterboxing, false)) {
+      Services.ww.registerNotification(this);
+      this._attachAllWindows();
+    } else {
+      this._detachAllWindows();
+      Services.ww.unregisterNotification(this);
+    }
+  }
+
+  // The function to parse the dimension set from the pref value. The pref value
+  // should be formated as 'width1xheight1, width2xheight2, ...'. For
+  // example, '100x100, 200x200, 400x200 ...'.
+  _parseLetterboxingDimensions(aPrefValue) {
+    if (!aPrefValue || !aPrefValue.match(/^(?:\d+x\d+,\s*)*(?:\d+x\d+)$/)) {
+      if (aPrefValue) {
+        Cu.reportError(`Invalid pref value for ${kPrefLetterboxingDimensions}: ${aPrefValue}`);
+      }
+      return [];
+    }
+
+    return aPrefValue.split(",").map(item => {
+      let sizes = item.split("x").map(size => parseInt(size, 10));
+
+      return {
+        width: sizes[0],
+        height: sizes[1],
+      };
+    });
+  }
+
+  _addOrClearContentMargin(aBrowser) {
+    let tab = aBrowser.getTabBrowser()
+                      .getTabForBrowser(aBrowser);
+
+    // We won't do anything for lazy browsers.
+    if (!aBrowser.isConnected) {
+      return;
+    }
+
+    // We should apply no margin around an empty tab or a tab with system
+    // principal.
+    if (tab.isEmpty || aBrowser.contentPrincipal.isSystemPrincipal) {
+      this._clearContentViewMargin(aBrowser);
+    } else {
+      this._roundContentView(aBrowser);
+    }
+  }
+
+  /**
+   * The function will round the given browser by adding margins around the
+   * content viewport.
+   */
+  async _roundContentView(aBrowser) {
+    let logId = Math.random();
+    log("_roundContentView[" + logId + "]");
+    let win = aBrowser.ownerGlobal;
+    let browserContainer = aBrowser.getTabBrowser()
+                                   .getBrowserContainer(aBrowser);
+
+    let {contentWidth, contentHeight, containerWidth, containerHeight} =
+      await win.promiseDocumentFlushed(() => {
+        let contentWidth = aBrowser.clientWidth;
+        let contentHeight = aBrowser.clientHeight;
+        let containerWidth = browserContainer.clientWidth;
+        let containerHeight = browserContainer.clientHeight;
+
+        return {
+          contentWidth,
+          contentHeight,
+          containerWidth,
+          containerHeight,
+        };
+      });
+
+    log("_roundContentView[" + logId + "] contentWidth=" + contentWidth + " contentHeight=" + contentHeight +
+      " containerWidth=" + containerWidth + " containerHeight=" + containerHeight + " ");
+
+    let calcMargins = (aWidth, aHeight) => {
+      let result;
+      log("_roundContentView[" + logId + "] calcMargins(" + aWidth + ", " + aHeight + ")");
+      // If the set is empty, we will round the content with the default
+      // stepping size.
+      if (!this._letterboxingDimensions.length) {
+        result = {
+          width: (aWidth % kDefaultWidthStepping) / 2,
+          height: (aHeight % kDefaultHeightStepping) / 2,
+        };
+        log("_roundContentView[" + logId + "] calcMargins(" + aWidth + ", " + aHeight + ") = " + result.width + " x " + result.height);
+        return result;
+      }
+
+      let matchingArea = aWidth * aHeight;
+      let minWaste = Number.MAX_SAFE_INTEGER;
+      let targetDimensions = undefined;
+
+      // Find the desired dimensions which waste the least content area.
+      for (let dim of this._letterboxingDimensions) {
+        // We don't need to consider the dimensions which cannot fit into the
+        // real content size.
+        if (dim.width > aWidth || dim.height > aHeight) {
+          continue;
+        }
+
+        let waste = matchingArea - dim.width * dim.height;
+
+        if (waste >= 0 && waste < minWaste) {
+          targetDimensions = dim;
+          minWaste = waste;
+        }
+      }
+
+      // If we cannot find any dimensions match to the real content window, this
+      // means the content area is smaller the smallest size in the set. In this
+      // case, we won't apply any margins.
+      if (!targetDimensions) {
+        result = {
+          width: 0,
+          height: 0,
+        };
+      } else {
+        result = {
+          width: (aWidth - targetDimensions.width) / 2,
+          height: (aHeight - targetDimensions.height) / 2,
+        };
+      }
+
+      log("_roundContentView[" + logId + "] calcMargins(" + aWidth + ", " + aHeight + ") = " + result.width + " x " + result.height);
+      return result;
+    };
+
+    // Calculating the margins around the browser element in order to round the
+    // content viewport. We will use a 200x100 stepping if the dimension set
+    // is not given.
+    let margins = calcMargins(containerWidth, containerHeight);
+
+    // If the size of the content is already quantized, we do nothing.
+    if (aBrowser.style.margin == `${margins.height}px ${margins.width}px`) {
+      log("_roundContentView[" + logId + "] is_rounded == true");
+      if (this._isLetterboxingTesting) {
+        log("_roundContentView[" + logId + "] is_rounded == true test:letterboxing:update-margin-finish");
+        Services.obs.notifyObservers(null, "test:letterboxing:update-margin-finish");
+      }
+      return;
+    }
+
+    win.requestAnimationFrame(() => {
+      log("_roundContentView[" + logId + "] setting margins to " + margins.width + " x " + margins.height);
+      // One cannot (easily) control the color of a margin unfortunately.
+      // An initial attempt to use a border instead of a margin resulted
+      // in offset event dispatching; so for now we use a colorless margin.
+      aBrowser.style.margin = `${margins.height}px ${margins.width}px`;
+    });
+  }
+
+  _clearContentViewMargin(aBrowser) {
+    aBrowser.ownerGlobal.requestAnimationFrame(() => {
+      aBrowser.style.margin = "";
+    });
+  }
+
+  _updateMarginsForTabsInWindow(aWindow) {
+    let tabBrowser = aWindow.gBrowser;
+
+    for (let tab of tabBrowser.tabs) {
+      let browser = tab.linkedBrowser;
+      this._addOrClearContentMargin(browser);
+    }
+  }
+
+  _attachWindow(aWindow) {
+    aWindow.gBrowser
+           .addTabsProgressListener(this);
+    aWindow.addEventListener("TabOpen", this);
+    aWindow.messageManager
+           .addMessageListener(kEventLetterboxingSizeUpdate, this);
+
+    // Rounding the content viewport.
+    this._updateMarginsForTabsInWindow(aWindow);
+  }
+
+  _attachAllWindows() {
+    let windowList = Services.wm.getEnumerator("navigator:browser");
+
+    while (windowList.hasMoreElements()) {
+      let win = windowList.getNext();
+
+      if (win.closed || !win.gBrowser) {
+        continue;
+      }
+
+      this._attachWindow(win);
+    }
+  }
+
+  _detachWindow(aWindow) {
+    let tabBrowser = aWindow.gBrowser;
+    tabBrowser.removeTabsProgressListener(this);
+    aWindow.removeEventListener("TabOpen", this);
+    aWindow.messageManager
+           .removeMessageListener(kEventLetterboxingSizeUpdate, this);
+
+    // Clear all margins and tooltip for all browsers.
+    for (let tab of tabBrowser.tabs) {
+      let browser = tab.linkedBrowser;
+      this._clearContentViewMargin(browser);
+    }
+  }
+
+  _detachAllWindows() {
+    let windowList = Services.wm.getEnumerator("navigator:browser");
+
+    while (windowList.hasMoreElements()) {
+      let win = windowList.getNext();
+
+      if (win.closed || !win.gBrowser) {
+        continue;
+      }
+
+      this._detachWindow(win);
+    }
+  }
+
+  _handleDOMWindowOpened(aSubject) {
+    let win = aSubject.QueryInterface(Ci.nsIDOMWindow);
+    let self = this;
+
+    win.addEventListener("load", () => {
+      // We attach to the new window when it has been loaded if the new loaded
+      // window is a browsing window.
+      if (win.document
+             .documentElement
+             .getAttribute("windowtype") !== "navigator:browser") {
+        return;
+      }
+      self._attachWindow(win);
+    }, {once: true});
+  }
 }
 
-let LanguagePrompt = new _LanguagePrompt();
+let RFPHelper = new _RFPHelper();
--- a/toolkit/components/resistfingerprinting/moz.build
+++ b/toolkit/components/resistfingerprinting/moz.build
@@ -16,10 +16,10 @@ FINAL_LIBRARY = 'xul'
 EXPORTS += [
     'nsRFPService.h'
 ]
 EXPORTS.mozilla += [
     'RelativeTimeline.h',
 ]
 
 EXTRA_JS_MODULES += [
-    'LanguagePrompt.jsm',
+    'RFPHelper.jsm',
 ]
--- a/toolkit/components/satchel/moz.build
+++ b/toolkit/components/satchel/moz.build
@@ -35,11 +35,15 @@ EXTRA_JS_MODULES += [
     'InputListAutoComplete.jsm',
     'nsFormAutoCompleteResult.jsm',
 ]
 
 XPCOM_MANIFESTS += [
     'components.conf',
 ]
 
+XPCOM_MANIFESTS += [
+    'components.conf',
+]
+
 FINAL_LIBRARY = 'xul'
 
 JAR_MANIFESTS += ['jar.mn']
--- a/toolkit/recordreplay/ipc/ParentGraphics.cpp
+++ b/toolkit/recordreplay/ipc/ParentGraphics.cpp
@@ -5,16 +5,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // This file has the logic which the middleman process uses to send messages to
 // the UI process with painting data from the child process.
 
 #include "ParentInternal.h"
 
 #include "chrome/common/mach_ipc_mac.h"
+#include "mozilla/Assertions.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/layers/CompositorBridgeChild.h"
 #include "mozilla/layers/ImageDataSerializer.h"
 #include "mozilla/layers/LayerTransactionChild.h"
 #include "mozilla/layers/PTextureChild.h"
 #include "nsImportModule.h"
@@ -158,26 +159,33 @@ void UpdateGraphicsInUIProcess(const Pai
       char* dst = (char*)gBufferMemory + y * width * 4;
       memcpy(dst, src, width * 4);
     }
   }
 
   AutoSafeJSContext cx;
   JSAutoRealm ar(cx, xpc::PrivilegedJunkScope());
 
-  JSObject* bufferObject =
+  // Create an ArrayBuffer whose contents are the externally-provided |memory|.
+  JS::RootedObject bufferObject(cx);
+  bufferObject =
       JS_NewArrayBufferWithExternalContents(cx, width * height * 4, memory);
   MOZ_RELEASE_ASSERT(bufferObject);
 
   JS::RootedValue buffer(cx, ObjectValue(*bufferObject));
 
   // Call into the graphics module to update the canvas it manages.
   if (NS_FAILED(gGraphics->UpdateCanvas(buffer, width, height, hadFailure))) {
     MOZ_CRASH("UpdateGraphicsInUIProcess");
   }
+
+  // Manually detach this ArrayBuffer once this update completes, as the
+  // JS_NewArrayBufferWithExternalContents API mandates.  (The API also
+  // guarantees that this call always succeeds.)
+  MOZ_ALWAYS_TRUE(JS_DetachArrayBuffer(cx, bufferObject));
 }
 
 static void MaybeTriggerExplicitPaint() {
   if (gLastExplicitPaint &&
       gLastExplicitPaint->mCheckpointId == gLastCheckpoint) {
     UpdateGraphicsInUIProcess(gLastExplicitPaint.get());
   }
 }
--- a/tools/profiler/core/platform.h
+++ b/tools/profiler/core/platform.h
@@ -28,16 +28,18 @@
 
 #ifndef TOOLS_PLATFORM_H_
 #define TOOLS_PLATFORM_H_
 
 #include "PlatformMacros.h"
 
 #include "mozilla/Logging.h"
 #include "mozilla/UniquePtr.h"
+#include "nsStringFwd.h"
+#include "nsTArray.h"
 
 #include <functional>
 #include <stdint.h>
 
 // We need a definition of gettid(), but glibc doesn't provide a
 // wrapper for it.
 #if defined(__GLIBC__)
 #  include <unistd.h>
new file mode 100644
--- /dev/null
+++ b/tools/profiler/gecko/components.conf
@@ -0,0 +1,15 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Classes = [
+    {
+        'cid': '{25db9b8e-8123-4de1-b66d-8bbbedf2cdf4}',
+        'contract_ids': ['@mozilla.org/tools/profiler;1'],
+        'type': 'nsProfiler',
+        'headers': ['/tools/profiler/gecko/nsProfiler.h'],
+        'init_method': 'Init',
+    },
+]
deleted file mode 100644
--- a/tools/profiler/gecko/nsProfilerFactory.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-#include "nsProfiler.h"
-#include "nsProfilerCIID.h"
-
-#include "mozilla/ModuleUtils.h"
-#include "nsCOMPtr.h"
-
-NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsProfiler, Init)
-
-NS_DEFINE_NAMED_CID(NS_PROFILER_CID);
-
-static const mozilla::Module::CIDEntry kProfilerCIDs[] = {
-    {&kNS_PROFILER_CID, false, nullptr, nsProfilerConstructor}, {nullptr}};
-
-static const mozilla::Module::ContractIDEntry kProfilerContracts[] = {
-    {"@mozilla.org/tools/profiler;1", &kNS_PROFILER_CID}, {nullptr}};
-
-static const mozilla::Module kProfilerModule = {
-    mozilla::Module::kVersion, kProfilerCIDs, kProfilerContracts};
-
-NSMODULE_DEFN(nsProfilerModule) = &kProfilerModule;
--- a/tools/profiler/moz.build
+++ b/tools/profiler/moz.build
@@ -25,28 +25,31 @@ if CONFIG['MOZ_GECKO_PROFILER']:
         'core/ProfileBuffer.cpp',
         'core/ProfileBufferEntry.cpp',
         'core/ProfiledThreadData.cpp',
         'core/ProfileJSONWriter.cpp',
         'core/ProfilerBacktrace.cpp',
         'core/ProfilerMarkerPayload.cpp',
         'core/RegisteredThread.cpp',
         'gecko/ChildProfilerController.cpp',
-        'gecko/nsProfilerFactory.cpp',
         'gecko/nsProfilerStartParams.cpp',
         'gecko/ProfilerChild.cpp',
         'gecko/ProfilerIOInterposeObserver.cpp',
         'gecko/ProfilerParent.cpp',
         'gecko/ThreadResponsiveness.cpp',
     ]
     if CONFIG['MOZ_REPLACE_MALLOC'] and CONFIG['MOZ_PROFILER_MEMORY']:
         SOURCES += [
             'core/memory_hooks.cpp', # conflicts with platform.h class Thread
         ]
 
+    XPCOM_MANIFESTS += [
+        'gecko/components.conf',
+    ]
+
     if CONFIG['OS_TARGET'] == 'Darwin':
         # This file cannot be built in unified mode because it includes
         # "nsLocalFile.h", which pulls in a system header which uses a type
         # called TextRange, which conflicts with mozilla::TextRange due to
         # a "using namespace mozilla;" declaration from a different file.
         SOURCES += [
             'gecko/nsProfiler.cpp',
         ]
--- a/widget/moz.build
+++ b/widget/moz.build
@@ -122,16 +122,17 @@ XPIDL_SOURCES += [
 
 XPIDL_MODULE = 'widget'
 
 EXPORTS += [
     'GfxDriverInfo.h',
     'GfxInfoBase.h',
     'GfxInfoCollector.h',
     'InputData.h',
+    'nsBaseAppShell.h',
     'nsBaseDragService.h',
     'nsBaseFilePicker.h',
     'nsBaseScreen.h',
     'nsBaseWidget.h',
     'nsIDeviceContextSpec.h',
     'nsIdleService.h',
     'nsIKeyEventInPluginCallback.h',
     'nsIPluginWidget.h',
new file mode 100644
--- /dev/null
+++ b/widget/windows/components.conf
@@ -0,0 +1,199 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Headers = [
+    '/widget/windows/nsWidgetFactory.h',
+]
+
+InitFunc = 'nsWidgetWindowsModuleCtor'
+UnloadFunc = 'nsWidgetWindowsModuleDtor'
+
+Classes = [
+    {
+        'cid': '{c401eb80-f9ea-11d3-bb6f-e732b73ebe7c}',
+        'contract_ids': ['@mozilla.org/gfx/screenmanager;1'],
+        'singleton': True,
+        'type': 'mozilla::widget::ScreenManager',
+        'constructor': 'mozilla::widget::ScreenManager::GetAddRefedSingleton',
+        'headers': ['/widget/ScreenManager.h'],
+        'processes': ProcessSelector.MAIN_PROCESS_ONLY,
+    },
+    {
+        'cid': '{2d96b3df-c051-11d1-a827-0040959a28c9}',
+        'contract_ids': ['@mozilla.org/widget/appshell/win;1'],
+        'headers': ['/widget/windows/nsWidgetFactory.h'],
+        'legacy_constructor': 'nsAppShellConstructor',
+        'processes': ProcessSelector.ALLOW_IN_GPU_VR_AND_SOCKET_PROCESS,
+    },
+    {
+        'cid': '{6987230e-0098-4e78-bc5f-1493ee7519fa}',
+        'contract_ids': ['@mozilla.org/widget/idleservice;1'],
+        'singleton': True,
+        'type': 'nsIdleServiceWin',
+        'constructor': 'nsIdleServiceWin::GetInstance',
+        'headers': ['/widget/windows/nsIdleServiceWin.h', 'nsIdleService.h'],
+    },
+    {
+        'cid': '{b148eed2-236d-11d3-b35c-00a0cc3c1cde}',
+        'contract_ids': ['@mozilla.org/sound;1'],
+        'singleton': True,
+        'type': 'nsISound',
+        'constructor': 'nsSound::GetInstance',
+        'headers': ['/widget/windows/nsSound.h'],
+        'processes': ProcessSelector.MAIN_PROCESS_ONLY,
+    },
+    {
+        'cid': '{77221d5a-1dd2-11b2-8c69-c710f15d2ed5}',
+        'contract_ids': ['@mozilla.org/widget/clipboardhelper;1'],
+        'type': 'nsClipboardHelper',
+        'headers': ['/widget/nsClipboardHelper.h'],
+    },
+    {
+        'cid': '{b8e5bc54-a22f-4eb2-b061-24cb6d19c15f}',
+        'contract_ids': ['@mozilla.org/windows-taskbar;1'],
+        'type': 'mozilla::widget::WinTaskbar',
+        'headers': ['/widget/windows/WinTaskbar.h'],
+    },
+    {
+        'cid': '{73a5946f-608d-454f-9d33-0b8f8c7294b6}',
+        'contract_ids': ['@mozilla.org/windows-jumplistbuilder;1'],
+        'type': 'mozilla::widget::JumpListBuilder',
+        'headers': ['/widget/windows/JumpListBuilder.h'],
+    },
+    {
+        'cid': '{2b9a1f2c-27ce-45b6-8d4e-755d0e34f8db}',
+        'contract_ids': ['@mozilla.org/windows-jumplistitem;1'],
+        'type': 'mozilla::widget::JumpListItem',
+        'headers': ['/widget/windows/JumpListItem.h'],
+    },
+    {
+        'cid': '{21f1f13b-f75a-42ad-867a-d91ad694447e}',
+        'contract_ids': ['@mozilla.org/windows-jumplistseparator;1'],
+        'type': 'mozilla::widget::JumpListSeparator',
+        'headers': ['/widget/windows/JumpListItem.h'],
+    },
+    {
+        'cid': '{f72c5dc4-5a12-47be-be28-ab105f33b08f}',
+        'contract_ids': ['@mozilla.org/windows-jumplistlink;1'],
+        'type': 'mozilla::widget::JumpListLink',
+        'headers': ['/widget/windows/JumpListItem.h'],
+    },
+    {
+        'cid': '{b16656b2-5187-498f-abf4-56346126bfdb}',
+        'contract_ids': ['@mozilla.org/windows-jumplistshortcut;1'],
+        'type': 'mozilla::widget::JumpListShortcut',
+        'headers': ['/widget/windows/JumpListItem.h'],
+    },
+    {
+        'cid': '{e04a55e8-fee3-4ea2-a98b-41d2621adc3c}',
+        'contract_ids': ['@mozilla.org/windows-ui-utils;1'],
+        'type': 'WindowsUIUtils',
+        'headers': ['/widget/windows/WindowsUIUtils.h'],
+    },
+    {
+        'cid': '{8b5314bc-db01-11d2-96ce-0060b0fb9956}',
+        'contract_ids': ['@mozilla.org/widget/transferable;1'],
+        'type': 'nsTransferable',
+        'headers': ['/widget/nsTransferable.h'],
+    },
+    {
+        'cid': '{948a0023-e3a7-11d2-96cf-0060b0fb9956}',
+        'contract_ids': ['@mozilla.org/widget/htmlformatconverter;1'],
+        'type': 'nsHTMLFormatConverter',
+        'headers': ['/widget/nsHTMLFormatConverter.h'],
+    },
+    {
+        'cid': '{8b5314bb-db01-11d2-96ce-0060b0fb9956}',
+        'contract_ids': ['@mozilla.org/widget/dragservice;1'],
+        'type': 'nsDragService',
+        'headers': ['/widget/windows/nsDragService.h'],
+        'processes': ProcessSelector.MAIN_PROCESS_ONLY,
+    },
+    {
+        'cid': '{9a0cb62b-d638-4faf-9588-ae96f5e29093}',
+        'contract_ids': ['@mozilla.org/widget/taskbar-preview-callback;1'],
+        'type': 'mozilla::widget::TaskbarPreviewCallback',
+        'headers': ['/widget/windows/TaskbarPreview.h'],
+    },
+    {
+        'cid': '{d755a760-9f27-11df-0800-200c9a664242}',
+        'contract_ids': ['@mozilla.org/gfx/info;1'],
+        'type': 'mozilla::widget::GfxInfo',
+        'headers': ['/widget/windows/GfxInfo.h'],
+        'init_method': 'Init',
+        'processes': ProcessSelector.ALLOW_IN_GPU_AND_SOCKET_PROCESS,
+    },
+    {
+        'cid': '{bd57cee8-1dd1-11b2-9fe7-95cf4709aea3}',
+        'contract_ids': ['@mozilla.org/filepicker;1'],
+        'type': 'nsFilePicker',
+        'headers': ['/widget/windows/nsFilePicker.h'],
+        'processes': ProcessSelector.MAIN_PROCESS_ONLY,
+    },
+    {
+        'cid': '{0f872c8c-3ee6-46bd-92a2-69652c6b474e}',
+        'contract_ids': ['@mozilla.org/colorpicker;1'],
+        'type': 'nsColorPicker',
+        'headers': ['/widget/windows/nsColorPicker.h'],
+        'processes': ProcessSelector.MAIN_PROCESS_ONLY,
+    },
+    {
+        'cid': '{8b5314ba-db01-11d2-96ce-0060b0fb9956}',
+        'contract_ids': ['@mozilla.org/widget/clipboard;1'],
+        'type': 'nsIClipboard',
+        'processes': ProcessSelector.MAIN_PROCESS_ONLY,
+    },
+]
+
+if buildconfig.substs['CC_TYPE'] in ('msvc', 'clang-cl'):
+    Classes += [
+        {
+            'cid': '{84e11f80-ca55-11dd-ad8b-0800200c9a66}',
+            'contract_ids': ['@mozilla.org/system-alerts-service;1'],
+            'type': 'mozilla::widget::ToastNotification',
+            'headers': ['/widget/windows/ToastNotification.h'],
+            'init_method': 'Init',
+            'processes': ProcessSelector.MAIN_PROCESS_ONLY,
+        },
+    ]
+
+if defined('NS_PRINTING'):
+    Classes += [
+        {
+            'cid': '{d3f69889-e13a-4321-980c-a39332e21f34}',
+            'contract_ids': ['@mozilla.org/gfx/devicecontextspec;1'],
+            'type': 'nsDeviceContextSpecWin',
+            'headers': ['/widget/windows/nsDeviceContextSpecWin.h'],
+        },
+        {
+            'cid': '{06beec76-a183-4d9f-85dd-085f26da565a}',
+            'contract_ids': ['@mozilla.org/widget/printdialog-service;1'],
+            'type': 'nsPrintDialogServiceWin',
+            'headers': ['/widget/windows/nsPrintDialogWin.h'],
+            'init_method': 'Init',
+            'processes': ProcessSelector.MAIN_PROCESS_ONLY,
+        },
+        {
+            'cid': '{841387c8-72e6-484b-9296-bf6eea80d58a}',
+            'contract_ids': ['@mozilla.org/gfx/printsettings-service;1'],
+            'type': 'nsPrintSettingsServiceWin',
+            'headers': ['/widget/windows/nsPrintSettingsServiceWin.h'],
+            'init_method': 'Init',
+        },
+        {
+            'cid': '{a6cf9129-15b3-11d2-932e-00805f8add32}',
+            'contract_ids': ['@mozilla.org/gfx/printerenumerator;1'],
+            'type': 'nsPrinterEnumeratorWin',
+            'headers': ['/widget/windows/nsDeviceContextSpecWin.h'],
+        },
+        {
+            'cid': '{2f977d53-5485-11d4-87e2-0010a4e75ef2}',
+            'contract_ids': ['@mozilla.org/gfx/printsession;1'],
+            'type': 'nsPrintSession',
+            'headers': ['/widget/nsPrintSession.h'],
+            'init_method': 'Init',
+        },
+    ]
--- a/widget/windows/moz.build
+++ b/widget/windows/moz.build
@@ -111,16 +111,20 @@ if CONFIG['MOZ_ENABLE_SKIA_PDF']:
         'WindowsEMF.cpp',
     ]
 
 if CONFIG['NS_ENABLE_TSF']:
     SOURCES += [
         'TSFTextStore.cpp',
     ]
 
+XPCOM_MANIFESTS += [
+    'components.conf',
+]
+
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 if CONFIG['MOZ_ENABLE_SKIA_PDF']:
   LOCAL_INCLUDES += CONFIG['SKIA_INCLUDES']
   LOCAL_INCLUDES += [
     '/gfx/skia/skia/include/config',
--- a/widget/windows/nsWidgetFactory.cpp
+++ b/widget/windows/nsWidgetFactory.cpp
@@ -1,277 +1,64 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/. */
 
+#include "nsWidgetFactory.h"
+
+#include "mozilla/Components.h"
 #include "nsIFactory.h"
 #include "nsISupports.h"
 #include "nsdefs.h"
 #include "nsWidgetsCID.h"
 #include "nsAppShell.h"
 #include "nsAppShellSingleton.h"
-#include "mozilla/ModuleUtils.h"
 #include "mozilla/WidgetUtils.h"
 #include "mozilla/widget/ScreenManager.h"
 #include "nsIServiceManager.h"
-#include "nsIdleServiceWin.h"
 #include "nsLookAndFeel.h"
-#include "nsSound.h"
 #include "WinMouseScrollHandler.h"
 #include "KeyboardLayout.h"
-#include "GfxInfo.h"
 #include "nsToolkit.h"
 
 // Modules that switch out based on the environment
 #include "nsXULAppAPI.h"
 // Desktop
 #include "nsFilePicker.h"  // needs to be included before other shobjidl.h includes
 #include "nsColorPicker.h"
 // Content processes
 #include "nsFilePickerProxy.h"
 
-// Drag & Drop, Clipboard
+// Clipboard
 #include "nsClipboardHelper.h"
 #include "nsClipboard.h"
 #include "HeadlessClipboard.h"
-#include "nsDragService.h"
-#include "nsTransferable.h"
-#include "nsHTMLFormatConverter.h"
-
-#include "WinTaskbar.h"
-#include "JumpListBuilder.h"
-#include "JumpListItem.h"
-#include "TaskbarPreview.h"
-// Toast notification support
-#ifndef __MINGW32__
-#  include "ToastNotification.h"
-#  include "nsToolkitCompsCID.h"
-#endif
 
 #include "WindowsUIUtils.h"
 
-#ifdef NS_PRINTING
-#  include "nsDeviceContextSpecWin.h"
-#  include "nsPrintDialogWin.h"
-#  include "nsPrintSettingsServiceWin.h"
-#  include "nsPrintSession.h"
-#endif
-
 using namespace mozilla;
 using namespace mozilla::widget;
 
-static nsresult FilePickerConstructor(nsISupports *aOuter, REFNSIID aIID,
-                                      void **aResult) {
-  *aResult = nullptr;
-  if (aOuter != nullptr) {
-    return NS_ERROR_NO_AGGREGATION;
-  }
-  nsCOMPtr<nsIFilePicker> picker = new nsFilePicker;
-  return picker->QueryInterface(aIID, aResult);
-}
-
-static nsresult ColorPickerConstructor(nsISupports *aOuter, REFNSIID aIID,
-                                       void **aResult) {
-  *aResult = nullptr;
-  if (aOuter != nullptr) {
-    return NS_ERROR_NO_AGGREGATION;
-  }
-  nsCOMPtr<nsIColorPicker> picker = new nsColorPicker;
-  return picker->QueryInterface(aIID, aResult);
-}
-
-static nsresult nsClipboardConstructor(nsISupports *aOuter, REFNSIID aIID,
-                                       void **aResult) {
-  *aResult = nullptr;
-  if (aOuter != nullptr) {
-    return NS_ERROR_NO_AGGREGATION;
-  }
+NS_IMPL_COMPONENT_FACTORY(nsIClipboard) {
   nsCOMPtr<nsIClipboard> inst;
   if (gfxPlatform::IsHeadless()) {
     inst = new HeadlessClipboard();
   } else {
     inst = new nsClipboard();
   }
-  return inst->QueryInterface(aIID, aResult);
+  return inst.forget().downcast<nsISupports>();
 }
 
-NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(ScreenManager,
-                                         ScreenManager::GetAddRefedSingleton)
-NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIdleServiceWin,
-                                         nsIdleServiceWin::GetInstance)
-NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsISound, nsSound::GetInstance)
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsClipboardHelper)
-NS_GENERIC_FACTORY_CONSTRUCTOR(WinTaskbar)
-NS_GENERIC_FACTORY_CONSTRUCTOR(JumpListBuilder)
-NS_GENERIC_FACTORY_CONSTRUCTOR(JumpListItem)
-NS_GENERIC_FACTORY_CONSTRUCTOR(JumpListSeparator)
-NS_GENERIC_FACTORY_CONSTRUCTOR(JumpListLink)
-NS_GENERIC_FACTORY_CONSTRUCTOR(JumpListShortcut)
-NS_GENERIC_FACTORY_CONSTRUCTOR(WindowsUIUtils)
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsTransferable)
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsHTMLFormatConverter)
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsDragService)
-#ifndef __MINGW32__
-NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(ToastNotification, Init)
-#endif
-NS_GENERIC_FACTORY_CONSTRUCTOR(TaskbarPreviewCallback)
-#ifdef NS_PRINTING
-NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintDialogServiceWin, Init)
-NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintSettingsServiceWin, Init)
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsPrinterEnumeratorWin)
-NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintSession, Init)
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsDeviceContextSpecWin)
-#endif
-
-namespace mozilla {
-namespace widget {
-// This constructor should really be shared with all platforms.
-NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(GfxInfo, Init)
-}  // namespace widget
-}  // namespace mozilla
-
-NS_DEFINE_NAMED_CID(NS_FILEPICKER_CID);
-NS_DEFINE_NAMED_CID(NS_COLORPICKER_CID);
-NS_DEFINE_NAMED_CID(NS_APPSHELL_CID);
-NS_DEFINE_NAMED_CID(NS_SCREENMANAGER_CID);
-NS_DEFINE_NAMED_CID(NS_GFXINFO_CID);
-NS_DEFINE_NAMED_CID(NS_IDLE_SERVICE_CID);
-NS_DEFINE_NAMED_CID(NS_CLIPBOARD_CID);
-NS_DEFINE_NAMED_CID(NS_CLIPBOARDHELPER_CID);
-NS_DEFINE_NAMED_CID(NS_SOUND_CID);
-NS_DEFINE_NAMED_CID(NS_TRANSFERABLE_CID);
-NS_DEFINE_NAMED_CID(NS_HTMLFORMATCONVERTER_CID);
-NS_DEFINE_NAMED_CID(NS_WIN_TASKBAR_CID);
-NS_DEFINE_NAMED_CID(NS_WIN_JUMPLISTBUILDER_CID);
-NS_DEFINE_NAMED_CID(NS_WIN_JUMPLISTITEM_CID);
-NS_DEFINE_NAMED_CID(NS_WIN_JUMPLISTSEPARATOR_CID);
-NS_DEFINE_NAMED_CID(NS_WIN_JUMPLISTLINK_CID);
-NS_DEFINE_NAMED_CID(NS_WIN_JUMPLISTSHORTCUT_CID);
-NS_DEFINE_NAMED_CID(NS_WINDOWS_UIUTILS_CID);
-NS_DEFINE_NAMED_CID(NS_DRAGSERVICE_CID);
-#ifndef __MINGW32__
-NS_DEFINE_NAMED_CID(NS_SYSTEMALERTSSERVICE_CID);
-#endif
-NS_DEFINE_NAMED_CID(NS_TASKBARPREVIEWCALLBACK_CID);
-#ifdef NS_PRINTING
-NS_DEFINE_NAMED_CID(NS_PRINTDIALOGSERVICE_CID);
-NS_DEFINE_NAMED_CID(NS_PRINTSETTINGSSERVICE_CID);
-NS_DEFINE_NAMED_CID(NS_PRINTER_ENUMERATOR_CID);
-NS_DEFINE_NAMED_CID(NS_PRINTSESSION_CID);
-NS_DEFINE_NAMED_CID(NS_DEVICE_CONTEXT_SPEC_CID);
-#endif
+nsresult nsWidgetWindowsModuleCtor() {
+  return nsAppShellInit();
+}
 
-static const mozilla::Module::CIDEntry kWidgetCIDs[] = {
-    {&kNS_FILEPICKER_CID, false, nullptr, FilePickerConstructor,
-     Module::MAIN_PROCESS_ONLY},
-    {&kNS_COLORPICKER_CID, false, nullptr, ColorPickerConstructor,
-     Module::MAIN_PROCESS_ONLY},
-    {&kNS_APPSHELL_CID, false, nullptr, nsAppShellConstructor,
-     Module::ALLOW_IN_GPU_VR_AND_SOCKET_PROCESS},
-    {&kNS_SCREENMANAGER_CID, false, nullptr, ScreenManagerConstructor,
-     Module::MAIN_PROCESS_ONLY},
-    {&kNS_GFXINFO_CID, false, nullptr, GfxInfoConstructor,
-     Module::ALLOW_IN_GPU_AND_SOCKET_PROCESS},
-    {&kNS_IDLE_SERVICE_CID, false, nullptr, nsIdleServiceWinConstructor},
-    {&kNS_CLIPBOARD_CID, false, nullptr, nsClipboardConstructor,
-     Module::MAIN_PROCESS_ONLY},
-    {&kNS_CLIPBOARDHELPER_CID, false, nullptr, nsClipboardHelperConstructor},
-    {&kNS_SOUND_CID, false, nullptr, nsISoundConstructor,
-     Module::MAIN_PROCESS_ONLY},
-    {&kNS_TRANSFERABLE_CID, false, nullptr, nsTransferableConstructor},
-    {&kNS_HTMLFORMATCONVERTER_CID, false, nullptr,
-     nsHTMLFormatConverterConstructor},
-    {&kNS_WIN_TASKBAR_CID, false, nullptr, WinTaskbarConstructor},
-    {&kNS_WIN_JUMPLISTBUILDER_CID, false, nullptr, JumpListBuilderConstructor},
-    {&kNS_WIN_JUMPLISTITEM_CID, false, nullptr, JumpListItemConstructor},
-    {&kNS_WIN_JUMPLISTSEPARATOR_CID, false, nullptr,
-     JumpListSeparatorConstructor},
-    {&kNS_WIN_JUMPLISTLINK_CID, false, nullptr, JumpListLinkConstructor},
-    {&kNS_WIN_JUMPLISTSHORTCUT_CID, false, nullptr,
-     JumpListShortcutConstructor},
-    {&kNS_WINDOWS_UIUTILS_CID, false, nullptr, WindowsUIUtilsConstructor},
-    {&kNS_DRAGSERVICE_CID, false, nullptr, nsDragServiceConstructor,
-     Module::MAIN_PROCESS_ONLY},
-#ifndef __MINGW32__
-    {&kNS_SYSTEMALERTSSERVICE_CID, false, nullptr, ToastNotificationConstructor,
-     Module::MAIN_PROCESS_ONLY},
-#endif
-    {&kNS_TASKBARPREVIEWCALLBACK_CID, false, nullptr,
-     TaskbarPreviewCallbackConstructor},
-#ifdef NS_PRINTING
-    {&kNS_PRINTDIALOGSERVICE_CID, false, nullptr,
-     nsPrintDialogServiceWinConstructor, Module::MAIN_PROCESS_ONLY},
-    {&kNS_PRINTSETTINGSSERVICE_CID, false, nullptr,
-     nsPrintSettingsServiceWinConstructor},
-    {&kNS_PRINTER_ENUMERATOR_CID, false, nullptr,
-     nsPrinterEnumeratorWinConstructor},
-    {&kNS_PRINTSESSION_CID, false, nullptr, nsPrintSessionConstructor},
-    {&kNS_DEVICE_CONTEXT_SPEC_CID, false, nullptr,
-     nsDeviceContextSpecWinConstructor},
-#endif
-    {nullptr}};
-
-static const mozilla::Module::ContractIDEntry kWidgetContracts[] = {
-    {"@mozilla.org/filepicker;1", &kNS_FILEPICKER_CID,
-     Module::MAIN_PROCESS_ONLY},
-    {"@mozilla.org/colorpicker;1", &kNS_COLORPICKER_CID,
-     Module::MAIN_PROCESS_ONLY},
-    {"@mozilla.org/widget/appshell/win;1", &kNS_APPSHELL_CID,
-     Module::ALLOW_IN_GPU_VR_AND_SOCKET_PROCESS},
-    {"@mozilla.org/gfx/screenmanager;1", &kNS_SCREENMANAGER_CID,
-     Module::MAIN_PROCESS_ONLY},
-    {"@mozilla.org/gfx/info;1", &kNS_GFXINFO_CID,
-     Module::ALLOW_IN_GPU_AND_SOCKET_PROCESS},
-    {"@mozilla.org/widget/idleservice;1", &kNS_IDLE_SERVICE_CID},
-    {"@mozilla.org/widget/clipboard;1", &kNS_CLIPBOARD_CID,
-     Module::MAIN_PROCESS_ONLY},
-    {"@mozilla.org/widget/clipboardhelper;1", &kNS_CLIPBOARDHELPER_CID},
-    {"@mozilla.org/sound;1", &kNS_SOUND_CID, Module::MAIN_PROCESS_ONLY},
-    {"@mozilla.org/widget/transferable;1", &kNS_TRANSFERABLE_CID},
-    {"@mozilla.org/widget/htmlformatconverter;1", &kNS_HTMLFORMATCONVERTER_CID},
-    {"@mozilla.org/windows-taskbar;1", &kNS_WIN_TASKBAR_CID},
-    {"@mozilla.org/windows-jumplistbuilder;1", &kNS_WIN_JUMPLISTBUILDER_CID},
-    {"@mozilla.org/windows-jumplistitem;1", &kNS_WIN_JUMPLISTITEM_CID},
-    {"@mozilla.org/windows-jumplistseparator;1",
-     &kNS_WIN_JUMPLISTSEPARATOR_CID},
-    {"@mozilla.org/windows-jumplistlink;1", &kNS_WIN_JUMPLISTLINK_CID},
-    {"@mozilla.org/windows-jumplistshortcut;1", &kNS_WIN_JUMPLISTSHORTCUT_CID},
-    {"@mozilla.org/windows-ui-utils;1", &kNS_WINDOWS_UIUTILS_CID},
-    {"@mozilla.org/widget/dragservice;1", &kNS_DRAGSERVICE_CID,
-     Module::MAIN_PROCESS_ONLY},
-#ifndef __MINGW32__
-    {NS_SYSTEMALERTSERVICE_CONTRACTID, &kNS_SYSTEMALERTSSERVICE_CID,
-     Module::MAIN_PROCESS_ONLY},
-#endif
-    {"@mozilla.org/widget/taskbar-preview-callback;1",
-     &kNS_TASKBARPREVIEWCALLBACK_CID},
-#ifdef NS_PRINTING
-    {NS_PRINTDIALOGSERVICE_CONTRACTID, &kNS_PRINTDIALOGSERVICE_CID},
-    {"@mozilla.org/gfx/printsettings-service;1", &kNS_PRINTSETTINGSSERVICE_CID},
-    {"@mozilla.org/gfx/printerenumerator;1", &kNS_PRINTER_ENUMERATOR_CID},
-    {"@mozilla.org/gfx/printsession;1", &kNS_PRINTSESSION_CID},
-    {"@mozilla.org/gfx/devicecontextspec;1", &kNS_DEVICE_CONTEXT_SPEC_CID},
-#endif
-    {nullptr}};
-
-static void nsWidgetWindowsModuleDtor() {
+void nsWidgetWindowsModuleDtor() {
   // Shutdown all XP level widget classes.
   WidgetUtils::Shutdown();
 
   KeyboardLayout::Shutdown();
   MouseScrollHandler::Shutdown();
   nsLookAndFeel::Shutdown();
   nsToolkit::Shutdown();
   nsAppShellShutdown();
 }
-
-static const mozilla::Module kWidgetModule = {
-    mozilla::Module::kVersion,
-    kWidgetCIDs,
-    kWidgetContracts,
-    nullptr,
-    nullptr,
-    nsAppShellInit,
-    nsWidgetWindowsModuleDtor,
-    Module::ALLOW_IN_GPU_VR_AND_SOCKET_PROCESS};
-
-NSMODULE_DEFN(nsWidgetModule) = &kWidgetModule;
new file mode 100644
--- /dev/null
+++ b/widget/windows/nsWidgetFactory.h
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
+/* 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/. */
+
+#ifndef widget_windows_nsWidgetFactory_h
+#define widget_windows_nsWidgetFactory_h
+
+#include "nscore.h"
+#include "nsID.h"
+
+class nsISupports;
+
+nsresult nsAppShellConstructor(nsISupports *outer, const nsIID &iid,
+                               void **result);
+
+nsresult nsWidgetWindowsModuleCtor();
+void nsWidgetWindowsModuleDtor();
+
+#endif  // defined widget_windows_nsWidgetFactory_h
--- a/xpcom/build/XPCOMInit.cpp
+++ b/xpcom/build/XPCOMInit.cpp
@@ -170,16 +170,36 @@ const mozilla::Module kXPCOMModule = {
     kXPCOMCIDEntries,
     kXPCOMContracts,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     Module::ALLOW_IN_GPU_VR_AND_SOCKET_PROCESS};
 
+// FIXME: Dummy modules to avoid Windows PGO bustage when we have too few
+// modules registered.
+static const mozilla::Module kDummy1 = {mozilla::Module::kVersion};
+static const mozilla::Module kDummy2 = {mozilla::Module::kVersion};
+static const mozilla::Module kDummy3 = {mozilla::Module::kVersion};
+static const mozilla::Module kDummy4 = {mozilla::Module::kVersion};
+static const mozilla::Module kDummy5 = {mozilla::Module::kVersion};
+static const mozilla::Module kDummy6 = {mozilla::Module::kVersion};
+static const mozilla::Module kDummy7 = {mozilla::Module::kVersion};
+static const mozilla::Module kDummy8 = {mozilla::Module::kVersion};
+
+NSMODULE_DEFN(Dummy1) = &kDummy1;
+NSMODULE_DEFN(Dummy2) = &kDummy2;
+NSMODULE_DEFN(Dummy3) = &kDummy3;
+NSMODULE_DEFN(Dummy4) = &kDummy4;
+NSMODULE_DEFN(Dummy5) = &kDummy5;
+NSMODULE_DEFN(Dummy6) = &kDummy6;
+NSMODULE_DEFN(Dummy7) = &kDummy7;
+NSMODULE_DEFN(Dummy8) = &kDummy8;
+
 // gDebug will be freed during shutdown.
 static nsIDebug2* gDebug = nullptr;
 
 EXPORT_XPCOM_API(nsresult)
 NS_GetDebug(nsIDebug2** aResult) {
   return nsDebugImpl::Create(nullptr, NS_GET_IID(nsIDebug2), (void**)aResult);
 }
 
new file mode 100644
--- /dev/null
+++ b/xpfe/appshell/components.conf
@@ -0,0 +1,21 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Classes = [
+    {
+        'cid': '{0099907d-123c-4853-a46a-43098b5fb68c}',
+        'contract_ids': ['@mozilla.org/appshell/appShellService;1'],
+        'type': 'nsAppShellService',
+        'headers': ['/xpfe/appshell/nsAppShellService.h'],
+    },
+    {
+        'cid': '{79a2b7cc-f05b-4605-bfa0-fac54f27eec8}',
+        'contract_ids': ['@mozilla.org/appshell/window-mediator;1'],
+        'type': 'nsWindowMediator',
+        'headers': ['/xpfe/appshell/nsWindowMediator.h'],
+        'init_method': 'Init',
+    },
+]
--- a/xpfe/appshell/moz.build
+++ b/xpfe/appshell/moz.build
@@ -21,26 +21,29 @@ XPIDL_SOURCES += [
 XPIDL_MODULE = 'appshell'
 
 EXPORTS += [
     'LiveResizeListener.h',
     'nsAppShellCID.h',
 ]
 
 UNIFIED_SOURCES += [
-    'nsAppShellFactory.cpp',
     'nsAppShellService.cpp',
     'nsAppShellWindowEnumerator.cpp',
     'nsChromeTreeOwner.cpp',
     'nsContentTreeOwner.cpp',
     'nsWebShellWindow.cpp',
     'nsWindowMediator.cpp',
     'nsXULWindow.cpp',
 ]
 
+XPCOM_MANIFESTS += [
+    'components.conf',
+]
+
 LOCAL_INCLUDES += [
     '/dom/base',
     '/dom/xul',
 ]
 
 FINAL_LIBRARY = 'xul'
 
 include('/ipc/chromium/chromium-config.mozbuild')
deleted file mode 100644
--- a/xpfe/appshell/nsAppShellFactory.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-#include "mozilla/ModuleUtils.h"
-#include "nscore.h"
-#include "nsIWindowMediator.h"
-
-#include "nsIAppShellService.h"
-#include "nsAppShellService.h"
-#include "nsWindowMediator.h"
-#include "nsChromeTreeOwner.h"
-#include "nsAppShellCID.h"
-
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsAppShellService)
-NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsWindowMediator, Init)
-
-NS_DEFINE_NAMED_CID(NS_APPSHELLSERVICE_CID);
-NS_DEFINE_NAMED_CID(NS_WINDOWMEDIATOR_CID);
-
-static const mozilla::Module::CIDEntry kAppShellCIDs[] = {
-    {&kNS_APPSHELLSERVICE_CID, false, nullptr, nsAppShellServiceConstructor},
-    {&kNS_WINDOWMEDIATOR_CID, false, nullptr, nsWindowMediatorConstructor},
-    {nullptr}};
-
-static const mozilla::Module::ContractIDEntry kAppShellContracts[] = {
-    {NS_APPSHELLSERVICE_CONTRACTID, &kNS_APPSHELLSERVICE_CID},
-    {NS_WINDOWMEDIATOR_CONTRACTID, &kNS_WINDOWMEDIATOR_CID},
-    {nullptr}};
-
-static const mozilla::Module kAppShellModule = {
-    mozilla::Module::kVersion,
-    kAppShellCIDs,
-    kAppShellContracts,
-};
-
-NSMODULE_DEFN(appshell) = &kAppShellModule;