Merge mozilla-central to autoland. on a CLOSED TREE
authorAndreea Pavel <apavel@mozilla.com>
Thu, 15 Feb 2018 12:34:12 +0200
changeset 404012 3e332972119f8f76c5c523157c7e5cb2b987fab2
parent 404011 e8d05c7218c66151e2e30dcff2fabdf9bccae40e (current diff)
parent 403921 9b69cc60e5848f2f8802c911fd00771b50eed41f (diff)
child 404013 d4937a53d8261e603b9fbb3f1393e2a509b3e96c
push id99924
push userebalazs@mozilla.com
push dateThu, 15 Feb 2018 20:43:51 +0000
treeherdermozilla-inbound@a7d2a49f46fb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone60.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to autoland. on a CLOSED TREE
browser/components/places/tests/browser/browser_library_remove_bookmark.js
devtools/client/debugger/new/test/mochitest/browser_dbg_keyboard-shortcuts.js
devtools/client/debugger/new/test/mochitest/browser_dbg_keyboard_navigation.js
dom/serviceworkers/ServiceWorkerCommon.h
gfx/cairo/libpixman/src/Makefile.in
js/src/jsatom.cpp
js/src/jsatom.h
js/src/jsatominlines.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscntxtinlines.h
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jscompartmentinlines.h
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsfuninlines.h
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jsscriptinlines.h
mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
modules/libpref/init/all.js
testing/mozharness/mozharness/mozilla/proxxy.py
third_party/rust/advapi32-sys/.cargo-checksum.json
third_party/rust/advapi32-sys/Cargo.toml
third_party/rust/advapi32-sys/README.md
third_party/rust/advapi32-sys/build.rs
third_party/rust/advapi32-sys/src/lib.rs
third_party/rust/kernel32-sys-0.1.4/.cargo-checksum.json
third_party/rust/kernel32-sys-0.1.4/Cargo.toml
third_party/rust/kernel32-sys-0.1.4/README.md
third_party/rust/kernel32-sys-0.1.4/build.rs
third_party/rust/kernel32-sys-0.1.4/src/lib.rs
third_party/rust/ktmw32-sys/.cargo-checksum.json
third_party/rust/ktmw32-sys/Cargo.toml
third_party/rust/ktmw32-sys/README.md
third_party/rust/ktmw32-sys/build.rs
third_party/rust/ktmw32-sys/i686/libktmw32.a
third_party/rust/ktmw32-sys/src/lib.rs
third_party/rust/ktmw32-sys/x86_64/libktmw32.a
third_party/rust/winapi/LICENSE.md
third_party/rust/winapi/src/activation.rs
third_party/rust/winapi/src/audioclient.rs
third_party/rust/winapi/src/audiosessiontypes.rs
third_party/rust/winapi/src/basetsd.rs
third_party/rust/winapi/src/bcrypt.rs
third_party/rust/winapi/src/cfg.rs
third_party/rust/winapi/src/cfgmgr32.rs
third_party/rust/winapi/src/combaseapi.rs
third_party/rust/winapi/src/commctrl.rs
third_party/rust/winapi/src/commdlg.rs
third_party/rust/winapi/src/corsym.rs
third_party/rust/winapi/src/d2d1.rs
third_party/rust/winapi/src/d2dbasetypes.rs
third_party/rust/winapi/src/d3d10shader.rs
third_party/rust/winapi/src/d3d11.rs
third_party/rust/winapi/src/d3d11shader.rs
third_party/rust/winapi/src/d3d12.rs
third_party/rust/winapi/src/d3d12sdklayers.rs
third_party/rust/winapi/src/d3d12shader.rs
third_party/rust/winapi/src/d3d9.rs
third_party/rust/winapi/src/d3d9caps.rs
third_party/rust/winapi/src/d3d9types.rs
third_party/rust/winapi/src/d3dcommon.rs
third_party/rust/winapi/src/d3dcompiler.rs
third_party/rust/winapi/src/dbghelp.rs
third_party/rust/winapi/src/dcommon.rs
third_party/rust/winapi/src/devpropdef.rs
third_party/rust/winapi/src/docobj.rs
third_party/rust/winapi/src/dpapi.rs
third_party/rust/winapi/src/dsgetdc.rs
third_party/rust/winapi/src/dsound.rs
third_party/rust/winapi/src/dsrole.rs
third_party/rust/winapi/src/dwmapi.rs
third_party/rust/winapi/src/dwrite.rs
third_party/rust/winapi/src/dxgi.rs
third_party/rust/winapi/src/dxgi1_2.rs
third_party/rust/winapi/src/dxgi1_3.rs
third_party/rust/winapi/src/dxgi1_4.rs
third_party/rust/winapi/src/dxgiformat.rs
third_party/rust/winapi/src/dxgitype.rs
third_party/rust/winapi/src/errhandlingapi.rs
third_party/rust/winapi/src/excpt.rs
third_party/rust/winapi/src/fileapi.rs
third_party/rust/winapi/src/gl.rs
third_party/rust/winapi/src/guiddef.rs
third_party/rust/winapi/src/heapapi.rs
third_party/rust/winapi/src/hidclass.rs
third_party/rust/winapi/src/hidpi.rs
third_party/rust/winapi/src/hidsdi.rs
third_party/rust/winapi/src/hidusage.rs
third_party/rust/winapi/src/hstring.rs
third_party/rust/winapi/src/http.rs
third_party/rust/winapi/src/imm.rs
third_party/rust/winapi/src/inaddr.rs
third_party/rust/winapi/src/inspectable.rs
third_party/rust/winapi/src/ksmedia.rs
third_party/rust/winapi/src/libloaderapi.rs
third_party/rust/winapi/src/lmaccess.rs
third_party/rust/winapi/src/lmcons.rs
third_party/rust/winapi/src/lmdfs.rs
third_party/rust/winapi/src/lmerrlog.rs
third_party/rust/winapi/src/lmjoin.rs
third_party/rust/winapi/src/lsalookup.rs
third_party/rust/winapi/src/memoryapi.rs
third_party/rust/winapi/src/minschannel.rs
third_party/rust/winapi/src/minwinbase.rs
third_party/rust/winapi/src/minwindef.rs
third_party/rust/winapi/src/mmdeviceapi.rs
third_party/rust/winapi/src/mmreg.rs
third_party/rust/winapi/src/mmsystem.rs
third_party/rust/winapi/src/mscat.rs
third_party/rust/winapi/src/mssip.rs
third_party/rust/winapi/src/nb30.rs
third_party/rust/winapi/src/ncrypt.rs
third_party/rust/winapi/src/ntdef.rs
third_party/rust/winapi/src/ntsecapi.rs
third_party/rust/winapi/src/ntstatus.rs
third_party/rust/winapi/src/oaidl.rs
third_party/rust/winapi/src/objbase.rs
third_party/rust/winapi/src/objidl.rs
third_party/rust/winapi/src/objidlbase.rs
third_party/rust/winapi/src/olectl.rs
third_party/rust/winapi/src/pdh.rs
third_party/rust/winapi/src/playsoundapi.rs
third_party/rust/winapi/src/processsnapshot.rs
third_party/rust/winapi/src/processthreadsapi.rs
third_party/rust/winapi/src/propidl.rs
third_party/rust/winapi/src/propsys.rs
third_party/rust/winapi/src/prsht.rs
third_party/rust/winapi/src/psapi.rs
third_party/rust/winapi/src/qos.rs
third_party/rust/winapi/src/reason.rs
third_party/rust/winapi/src/restrictederrorinfo.rs
third_party/rust/winapi/src/roapi.rs
third_party/rust/winapi/src/roerrorapi.rs
third_party/rust/winapi/src/rpc.rs
third_party/rust/winapi/src/rpcdce.rs
third_party/rust/winapi/src/sapi.rs
third_party/rust/winapi/src/schannel.rs
third_party/rust/winapi/src/servprov.rs
third_party/rust/winapi/src/setupapi.rs
third_party/rust/winapi/src/shellapi.rs
third_party/rust/winapi/src/shellscalingapi.rs
third_party/rust/winapi/src/shlguid.rs
third_party/rust/winapi/src/shlobj.rs
third_party/rust/winapi/src/shobjidl.rs
third_party/rust/winapi/src/shtypes.rs
third_party/rust/winapi/src/spapidef.rs
third_party/rust/winapi/src/sql.rs
third_party/rust/winapi/src/sqltypes.rs
third_party/rust/winapi/src/sspi.rs
third_party/rust/winapi/src/strmif.rs
third_party/rust/winapi/src/subauth.rs
third_party/rust/winapi/src/synchapi.rs
third_party/rust/winapi/src/sysinfoapi.rs
third_party/rust/winapi/src/threadpoolapi.rs
third_party/rust/winapi/src/timezoneapi.rs
third_party/rust/winapi/src/tlhelp32.rs
third_party/rust/winapi/src/unknwnbase.rs
third_party/rust/winapi/src/urlhist.rs
third_party/rust/winapi/src/urlmon.rs
third_party/rust/winapi/src/usb.rs
third_party/rust/winapi/src/usbspec.rs
third_party/rust/winapi/src/usp10.rs
third_party/rust/winapi/src/vadefs.rs
third_party/rust/winapi/src/vsbackup.rs
third_party/rust/winapi/src/vss.rs
third_party/rust/winapi/src/vsserror.rs
third_party/rust/winapi/src/vswriter.rs
third_party/rust/winapi/src/werapi.rs
third_party/rust/winapi/src/winbase.rs
third_party/rust/winapi/src/wincon.rs
third_party/rust/winapi/src/wincred.rs
third_party/rust/winapi/src/wincrypt.rs
third_party/rust/winapi/src/windef.rs
third_party/rust/winapi/src/windowscodecs.rs
third_party/rust/winapi/src/windowsx.rs
third_party/rust/winapi/src/winerror.rs
third_party/rust/winapi/src/winevt.rs
third_party/rust/winapi/src/wingdi.rs
third_party/rust/winapi/src/winhttp.rs
third_party/rust/winapi/src/winioctl.rs
third_party/rust/winapi/src/winnetwk.rs
third_party/rust/winapi/src/winnls.rs
third_party/rust/winapi/src/winnt.rs
third_party/rust/winapi/src/winreg.rs
third_party/rust/winapi/src/winscard.rs
third_party/rust/winapi/src/winsmcrd.rs
third_party/rust/winapi/src/winsock2.rs
third_party/rust/winapi/src/winspool.rs
third_party/rust/winapi/src/winstring.rs
third_party/rust/winapi/src/winsvc.rs
third_party/rust/winapi/src/winusb.rs
third_party/rust/winapi/src/winusbio.rs
third_party/rust/winapi/src/winuser.rs
third_party/rust/winapi/src/ws2def.rs
third_party/rust/winapi/src/ws2ipdef.rs
third_party/rust/winapi/src/ws2spi.rs
third_party/rust/winapi/src/ws2tcpip.rs
third_party/rust/winapi/src/wtypes.rs
third_party/rust/winapi/src/wtypesbase.rs
third_party/rust/winapi/src/xinput.rs
third_party/rust/winreg/examples/transaction.rs
third_party/rust/winreg/src/serialization.rs
--- a/.eslintignore
+++ b/.eslintignore
@@ -319,16 +319,17 @@ js/src/Y.js
 # Third-party
 media/webrtc/trunk/**
 
 # mobile/android/ exclusions
 mobile/android/tests/browser/chrome/tp5/**
 
 # Uses `#filter substitution`
 mobile/android/app/mobile.js
+mobile/android/app/geckoview-prefs.js
 
 # Uses `#expand`
 mobile/android/chrome/content/about.js
 
 # Not much JS to lint and non-standard at that
 mobile/android/installer/
 mobile/android/locales/
 
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1249,16 +1249,28 @@ var gBrowserInit = {
       remoteType, sameProcessAsFrameLoader
     });
 
     gUIDensity.init();
 
     if (AppConstants.CAN_DRAW_IN_TITLEBAR) {
       gDragSpaceObserver.init();
     }
+
+    // Hack to ensure that the about:home favicon is loaded
+    // instantaneously, to avoid flickering and improve perceived performance.
+    this._callWithURIToLoad(uriToLoad => {
+      if (uriToLoad == "about:home") {
+        gBrowser.setIcon(gBrowser.selectedTab, "chrome://branding/content/icon32.png");
+      } else if (uriToLoad == "about:privatebrowsing") {
+        gBrowser.setIcon(gBrowser.selectedTab, "chrome://browser/skin/privatebrowsing/favicon.svg");
+      }
+    });
+
+    this._setInitialFocus();
   },
 
   onLoad() {
     gBrowser.addEventListener("DOMUpdatePageReport", gPopupBlockerObserver);
 
     Services.obs.addObserver(gPluginHandler.NPAPIPluginCrashed, "plugin-crashed");
 
     window.addEventListener("AppCommand", HandleAppCommandEvent, true);
@@ -1349,28 +1361,16 @@ var gBrowserInit = {
 
       try {
         gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, tabToOpen);
       } catch (e) {
         Cu.reportError(e);
       }
     }
 
-    this._setInitialFocus();
-
-    // Hack to ensure that the about:home favicon is loaded
-    // instantaneously, to avoid flickering and improve perceived performance.
-    this._uriToLoadPromise.then(uriToLoad => {
-      if (uriToLoad == "about:home") {
-        gBrowser.setIcon(gBrowser.selectedTab, "chrome://branding/content/icon32.png");
-      } else if (uriToLoad == "about:privatebrowsing") {
-        gBrowser.setIcon(gBrowser.selectedTab, "chrome://browser/skin/privatebrowsing/favicon.svg");
-      }
-    });
-
     // Wait until chrome is painted before executing code not critical to making the window visible
     this._boundDelayedStartup = this._delayedStartup.bind(this);
     window.addEventListener("MozAfterPaint", this._boundDelayedStartup);
 
     this._loadHandled = true;
   },
 
   _cancelDelayedStartup() {
@@ -1587,19 +1587,25 @@ var gBrowserInit = {
 
     let initialBrowser = gBrowser.selectedBrowser;
     mm.addMessageListener("Browser:FirstNonBlankPaint",
                           function onFirstNonBlankPaint() {
       mm.removeMessageListener("Browser:FirstNonBlankPaint", onFirstNonBlankPaint);
       initialBrowser.removeAttribute("blank");
     });
 
-    this._uriToLoadPromise.then(uriToLoad => {
+    // To prevent flickering of the urlbar-history-dropmarker in the general
+    // case, the urlbar has the 'focused' attribute set by default.
+    // If we are not fully sure the urlbar will be focused in this window,
+    // we should remove the attribute before first paint.
+    let shouldRemoveFocusedAttribute = true;
+    this._callWithURIToLoad(uriToLoad => {
       if ((isBlankPageURL(uriToLoad) || uriToLoad == "about:privatebrowsing") &&
           focusAndSelectUrlBar()) {
+        shouldRemoveFocusedAttribute = false;
         return;
       }
 
       if (gBrowser.selectedBrowser.isRemoteBrowser) {
         // If the initial browser is remote, in order to optimize for first paint,
         // we'll defer switching focus to that browser until it has painted.
         firstBrowserPaintDeferred.promise.then(() => {
           // If focus didn't move while we were waiting for first paint, we're okay
@@ -1609,20 +1615,22 @@ var gBrowserInit = {
           }
         });
       } else {
         // If the initial browser is not remote, we can focus the browser
         // immediately with no paint performance impact.
         gBrowser.selectedBrowser.focus();
       }
     });
+    if (shouldRemoveFocusedAttribute)
+      gURLBar.removeAttribute("focused");
   },
 
   _handleURIToLoad() {
-    this._uriToLoadPromise.then(uriToLoad => {
+    this._callWithURIToLoad(uriToLoad => {
       if (!uriToLoad || uriToLoad == "about:blank") {
         return;
       }
 
       // We don't check if uriToLoad is a XULElement because this case has
       // already been handled before first paint, and the argument cleared.
       if (uriToLoad instanceof Ci.nsIArray) {
         let count = uriToLoad.length;
@@ -1731,47 +1739,59 @@ var gBrowserInit = {
         ChromeUtils.import("resource:///modules/DownloadsTaskbar.jsm", {})
           .DownloadsTaskbar.registerIndicator(window);
       } catch (ex) {
         Cu.reportError(ex);
       }
     }, {timeout: 10000});
   },
 
-  // Returns the URI(s) to load at startup.
+  // Returns the URI(s) to load at startup if it is immediately known, or a
+  // promise resolving to the URI to load.
   get _uriToLoadPromise() {
     delete this._uriToLoadPromise;
-    return this._uriToLoadPromise = new Promise(resolve => {
+    return this._uriToLoadPromise = function() {
       // window.arguments[0]: URI to load (string), or an nsIArray of
       //                      nsISupportsStrings to load, or a xul:tab of
       //                      a tabbrowser, which will be replaced by this
       //                      window (for this case, all other arguments are
       //                      ignored).
       if (!window.arguments || !window.arguments[0]) {
-        resolve(null);
-        return;
+        return null;
       }
 
       let uri = window.arguments[0];
       let defaultArgs = Cc["@mozilla.org/browser/clh;1"]
                           .getService(Ci.nsIBrowserHandler)
                           .defaultArgs;
 
       // If the given URI is different from the homepage, we want to load it.
       if (uri != defaultArgs) {
-        resolve(uri);
-        return;
+        return uri;
       }
 
       // The URI appears to be the the homepage. We want to load it only if
       // session restore isn't about to override the homepage.
-      SessionStartup.willOverrideHomepagePromise.then(willOverrideHomepage => {
-        resolve(willOverrideHomepage ? null : uri);
-      });
-    });
+      let willOverride = SessionStartup.willOverrideHomepage;
+      if (!(willOverride instanceof Promise)) {
+        return willOverride ? null : uri;
+      }
+      return willOverride.then(willOverrideHomepage =>
+                                 willOverrideHomepage ? null : uri);
+    }();
+  },
+
+  // Calls the given callback with the URI to load at startup.
+  // Synchronously if possible, or after _uriToLoadPromise resolves otherwise.
+  _callWithURIToLoad(callback) {
+    let uriToLoad = this._uriToLoadPromise;
+    if (uriToLoad instanceof Promise)
+      uriToLoad.then(callback);
+    else
+      callback(uriToLoad);
   },
 
   onUnload() {
     gUIDensity.uninit();
 
     if (AppConstants.CAN_DRAW_IN_TITLEBAR) {
       gDragSpaceObserver.uninit();
     }
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -769,16 +769,17 @@
                        cui-areatype="toolbar"
                        aboutHomeOverrideTooltip="&abouthome.pageTitle;"/>
         <toolbarspring cui-areatype="toolbar" class="chromeclass-toolbar-additional"/>
         <toolbaritem id="urlbar-container" flex="400" persist="width"
                      removable="false"
                      class="chromeclass-location" overflows="false">
             <textbox id="urlbar" flex="1"
                      placeholder="&urlbar.placeholder2;"
+                     focused="true"
                      type="autocomplete"
                      autocompletesearch="unifiedcomplete"
                      autocompletesearchparam="enable-actions"
                      autocompletepopup="PopupAutoCompleteRichResult"
                      completeselectedindex="true"
                      shrinkdelay="250"
                      tabscrolling="true"
                      newlines="stripsurroundingwhitespace"
--- a/browser/base/content/test/performance/browser_startup_flicker.js
+++ b/browser/base/content/test/performance/browser_startup_flicker.js
@@ -31,35 +31,22 @@ add_task(async function() {
       alreadyFocused = true;
       // This is likely an issue caused by the test harness, but log it anyway.
       todo(false,
            "the window should be focused at first paint, " + rects.toSource());
       continue;
     }
 
     rects = rects.filter(rect => {
-      let inRange = (val, min, max) => min <= val && val <= max;
       let width = frame.width;
 
       let exceptions = [
-        {name: "bug 1403648 - urlbar down arrow shouldn't flicker",
-         condition: r => // 5x9px area, sometimes less at the end of the opacity transition
-                         inRange(r.h, 3, 5) && inRange(r.w, 7, 9) &&
-                         inRange(r.y1, 40, 80) && // in the toolbar
-                         // at ~80% of the window width
-                         inRange(r.x1, width * .75, width * .9)
-        },
-
-        {name: "bug 1403648 - urlbar should be focused at first paint",
-         condition: r => inRange(r.y2, 60, 80) && // in the toolbar
-                         // taking 50% to 75% of the window width
-                         inRange(r.w, width * .5, width * .75) &&
-                         // starting at 15 to 25% of the window width
-                         inRange(r.x1, width * .15, width * .25)
-        },
+        /**
+         * Nothing here! Please don't add anything new!
+         */
       ];
 
       let rectText = `${rect.toSource()}, window width: ${width}`;
       for (let e of exceptions) {
         if (e.condition(rect)) {
           todo(false, e.name + ", " + rectText);
           return false;
         }
--- a/browser/base/content/test/performance/browser_urlbar_keyed_search_reflows.js
+++ b/browser/base/content/test/performance/browser_urlbar_keyed_search_reflows.js
@@ -116,18 +116,17 @@ add_task(async function setup() {
   await addDummyHistoryEntries();
 });
 
 /**
  * This test ensures that there are no unexpected uninterruptible reflows when
  * typing into the URL bar with the default values in Places.
  */
 add_task(async function() {
-  let win = await BrowserTestUtils.openNewBrowserWindow();
-  await ensureNoPreloadedBrowser(win);
+  let win = await prepareSettledWindow();
 
   let URLBar = win.gURLBar;
   let popup = URLBar.popup;
 
   URLBar.focus();
   URLBar.value = "";
 
   await withReflowObserver(async function(dirtyFrameFn) {
--- a/browser/base/content/test/performance/browser_urlbar_search_reflows.js
+++ b/browser/base/content/test/performance/browser_urlbar_search_reflows.js
@@ -133,18 +133,17 @@ add_task(async function setup() {
 });
 
 /**
  * This test ensures that there are no unexpected
  * uninterruptible reflows when typing into the URL bar
  * with the default values in Places.
  */
 add_task(async function() {
-  let win = await BrowserTestUtils.openNewBrowserWindow();
-  await ensureNoPreloadedBrowser(win);
+  let win = await prepareSettledWindow();
 
   let URLBar = win.gURLBar;
   let popup = URLBar.popup;
 
   URLBar.focus();
   URLBar.value = SEARCH_TERM;
   let testFn = async function(dirtyFrameFn) {
     let oldInvalidate = popup.invalidate.bind(popup);
--- a/browser/base/content/test/performance/browser_windowopen_flicker.js
+++ b/browser/base/content/test/performance/browser_windowopen_flicker.js
@@ -80,54 +80,26 @@ add_task(async function() {
       continue;
     }
 
     ignoreTinyPaint = false;
     let rects = compareFrames(frame, previousFrame).filter(rect => {
       let inRange = (val, min, max) => min <= val && val <= max;
       let width = frame.width;
 
-      const spaceBeforeFirstTab = AppConstants.platform == "macosx" ? 100 : 0;
-      let inFirstTab = r =>
-        inRange(r.x1, spaceBeforeFirstTab, spaceBeforeFirstTab + 50) && r.y1 < 30;
-
       let exceptions = [
-        {name: "bug 1403648 - urlbar down arrow shouldn't flicker",
-         condition: r => // 5x9px area, sometimes less at the end of the opacity transition
-                         inRange(r.h, 3, 5) && inRange(r.w, 7, 9) &&
-                         inRange(r.y1, 40, 80) && // in the toolbar
-                         // at ~80% of the window width
-                         inRange(r.x1, width * .75, width * .9)
-        },
-
-        {name: "bug 1403648 - urlbar should be focused at first paint",
-         condition: r => inRange(r.y2, 60, 80) && // in the toolbar
-                         // taking 50% to 75% of the window width
-                         inRange(r.w, width * .5, width * .75) &&
-                         // starting at 15 to 25% of the window width
-                         inRange(r.x1, width * .15, width * .25)
-        },
-
         {name: "bug 1421463 - reload toolbar icon shouldn't flicker",
          condition: r => r.h == 13 && inRange(r.w, 14, 16) && // icon size
                          inRange(r.y1, 40, 80) && // in the toolbar
                          // near the left side of the screen
                          // The reload icon is shifted on devedition builds
                          // where there's an additional devtools toolbar icon.
                          AppConstants.MOZ_DEV_EDITION ? inRange(r.x1, 100, 120) :
                                                         inRange(r.x1, 65, 100)
         },
-
-        {name: "bug 1401955 - about:home favicon should be visible at first paint",
-         condition: r => inFirstTab(r) && inRange(r.h, 14, 15) && inRange(r.w, 14, 15)
-        },
-
-        {name: "bug 1401955 - space for about:home favicon should be there at first paint",
-         condition: r => inFirstTab(r) && inRange(r.w, 60, 80) && inRange(r.h, 8, 15)
-        },
       ];
 
       let rectText = `${rect.toSource()}, window width: ${width}`;
       for (let e of exceptions) {
         if (e.condition(rect)) {
           todo(false, e.name + ", " + rectText);
           return false;
         }
--- a/browser/base/content/test/performance/browser_windowopen_reflows.js
+++ b/browser/base/content/test/performance/browser_windowopen_reflows.js
@@ -31,22 +31,16 @@ if (Services.appinfo.OS == "WINNT") {
     },
   );
 }
 
 if (Services.appinfo.OS == "WINNT" || Services.appinfo.OS == "Darwin") {
   EXPECTED_REFLOWS.push(
     {
       stack: [
-        "select@chrome://global/content/bindings/textbox.xml",
-        "focusAndSelectUrlBar@chrome://browser/content/browser.js",
-      ],
-    },
-    {
-      stack: [
         "rect@chrome://browser/content/browser-tabsintitlebar.js",
         "_update@chrome://browser/content/browser-tabsintitlebar.js",
         "init@chrome://browser/content/browser-tabsintitlebar.js",
         "handleEvent@chrome://browser/content/tabbrowser.xml",
       ],
       // These numbers should only ever go down - never up.
       times: Services.appinfo.OS == "WINNT" ? 5 : 4,
     },
--- a/browser/base/content/test/performance/head.js
+++ b/browser/base/content/test/performance/head.js
@@ -158,23 +158,23 @@ async function withReflowObserver(testFn
       Assert.ok(true, "All expected reflows were observed");
     }
 
     Services.els.removeListenerForAllEvents(win, dirtyFrameFn, true);
     docShell.removeWeakReflowObserver(observer);
   }
 }
 
-async function ensureNoPreloadedBrowser() {
+async function ensureNoPreloadedBrowser(win = window) {
   // If we've got a preloaded browser, get rid of it so that it
   // doesn't interfere with the test if it's loading. We have to
   // do this before we disable preloading or changing the new tab
   // URL, otherwise _getPreloadedBrowser will return null, despite
   // the preloaded browser existing.
-  let preloaded = gBrowser._getPreloadedBrowser();
+  let preloaded = win.gBrowser._getPreloadedBrowser();
   if (preloaded) {
     preloaded.remove();
   }
 
   await SpecialPowers.pushPrefEnv({
     set: [["browser.newtab.preload", false]],
   });
 
@@ -182,16 +182,31 @@ async function ensureNoPreloadedBrowser(
                              .getService(Ci.nsIAboutNewTabService);
   aboutNewTabService.newTabURL = "about:blank";
 
   registerCleanupFunction(() => {
     aboutNewTabService.resetNewTabURL();
   });
 }
 
+async function prepareSettledWindow() {
+  let win = await BrowserTestUtils.openNewBrowserWindow();
+
+  await ensureNoPreloadedBrowser(win);
+
+  let overflowableToolbar = win.document.getElementById("nav-bar").overflowable;
+  if (overflowableToolbar._lazyResizeHandler && overflowableToolbar._lazyResizeHandler.isArmed) {
+    info("forcing deferred overflow handling of the navigation toolbar to happen immediately");
+    overflowableToolbar._lazyResizeHandler.disarm();
+    overflowableToolbar._onLazyResize();
+  }
+
+  return win;
+}
+
 /**
  * Calculate and return how many additional tabs can be fit into the
  * tabstrip without causing it to overflow.
  *
  * @return int
  *         The maximum additional tabs that can be fit into the
  *         tabstrip without causing it to overflow.
  */
--- a/browser/components/places/tests/browser/browser.ini
+++ b/browser/components/places/tests/browser/browser.ini
@@ -77,27 +77,27 @@ skip-if = (os == 'win' && ccov) # Bug 14
 skip-if = (os == 'win' && ccov) # Bug 1423667
 [browser_library_middleclick.js]
 skip-if = (os == 'win' && ccov) # Bug 1423667
 [browser_library_open_leak.js]
 [browser_library_openFlatContainer.js]
 skip-if = (os == 'win' && ccov) # Bug 1423667
 [browser_library_panel_leak.js]
 skip-if = (os == 'win' && ccov) # Bug 1423667
-[browser_library_remove_bookmark.js]
-skip-if = (os == 'win' && ccov) # Bug 1423667
 [browser_library_search.js]
 skip-if = (os == 'win' && ccov) # Bug 1423667
 [browser_library_views_liveupdate.js]
 [browser_markPageAsFollowedLink.js]
 [browser_paste_bookmarks.js]
 skip-if = (os == 'win' && ccov) # Bug 1423667
 subsuite = clipboard
 [browser_paste_into_tags.js]
 skip-if = (os == 'win' && ccov) # Bug 1423667
+[browser_remove_bookmarks.js]
+skip-if = (os == 'win' && ccov) # Bug 1423667
 subsuite = clipboard
 [browser_sidebarpanels_click.js]
 skip-if = true # temporarily disabled for breaking the treeview - bug 658744
 [browser_sort_in_library.js]
 skip-if = (os == 'win' && ccov) # Bug 1423667
 [browser_stayopenmenu.js]
 skip-if = os == "mac" && debug # bug 1400323
 [browser_toolbar_drop_text.js]
deleted file mode 100644
--- a/browser/components/places/tests/browser/browser_library_remove_bookmark.js
+++ /dev/null
@@ -1,67 +0,0 @@
-/**
- * Test deleting bookmarks from library.
- */
-"use strict";
-
-add_task(async function test_remove_bookmark() {
-  const uris = [
-    "http://example.com/1",
-    "http://example.com/2",
-    "http://example.com/3",
-  ];
-
-  let children = uris.map((uri, index) => {
-    return {
-      title: `bm${index}`,
-      url: uri,
-    };
-  });
-
-  // Insert Bookmarks.
-  await PlacesUtils.bookmarks.insertTree({
-    guid: PlacesUtils.bookmarks.unfiledGuid,
-    children
-  });
-
-  // Open the Library and select the "UnfiledBookmarks".
-  let library = await promiseLibrary("UnfiledBookmarks");
-
-  let PO = library.PlacesOrganizer;
-
-  Assert.equal(PlacesUtils.getConcreteItemGuid(PO._places.selectedNode),
-    PlacesUtils.bookmarks.unfiledGuid, "Should have selected unfiled bookmarks.");
-
-  let contextMenu = library.document.getElementById("placesContext");
-  let contextMenuDeleteItem = library.document.getElementById("placesContext_delete");
-
-  let popupShownPromise = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
-
-  let treeBoxObject = library.ContentTree.view.treeBoxObject;
-  let firstColumn = library.ContentTree.view.columns[0];
-  let firstBookmarkRect = treeBoxObject.getCoordsForCellItem(0, firstColumn, "bm0");
-
-  EventUtils.synthesizeMouse(
-    library.ContentTree.view.body,
-    firstBookmarkRect.x,
-    firstBookmarkRect.y,
-    { type: "contextmenu", button: 2 },
-    library
-  );
-
-  await popupShownPromise;
-
-  Assert.equal(library.ContentTree.view.result.root.childCount, 3, "Number of bookmarks before removal is right");
-
-  let removePromise = PlacesTestUtils.waitForNotification("onItemRemoved", (itemId, parentId, index, type, uri, guid) => uri.spec == uris[0]);
-  EventUtils.synthesizeMouseAtCenter(contextMenuDeleteItem, {}, library);
-
-  await removePromise;
-
-  Assert.equal(library.ContentTree.view.result.root.childCount, 2, "Should have removed the bookmark from the display");
-
-  // Cleanup
-  registerCleanupFunction(async function() {
-    await promiseLibraryClosed(library);
-    await PlacesUtils.bookmarks.eraseEverything();
-  });
-});
new file mode 100644
--- /dev/null
+++ b/browser/components/places/tests/browser/browser_remove_bookmarks.js
@@ -0,0 +1,121 @@
+/* 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";
+
+/**
+ *  Test removing bookmarks from the Bookmarks Toolbar and Library.
+ */
+
+const TEST_URL = "about:mozilla";
+
+add_task(async function setup() {
+  await PlacesUtils.bookmarks.eraseEverything();
+
+  let toolbar = document.getElementById("PersonalToolbar");
+  let wasCollapsed = toolbar.collapsed;
+
+  // Uncollapse the personal toolbar if needed.
+  if (wasCollapsed) {
+    await promiseSetToolbarVisibility(toolbar, true);
+  }
+
+  registerCleanupFunction(async () => {
+    // Collapse the personal toolbar if needed.
+    if (wasCollapsed) {
+      await promiseSetToolbarVisibility(toolbar, false);
+    }
+    await PlacesUtils.bookmarks.eraseEverything();
+  });
+});
+
+add_task(async function test_remove_bookmark_from_toolbar() {
+  let toolbarBookmark = await PlacesUtils.bookmarks.insert({
+    parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+    title: "Bookmark Title",
+    url: TEST_URL
+  });
+
+  let toolbarNode = getToolbarNodeForItemGuid(toolbarBookmark.guid);
+
+  let contextMenu = document.getElementById("placesContext");
+  let popupShownPromise = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
+
+  EventUtils.synthesizeMouseAtCenter(toolbarNode, {
+    button: 2,
+    type: "contextmenu"
+  });
+  await popupShownPromise;
+
+  let contextMenuDeleteItem = document.getElementById("placesContext_delete");
+
+  let removePromise = PlacesTestUtils.waitForNotification("onItemRemoved", (itemId, parentId, index, type, uri, guid) => uri.spec == TEST_URL);
+
+  EventUtils.synthesizeMouseAtCenter(contextMenuDeleteItem, {});
+
+  await removePromise;
+
+  Assert.deepEqual(PlacesUtils.bookmarks.fetch({ url: TEST_URL }), {}, "Should have removed the bookmark from the database");
+});
+
+add_task(async function test_remove_bookmark_from_library() {
+  const uris = [
+    "http://example.com/1",
+    "http://example.com/2",
+    "http://example.com/3",
+  ];
+
+  let children = uris.map((uri, index) => {
+    return {
+      title: `bm${index}`,
+      url: uri,
+    };
+  });
+
+  // Insert bookmarks.
+  await PlacesUtils.bookmarks.insertTree({
+    guid: PlacesUtils.bookmarks.unfiledGuid,
+    children
+  });
+
+  // Open the Library and select the "UnfiledBookmarks".
+  let library = await promiseLibrary("UnfiledBookmarks");
+
+  registerCleanupFunction(async function() {
+    await promiseLibraryClosed(library);
+  });
+
+  let PO = library.PlacesOrganizer;
+
+  Assert.equal(PlacesUtils.getConcreteItemGuid(PO._places.selectedNode),
+    PlacesUtils.bookmarks.unfiledGuid, "Should have selected unfiled bookmarks.");
+
+  let contextMenu = library.document.getElementById("placesContext");
+  let contextMenuDeleteItem = library.document.getElementById("placesContext_delete");
+
+  let popupShownPromise = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
+
+  let treeBoxObject = library.ContentTree.view.treeBoxObject;
+  let firstColumn = library.ContentTree.view.columns[0];
+  let firstBookmarkRect = treeBoxObject.getCoordsForCellItem(0, firstColumn, "bm0");
+
+  EventUtils.synthesizeMouse(
+    library.ContentTree.view.body,
+    firstBookmarkRect.x,
+    firstBookmarkRect.y,
+    { type: "contextmenu", button: 2 },
+    library
+  );
+
+  await popupShownPromise;
+
+  Assert.equal(library.ContentTree.view.result.root.childCount, 3, "Number of bookmarks before removal is right");
+
+  let removePromise = PlacesTestUtils.waitForNotification("onItemRemoved", (itemId, parentId, index, type, uri, guid) => uri.spec == uris[0]);
+  EventUtils.synthesizeMouseAtCenter(contextMenuDeleteItem, {}, library);
+
+  await removePromise;
+
+  Assert.equal(library.ContentTree.view.result.root.childCount, 2, "Should have removed the bookmark from the display");
+});
--- a/browser/components/sessionstore/nsISessionStartup.idl
+++ b/browser/components/sessionstore/nsISessionStartup.idl
@@ -31,23 +31,24 @@ interface nsISessionStartup: nsISupports
   /**
    * Determines whether automatic session restoration is enabled for this
    * launch of the browser. This does not include crash restoration, and will
    * return false if restoration will only be caused by a crash.
    */
   boolean isAutomaticRestoreEnabled();
 
   /**
-   * Returns a promise that resolves to a boolean, indicating whether we will
-   * restore a session that ends up replacing the homepage. True guarantees
-   * that we'll restore a session; false means that we /probably/ won't do so.
+   * Returns a boolean or a promise that resolves to a boolean, indicating
+   * whether we will restore a session that ends up replacing the homepage.
+   * True guarantees that we'll restore a session; false means that we
+   * /probably/ won't do so.
    * The browser uses this to avoid unnecessarily loading the homepage when
    * restoring a session.
    */
-  readonly attribute jsval willOverrideHomepagePromise;
+  readonly attribute jsval willOverrideHomepage;
 
   /**
    * What type of session we're restoring.
    * NO_SESSION       There is no data available from the previous session
    * RECOVER_SESSION  The last session crashed. It will either be restored or
    *                  about:sessionrestore will be shown.
    * RESUME_SESSION   The previous session should be restored at startup
    * DEFER_SESSION    The previous session is fine, but it shouldn't be restored
--- a/browser/components/sessionstore/nsSessionStartup.js
+++ b/browser/components/sessionstore/nsSessionStartup.js
@@ -305,30 +305,31 @@ SessionStartup.prototype = {
    * @returns bool
    */
   _willRestore() {
     return this._sessionType == Ci.nsISessionStartup.RECOVER_SESSION ||
            this._sessionType == Ci.nsISessionStartup.RESUME_SESSION;
   },
 
   /**
-   * Returns a promise that resolves to a boolean, indicating whether we will
-   * restore a session that ends up replacing the homepage. True guarantees
-   * that we'll restore a session; false means that we /probably/ won't do so.
+   * Returns a boolean or a promise that resolves to a boolean, indicating
+   * whether we will restore a session that ends up replacing the homepage.
+   * True guarantees that we'll restore a session; false means that we
+   * /probably/ won't do so.
    * The browser uses this to avoid unnecessarily loading the homepage when
    * restoring a session.
    */
-  get willOverrideHomepagePromise() {
+  get willOverrideHomepage() {
     // If the session file hasn't been read yet and resuming the session isn't
     // enabled via prefs, go ahead and load the homepage. We may still replace
     // it when recovering from a crash, which we'll only know after reading the
     // session file, but waiting for that would delay loading the homepage in
     // the non-crash case.
     if (!this._initialState && !this._resumeSessionEnabled) {
-      return Promise.resolve(false);
+      return false;
     }
 
     return new Promise(resolve => {
       this.onceInitialized.then(() => {
         // If there are valid windows with not only pinned tabs, signal that we
         // will override the default homepage by restoring a session.
         resolve(this._willRestore() &&
                 this._initialState &&
new file mode 100644
--- /dev/null
+++ b/browser/modules/ThemeVariableMap.jsm
@@ -0,0 +1,26 @@
+/* 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 = ["ThemeVariableMap"];
+
+const ThemeVariableMap = [
+  ["--lwt-accent-color-inactive", "accentcolorInactive"],
+  ["--lwt-background-alignment", "backgroundsAlignment"],
+  ["--lwt-background-tiling", "backgroundsTiling"],
+  ["--tab-loading-fill", "tab_loading", "tabbrowser-tabs"],
+  ["--lwt-tab-text", "tab_text"],
+  ["--toolbar-bgcolor", "toolbarColor"],
+  ["--toolbar-color", "toolbar_text"],
+  ["--url-and-searchbar-background-color", "toolbar_field"],
+  ["--url-and-searchbar-color", "toolbar_field_text"],
+  ["--lwt-toolbar-field-border-color", "toolbar_field_border"],
+  ["--urlbar-separator-color", "toolbar_field_separator"],
+  ["--tabs-border-color", "toolbar_top_separator", "navigator-toolbox"],
+  ["--lwt-toolbar-vertical-separator", "toolbar_vertical_separator"],
+  ["--toolbox-border-bottom-color", "toolbar_bottom_separator"],
+  ["--lwt-toolbarbutton-icon-fill", "icon_color"],
+  ["--lwt-toolbarbutton-icon-fill-attention", "icon_attention_color"],
+  ["--lwt-toolbarbutton-hover-background", "button_background_hover"],
+  ["--lwt-toolbarbutton-active-background", "button_background_active"],
+];
--- a/browser/modules/moz.build
+++ b/browser/modules/moz.build
@@ -83,16 +83,19 @@ with Files("ReaderParent.jsm"):
     BUG_COMPONENT = ("Toolkit", "Reader Mode")
 
 with Files("Sanitizer.jsm"):
     BUG_COMPONENT = ("Firefox", "Preferences")
 
 with Files("SitePermissions.jsm"):
     BUG_COMPONENT = ("Firefox", "Site Identity and Permission Panels")
 
+with Files("ThemeVariableMap.jsm"):
+    BUG_COMPONENT = ("Toolkit", "WebExtensions: Themes")
+
 with Files("TransientPrefs.jsm"):
     BUG_COMPONENT = ("Firefox", "Preferences")
 
 with Files("Windows8WindowFrameColor.jsm"):
     BUG_COMPONENT = ("Firefox", "Theme")
 
 with Files("WindowsJumpLists.jsm"):
     BUG_COMPONENT = ("Firefox", "Shell Integration")
@@ -147,16 +150,17 @@ EXTRA_JS_MODULES += [
     'PluginContent.jsm',
     'ProcessHangMonitor.jsm',
     'ReaderParent.jsm',
     'RecentWindow.jsm',
     'RemotePrompt.jsm',
     'Sanitizer.jsm',
     'SchedulePressure.jsm',
     'SitePermissions.jsm',
+    'ThemeVariableMap.jsm',
     'TransientPrefs.jsm',
     'UpdateTopLevelContentWindowIDHelper.jsm',
     'webrtcUI.jsm',
     'ZoomUI.jsm',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     EXTRA_JS_MODULES += [
--- a/config/check_spidermonkey_style.py
+++ b/config/check_spidermonkey_style.py
@@ -143,35 +143,35 @@ js/src/tests/style/BadIncludes.h:8: erro
     <tests/style/BadIncludes2.h> should be included using
     the #include "..." form
 
 js/src/tests/style/BadIncludes.h:10: error:
     "stdio.h" is included using the wrong path;
     did you forget a prefix, or is the file not yet committed?
 
 js/src/tests/style/BadIncludesOrder-inl.h:5:6: error:
-    "vm/Interpreter-inl.h" should be included after "jsscriptinlines.h"
+    "vm/JSScript-inl.h" should be included after "vm/Interpreter-inl.h"
 
 js/src/tests/style/BadIncludesOrder-inl.h:6:7: error:
-    "jsscriptinlines.h" should be included after "js/Value.h"
+    "vm/Interpreter-inl.h" should be included after "js/Value.h"
 
 js/src/tests/style/BadIncludesOrder-inl.h:7:8: error:
     "js/Value.h" should be included after "ds/LifoAlloc.h"
 
 js/src/tests/style/BadIncludesOrder-inl.h:8:9: error:
     "ds/LifoAlloc.h" should be included after "jsapi.h"
 
 js/src/tests/style/BadIncludesOrder-inl.h:9:10: error:
     "jsapi.h" should be included after <stdio.h>
 
 js/src/tests/style/BadIncludesOrder-inl.h:10:11: error:
     <stdio.h> should be included after "mozilla/HashFunctions.h"
 
-js/src/tests/style/BadIncludesOrder-inl.h:27:28: error:
-    "jsobj.h" should be included after "jsfun.h"
+js/src/tests/style/BadIncludesOrder-inl.h:28:29: error:
+    "vm/JSScript.h" should be included after "vm/JSFunction.h"
 
 (multiple files): error:
     header files form one or more cycles
 
    tests/style/HeaderCycleA1.h
    -> tests/style/HeaderCycleA2.h
       -> tests/style/HeaderCycleA3.h
          -> tests/style/HeaderCycleA1.h
@@ -231,17 +231,17 @@ class FileKind(object):
             return FileKind.TBL
 
         if filename.endswith('.msg'):
             return FileKind.MSG
 
         error(filename, None, 'unknown file kind')
 
 
-def check_style():
+def check_style(enable_fixup):
     # We deal with two kinds of name.
     # - A "filename" is a full path to a file from the repository root.
     # - An "inclname" is how a file is referred to in a #include statement.
     #
     # Examples (filename -> inclname)
     # - "mfbt/Attributes.h"         -> "mozilla/Attributes.h"
     # - "mfbt/decimal/Decimal.h     -> "mozilla/Decimal.h"
     # - "mozglue/misc/TimeStamp.h   -> "mozilla/TimeStamp.h"
@@ -287,18 +287,26 @@ def check_style():
         inclname = js_names[filename]
         file_kind = FileKind.get(filename)
         if file_kind == FileKind.C or file_kind == FileKind.CPP or \
            file_kind == FileKind.H or file_kind == FileKind.INL_H:
             included_h_inclnames = set()    # type: set(inclname)
 
             # This script is run in js/src/, so prepend '../../' to get to the root of the Mozilla
             # source tree.
-            with open(os.path.join(repo.path, filename)) as f:
-                do_file(filename, inclname, file_kind, f, all_inclnames, included_h_inclnames)
+            filepath = os.path.join(repo.path, filename)
+            with open(filepath) as f:
+                code = read_file(f)
+
+            if enable_fixup:
+                code = code.sorted(inclname)
+                with open(filepath, 'w') as f:
+                    f.write(code.to_source())
+
+            check_file(filename, inclname, file_kind, code, all_inclnames, included_h_inclnames)
 
         edges[inclname] = included_h_inclnames
 
     find_cycles(all_inclnames, edges)
 
     # Compare expected and actual output.
     difflines = difflib.unified_diff(expected_output, actual_output,
                                      fromfile='check_spidermonkey_style.py expected output',
@@ -333,22 +341,26 @@ def is_module_header(enclosing_inclname,
         return True
 
     return False
 
 
 class Include(object):
     '''Important information for a single #include statement.'''
 
-    def __init__(self, inclname, linenum, is_system):
+    def __init__(self, include_prefix, inclname, line_suffix, linenum, is_system):
+        self.include_prefix = include_prefix
+        self.line_suffix = line_suffix
         self.inclname = inclname
         self.linenum = linenum
         self.is_system = is_system
 
-    def isLeaf(self):
+    def is_style_relevant(self):
+        # Includes are style-relevant; that is, they're checked by the pairwise
+        # style-checking algorithm in check_file.
         return True
 
     def section(self, enclosing_inclname):
         '''Identify which section inclname belongs to.
 
         The section numbers are as follows.
           0. Module header (e.g. jsfoo.h or jsfooinlines.h within jsfoo.cpp)
           1. mozilla/Foo.h
@@ -386,74 +398,210 @@ class Include(object):
         return 3
 
     def quote(self):
         if self.is_system:
             return '<' + self.inclname + '>'
         else:
             return '"' + self.inclname + '"'
 
+    def sort_key(self, enclosing_inclname):
+        return (self.section(enclosing_inclname), self.inclname.lower())
 
-class HashIfBlock(object):
-    '''Important information about a #if/#endif block.
+    def to_source(self):
+        return self.include_prefix + self.quote() + self.line_suffix + '\n'
+
+
+class CppBlock(object):
+    '''C preprocessor block: a whole file or a single #if/#elif/#else block.
 
     A #if/#endif block is the contents of a #if/#endif (or similar) section.
     The top-level block, which is not within a #if/#endif pair, is also
     considered a block.
 
-    Each leaf is either an Include (representing a #include), or another
-    nested HashIfBlock.'''
-    def __init__(self):
+    Each kid is either an Include (representing a #include), OrdinaryCode, or
+    a nested CppBlock.'''
+    def __init__(self, start_line=""):
+        self.start = start_line
+        self.end = ''
         self.kids = []
 
-    def isLeaf(self):
-        return False
+    def is_style_relevant(self):
+        return True
+
+    def append_ordinary_line(self, line):
+        if len(self.kids) == 0 or not isinstance(self.kids[-1], OrdinaryCode):
+            self.kids.append(OrdinaryCode())
+        self.kids[-1].lines.append(line)
+
+    def style_relevant_kids(self):
+        """ Return a list of kids in this block that are style-relevant. """
+        return [kid for kid in self.kids if kid.is_style_relevant()]
+
+    def sorted(self, enclosing_inclname):
+        """Return a hopefully-sorted copy of this block. Implements --fixup.
+
+        When in doubt, this leaves the code unchanged.
+        """
+
+        def pretty_sorted_includes(includes):
+            """ Return a new list containing the given includes, in order,
+            with blank lines separating sections. """
+            keys = [inc.sort_key(enclosing_inclname) for inc in includes]
+            if sorted(keys) == keys:
+                return includes  # if nothing is out of order, don't touch anything
+
+            output = []
+            current_section = None
+            for (section, _), inc in sorted(zip(keys, includes)):
+                if current_section is not None and section != current_section:
+                    output.append(OrdinaryCode(["\n"]))  # blank line
+                output.append(inc)
+                current_section = section
+            return output
+
+        def should_try_to_sort(includes):
+            if 'tests/style/BadIncludes' in enclosing_inclname:
+                return False  # don't straighten the counterexample
+            if any(inc.inclname in oddly_ordered_inclnames for inc in includes):
+                return False  # don't sort batches containing odd includes
+            if includes == sorted(includes, key=lambda inc: inc.sort_key(enclosing_inclname)):
+                return False  # it's already sorted, avoid whitespace-only fixups
+            return True
+
+        # The content of the eventual output of this method.
+        output = []
+
+        # The current batch of includes to sort. This list only ever contains Include objects
+        # and whitespace OrdinaryCode objects.
+        batch = []
+
+        def flush_batch():
+            """Sort the contents of `batch` and move it to `output`."""
+
+            assert all(isinstance(item, Include)
+                       or (isinstance(item, OrdinaryCode) and "".join(item.lines).isspace())
+                       for item in batch)
+
+            # Here we throw away the blank lines.
+            # `pretty_sorted_includes` puts them back.
+            includes = []
+            last_include_index = -1
+            for i, item in enumerate(batch):
+                if isinstance(item, Include):
+                    includes.append(item)
+                    last_include_index = i
+            cutoff = last_include_index + 1
+
+            if should_try_to_sort(includes):
+                output.extend(pretty_sorted_includes(includes) + batch[cutoff:])
+            else:
+                output.extend(batch)
+            del batch[:]
+
+        for kid in self.kids:
+            if isinstance(kid, CppBlock):
+                flush_batch()
+                output.append(kid.sorted(enclosing_inclname))
+            elif isinstance(kid, Include):
+                batch.append(kid)
+            else:
+                assert isinstance(kid, OrdinaryCode)
+                if kid.to_source().isspace():
+                    batch.append(kid)
+                else:
+                    flush_batch()
+                    output.append(kid)
+        flush_batch()
+
+        result = CppBlock()
+        result.start = self.start
+        result.end = self.end
+        result.kids = output
+        return result
+
+    def to_source(self):
+        return self.start + ''.join(kid.to_source() for kid in self.kids) + self.end
 
 
-def do_file(filename, inclname, file_kind, f, all_inclnames, included_h_inclnames):
-    block_stack = [HashIfBlock()]
+class OrdinaryCode(object):
+    ''' A list of lines of code that aren't #include/#if/#else/#endif lines. '''
+    def __init__(self, lines=None):
+        self.lines = lines if lines is not None else []
+
+    def is_style_relevant(self):
+        return False
 
-    # Extract the #include statements as a tree of IBlocks and IIncludes.
-    for linenum, line in enumerate(f, start=1):
-        # We're only interested in lines that contain a '#'.
-        if not '#' in line:
-            continue
+    def to_source(self):
+        return ''.join(self.lines)
+
+
+# A "snippet" is one of:
+#
+# *   Include - representing an #include line
+# *   CppBlock - a whole file or #if/#elif/#else block; contains a list of snippets
+# *   OrdinaryCode - representing lines of non-#include-relevant code
 
-        # Look for a |#include "..."| line.
-        m = re.match(r'\s*#\s*include\s+"([^"]*)"', line)
-        if m is not None:
-            block_stack[-1].kids.append(Include(m.group(1), linenum, False))
+def read_file(f):
+    block_stack = [CppBlock()]
 
-        # Look for a |#include <...>| line.
-        m = re.match(r'\s*#\s*include\s+<([^>]*)>', line)
-        if m is not None:
-            block_stack[-1].kids.append(Include(m.group(1), linenum, True))
+    # Extract the #include statements as a tree of snippets.
+    for linenum, line in enumerate(f, start=1):
+        if line.lstrip().startswith('#'):
+            # Look for a |#include "..."| line.
+            m = re.match(r'(\s*#\s*include\s+)"([^"]*)"(.*)', line)
+            if m is not None:
+                prefix, inclname, suffix = m.groups()
+                block_stack[-1].kids.append(Include(prefix, inclname, suffix, linenum, is_system=False))
+                continue
+
+            # Look for a |#include <...>| line.
+            m = re.match(r'(\s*#\s*include\s+)<([^>]*)>(.*)', line)
+            if m is not None:
+                prefix, inclname, suffix = m.groups()
+                block_stack[-1].kids.append(Include(prefix, inclname, suffix, linenum, is_system=True))
+                continue
 
-        # Look for a |#{if,ifdef,ifndef}| line.
-        m = re.match(r'\s*#\s*(if|ifdef|ifndef)\b', line)
-        if m is not None:
-            # Open a new block.
-            new_block = HashIfBlock()
-            block_stack[-1].kids.append(new_block)
-            block_stack.append(new_block)
+            # Look for a |#{if,ifdef,ifndef}| line.
+            m = re.match(r'\s*#\s*(if|ifdef|ifndef)\b', line)
+            if m is not None:
+                # Open a new block.
+                new_block = CppBlock(line)
+                block_stack[-1].kids.append(new_block)
+                block_stack.append(new_block)
+                continue
+
+            # Look for a |#{elif,else}| line.
+            m = re.match(r'\s*#\s*(elif|else)\b', line)
+            if m is not None:
+                # Close the current block, and open an adjacent one.
+                block_stack.pop()
+                new_block = CppBlock(line)
+                block_stack[-1].kids.append(new_block)
+                block_stack.append(new_block)
+                continue
 
-        # Look for a |#{elif,else}| line.
-        m = re.match(r'\s*#\s*(elif|else)\b', line)
-        if m is not None:
-            # Close the current block, and open an adjacent one.
-            block_stack.pop()
-            new_block = HashIfBlock()
-            block_stack[-1].kids.append(new_block)
-            block_stack.append(new_block)
+            # Look for a |#endif| line.
+            m = re.match(r'\s*#\s*endif\b', line)
+            if m is not None:
+                # Close the current block.
+                block_stack.pop().end = line
+                if len(block_stack) == 0:
+                    raise ValueError("#endif without #if at line " + str(linenum))
+                continue
 
-        # Look for a |#endif| line.
-        m = re.match(r'\s*#\s*endif\b', line)
-        if m is not None:
-            # Close the current block.
-            block_stack.pop()
+        # Otherwise, we have an ordinary line.
+        block_stack[-1].append_ordinary_line(line)
+
+    if len(block_stack) > 1:
+        raise ValueError("unmatched #if")
+    return block_stack[-1]
+
+
+def check_file(filename, inclname, file_kind, code, all_inclnames, included_h_inclnames):
 
     def check_include_statement(include):
         '''Check the style of a single #include statement.'''
 
         if include.is_system:
             # Check it is not a known local file (in which case it's probably a system header).
             if include.inclname in included_inclnames_to_ignore or \
                include.inclname in all_inclnames:
@@ -498,25 +646,26 @@ def do_file(filename, inclname, file_kin
         if (section1 > section2) or \
            ((section1 == section2) and (include1.inclname.lower() > include2.inclname.lower())):
             error(filename, str(include1.linenum) + ':' + str(include2.linenum),
                   include1.quote() + ' should be included after ' + include2.quote())
 
     # Check the extracted #include statements, both individually, and the ordering of
     # adjacent pairs that live in the same block.
     def pair_traverse(prev, this):
-        if this.isLeaf():
+        if isinstance(this, Include):
             check_include_statement(this)
-            if prev is not None and prev.isLeaf():
+            if isinstance(prev, Include):
                 check_includes_order(prev, this)
         else:
-            for prev2, this2 in zip([None] + this.kids[0:-1], this.kids):
+            kids = this.style_relevant_kids()
+            for prev2, this2 in zip([None] + kids[0:-1], kids):
                 pair_traverse(prev2, this2)
 
-    pair_traverse(None, block_stack[-1])
+    pair_traverse(None, code)
 
 
 def find_cycles(all_inclnames, edges):
     '''Find and draw any cycles.'''
 
     SCCs = tarjan(all_inclnames, edges)
 
     # The various sorted() calls below ensure the output is deterministic.
@@ -583,20 +732,39 @@ def tarjan(V, E):
     for v in V:
         if v not in vertex_index:
             index = strongconnect(v, index)
 
     return all_SCCs
 
 
 def main():
-    ok = check_style()
+    if sys.argv[1:] == ["--fixup"]:
+        # Sort #include directives in-place.  Fixup mode doesn't solve
+        # all possible silliness that the script checks for; it's just a
+        # hack for the common case where renaming a header causes style
+        # errors.
+        fixup = True
+    elif sys.argv[1:] == []:
+        fixup = False
+    else:
+        print("TEST-UNEXPECTED-FAIL | check_spidermonkey_style.py | unexpected command line options: " +
+              repr(sys.argv[1:]))
+        sys.exit(1)
+
+    ok = check_style(fixup)
 
     if ok:
         print('TEST-PASS | check_spidermonkey_style.py | ok')
     else:
-        print('TEST-UNEXPECTED-FAIL | check_spidermonkey_style.py | actual output does not match expected output;  diff is above')
+        print('TEST-UNEXPECTED-FAIL | check_spidermonkey_style.py | ' +
+              'actual output does not match expected output;  diff is above.')
+        print('TEST-UNEXPECTED-FAIL | check_spidermonkey_style.py | ' +
+              'Hint: If the problem is that you renamed a header, and many #includes ' +
+              'are no longer in alphabetical order, commit your work and then try ' +
+              '`check_spidermonkey_style.py --fixup`. ' +
+              'You need to commit first because --fixup modifies your files in place.')
 
     sys.exit(0 if ok else 1)
 
 
 if __name__ == '__main__':
     main()
--- a/devtools/client/debugger/new/test/mochitest/browser.ini
+++ b/devtools/client/debugger/new/test/mochitest/browser.ini
@@ -96,19 +96,18 @@ skip-if = !e10s # This test is only vali
 [browser_dbg-content-script-sources.js]
 [browser_dbg-debugger-buttons.js]
 [browser_dbg-editor-gutter.js]
 [browser_dbg-editor-select.js]
 [browser_dbg-editor-highlight.js]
 [browser_dbg-expressions.js]
 [browser_dbg-expressions-error.js]
 [browser_dbg-iframes.js]
-[browser_dbg_keyboard_navigation.js]
-skip-if = true # regular failures during release in Bug 1415300
-[browser_dbg_keyboard-shortcuts.js]
+[browser_dbg-keyboard-navigation.js]
+[browser_dbg-keyboard-shortcuts.js]
 skip-if = os == "linux" # bug 1351952
 [browser_dbg-layout-changes.js]
 [browser_dbg-outline.js]
 [browser_dbg-pause-exceptions.js]
 [browser_dbg-pause-ux.js]
 skip-if = os == "win"
 [browser_dbg-navigation.js]
 [browser_dbg-minified.js]
@@ -119,25 +118,24 @@ skip-if = os == "win"
 skip-if = os == "win"
 [browser_dbg-preview-source-maps.js]
 skip-if = os == "win"
 [browser_dbg-returnvalues.js]
 [browser_dbg-scopes-mutations.js]
 [browser_dbg-search-file.js]
 skip-if = os == "win" # Bug 1393121
 [browser_dbg-quick-open.js]
-skip-if = true # regular failures during release in Bug 1415300
+skip-if = os == "win" 
 [browser_dbg-search-project.js]
 [browser_dbg-sourcemaps.js]
 [browser_dbg-sourcemaps-reload.js]
 [browser_dbg-sourcemaps-reloading.js]
 [browser_dbg-sourcemaps2.js]
 [browser_dbg-sourcemaps3.js]
 [browser_dbg-sourcemaps-bogus.js]
 [browser_dbg-sources.js]
 [browser_dbg-sources-named-eval.js]
 [browser_dbg-tabs.js]
 [browser_dbg-tabs-pretty-print.js]
 [browser_dbg-toggling-tools.js]
-skip-if = true # Bug 1414124
 [browser_dbg-wasm-sourcemaps.js]
 skip-if = true
 [browser_dbg-reload.js]
rename from devtools/client/debugger/new/test/mochitest/browser_dbg_keyboard_navigation.js
rename to devtools/client/debugger/new/test/mochitest/browser_dbg-keyboard-navigation.js
rename from devtools/client/debugger/new/test/mochitest/browser_dbg_keyboard-shortcuts.js
rename to devtools/client/debugger/new/test/mochitest/browser_dbg-keyboard-shortcuts.js
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-quick-open.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-quick-open.js
@@ -57,63 +57,58 @@ function quickOpen(dbg, query, shortcut 
 add_task(async function() {
   const dbg = await initDebugger("doc-script-switching.html");
 
   info("test opening and closing");
   quickOpen(dbg, "");
   pressKey(dbg, "Escape");
   assertDisabled(dbg);
 
+  await waitForSource(dbg, "switching-01");
   quickOpen(dbg, "sw");
   pressKey(dbg, "Enter");
 
-  let source = dbg.selectors.getSelectedSource(dbg.getState());
-  ok(source.get("url").match(/switching-01/), "first source is selected");
   await waitForSelectedSource(dbg, "switching-01");
-
   info("Arrow keys and check to see if source is selected");
   quickOpen(dbg, "sw");
   is(resultCount(dbg), 2, "two file results");
   pressKey(dbg, "Down");
   pressKey(dbg, "Enter");
 
-  source = dbg.selectors.getSelectedSource(dbg.getState());
-  ok(source.get("url").match(/switching-02/), "second source is selected");
   await waitForSelectedSource(dbg, "switching-02");
   quickOpen(dbg, "sw");
   pressKey(dbg, "Tab");
   assertDisabled(dbg);
 
   info("Testing function search");
   quickOpen(dbg, "", "quickOpenFunc");
   is(resultCount(dbg), 2, "two function results");
 
-  type(dbg, "x");
+  type(dbg, "@x");
   is(resultCount(dbg), 0, "no functions with 'x' in name");
 
   pressKey(dbg, "Escape");
   assertDisabled(dbg);
 
   info("Testing variable search");
   quickOpen(dbg, "sw2");
   pressKey(dbg, "Enter");
 
   quickOpen(dbg, "#");
   is(resultCount(dbg), 1, "one variable result");
   const results = findAllElements(dbg, "resultItems");
-  results.forEach(result => is(result.textContent, "x13"));
+  results.forEach(result => is(result.textContent, "13"));
   await waitToClose(dbg);
 
   info("Testing goto line:column");
-  assertLine(dbg, undefined);
-  assertColumn(dbg, undefined);
+  assertLine(dbg, 0);
+  assertColumn(dbg, null);
   quickOpen(dbg, ":7:12");
   pressKey(dbg, "Enter");
   assertLine(dbg, 7);
   assertColumn(dbg, 12);
 
   info("Testing gotoSource");
   quickOpen(dbg, "sw1:5");
   pressKey(dbg, "Enter");
-  source = dbg.selectors.getSelectedSource(dbg.getState());
-  ok(source.get("url").match(/switching-01/), "first source is selected");
+  await waitForSelectedSource(dbg, "switching-01");
   assertLine(dbg, 5);
 });
--- a/devtools/client/framework/components/toolbox-toolbar.js
+++ b/devtools/client/framework/components/toolbox-toolbar.js
@@ -109,24 +109,26 @@ function renderToolboxButtons({toolboxBu
     return null;
   }
 
   return div({id: `toolbox-buttons-${isStart ? "start" : "end"}`},
     ...visibleButtons.map(command => {
       const {
         id,
         description,
+        disabled,
         onClick,
         isChecked,
         className: buttonClass,
         onKeyDown
       } = command;
       return button({
         id,
         title: description,
+        disabled,
         className: (
           "command-button devtools-button "
           + buttonClass + (isChecked ? " checked" : "")
         ),
         onClick: (event) => {
           onClick(event);
           focusButton(id);
         },
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -149,16 +149,17 @@ function Toolbox(target, selectedTool, h
   this._onToolbarFocus = this._onToolbarFocus.bind(this);
   this._onToolbarArrowKeypress = this._onToolbarArrowKeypress.bind(this);
   this._onPickerClick = this._onPickerClick.bind(this);
   this._onPickerKeypress = this._onPickerKeypress.bind(this);
   this._onPickerStarted = this._onPickerStarted.bind(this);
   this._onPickerStopped = this._onPickerStopped.bind(this);
   this._onInspectObject = this._onInspectObject.bind(this);
   this._onNewSelectedNodeFront = this._onNewSelectedNodeFront.bind(this);
+  this._updatePickerButton = this._updatePickerButton.bind(this);
   this.selectTool = this.selectTool.bind(this);
 
   this._target.on("close", this.destroy);
 
   if (!selectedTool) {
     selectedTool = Services.prefs.getCharPref(this._prefs.LAST_TOOL);
   }
   this._defaultToolId = selectedTool;
@@ -172,16 +173,17 @@ function Toolbox(target, selectedTool, h
 
   this._target.on("will-navigate", this._onWillNavigate);
   this._target.on("navigate", this._refreshHostTitle);
   this._target.on("frame-update", this._updateFrames);
   this._target.on("inspect-object", this._onInspectObject);
 
   this.on("host-changed", this._refreshHostTitle);
   this.on("select", this._refreshHostTitle);
+  this.on("select", this._updatePickerButton);
 
   this.on("ready", this._showDevEditionPromo);
 
   gDevTools.on("tool-registered", this._toolRegistered);
   gDevTools.on("tool-unregistered", this._toolUnregistered);
 
   this.on("picker-started", this._onPickerStarted);
   this.on("picker-stopped", this._onPickerStopped);
@@ -718,16 +720,17 @@ Toolbox.prototype = {
    * components to listen and respond to updates.
    *
    * @param {Object} options:
    *
    * @property {String} id - The id of the button or command.
    * @property {String} className - An optional additional className for the button.
    * @property {String} description - The value that will display as a tooltip and in
    *                    the options panel for enabling/disabling.
+   * @property {Boolean} disabled - An optional disabled state for the button.
    * @property {Function} onClick - The function to run when the button is activated by
    *                      click or keyboard shortcut. First argument will be the 'click'
    *                      event, and second argument is the toolbox instance.
    * @property {Boolean} isInStartContainer - Buttons can either be placed at the start
    *                     of the toolbar, or at the end.
    * @property {Function} setup - Function run immediately to listen for events changing
    *                      whenever the button is checked or unchecked. The toolbox object
    *                      is passed as first argument and a callback is passed as second
@@ -744,29 +747,31 @@ Toolbox.prototype = {
    *                      the button should be displayed as toggled on.
    */
   _createButtonState: function (options) {
     let isCheckedValue = false;
     const {
       id,
       className,
       description,
+      disabled,
       onClick,
       isInStartContainer,
       setup,
       teardown,
       isTargetSupported,
       isChecked,
       onKeyDown
     } = options;
     const toolbox = this;
     const button = {
       id,
       className,
       description,
+      disabled,
       onClick(event) {
         if (typeof onClick == "function") {
           onClick(event, toolbox);
         }
       },
       onKeyDown(event) {
         if (typeof onKeyDown == "function") {
           onKeyDown(event, toolbox);
@@ -1304,30 +1309,43 @@ Toolbox.prototype = {
     return this.autohideButton;
   }),
 
   /**
    * Toggle the picker, but also decide whether or not the highlighter should
    * focus the window. This is only desirable when the toolbox is mounted to the
    * window. When devtools is free floating, then the target window should not
    * pop in front of the viewer when the picker is clicked.
+   *
+   * Note: Toggle picker can be overwritten by panel other than the inspector to
+   * allow for custom picker behaviour.
    */
   _onPickerClick: function () {
     let focus = this.hostType === Toolbox.HostType.BOTTOM ||
                 this.hostType === Toolbox.HostType.SIDE;
-    this.highlighterUtils.togglePicker(focus);
+    let currentPanel = this.getCurrentPanel();
+    if (currentPanel.togglePicker) {
+      currentPanel.togglePicker(focus);
+    } else {
+      this.highlighterUtils.togglePicker(focus);
+    }
   },
 
   /**
    * If the picker is activated, then allow the Escape key to deactivate the
    * functionality instead of the default behavior of toggling the console.
    */
   _onPickerKeypress: function (event) {
     if (event.keyCode === KeyCodes.DOM_VK_ESCAPE) {
-      this.highlighterUtils.cancelPicker();
+      let currentPanel = this.getCurrentPanel();
+      if (currentPanel.cancelPicker) {
+        currentPanel.cancelPicker();
+      } else {
+        this.highlighterUtils.cancelPicker();
+      }
       // Stop the console from toggling.
       event.stopImmediatePropagation();
     }
   },
 
   _onPickerStarted: function () {
     this.doc.addEventListener("keypress", this._onPickerKeypress, true);
   },
@@ -1393,16 +1411,39 @@ Toolbox.prototype = {
   updateToolboxButtonsVisibility() {
     this.toolbarButtons.forEach(button => {
       button.isVisible = this._commandIsVisible(button);
     });
     this.component.setToolboxButtons(this.toolbarButtons);
   },
 
   /**
+   * Visually update picker button.
+   * This function is called on every "select" event. Newly selected panel can
+   * update the visual state of the picker button such as disabled state,
+   * additional CSS classes (className), and tooltip (description).
+   */
+  _updatePickerButton() {
+    const button = this.pickerButton;
+    let currentPanel = this.getCurrentPanel();
+
+    if (currentPanel && currentPanel.updatePickerButton) {
+      currentPanel.updatePickerButton();
+    } else {
+      // If the current panel doesn't define a custom updatePickerButton,
+      // revert the button to its default state
+      button.description = L10N.getStr("pickButton.tooltip");
+      button.className = null;
+      button.disabled = null;
+    }
+
+    this.component.setToolboxButtons(this.toolbarButtons);
+  },
+
+  /**
    * Ensure the visibility of each toolbox button matches the preference value.
    */
   _commandIsVisible: function (button) {
     const {
       isTargetSupported,
       visibilityswitch
     } = button;
 
@@ -2578,17 +2619,23 @@ Toolbox.prototype = {
       if (!this._inspector) {
         return;
       }
 
       // Ensure that the inspector isn't still being initiated, otherwise race conditions
       // in the initialization process can throw errors.
       yield this._initInspector;
 
-      yield this.highlighterUtils.stopPicker();
+      let currentPanel = this.getCurrentPanel();
+      if (currentPanel.stopPicker) {
+        yield currentPanel.stopPicker();
+      } else {
+        yield this.highlighterUtils.stopPicker();
+      }
+
       yield this._inspector.destroy();
       if (this._highlighter) {
         // Note that if the toolbox is closed, this will work fine, but will fail
         // in case the browser is closed and will trigger a noSuchActor message.
         // We ignore the promise that |_hideBoxModel| returns, since we should still
         // proceed with the rest of destruction if it fails.
         // FF42+ now does the cleanup from the actor.
         if (!this.highlighter.traits.autoHideOnDestroy) {
@@ -2637,16 +2684,17 @@ Toolbox.prototype = {
 
     this.emit("destroy");
 
     this._target.off("inspect-object", this._onInspectObject);
     this._target.off("will-navigate", this._onWillNavigate);
     this._target.off("navigate", this._refreshHostTitle);
     this._target.off("frame-update", this._updateFrames);
     this.off("select", this._refreshHostTitle);
+    this.off("select", this._updatePickerButton);
     this.off("host-changed", this._refreshHostTitle);
     this.off("ready", this._showDevEditionPromo);
 
     gDevTools.off("tool-registered", this._toolRegistered);
     gDevTools.off("tool-unregistered", this._toolUnregistered);
 
     Services.prefs.removeObserver("devtools.cache.disabled", this._applyCacheSettings);
     Services.prefs.removeObserver("devtools.serviceWorkers.testing.enabled",
--- a/devtools/client/inspector/markup/views/element-editor.js
+++ b/devtools/client/inspector/markup/views/element-editor.js
@@ -38,17 +38,17 @@ const HTML_VOID_ELEMENTS = [
 // Contains only valid computed display property types of the node to display in the
 // element markup and their respective title tooltip text.
 const DISPLAY_TYPES = {
   "flex": INSPECTOR_L10N.getStr("markupView.display.flex.tooltiptext"),
   "inline-flex": INSPECTOR_L10N.getStr("markupView.display.flex.tooltiptext"),
   "grid": INSPECTOR_L10N.getStr("markupView.display.grid.tooltiptext"),
   "inline-grid": INSPECTOR_L10N.getStr("markupView.display.inlineGrid.tooltiptext"),
   "flow-root": INSPECTOR_L10N.getStr("markupView.display.flowRoot.tooltiptext"),
-  "contents": INSPECTOR_L10N.getStr("markupView.display.contents.tooltiptext"),
+  "contents": INSPECTOR_L10N.getStr("markupView.display.contents.tooltiptext2"),
 };
 
 /**
  * Creates an editor for an Element node.
  *
  * @param  {MarkupContainer} container
  *         The container owning this editor.
  * @param  {Element} node
--- a/devtools/client/locales/en-US/inspector.properties
+++ b/devtools/client/locales/en-US/inspector.properties
@@ -58,20 +58,20 @@ markupView.display.grid.tooltiptext=This
 # the markup view.
 markupView.display.inlineGrid.tooltiptext=This element behaves like an inline element and lays out its content according to the grid model.
 
 # LOCALIZATION NOTE (markupView.display.flowRoot.tooltiptext)
 # Used in a tooltip that appears when the user hovers over the display type button in
 # the markup view.
 markupView.display.flowRoot.tooltiptext=This element generates a block element box that establishes a new block formatting context.
 
-# LOCALIZATION NOTE (markupView.display.contents.tooltiptext)
+# LOCALIZATION NOTE (markupView.display.contents.tooltiptext2)
 # Used in a tooltip that appears when the user hovers over the display type button in
 # the markup view.
-markupView.display.contents.tooltiptext=This element doesn’t produce a specific box by themselves, but renders its contents.
+markupView.display.contents.tooltiptext2=This element doesn’t produce a specific box by itself, but renders its contents.
 
 # LOCALIZATION NOTE (markupView.event.tooltiptext)
 # Used in a tooltip that appears when the user hovers over 'ev' button in
 # the markup view.
 markupView.event.tooltiptext=Event listener
 
 #LOCALIZATION NOTE: Used in the image preview tooltip when the image could not be loaded
 previewTooltip.image.brokenImage=Could not load the image
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -238,16 +238,17 @@
 #include "nsITabChild.h"
 #include "mozilla/dom/MediaQueryList.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/NavigatorBinding.h"
 #include "mozilla/dom/ImageBitmap.h"
 #include "mozilla/dom/ImageBitmapBinding.h"
 #include "mozilla/dom/ServiceWorker.h"
 #include "mozilla/dom/ServiceWorkerRegistration.h"
+#include "mozilla/dom/ServiceWorkerRegistrationDescriptor.h"
 #include "mozilla/dom/U2F.h"
 #include "mozilla/dom/WebIDLGlobalNameHash.h"
 #include "mozilla/dom/Worklet.h"
 #ifdef HAVE_SIDEBAR
 #include "mozilla/dom/ExternalBinding.h"
 #endif
 
 #ifdef MOZ_WEBSPEECH
@@ -5168,24 +5169,25 @@ nsGlobalWindowInner::GetCaches(ErrorResu
                                                      forceTrustedOrigin, aRv);
   }
 
   RefPtr<CacheStorage> ref = mCacheStorage;
   return ref.forget();
 }
 
 already_AddRefed<ServiceWorkerRegistration>
-nsPIDOMWindowInner::GetServiceWorkerRegistration(const nsAString& aScope)
-{
+nsPIDOMWindowInner::GetServiceWorkerRegistration(const ServiceWorkerRegistrationDescriptor& aDescriptor)
+{
+  NS_ConvertUTF8toUTF16 scope(aDescriptor.Scope());
   RefPtr<ServiceWorkerRegistration> registration;
-  if (!mServiceWorkerRegistrationTable.Get(aScope,
+  if (!mServiceWorkerRegistrationTable.Get(scope,
                                            getter_AddRefs(registration))) {
     registration =
-      ServiceWorkerRegistration::CreateForMainThread(this, aScope);
-    mServiceWorkerRegistrationTable.Put(aScope, registration);
+      ServiceWorkerRegistration::CreateForMainThread(this, aDescriptor);
+    mServiceWorkerRegistrationTable.Put(scope, registration);
   }
   return registration.forget();
 }
 
 void
 nsPIDOMWindowInner::InvalidateServiceWorkerRegistration(const nsAString& aScope)
 {
   mServiceWorkerRegistrationTable.Remove(aScope);
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -51,16 +51,17 @@ class ClientState;
 class DocGroup;
 class TabGroup;
 class Element;
 class Navigator;
 class Performance;
 class ServiceWorker;
 class ServiceWorkerDescriptor;
 class ServiceWorkerRegistration;
+class ServiceWorkerRegistrationDescriptor;
 class Timeout;
 class TimeoutManager;
 class CustomElementRegistry;
 enum class CallerType : uint32_t;
 } // namespace dom
 } // namespace mozilla
 
 // Popup control state enum. The values in this enum must go from most
@@ -182,17 +183,17 @@ public:
   void RemoveAudioContext(mozilla::dom::AudioContext* aAudioContext);
   void MuteAudioContexts();
   void UnmuteAudioContexts();
 
   bool GetAudioCaptured() const;
   nsresult SetAudioCapture(bool aCapture);
 
   already_AddRefed<mozilla::dom::ServiceWorkerRegistration>
-    GetServiceWorkerRegistration(const nsAString& aScope);
+    GetServiceWorkerRegistration(const mozilla::dom::ServiceWorkerRegistrationDescriptor& aDescriptor);
   void InvalidateServiceWorkerRegistration(const nsAString& aScope);
 
   mozilla::dom::Performance* GetPerformance();
 
   bool HasMutationListeners(uint32_t aMutationEventType) const
   {
     if (!mOuterWindow) {
       NS_ERROR("HasMutationListeners() called on orphan inner window!");
--- a/dom/fetch/FetchDriver.cpp
+++ b/dom/fetch/FetchDriver.cpp
@@ -846,18 +846,19 @@ FetchDriver::OnStartRequest(nsIRequest* 
 
     response->Headers()->FillResponseHeaders(httpChannel);
 
     // If Content-Encoding or Transfer-Encoding headers are set, then the actual
     // Content-Length (which refer to the decoded data) is obscured behind the encodings.
     ErrorResult result;
     if (response->Headers()->Has(NS_LITERAL_CSTRING("content-encoding"), result) ||
         response->Headers()->Has(NS_LITERAL_CSTRING("transfer-encoding"), result)) {
-      NS_WARNING("Cannot know response Content-Length due to presence of Content-Encoding "
-                 "or Transfer-Encoding headers.");
+      // We cannot trust the content-length when content-encoding or
+      // transfer-encoding are set.  There are many servers which just
+      // get this wrong.
       contentLength = InternalResponse::UNKNOWN_BODY_SIZE;
     }
     MOZ_ASSERT(!result.Failed());
   } else {
     response = new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
 
     ErrorResult result;
     nsAutoCString contentType;
--- a/dom/interfaces/base/nsIServiceWorkerManager.idl
+++ b/dom/interfaces/base/nsIServiceWorkerManager.idl
@@ -152,26 +152,16 @@ interface nsIServiceWorkerManager : nsIS
 
   nsIServiceWorkerRegistrationInfo getRegistrationByPrincipal(in nsIPrincipal aPrincipal,
                                                               in DOMString aScope);
 
   [notxpcom, nostdcall] bool StartControlling(in const_ClientInfoRef aClientInfo,
                                               in const_ServiceWorkerDescriptorRef aServiceWorker);
 
   /*
-   * Returns a ServiceWorker.
-   * window is the window of the caller. scope is the registration's scope and must be
-   * a valid entry that window is allowed to load, otherwise this will return nullptr.
-   * These are only meant to be called from ServiceWorkerRegistration instances.
-   */
-  [noscript] nsISupports GetInstalling(in nsPIDOMWindowInner aWindow, in DOMString aScope);
-  [noscript] nsISupports GetWaiting(in nsPIDOMWindowInner aWindow, in DOMString aScope);
-  [noscript] nsISupports GetActive(in nsPIDOMWindowInner aWindow, in DOMString aScope);
-
-  /*
    * Clears ServiceWorker registrations from memory and disk for the specified
    * host.
    * - All ServiceWorker instances change their state to redundant.
    * - Existing ServiceWorker instances handling fetches will keep running.
    * - All documents will immediately stop being controlled.
    * - Unregister jobs will be queued for all registrations.
    *   This eventually results in the registration being deleted from disk too.
    */
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -39,16 +39,17 @@
 #include "mozilla/dom/PLoginReputationChild.h"
 #include "mozilla/dom/ProcessGlobal.h"
 #include "mozilla/dom/PushNotifier.h"
 #include "mozilla/dom/ServiceWorkerManager.h"
 #include "mozilla/dom/TabGroup.h"
 #include "mozilla/dom/nsIContentChild.h"
 #include "mozilla/dom/URLClassifierChild.h"
 #include "mozilla/gfx/gfxVars.h"
+#include "mozilla/gfx/Logging.h"
 #include "mozilla/psm/PSMContentListener.h"
 #include "mozilla/hal_sandbox/PHalChild.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/FileDescriptorSetChild.h"
 #include "mozilla/ipc/FileDescriptorUtils.h"
 #include "mozilla/ipc/GeckoChildProcessHost.h"
 #include "mozilla/ipc/ProcessChild.h"
 #include "mozilla/ipc/PChildToParentStreamChild.h"
@@ -1316,35 +1317,57 @@ ContentChild::RecvGMPsChanged(nsTArray<G
 mozilla::ipc::IPCResult
 ContentChild::RecvInitProcessHangMonitor(Endpoint<PProcessHangMonitorChild>&& aHangMonitor)
 {
   CreateHangMonitorChild(Move(aHangMonitor));
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
+ContentChild::GetResultForRenderingInitFailure(base::ProcessId aOtherPid)
+{
+  if (aOtherPid == base::GetCurrentProcId() || aOtherPid == OtherPid()) {
+    // If we are talking to ourselves, or the UI process, then that is a fatal
+    // protocol error.
+    return IPC_FAIL_NO_REASON(this);
+  }
+
+  // If we are talking to the GPU process, then we should recover from this on
+  // the next ContentChild::RecvReinitRendering call.
+  gfxCriticalNote << "Could not initialize rendering with GPU process";
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
 ContentChild::RecvInitRendering(Endpoint<PCompositorManagerChild>&& aCompositor,
                                 Endpoint<PImageBridgeChild>&& aImageBridge,
                                 Endpoint<PVRManagerChild>&& aVRBridge,
                                 Endpoint<PVideoDecoderManagerChild>&& aVideoManager,
                                 nsTArray<uint32_t>&& namespaces)
 {
   MOZ_ASSERT(namespaces.Length() == 3);
 
+  // Note that for all of the methods below, if it can fail, it should only
+  // return false if the failure is an IPDL error. In such situations,
+  // ContentChild can reason about whether or not to wait for
+  // RecvReinitRendering (because we surmised the GPU process crashed), or if it
+  // should crash itself (because we are actually talking to the UI process). If
+  // there are localized failures (e.g. failed to spawn a thread), then it
+  // should MOZ_RELEASE_ASSERT or MOZ_CRASH as necessary instead.
   if (!CompositorManagerChild::Init(Move(aCompositor), namespaces[0])) {
-    return IPC_FAIL_NO_REASON(this);
+    return GetResultForRenderingInitFailure(aCompositor.OtherPid());
   }
   if (!CompositorManagerChild::CreateContentCompositorBridge(namespaces[1])) {
-    return IPC_FAIL_NO_REASON(this);
+    return GetResultForRenderingInitFailure(aCompositor.OtherPid());
   }
   if (!ImageBridgeChild::InitForContent(Move(aImageBridge), namespaces[2])) {
-    return IPC_FAIL_NO_REASON(this);
+    return GetResultForRenderingInitFailure(aImageBridge.OtherPid());
   }
   if (!gfx::VRManagerChild::InitForContent(Move(aVRBridge))) {
-    return IPC_FAIL_NO_REASON(this);
+    return GetResultForRenderingInitFailure(aVRBridge.OtherPid());
   }
   VideoDecoderManagerChild::InitForContent(Move(aVideoManager));
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ContentChild::RecvReinitRendering(Endpoint<PCompositorManagerChild>&& aCompositor,
                                   Endpoint<PImageBridgeChild>&& aImageBridge,
@@ -1359,26 +1382,26 @@ ContentChild::RecvReinitRendering(Endpoi
   for (const auto& tabChild : tabs) {
     if (tabChild->LayersId()) {
       tabChild->InvalidateLayers();
     }
   }
 
   // Re-establish singleton bridges to the compositor.
   if (!CompositorManagerChild::Init(Move(aCompositor), namespaces[0])) {
-    return IPC_FAIL_NO_REASON(this);
+    return GetResultForRenderingInitFailure(aCompositor.OtherPid());
   }
   if (!CompositorManagerChild::CreateContentCompositorBridge(namespaces[1])) {
-    return IPC_FAIL_NO_REASON(this);
+    return GetResultForRenderingInitFailure(aCompositor.OtherPid());
   }
   if (!ImageBridgeChild::ReinitForContent(Move(aImageBridge), namespaces[2])) {
-    return IPC_FAIL_NO_REASON(this);
+    return GetResultForRenderingInitFailure(aImageBridge.OtherPid());
   }
   if (!gfx::VRManagerChild::ReinitForContent(Move(aVRBridge))) {
-    return IPC_FAIL_NO_REASON(this);
+    return GetResultForRenderingInitFailure(aVRBridge.OtherPid());
   }
   gfxPlatform::GetPlatform()->CompositorUpdated();
 
   // Establish new PLayerTransactions.
   for (const auto& tabChild : tabs) {
     if (tabChild->LayersId()) {
       tabChild->ReinitRendering();
     }
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -724,16 +724,19 @@ public:
 #endif
 
 private:
   static void ForceKillTimerCallback(nsITimer* aTimer, void* aClosure);
   void StartForceKillTimer();
 
   void ShutdownInternal();
 
+  mozilla::ipc::IPCResult
+  GetResultForRenderingInitFailure(base::ProcessId aOtherPid);
+
   virtual void ActorDestroy(ActorDestroyReason why) override;
 
   virtual void ProcessingError(Result aCode, const char* aReason) override;
 
   virtual already_AddRefed<nsIEventTarget>
   GetConstructedEventTarget(const Message& aMsg) override;
 
   virtual already_AddRefed<nsIEventTarget>
--- a/dom/ipc/ContentPrefs.cpp
+++ b/dom/ipc/ContentPrefs.cpp
@@ -309,17 +309,16 @@ const char* mozilla::dom::ContentPrefs::
   "security.sandbox.content.tempDirSuffix",
   "security.sandbox.logging.enabled",
   "security.sandbox.mac.track.violations",
   "security.sandbox.windows.log.stackTraceDepth",
   "svg.disabled",
   "svg.display-lists.hit-testing.enabled",
   "svg.display-lists.painting.enabled",
   "svg.new-getBBox.enabled",
-  "svg.paint-order.enabled",
   "svg.path-caching.enabled",
   "svg.transform-box.enabled",
   "toolkit.asyncshutdown.crash_timeout",
   "toolkit.asyncshutdown.log",
   "toolkit.osfile.log",
   "toolkit.osfile.log.redirect",
   "toolkit.telemetry.enabled",
   "toolkit.telemetry.idleTimeout",
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1115,18 +1115,20 @@ TabChild::ActorDestroy(ActorDestroyReaso
       // The messageManager relays messages via the TabChild which
       // no longer exists.
       static_cast<nsFrameMessageManager*>
         (mTabChildGlobal->mMessageManager.get())->Disconnect();
       mTabChildGlobal->mMessageManager = nullptr;
     }
   }
 
-  CompositorBridgeChild* compositorChild = static_cast<CompositorBridgeChild*>(CompositorBridgeChild::Get());
-  compositorChild->CancelNotifyAfterRemotePaint(this);
+  CompositorBridgeChild* compositorChild = CompositorBridgeChild::Get();
+  if (compositorChild) {
+    compositorChild->CancelNotifyAfterRemotePaint(this);
+  }
 
   if (GetTabId() != 0) {
     NestedTabChildMap().erase(GetTabId());
   }
 }
 
 TabChild::~TabChild()
 {
--- a/dom/script/ScriptLoader.cpp
+++ b/dom/script/ScriptLoader.cpp
@@ -3193,16 +3193,21 @@ ScriptLoader::PreloadURI(nsIURI* aURI,
     }
 
     // TODO: Preload module scripts.
     if (aType.LowerCaseEqualsASCII("module")) {
       return;
     }
   }
 
+  if (!aType.IsEmpty() && !nsContentUtils::IsJavascriptMIMEType(aType)) {
+    // Unknown type.  Don't load it.
+    return;
+  }
+
   SRIMetadata sriMetadata;
   GetSRIMetadata(aIntegrity, &sriMetadata);
 
   RefPtr<ScriptLoadRequest> request =
     CreateLoadRequest(ScriptKind::eClassic, aURI, nullptr,
                       Element::StringToCORSMode(aCrossOrigin), sriMetadata,
                       aReferrerPolicy);
   request->mTriggeringPrincipal = mDocument->NodePrincipal();
deleted file mode 100644
--- a/dom/serviceworkers/ServiceWorkerCommon.h
+++ /dev/null
@@ -1,25 +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/. */
-
-#ifndef mozilla_dom_ServiceWorkerCommon_h
-#define mozilla_dom_ServiceWorkerCommon_h
-
-namespace mozilla {
-namespace dom {
-
-// Use multiples of 2 since they can be bitwise ORed when calling
-// InvalidateServiceWorkerRegistrationWorker.
-enum class WhichServiceWorker {
-  INSTALLING_WORKER = 1,
-  WAITING_WORKER    = 2,
-  ACTIVE_WORKER     = 4,
-};
-MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(WhichServiceWorker)
-
-} // namespace dom
-} // namespace mozilla
-
-#endif // mozilla_dom_ServiceWorkerCommon_h
--- a/dom/serviceworkers/ServiceWorkerDescriptor.cpp
+++ b/dom/serviceworkers/ServiceWorkerDescriptor.cpp
@@ -47,16 +47,19 @@ ServiceWorkerDescriptor::ServiceWorkerDe
 ServiceWorkerDescriptor::ServiceWorkerDescriptor(const ServiceWorkerDescriptor& aRight)
 {
   operator=(aRight);
 }
 
 ServiceWorkerDescriptor&
 ServiceWorkerDescriptor::operator=(const ServiceWorkerDescriptor& aRight)
 {
+  if (this == &aRight) {
+    return *this;
+  }
   mData.reset();
   mData = MakeUnique<IPCServiceWorkerDescriptor>(*aRight.mData);
   return *this;
 }
 
 ServiceWorkerDescriptor::ServiceWorkerDescriptor(ServiceWorkerDescriptor&& aRight)
   : mData(Move(aRight.mData))
 {
--- a/dom/serviceworkers/ServiceWorkerDescriptor.h
+++ b/dom/serviceworkers/ServiceWorkerDescriptor.h
@@ -1,16 +1,17 @@
 /* -*- 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 _mozilla_dom_ServiceWorkerDescriptor_h
 #define _mozilla_dom_ServiceWorkerDescriptor_h
 
+#include "mozilla/UniquePtr.h"
 #include "nsString.h"
 
 class nsIPrincipal;
 
 namespace mozilla {
 
 namespace ipc {
 class PrincipalInfo;
--- a/dom/serviceworkers/ServiceWorkerManager.cpp
+++ b/dom/serviceworkers/ServiceWorkerManager.cpp
@@ -75,16 +75,17 @@
 #include "ServiceWorkerContainer.h"
 #include "ServiceWorkerInfo.h"
 #include "ServiceWorkerJobQueue.h"
 #include "ServiceWorkerManagerChild.h"
 #include "ServiceWorkerPrivate.h"
 #include "ServiceWorkerRegisterJob.h"
 #include "ServiceWorkerRegistrar.h"
 #include "ServiceWorkerRegistration.h"
+#include "ServiceWorkerRegistrationListener.h"
 #include "ServiceWorkerScriptCache.h"
 #include "ServiceWorkerEvents.h"
 #include "ServiceWorkerUnregisterJob.h"
 #include "ServiceWorkerUpdateJob.h"
 #include "ServiceWorkerUpdaterChild.h"
 
 #ifdef PostMessage
 #undef PostMessage
@@ -460,18 +461,25 @@ class ServiceWorkerResolveWindowPromiseO
     }
 
     MOZ_ASSERT(aJob->GetType() == ServiceWorkerJob::Type::Register);
     RefPtr<ServiceWorkerRegisterJob> registerJob =
       static_cast<ServiceWorkerRegisterJob*>(aJob);
     RefPtr<ServiceWorkerRegistrationInfo> reg = registerJob->GetRegistration();
 
     RefPtr<ServiceWorkerRegistration> swr =
-      window->GetServiceWorkerRegistration(NS_ConvertUTF8toUTF16(reg->Scope()));
-    promise->MaybeResolve(swr);
+      window->GetServiceWorkerRegistration(reg->Descriptor());
+
+    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
+      "ServiceWorkerResolveWindowPromiseOnRegisterCallback::JobFinished",
+      [promise = Move(promise), swr = Move(swr)] () {
+        promise->MaybeResolve(swr);
+      });
+    MOZ_ALWAYS_SUCCEEDS(
+      window->EventTargetFor(TaskCategory::Other)->Dispatch(r.forget()));
   }
 
 public:
   ServiceWorkerResolveWindowPromiseOnRegisterCallback(nsPIDOMWindowInner* aWindow,
                                                       Promise* aPromise)
     : mPromise(aWindow, aPromise)
   {}
 
@@ -1018,17 +1026,17 @@ public:
 
       rv = principal->CheckMayLoad(scopeURI, true /* report */,
                                    false /* allowIfInheritsPrincipal */);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         continue;
       }
 
       RefPtr<ServiceWorkerRegistration> swr =
-        mWindow->GetServiceWorkerRegistration(scope);
+        mWindow->GetServiceWorkerRegistration(info->Descriptor());
 
       array.AppendElement(swr);
     }
 
     mPromise->MaybeResolve(array);
     return NS_OK;
   }
 };
@@ -1141,19 +1149,18 @@ public:
     RefPtr<ServiceWorkerRegistrationInfo> registration =
       swm->GetServiceWorkerRegistrationInfo(principal, uri);
 
     if (!registration) {
       mPromise->MaybeResolveWithUndefined();
       return NS_OK;
     }
 
-    NS_ConvertUTF8toUTF16 scope(registration->Scope());
     RefPtr<ServiceWorkerRegistration> swr =
-      mWindow->GetServiceWorkerRegistration(scope);
+      mWindow->GetServiceWorkerRegistration(registration->Descriptor());
     mPromise->MaybeResolve(swr);
 
     return NS_OK;
   }
 };
 
 // If we return an error code here, the ServiceWorkerContainer will
 // automatically reject the Promise.
@@ -1458,19 +1465,18 @@ ServiceWorkerManager::CheckReadyPromise(
 
   nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
   MOZ_ASSERT(principal);
 
   RefPtr<ServiceWorkerRegistrationInfo> registration =
     GetServiceWorkerRegistrationInfo(principal, aURI);
 
   if (registration && registration->GetActive()) {
-    NS_ConvertUTF8toUTF16 scope(registration->Scope());
     RefPtr<ServiceWorkerRegistration> swr =
-      aWindow->GetServiceWorkerRegistration(scope);
+      aWindow->GetServiceWorkerRegistration(registration->Descriptor());
     aPromise->MaybeResolve(swr);
     return true;
   }
 
   return false;
 }
 
 ServiceWorkerInfo*
@@ -2303,82 +2309,16 @@ ServiceWorkerManager::FireUpdateFoundOnS
 
     NS_ConvertUTF16toUTF8 utf8Scope(regScope);
     if (utf8Scope.Equals(aRegistration->Scope())) {
       target->UpdateFound();
     }
   }
 }
 
-/*
- * This is used for installing, waiting and active.
- */
-nsresult
-ServiceWorkerManager::GetServiceWorkerForScope(nsPIDOMWindowInner* aWindow,
-                                               const nsAString& aScope,
-                                               WhichServiceWorker aWhichWorker,
-                                               nsISupports** aServiceWorker)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  if (NS_WARN_IF(!aWindow)) {
-    return NS_ERROR_DOM_INVALID_STATE_ERR;
-  }
-
-  nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
-  MOZ_ASSERT(doc);
-
-  ///////////////////////////////////////////
-  // Security check
-  nsAutoCString scope = NS_ConvertUTF16toUTF8(aScope);
-  nsCOMPtr<nsIURI> scopeURI;
-  // We pass nullptr as the base URI since scopes obtained from
-  // ServiceWorkerRegistrations MUST be fully qualified URIs.
-  nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), scope, nullptr, nullptr);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return NS_ERROR_DOM_SECURITY_ERR;
-  }
-
-  nsCOMPtr<nsIPrincipal> documentPrincipal = doc->NodePrincipal();
-  rv = documentPrincipal->CheckMayLoad(scopeURI, true /* report */,
-                                       false /* allowIfInheritsPrinciple */);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return NS_ERROR_DOM_SECURITY_ERR;
-  }
-  ////////////////////////////////////////////
-
-  RefPtr<ServiceWorkerRegistrationInfo> registration =
-    GetRegistration(documentPrincipal, scope);
-  if (NS_WARN_IF(!registration)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  RefPtr<ServiceWorkerInfo> info;
-  if (aWhichWorker == WhichServiceWorker::INSTALLING_WORKER) {
-    info = registration->GetInstalling();
-  } else if (aWhichWorker == WhichServiceWorker::WAITING_WORKER) {
-    info = registration->GetWaiting();
-  } else if (aWhichWorker == WhichServiceWorker::ACTIVE_WORKER) {
-    info = registration->GetActive();
-  } else {
-    MOZ_CRASH("Invalid worker type");
-  }
-
-  if (NS_WARN_IF(!info)) {
-    return NS_ERROR_DOM_NOT_FOUND_ERR;
-  }
-
-  RefPtr<ServiceWorker> serviceWorker =
-    aWindow->GetOrCreateServiceWorker(info->Descriptor());
-
-  serviceWorker->SetState(info->State());
-  serviceWorker.forget(aServiceWorker);
-  return NS_OK;
-}
-
 namespace {
 
 class ContinueDispatchFetchEventRunnable : public Runnable
 {
   RefPtr<ServiceWorkerPrivate> mServiceWorkerPrivate;
   nsCOMPtr<nsIInterceptedChannel> mChannel;
   nsCOMPtr<nsILoadGroup> mLoadGroup;
   bool mIsReload;
@@ -2621,82 +2561,25 @@ ServiceWorkerManager::GetClientRegistrat
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   RefPtr<ServiceWorkerRegistrationInfo> ref = data->mRegistrationInfo;
   ref.forget(aRegistrationInfo);
   return NS_OK;
 }
 
-NS_IMETHODIMP
-ServiceWorkerManager::GetInstalling(nsPIDOMWindowInner* aWindow,
-                                    const nsAString& aScope,
-                                    nsISupports** aServiceWorker)
-{
-  return GetServiceWorkerForScope(aWindow, aScope,
-                                  WhichServiceWorker::INSTALLING_WORKER,
-                                  aServiceWorker);
-}
-
-NS_IMETHODIMP
-ServiceWorkerManager::GetWaiting(nsPIDOMWindowInner* aWindow,
-                                 const nsAString& aScope,
-                                 nsISupports** aServiceWorker)
-{
-  return GetServiceWorkerForScope(aWindow, aScope,
-                                  WhichServiceWorker::WAITING_WORKER,
-                                  aServiceWorker);
-}
-
-NS_IMETHODIMP
-ServiceWorkerManager::GetActive(nsPIDOMWindowInner* aWindow,
-                                const nsAString& aScope,
-                                nsISupports** aServiceWorker)
-{
-  return GetServiceWorkerForScope(aWindow, aScope,
-                                  WhichServiceWorker::ACTIVE_WORKER,
-                                  aServiceWorker);
-}
-
 void
-ServiceWorkerManager::TransitionServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration,
-                                                                WhichServiceWorker aWhichOne)
+ServiceWorkerManager::UpdateRegistrationListeners(ServiceWorkerRegistrationInfo* aReg)
 {
   MOZ_ASSERT(NS_IsMainThread());
   nsTObserverArray<ServiceWorkerRegistrationListener*>::ForwardIterator it(mServiceWorkerRegistrationListeners);
   while (it.HasMore()) {
     RefPtr<ServiceWorkerRegistrationListener> target = it.GetNext();
-    nsAutoString regScope;
-    target->GetScope(regScope);
-    MOZ_ASSERT(!regScope.IsEmpty());
-
-    NS_ConvertUTF16toUTF8 utf8Scope(regScope);
-
-    if (utf8Scope.Equals(aRegistration->Scope())) {
-      target->TransitionWorker(aWhichOne);
-    }
-  }
-}
-
-void
-ServiceWorkerManager::InvalidateServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration,
-                                                                WhichServiceWorker aWhichOnes)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  nsTObserverArray<ServiceWorkerRegistrationListener*>::ForwardIterator it(mServiceWorkerRegistrationListeners);
-  while (it.HasMore()) {
-    RefPtr<ServiceWorkerRegistrationListener> target = it.GetNext();
-    nsAutoString regScope;
-    target->GetScope(regScope);
-    MOZ_ASSERT(!regScope.IsEmpty());
-
-    NS_ConvertUTF16toUTF8 utf8Scope(regScope);
-
-    if (utf8Scope.Equals(aRegistration->Scope())) {
-      target->InvalidateWorkers(aWhichOnes);
+    if (target->MatchesDescriptor(aReg->Descriptor())) {
+      target->UpdateState(aReg->Descriptor());
     }
   }
 }
 
 void
 ServiceWorkerManager::NotifyServiceWorkerRegistrationRemoved(ServiceWorkerRegistrationInfo* aRegistration)
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/dom/serviceworkers/ServiceWorkerManager.h
+++ b/dom/serviceworkers/ServiceWorkerManager.h
@@ -18,17 +18,16 @@
 #include "mozilla/MozPromise.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/TypedEnumBits.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/WeakPtr.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/ClientHandle.h"
 #include "mozilla/dom/Promise.h"
-#include "mozilla/dom/ServiceWorkerCommon.h"
 #include "mozilla/dom/ServiceWorkerRegistrar.h"
 #include "mozilla/dom/ServiceWorkerRegistrarTypes.h"
 #include "mozilla/dom/ServiceWorkerRegistrationInfo.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "nsClassHashtable.h"
 #include "nsDataHashtable.h"
 #include "nsRefPtrHashtable.h"
 #include "nsTArrayForwardDeclare.h"
@@ -347,35 +346,25 @@ private:
 
   nsresult
   Update(ServiceWorkerRegistrationInfo* aRegistration);
 
   nsresult
   GetClientRegistration(const ClientInfo& aClientInfo,
                         ServiceWorkerRegistrationInfo** aRegistrationInfo);
 
-  nsresult
-  GetServiceWorkerForScope(nsPIDOMWindowInner* aWindow,
-                           const nsAString& aScope,
-                           WhichServiceWorker aWhichWorker,
-                           nsISupports** aServiceWorker);
-
   ServiceWorkerInfo*
   GetActiveWorkerInfoForScope(const OriginAttributes& aOriginAttributes,
                               const nsACString& aScope);
 
   ServiceWorkerInfo*
   GetActiveWorkerInfoForDocument(nsIDocument* aDocument);
 
   void
-  TransitionServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration,
-                                            WhichServiceWorker aWhichOne);
-  void
-  InvalidateServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration,
-                                            WhichServiceWorker aWhichOnes);
+  UpdateRegistrationListeners(ServiceWorkerRegistrationInfo* aReg);
 
   void
   NotifyServiceWorkerRegistrationRemoved(ServiceWorkerRegistrationInfo* aRegistration);
 
   void
   StopControllingRegistration(ServiceWorkerRegistrationInfo* aRegistration);
 
   already_AddRefed<ServiceWorkerRegistrationInfo>
--- a/dom/serviceworkers/ServiceWorkerPrivate.cpp
+++ b/dom/serviceworkers/ServiceWorkerPrivate.cpp
@@ -1828,16 +1828,23 @@ ServiceWorkerPrivate::SpawnWorkerIfNeede
   // spin up a new worker.
   MOZ_ASSERT(mSupportsArray.IsEmpty());
 
   if (NS_WARN_IF(!mInfo)) {
     NS_WARNING("Trying to wake up a dead service worker.");
     return NS_ERROR_FAILURE;
   }
 
+  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+  NS_ENSURE_TRUE(swm, NS_ERROR_FAILURE);
+
+  RefPtr<ServiceWorkerRegistrationInfo> reg =
+    swm->GetRegistration(mInfo->Principal(), mInfo->Scope());
+  NS_ENSURE_TRUE(reg, NS_ERROR_FAILURE);
+
   // TODO(catalinb): Bug 1192138 - Add telemetry for service worker wake-ups.
 
   // Ensure that the IndexedDatabaseManager is initialized
   Unused << NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate());
 
   WorkerLoadInfo info;
   nsresult rv = NS_NewURI(getter_AddRefs(info.mBaseURI), mInfo->ScriptSpec(),
                           nullptr, nullptr);
@@ -1845,21 +1852,18 @@ ServiceWorkerPrivate::SpawnWorkerIfNeede
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   info.mResolvedScriptURI = info.mBaseURI;
   MOZ_ASSERT(!mInfo->CacheName().IsEmpty());
   info.mServiceWorkerCacheName = mInfo->CacheName();
 
-  PrincipalInfo principalInfo;
-  rv = PrincipalToPrincipalInfo(mInfo->Principal(), &principalInfo);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   info.mServiceWorkerDescriptor.emplace(mInfo->Descriptor());
+  info.mServiceWorkerRegistrationDescriptor.emplace(reg->Descriptor());
 
   info.mLoadGroup = aLoadGroup;
   info.mLoadFailedAsyncRunnable = aLoadFailedRunnable;
 
   // If we are loading a script for a ServiceWorker then we must not
   // try to intercept it.  If the interception matches the current
   // ServiceWorker's scope then we could deadlock the load.
   info.mLoadFlags = mInfo->GetImportsLoadFlags() |
--- a/dom/serviceworkers/ServiceWorkerRegistration.cpp
+++ b/dom/serviceworkers/ServiceWorkerRegistration.cpp
@@ -1,1380 +1,242 @@
 /* -*- 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 "ServiceWorkerRegistration.h"
 
-#include "ipc/ErrorIPCUtils.h"
-#include "mozilla/dom/DOMPrefs.h"
-#include "mozilla/dom/Notification.h"
 #include "mozilla/dom/Promise.h"
-#include "mozilla/dom/PromiseWindowProxy.h"
-#include "mozilla/dom/PromiseWorkerProxy.h"
-#include "mozilla/dom/PushManagerBinding.h"
 #include "mozilla/dom/PushManager.h"
+#include "mozilla/dom/ServiceWorker.h"
 #include "mozilla/dom/ServiceWorkerRegistrationBinding.h"
 #include "mozilla/dom/WorkerPrivate.h"
-#include "mozilla/dom/WorkerCommon.h"
-#include "mozilla/dom/WorkerScope.h"
-#include "mozilla/Services.h"
-#include "mozilla/Unused.h"
 #include "nsCycleCollectionParticipant.h"
-#include "nsNetUtil.h"
-#include "nsServiceManagerUtils.h"
-#include "ServiceWorker.h"
-#include "ServiceWorkerManager.h"
-
-#include "nsIDocument.h"
-#include "nsIServiceWorkerManager.h"
 #include "nsISupportsPrimitives.h"
 #include "nsPIDOMWindow.h"
-#include "nsContentUtils.h"
+#include "ServiceWorkerRegistrationImpl.h"
 
 namespace mozilla {
 namespace dom {
 
-////////////////////////////////////////////////////
-// Main Thread implementation
-
-class ServiceWorkerRegistrationMainThread final : public ServiceWorkerRegistration,
-                                                  public ServiceWorkerRegistrationListener
-{
-  friend nsPIDOMWindowInner;
-public:
-  NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorkerRegistrationMainThread,
-                                           ServiceWorkerRegistration)
-
-  ServiceWorkerRegistrationMainThread(nsPIDOMWindowInner* aWindow,
-                                      const nsAString& aScope);
-
-  already_AddRefed<Promise>
-  Update(ErrorResult& aRv) override;
-
-  already_AddRefed<Promise>
-  Unregister(ErrorResult& aRv) override;
-
-  // Partial interface from Notification API.
-  already_AddRefed<Promise>
-  ShowNotification(JSContext* aCx,
-                   const nsAString& aTitle,
-                   const NotificationOptions& aOptions,
-                   ErrorResult& aRv) override;
-
-  already_AddRefed<Promise>
-  GetNotifications(const GetNotificationOptions& aOptions,
-                   ErrorResult& aRv) override;
-
-  already_AddRefed<ServiceWorker>
-  GetInstalling() override;
-
-  already_AddRefed<ServiceWorker>
-  GetWaiting() override;
-
-  already_AddRefed<ServiceWorker>
-  GetActive() override;
-
-  already_AddRefed<PushManager>
-  GetPushManager(JSContext* aCx, ErrorResult& aRv) override;
-
-  // DOMEventTargethelper
-  void DisconnectFromOwner() override
-  {
-    StopListeningForEvents();
-    ServiceWorkerRegistration::DisconnectFromOwner();
-  }
-
-  // ServiceWorkerRegistrationListener
-  void
-  UpdateFound() override;
-
-  void
-  InvalidateWorkers(WhichServiceWorker aWhichOnes) override;
-
-  void
-  TransitionWorker(WhichServiceWorker aWhichOne) override;
-
-  void
-  RegistrationRemoved() override;
-
-  void
-  GetScope(nsAString& aScope) const override
-  {
-    aScope = mScope;
-  }
-
-  ServiceWorkerUpdateViaCache
-  GetUpdateViaCache(ErrorResult& aRv) const override
-  {
-    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
-    MOZ_ASSERT(swm);
-
-    nsCOMPtr<nsPIDOMWindowInner> window = GetOwner();
-    MOZ_ASSERT(window);
-
-    nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
-    MOZ_ASSERT(doc);
-
-    nsCOMPtr<nsIServiceWorkerRegistrationInfo> registration;
-    nsresult rv = swm->GetRegistrationByPrincipal(doc->NodePrincipal(), mScope,
-                                                  getter_AddRefs(registration));
-
-    /*
-     *  xxx: We should probably store the `updateViaCache` value on the
-     *  ServiceWorkerRegistration object and update it when necessary.
-     *  We don't have a good way to reach all ServiceWorkerRegistration objects
-     *  from the ServiceWorkerRegistratinInfo right now, though.
-     *  This is a short term fix to avoid crashing.
-     */
-    if (NS_FAILED(rv) || !registration) {
-      aRv = NS_ERROR_DOM_INVALID_STATE_ERR;
-      return ServiceWorkerUpdateViaCache::None;
-    }
-
-    uint16_t updateViaCache;
-    rv = registration->GetUpdateViaCache(&updateViaCache);
-    MOZ_ASSERT(NS_SUCCEEDED(rv));
-
-    // Silence possible compiler warnings.
-    Unused << rv;
-
-    return static_cast<ServiceWorkerUpdateViaCache>(updateViaCache);
-  }
-
-private:
-  ~ServiceWorkerRegistrationMainThread();
-
-  already_AddRefed<ServiceWorker>
-  GetWorkerReference(WhichServiceWorker aWhichOne);
-
-  void
-  StartListeningForEvents();
-
-  void
-  StopListeningForEvents();
-
-  bool mListeningForEvents;
-
-  // The following properties are cached here to ensure JS equality is satisfied
-  // instead of acquiring a new worker instance from the ServiceWorkerManager
-  // for every access. A null value is considered a cache miss.
-  // These three may change to a new worker at any time.
-  RefPtr<ServiceWorker> mInstallingWorker;
-  RefPtr<ServiceWorker> mWaitingWorker;
-  RefPtr<ServiceWorker> mActiveWorker;
-
-  RefPtr<PushManager> mPushManager;
-};
-
-NS_IMPL_ADDREF_INHERITED(ServiceWorkerRegistrationMainThread, ServiceWorkerRegistration)
-NS_IMPL_RELEASE_INHERITED(ServiceWorkerRegistrationMainThread, ServiceWorkerRegistration)
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorkerRegistrationMainThread)
-NS_INTERFACE_MAP_END_INHERITING(ServiceWorkerRegistration)
-
-NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorkerRegistrationMainThread,
-                                   ServiceWorkerRegistration,
-                                   mPushManager,
-                                   mInstallingWorker, mWaitingWorker, mActiveWorker);
-
-ServiceWorkerRegistrationMainThread::ServiceWorkerRegistrationMainThread(nsPIDOMWindowInner* aWindow,
-                                                                         const nsAString& aScope)
-  : ServiceWorkerRegistration(aWindow, aScope)
-  , mListeningForEvents(false)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(aWindow);
-  StartListeningForEvents();
-}
-
-ServiceWorkerRegistrationMainThread::~ServiceWorkerRegistrationMainThread()
-{
-  StopListeningForEvents();
-  MOZ_ASSERT(!mListeningForEvents);
-}
-
-
-already_AddRefed<ServiceWorker>
-ServiceWorkerRegistrationMainThread::GetWorkerReference(WhichServiceWorker aWhichOne)
-{
-  nsCOMPtr<nsPIDOMWindowInner> window = GetOwner();
-  if (!window) {
-    return nullptr;
-  }
-
-  nsresult rv;
-  nsCOMPtr<nsIServiceWorkerManager> swm =
-    do_GetService(SERVICEWORKERMANAGER_CONTRACTID, &rv);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return nullptr;
-  }
-
-  nsCOMPtr<nsISupports> serviceWorker;
-  switch(aWhichOne) {
-    case WhichServiceWorker::INSTALLING_WORKER:
-      rv = swm->GetInstalling(window, mScope, getter_AddRefs(serviceWorker));
-      break;
-    case WhichServiceWorker::WAITING_WORKER:
-      rv = swm->GetWaiting(window, mScope, getter_AddRefs(serviceWorker));
-      break;
-    case WhichServiceWorker::ACTIVE_WORKER:
-      rv = swm->GetActive(window, mScope, getter_AddRefs(serviceWorker));
-      break;
-    default:
-      MOZ_CRASH("Invalid enum value");
-  }
-
-  NS_WARNING_ASSERTION(
-    NS_SUCCEEDED(rv) || rv == NS_ERROR_DOM_NOT_FOUND_ERR,
-    "Unexpected error getting service worker instance from "
-    "ServiceWorkerManager");
-  if (NS_FAILED(rv)) {
-    return nullptr;
-  }
-
-  RefPtr<ServiceWorker> ref =
-    static_cast<ServiceWorker*>(serviceWorker.get());
-  return ref.forget();
-}
-
-// XXXnsm, maybe this can be optimized to only add when a event handler is
-// registered.
-void
-ServiceWorkerRegistrationMainThread::StartListeningForEvents()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(!mListeningForEvents);
-  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
-  if (swm) {
-    swm->AddRegistrationEventListener(mScope, this);
-    mListeningForEvents = true;
-  }
-}
-
-void
-ServiceWorkerRegistrationMainThread::StopListeningForEvents()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  if (!mListeningForEvents) {
-    return;
-  }
-
-  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
-  if (swm) {
-    swm->RemoveRegistrationEventListener(mScope, this);
-  }
-  mListeningForEvents = false;
-}
-
-already_AddRefed<ServiceWorker>
-ServiceWorkerRegistrationMainThread::GetInstalling()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  if (!mInstallingWorker) {
-    mInstallingWorker = GetWorkerReference(WhichServiceWorker::INSTALLING_WORKER);
-  }
-
-  RefPtr<ServiceWorker> ret = mInstallingWorker;
-  return ret.forget();
-}
-
-already_AddRefed<ServiceWorker>
-ServiceWorkerRegistrationMainThread::GetWaiting()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  if (!mWaitingWorker) {
-    mWaitingWorker = GetWorkerReference(WhichServiceWorker::WAITING_WORKER);
-  }
-
-  RefPtr<ServiceWorker> ret = mWaitingWorker;
-  return ret.forget();
-}
-
-already_AddRefed<ServiceWorker>
-ServiceWorkerRegistrationMainThread::GetActive()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  if (!mActiveWorker) {
-    mActiveWorker = GetWorkerReference(WhichServiceWorker::ACTIVE_WORKER);
-  }
-
-  RefPtr<ServiceWorker> ret = mActiveWorker;
-  return ret.forget();
-}
-
-void
-ServiceWorkerRegistrationMainThread::UpdateFound()
-{
-  DispatchTrustedEvent(NS_LITERAL_STRING("updatefound"));
-}
-
-void
-ServiceWorkerRegistrationMainThread::TransitionWorker(WhichServiceWorker aWhichOne)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  // We assert the worker's previous state because the 'statechange'
-  // event is dispatched in a queued runnable.
-  if (aWhichOne == WhichServiceWorker::INSTALLING_WORKER) {
-    MOZ_ASSERT_IF(mInstallingWorker, mInstallingWorker->State() == ServiceWorkerState::Installing);
-    mWaitingWorker = mInstallingWorker.forget();
-  } else if (aWhichOne == WhichServiceWorker::WAITING_WORKER) {
-    MOZ_ASSERT_IF(mWaitingWorker, mWaitingWorker->State() == ServiceWorkerState::Installed);
-    mActiveWorker = mWaitingWorker.forget();
-  } else {
-    MOZ_ASSERT_UNREACHABLE("Invalid transition!");
-  }
-}
-
-void
-ServiceWorkerRegistrationMainThread::InvalidateWorkers(WhichServiceWorker aWhichOnes)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  if (aWhichOnes & WhichServiceWorker::INSTALLING_WORKER) {
-    mInstallingWorker = nullptr;
-  }
-
-  if (aWhichOnes & WhichServiceWorker::WAITING_WORKER) {
-    mWaitingWorker = nullptr;
-  }
-
-  if (aWhichOnes & WhichServiceWorker::ACTIVE_WORKER) {
-    mActiveWorker = nullptr;
-  }
-
-}
-
-void
-ServiceWorkerRegistrationMainThread::RegistrationRemoved()
-{
-  // If the registration is being removed completely, remove it from the
-  // window registration hash table so that a new registration would get a new
-  // wrapper JS object.
-  if (nsCOMPtr<nsPIDOMWindowInner> window = GetOwner()) {
-    window->InvalidateServiceWorkerRegistration(mScope);
-  }
-}
-
-namespace {
-
-void
-UpdateInternal(nsIPrincipal* aPrincipal,
-               const nsAString& aScope,
-               ServiceWorkerUpdateFinishCallback* aCallback)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(aPrincipal);
-  MOZ_ASSERT(aCallback);
-
-  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
-  if (!swm) {
-    // browser shutdown
-    return;
-  }
-
-  swm->Update(aPrincipal, NS_ConvertUTF16toUTF8(aScope), aCallback);
-}
-
-class MainThreadUpdateCallback final : public ServiceWorkerUpdateFinishCallback
-{
-  PromiseWindowProxy mPromise;
-
-  ~MainThreadUpdateCallback()
-  { }
-
-public:
-  explicit MainThreadUpdateCallback(nsPIDOMWindowInner* aWindow,
-                                    Promise* aPromise)
-    : mPromise(aWindow, aPromise)
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-  }
-
-  void
-  UpdateSucceeded(ServiceWorkerRegistrationInfo* aRegistration) override
-  {
-    if (RefPtr<Promise> promise = mPromise.Get()) {
-      promise->MaybeResolveWithUndefined();
-    }
-  }
-
-  void
-  UpdateFailed(ErrorResult& aStatus) override
-  {
-    if (RefPtr<Promise> promise = mPromise.Get()) {
-      promise->MaybeReject(aStatus);
-    }
-  }
-};
-
-class UpdateResultRunnable final : public WorkerRunnable
-{
-  RefPtr<PromiseWorkerProxy> mPromiseProxy;
-  IPC::Message mSerializedErrorResult;
-
-  ~UpdateResultRunnable()
-  {}
-
-public:
-  UpdateResultRunnable(PromiseWorkerProxy* aPromiseProxy, ErrorResult& aStatus)
-    : WorkerRunnable(aPromiseProxy->GetWorkerPrivate())
-    , mPromiseProxy(aPromiseProxy)
-  {
-    // ErrorResult is not thread safe.  Serialize it for transfer across
-    // threads.
-    IPC::WriteParam(&mSerializedErrorResult, aStatus);
-    aStatus.SuppressException();
-  }
-
-  bool
-  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
-  {
-    // Deserialize the ErrorResult now that we are back in the worker
-    // thread.
-    ErrorResult status;
-    PickleIterator iter = PickleIterator(mSerializedErrorResult);
-    Unused << IPC::ReadParam(&mSerializedErrorResult, &iter, &status);
-
-    Promise* promise = mPromiseProxy->WorkerPromise();
-    if (status.Failed()) {
-      promise->MaybeReject(status);
-    } else {
-      promise->MaybeResolveWithUndefined();
-    }
-    status.SuppressException();
-    mPromiseProxy->CleanUp();
-    return true;
-  }
-};
-
-class WorkerThreadUpdateCallback final : public ServiceWorkerUpdateFinishCallback
-{
-  RefPtr<PromiseWorkerProxy> mPromiseProxy;
-
-  ~WorkerThreadUpdateCallback()
-  {
-  }
-
-public:
-  explicit WorkerThreadUpdateCallback(PromiseWorkerProxy* aPromiseProxy)
-    : mPromiseProxy(aPromiseProxy)
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-  }
-
-  void
-  UpdateSucceeded(ServiceWorkerRegistrationInfo* aRegistration) override
-  {
-    ErrorResult rv(NS_OK);
-    Finish(rv);
-  }
-
-  void
-  UpdateFailed(ErrorResult& aStatus) override
-  {
-    Finish(aStatus);
-  }
-
-  void
-  Finish(ErrorResult& aStatus)
-  {
-    if (!mPromiseProxy) {
-      return;
-    }
-
-    RefPtr<PromiseWorkerProxy> proxy = mPromiseProxy.forget();
-
-    MutexAutoLock lock(proxy->Lock());
-    if (proxy->CleanedUp()) {
-      return;
-    }
-
-    RefPtr<UpdateResultRunnable> r =
-      new UpdateResultRunnable(proxy, aStatus);
-    r->Dispatch();
-  }
-};
-
-class SWRUpdateRunnable final : public Runnable
-{
-public:
-  SWRUpdateRunnable(PromiseWorkerProxy* aPromiseProxy, const nsAString& aScope)
-    : Runnable("dom::SWRUpdateRunnable")
-    , mPromiseProxy(aPromiseProxy)
-    , mScope(aScope)
-  {}
-
-  NS_IMETHOD
-  Run() override
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    ErrorResult result;
-
-    nsCOMPtr<nsIPrincipal> principal;
-    // UpdateInternal may try to reject the promise synchronously leading
-    // to a deadlock.
-    {
-      MutexAutoLock lock(mPromiseProxy->Lock());
-      if (mPromiseProxy->CleanedUp()) {
-        return NS_OK;
-      }
-
-      principal = mPromiseProxy->GetWorkerPrivate()->GetPrincipal();
-    }
-    MOZ_ASSERT(principal);
-
-    RefPtr<WorkerThreadUpdateCallback> cb =
-      new WorkerThreadUpdateCallback(mPromiseProxy);
-    UpdateInternal(principal, mScope, cb);
-    return NS_OK;
-  }
-
-private:
-  ~SWRUpdateRunnable()
-  {}
-
-  RefPtr<PromiseWorkerProxy> mPromiseProxy;
-  const nsString mScope;
-};
-
-class UnregisterCallback final : public nsIServiceWorkerUnregisterCallback
-{
-  PromiseWindowProxy mPromise;
-
-public:
-  NS_DECL_ISUPPORTS
-
-  explicit UnregisterCallback(nsPIDOMWindowInner* aWindow,
-                              Promise* aPromise)
-    : mPromise(aWindow, aPromise)
-  {
-    MOZ_ASSERT(aPromise);
-  }
-
-  NS_IMETHOD
-  UnregisterSucceeded(bool aState) override
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    if (RefPtr<Promise> promise = mPromise.Get()) {
-      promise->MaybeResolve(aState);
-    }
-    return NS_OK;
-  }
-
-  NS_IMETHOD
-  UnregisterFailed() override
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    if (RefPtr<Promise> promise = mPromise.Get()) {
-      promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
-    }
-    return NS_OK;
-  }
-
-private:
-  ~UnregisterCallback()
-  { }
-};
-
-NS_IMPL_ISUPPORTS(UnregisterCallback, nsIServiceWorkerUnregisterCallback)
-
-class FulfillUnregisterPromiseRunnable final : public WorkerRunnable
-{
-  RefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
-  Maybe<bool> mState;
-public:
-  FulfillUnregisterPromiseRunnable(PromiseWorkerProxy* aProxy,
-                                   const Maybe<bool>& aState)
-    : WorkerRunnable(aProxy->GetWorkerPrivate())
-    , mPromiseWorkerProxy(aProxy)
-    , mState(aState)
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    MOZ_ASSERT(mPromiseWorkerProxy);
-  }
-
-  bool
-  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
-  {
-    RefPtr<Promise> promise = mPromiseWorkerProxy->WorkerPromise();
-    if (mState.isSome()) {
-      promise->MaybeResolve(mState.value());
-    } else {
-      promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
-    }
-
-    mPromiseWorkerProxy->CleanUp();
-    return true;
-  }
-};
-
-class WorkerUnregisterCallback final : public nsIServiceWorkerUnregisterCallback
-{
-  RefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
-public:
-  NS_DECL_ISUPPORTS
-
-  explicit WorkerUnregisterCallback(PromiseWorkerProxy* aProxy)
-    : mPromiseWorkerProxy(aProxy)
-  {
-    MOZ_ASSERT(aProxy);
-  }
-
-  NS_IMETHOD
-  UnregisterSucceeded(bool aState) override
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    Finish(Some(aState));
-    return NS_OK;
-  }
-
-  NS_IMETHOD
-  UnregisterFailed() override
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    Finish(Nothing());
-    return NS_OK;
-  }
-
-private:
-  ~WorkerUnregisterCallback()
-  {}
-
-  void
-  Finish(const Maybe<bool>& aState)
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    if (!mPromiseWorkerProxy) {
-      return;
-    }
-
-    RefPtr<PromiseWorkerProxy> proxy = mPromiseWorkerProxy.forget();
-    MutexAutoLock lock(proxy->Lock());
-    if (proxy->CleanedUp()) {
-      return;
-    }
-
-    RefPtr<WorkerRunnable> r =
-      new FulfillUnregisterPromiseRunnable(proxy, aState);
-
-    r->Dispatch();
-  }
-};
-
-NS_IMPL_ISUPPORTS(WorkerUnregisterCallback, nsIServiceWorkerUnregisterCallback);
-
-/*
- * If the worker goes away, we still continue to unregister, but we don't try to
- * resolve the worker Promise (which doesn't exist by that point).
- */
-class StartUnregisterRunnable final : public Runnable
-{
-  RefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
-  const nsString mScope;
-
-public:
-  StartUnregisterRunnable(PromiseWorkerProxy* aProxy, const nsAString& aScope)
-    : Runnable("dom::StartUnregisterRunnable")
-    , mPromiseWorkerProxy(aProxy)
-    , mScope(aScope)
-  {
-    MOZ_ASSERT(aProxy);
-  }
-
-  NS_IMETHOD
-  Run() override
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    // XXXnsm: There is a rare chance of this failing if the worker gets
-    // destroyed. In that case, unregister() called from a SW is no longer
-    // guaranteed to run. We should fix this by having a main thread proxy
-    // maintain a strongref to ServiceWorkerRegistrationInfo and use its
-    // principal. Can that be trusted?
-    nsCOMPtr<nsIPrincipal> principal;
-    {
-      MutexAutoLock lock(mPromiseWorkerProxy->Lock());
-      if (mPromiseWorkerProxy->CleanedUp()) {
-        return NS_OK;
-      }
-
-      WorkerPrivate* worker = mPromiseWorkerProxy->GetWorkerPrivate();
-      MOZ_ASSERT(worker);
-      principal = worker->GetPrincipal();
-    }
-    MOZ_ASSERT(principal);
-
-    RefPtr<WorkerUnregisterCallback> cb =
-      new WorkerUnregisterCallback(mPromiseWorkerProxy);
-    nsCOMPtr<nsIServiceWorkerManager> swm =
-      mozilla::services::GetServiceWorkerManager();
-    nsresult rv = swm->Unregister(principal, cb, mScope);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      cb->UnregisterFailed();
-    }
-
-    return NS_OK;
-  }
-};
-} // namespace
-
-already_AddRefed<Promise>
-ServiceWorkerRegistrationMainThread::Update(ErrorResult& aRv)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(GetOwner());
-  if (!go) {
-    aRv.Throw(NS_ERROR_FAILURE);
-    return nullptr;
-  }
-
-  RefPtr<Promise> promise = Promise::Create(go, aRv);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
-  nsCOMPtr<nsIDocument> doc = GetOwner()->GetExtantDoc();
-  MOZ_ASSERT(doc);
-
-  RefPtr<MainThreadUpdateCallback> cb =
-    new MainThreadUpdateCallback(GetOwner(), promise);
-  UpdateInternal(doc->NodePrincipal(), mScope, cb);
-
-  return promise.forget();
-}
-
-already_AddRefed<Promise>
-ServiceWorkerRegistrationMainThread::Unregister(ErrorResult& aRv)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(GetOwner());
-  if (!go) {
-    aRv.Throw(NS_ERROR_FAILURE);
-    return nullptr;
-  }
-
-  // Although the spec says that the same-origin checks should also be done
-  // asynchronously, we do them in sync because the Promise created by the
-  // WebIDL infrastructure due to a returned error will be resolved
-  // asynchronously. We aren't making any internal state changes in these
-  // checks, so ordering of multiple calls is not affected.
-  nsCOMPtr<nsIDocument> document = GetOwner()->GetExtantDoc();
-  if (!document) {
-    aRv.Throw(NS_ERROR_FAILURE);
-    return nullptr;
-  }
-
-  nsCOMPtr<nsIURI> scopeURI;
-  nsCOMPtr<nsIURI> baseURI = document->GetBaseURI();
-  // "If the origin of scope is not client's origin..."
-  nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), mScope, nullptr, baseURI);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
-    return nullptr;
-  }
-
-  nsCOMPtr<nsIPrincipal> documentPrincipal = document->NodePrincipal();
-  rv = documentPrincipal->CheckMayLoad(scopeURI, true /* report */,
-                                       false /* allowIfInheritsPrinciple */);
-  if (NS_FAILED(rv)) {
-    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
-    return nullptr;
-  }
-
-  nsAutoCString uriSpec;
-  aRv = scopeURI->GetSpecIgnoringRef(uriSpec);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
-  nsCOMPtr<nsIServiceWorkerManager> swm =
-    mozilla::services::GetServiceWorkerManager();
-
-  RefPtr<Promise> promise = Promise::Create(go, aRv);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
-  RefPtr<UnregisterCallback> cb = new UnregisterCallback(GetOwner(), promise);
-
-  NS_ConvertUTF8toUTF16 scope(uriSpec);
-  aRv = swm->Unregister(documentPrincipal, cb, scope);
-  if (aRv.Failed()) {
-    return nullptr;
-  }
-
-  return promise.forget();
-}
-
-// Notification API extension.
-already_AddRefed<Promise>
-ServiceWorkerRegistrationMainThread::ShowNotification(JSContext* aCx,
-                                                      const nsAString& aTitle,
-                                                      const NotificationOptions& aOptions,
-                                                      ErrorResult& aRv)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  nsCOMPtr<nsPIDOMWindowInner> window = GetOwner();
-  if (NS_WARN_IF(!window)) {
-    aRv.Throw(NS_ERROR_FAILURE);
-    return nullptr;
-  }
-
-  nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
-  if (NS_WARN_IF(!doc)) {
-    aRv.Throw(NS_ERROR_FAILURE);
-    return nullptr;
-  }
-
-  RefPtr<ServiceWorker> worker = GetActive();
-  if (!worker) {
-    aRv.ThrowTypeError<MSG_NO_ACTIVE_WORKER>(mScope);
-    return nullptr;
-  }
-
-  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(window);
-  RefPtr<Promise> p =
-    Notification::ShowPersistentNotification(aCx, global, mScope, aTitle,
-                                             aOptions, aRv);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
-  return p.forget();
-}
-
-already_AddRefed<Promise>
-ServiceWorkerRegistrationMainThread::GetNotifications(const GetNotificationOptions& aOptions, ErrorResult& aRv)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  nsCOMPtr<nsPIDOMWindowInner> window = GetOwner();
-  if (NS_WARN_IF(!window)) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return nullptr;
-  }
-  return Notification::Get(window, aOptions, mScope, aRv);
-}
-
-already_AddRefed<PushManager>
-ServiceWorkerRegistrationMainThread::GetPushManager(JSContext* aCx,
-                                                    ErrorResult& aRv)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  if (!mPushManager) {
-    nsCOMPtr<nsIGlobalObject> globalObject = do_QueryInterface(GetOwner());
-
-    if (!globalObject) {
-      aRv.Throw(NS_ERROR_FAILURE);
-      return nullptr;
-    }
-
-    GlobalObject global(aCx, globalObject->GetGlobalJSObject());
-    mPushManager = PushManager::Constructor(global, mScope, aRv);
-    if (aRv.Failed()) {
-      return nullptr;
-    }
-  }
-
-  RefPtr<PushManager> ret = mPushManager;
-  return ret.forget();
-}
-
-////////////////////////////////////////////////////
-// Worker Thread implementation
-
-class ServiceWorkerRegistrationWorkerThread final : public ServiceWorkerRegistration
-                                                  , public WorkerHolder
-{
-public:
-  NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorkerRegistrationWorkerThread,
-                                           ServiceWorkerRegistration)
-
-  ServiceWorkerRegistrationWorkerThread(WorkerPrivate* aWorkerPrivate,
-                                        const nsAString& aScope);
-
-  already_AddRefed<Promise>
-  Update(ErrorResult& aRv) override;
-
-  already_AddRefed<Promise>
-  Unregister(ErrorResult& aRv) override;
-
-  // Partial interface from Notification API.
-  already_AddRefed<Promise>
-  ShowNotification(JSContext* aCx,
-                   const nsAString& aTitle,
-                   const NotificationOptions& aOptions,
-                   ErrorResult& aRv) override;
-
-  already_AddRefed<Promise>
-  GetNotifications(const GetNotificationOptions& aOptions,
-                   ErrorResult& aRv) override;
-
-  already_AddRefed<ServiceWorker>
-  GetInstalling() override;
-
-  already_AddRefed<ServiceWorker>
-  GetWaiting() override;
-
-  already_AddRefed<ServiceWorker>
-  GetActive() override;
-
-  void
-  GetScope(nsAString& aScope) const override
-  {
-    aScope = mScope;
-  }
-
-  ServiceWorkerUpdateViaCache
-  GetUpdateViaCache(ErrorResult& aRv) const override
-  {
-    // FIXME(hopang): Will be implemented after Bug 1113522.
-    return ServiceWorkerUpdateViaCache::Imports;
-  }
-
-  bool
-  Notify(WorkerStatus aStatus) override;
-
-  already_AddRefed<PushManager>
-  GetPushManager(JSContext* aCx, ErrorResult& aRv) override;
-
-private:
-  ~ServiceWorkerRegistrationWorkerThread();
-
-  void
-  InitListener();
-
-  void
-  ReleaseListener();
-
-  WorkerPrivate* mWorkerPrivate;
-  RefPtr<WorkerListener> mListener;
-
-  RefPtr<PushManager> mPushManager;
-};
-
-class WorkerListener final : public ServiceWorkerRegistrationListener
-{
-  // Accessed on the main thread.
-  WorkerPrivate* mWorkerPrivate;
-  nsString mScope;
-  bool mListeningForEvents;
-
-  // Accessed on the worker thread.
-  ServiceWorkerRegistrationWorkerThread* mRegistration;
-
-public:
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WorkerListener, override)
-
-  WorkerListener(WorkerPrivate* aWorkerPrivate,
-                 ServiceWorkerRegistrationWorkerThread* aReg)
-    : mWorkerPrivate(aWorkerPrivate)
-    , mListeningForEvents(false)
-    , mRegistration(aReg)
-  {
-    MOZ_ASSERT(mWorkerPrivate);
-    mWorkerPrivate->AssertIsOnWorkerThread();
-    MOZ_ASSERT(mRegistration);
-    // Copy scope so we can return it on the main thread.
-    mRegistration->GetScope(mScope);
-  }
-
-  void
-  StartListeningForEvents()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    MOZ_ASSERT(!mListeningForEvents);
-    MOZ_ASSERT(mWorkerPrivate);
-    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
-    if (swm) {
-      // FIXME(nsm): Maybe the function shouldn't take an explicit scope.
-      swm->AddRegistrationEventListener(mScope, this);
-      mListeningForEvents = true;
-    }
-  }
-
-  void
-  StopListeningForEvents()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    MOZ_ASSERT(mListeningForEvents);
-
-    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
-
-    // We aren't going to need this anymore and we shouldn't hold on since the
-    // worker will go away soon.
-    mWorkerPrivate = nullptr;
-
-    if (swm) {
-      // FIXME(nsm): Maybe the function shouldn't take an explicit scope.
-      swm->RemoveRegistrationEventListener(mScope, this);
-      mListeningForEvents = false;
-    }
-  }
-
-  // ServiceWorkerRegistrationListener
-  void
-  UpdateFound() override;
-
-  void
-  TransitionWorker(WhichServiceWorker aWhichOne) override
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    NS_WARNING("FIXME: Not implemented!");
-  }
-
-  void
-  InvalidateWorkers(WhichServiceWorker aWhichOnes) override
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    // FIXME(nsm);
-  }
-
-  void
-  RegistrationRemoved() override
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-  }
-
-  void
-  GetScope(nsAString& aScope) const override
-  {
-    aScope = mScope;
-  }
-
-  ServiceWorkerRegistrationWorkerThread*
-  GetRegistration() const
-  {
-    if (mWorkerPrivate) {
-      mWorkerPrivate->AssertIsOnWorkerThread();
-    }
-    return mRegistration;
-  }
-
-  void
-  ClearRegistration()
-  {
-    if (mWorkerPrivate) {
-      mWorkerPrivate->AssertIsOnWorkerThread();
-    }
-    mRegistration = nullptr;
-  }
-
-private:
-  ~WorkerListener()
-  {
-    MOZ_ASSERT(!mListeningForEvents);
-  }
-};
-
-NS_IMPL_ADDREF_INHERITED(ServiceWorkerRegistrationWorkerThread, ServiceWorkerRegistration)
-NS_IMPL_RELEASE_INHERITED(ServiceWorkerRegistrationWorkerThread, ServiceWorkerRegistration)
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorkerRegistrationWorkerThread)
-NS_INTERFACE_MAP_END_INHERITING(ServiceWorkerRegistration)
-
-// Expanded macros since we need special behaviour to release the proxy.
-NS_IMPL_CYCLE_COLLECTION_CLASS(ServiceWorkerRegistrationWorkerThread)
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ServiceWorkerRegistrationWorkerThread,
-                                                  ServiceWorkerRegistration)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPushManager)
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ServiceWorkerRegistrationWorkerThread,
-                                                ServiceWorkerRegistration)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPushManager)
-  tmp->ReleaseListener();
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-ServiceWorkerRegistrationWorkerThread::ServiceWorkerRegistrationWorkerThread(WorkerPrivate* aWorkerPrivate,
-                                                                             const nsAString& aScope)
-  : ServiceWorkerRegistration(nullptr, aScope)
-  , WorkerHolder("ServiceWorkerRegistrationWorkerThread")
-  , mWorkerPrivate(aWorkerPrivate)
-{
-  InitListener();
-}
-
-ServiceWorkerRegistrationWorkerThread::~ServiceWorkerRegistrationWorkerThread()
-{
-  ReleaseListener();
-  MOZ_ASSERT(!mListener);
-}
-
-already_AddRefed<ServiceWorker>
-ServiceWorkerRegistrationWorkerThread::GetInstalling()
-{
-  // FIXME(nsm): Will be implemented after Bug 1113522.
-  return nullptr;
-}
-
-already_AddRefed<ServiceWorker>
-ServiceWorkerRegistrationWorkerThread::GetWaiting()
-{
-  // FIXME(nsm): Will be implemented after Bug 1113522.
-  return nullptr;
-}
-
-already_AddRefed<ServiceWorker>
-ServiceWorkerRegistrationWorkerThread::GetActive()
-{
-  // FIXME(nsm): Will be implemented after Bug 1113522.
-  return nullptr;
-}
-
-already_AddRefed<Promise>
-ServiceWorkerRegistrationWorkerThread::Update(ErrorResult& aRv)
-{
-  WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
-  MOZ_ASSERT(worker);
-  worker->AssertIsOnWorkerThread();
-
-  RefPtr<Promise> promise = Promise::Create(worker->GlobalScope(), aRv);
-  if (aRv.Failed()) {
-    return nullptr;
-  }
-
-  // Avoid infinite update loops by ignoring update() calls during top
-  // level script evaluation.  See:
-  // https://github.com/slightlyoff/ServiceWorker/issues/800
-  if (worker->LoadScriptAsPartOfLoadingServiceWorkerScript()) {
-    promise->MaybeResolveWithUndefined();
-    return promise.forget();
-  }
-
-  RefPtr<PromiseWorkerProxy> proxy = PromiseWorkerProxy::Create(worker, promise);
-  if (!proxy) {
-    aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
-    return nullptr;
-  }
-
-  RefPtr<SWRUpdateRunnable> r = new SWRUpdateRunnable(proxy, mScope);
-  MOZ_ALWAYS_SUCCEEDS(worker->DispatchToMainThread(r.forget()));
-
-  return promise.forget();
-}
-
-already_AddRefed<Promise>
-ServiceWorkerRegistrationWorkerThread::Unregister(ErrorResult& aRv)
-{
-  WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
-  MOZ_ASSERT(worker);
-  worker->AssertIsOnWorkerThread();
-
-  if (!worker->IsServiceWorker()) {
-    // For other workers, the registration probably originated from
-    // getRegistration(), so we may have to validate origin etc. Let's do this
-    // this later.
-    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
-    return nullptr;
-  }
-
-  RefPtr<Promise> promise = Promise::Create(worker->GlobalScope(), aRv);
-  if (aRv.Failed()) {
-    return nullptr;
-  }
-
-  RefPtr<PromiseWorkerProxy> proxy = PromiseWorkerProxy::Create(worker, promise);
-  if (!proxy) {
-    aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
-    return nullptr;
-  }
-
-  RefPtr<StartUnregisterRunnable> r = new StartUnregisterRunnable(proxy, mScope);
-  MOZ_ALWAYS_SUCCEEDS(worker->DispatchToMainThread(r.forget()));
-
-  return promise.forget();
-}
-
-void
-ServiceWorkerRegistrationWorkerThread::InitListener()
-{
-  MOZ_ASSERT(!mListener);
-  WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
-  MOZ_ASSERT(worker);
-  worker->AssertIsOnWorkerThread();
-
-  mListener = new WorkerListener(worker, this);
-  if (!HoldWorker(worker, Closing)) {
-    mListener = nullptr;
-    NS_WARNING("Could not add feature");
-    return;
-  }
-
-  nsCOMPtr<nsIRunnable> r =
-    NewRunnableMethod("dom::WorkerListener::StartListeningForEvents",
-                      mListener,
-                      &WorkerListener::StartListeningForEvents);
-  MOZ_ALWAYS_SUCCEEDS(worker->DispatchToMainThread(r.forget()));
-}
-
-void
-ServiceWorkerRegistrationWorkerThread::ReleaseListener()
-{
-  if (!mListener) {
-    return;
-  }
-
-  // We can assert worker here, because:
-  // 1) We always HoldWorker, so if the worker has shutdown already, we'll
-  //    have received Notify and removed it. If HoldWorker had failed,
-  //    mListener will be null and we won't reach here.
-  // 2) Otherwise, worker is still around even if we are going away.
-  mWorkerPrivate->AssertIsOnWorkerThread();
-  ReleaseWorker();
-
-  mListener->ClearRegistration();
-
-  nsCOMPtr<nsIRunnable> r =
-    NewRunnableMethod("dom::WorkerListener::StopListeningForEvents",
-                      mListener,
-                      &WorkerListener::StopListeningForEvents);
-  MOZ_ALWAYS_SUCCEEDS(mWorkerPrivate->DispatchToMainThread(r.forget()));
-
-  mListener = nullptr;
-  mWorkerPrivate = nullptr;
-}
-
-bool
-ServiceWorkerRegistrationWorkerThread::Notify(WorkerStatus aStatus)
-{
-  ReleaseListener();
-  return true;
-}
-
-class FireUpdateFoundRunnable final : public WorkerRunnable
-{
-  RefPtr<WorkerListener> mListener;
-public:
-  FireUpdateFoundRunnable(WorkerPrivate* aWorkerPrivate,
-                          WorkerListener* aListener)
-    : WorkerRunnable(aWorkerPrivate)
-    , mListener(aListener)
-  {
-    // Need this assertion for now since runnables which modify busy count can
-    // only be dispatched from parent thread to worker thread and we don't deal
-    // with nested workers. SW threads can't be nested.
-    MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
-  }
-
-  bool
-  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
-  {
-    MOZ_ASSERT(aWorkerPrivate);
-    aWorkerPrivate->AssertIsOnWorkerThread();
-
-    ServiceWorkerRegistrationWorkerThread* reg = mListener->GetRegistration();
-    if (reg) {
-      reg->DispatchTrustedEvent(NS_LITERAL_STRING("updatefound"));
-    }
-    return true;
-  }
-};
-
-void
-WorkerListener::UpdateFound()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  if (mWorkerPrivate) {
-    RefPtr<FireUpdateFoundRunnable> r =
-      new FireUpdateFoundRunnable(mWorkerPrivate, this);
-    Unused << NS_WARN_IF(!r->Dispatch());
-  }
-}
-
-// Notification API extension.
-already_AddRefed<Promise>
-ServiceWorkerRegistrationWorkerThread::ShowNotification(JSContext* aCx,
-                                                        const nsAString& aTitle,
-                                                        const NotificationOptions& aOptions,
-                                                        ErrorResult& aRv)
-{
-  // Until Bug 1131324 exposes ServiceWorkerContainer on workers,
-  // ShowPersistentNotification() checks for valid active worker while it is
-  // also verifying scope so that we block the worker on the main thread only
-  // once.
-  RefPtr<Promise> p =
-    Notification::ShowPersistentNotification(aCx, mWorkerPrivate->GlobalScope(),
-                                             mScope, aTitle, aOptions, aRv);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
-  return p.forget();
-}
-
-already_AddRefed<Promise>
-ServiceWorkerRegistrationWorkerThread::GetNotifications(const GetNotificationOptions& aOptions,
-                                                        ErrorResult& aRv)
-{
-  return Notification::WorkerGet(mWorkerPrivate, aOptions, mScope, aRv);
-}
-
-already_AddRefed<PushManager>
-ServiceWorkerRegistrationWorkerThread::GetPushManager(JSContext* aCx, ErrorResult& aRv)
-{
-  if (!mPushManager) {
-    mPushManager = new PushManager(mScope);
-  }
-
-  RefPtr<PushManager> ret = mPushManager;
-  return ret.forget();
-}
-
-////////////////////////////////////////////////////
-// Base class implementation
+NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorkerRegistration,
+                                   DOMEventTargetHelper,
+                                   mInstallingWorker,
+                                   mWaitingWorker,
+                                   mActiveWorker,
+                                   mPushManager);
 
 NS_IMPL_ADDREF_INHERITED(ServiceWorkerRegistration, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(ServiceWorkerRegistration, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorkerRegistration)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
-ServiceWorkerRegistration::ServiceWorkerRegistration(nsPIDOMWindowInner* aWindow,
-                                                     const nsAString& aScope)
-  : DOMEventTargetHelper(aWindow)
-  , mScope(aScope)
-{}
+ServiceWorkerRegistration::ServiceWorkerRegistration(nsIGlobalObject* aGlobal,
+                                                     const ServiceWorkerRegistrationDescriptor& aDescriptor,
+                                                     ServiceWorkerRegistration::Inner* aInner)
+  : DOMEventTargetHelper(aGlobal)
+  , mDescriptor(aDescriptor)
+  , mInner(aInner)
+{
+  MOZ_DIAGNOSTIC_ASSERT(mInner);
+  UpdateState(mDescriptor);
+  mInner->SetServiceWorkerRegistration(this);
+}
+
+ServiceWorkerRegistration::~ServiceWorkerRegistration()
+{
+  if (mInner) {
+    mInner->ClearServiceWorkerRegistration(this);
+  }
+}
 
 JSObject*
 ServiceWorkerRegistration::WrapObject(JSContext* aCx,
                                       JS::Handle<JSObject*> aGivenProto)
 {
   return ServiceWorkerRegistrationBinding::Wrap(aCx, this, aGivenProto);
 }
 
 /* static */ already_AddRefed<ServiceWorkerRegistration>
 ServiceWorkerRegistration::CreateForMainThread(nsPIDOMWindowInner* aWindow,
-                                               const nsAString& aScope)
+                                               const ServiceWorkerRegistrationDescriptor& aDescriptor)
 {
   MOZ_ASSERT(aWindow);
   MOZ_ASSERT(NS_IsMainThread());
 
+  nsCOMPtr<nsIGlobalObject> global(do_QueryInterface(aWindow));
+
+  RefPtr<Inner> inner = new ServiceWorkerRegistrationMainThread(aDescriptor);
+
   RefPtr<ServiceWorkerRegistration> registration =
-    new ServiceWorkerRegistrationMainThread(aWindow, aScope);
+    new ServiceWorkerRegistration(global, aDescriptor, inner);
 
   return registration.forget();
 }
 
 /* static */ already_AddRefed<ServiceWorkerRegistration>
 ServiceWorkerRegistration::CreateForWorker(WorkerPrivate* aWorkerPrivate,
-                                           const nsAString& aScope)
+                                           const ServiceWorkerRegistrationDescriptor& aDescriptor)
 {
   MOZ_ASSERT(aWorkerPrivate);
   aWorkerPrivate->AssertIsOnWorkerThread();
 
+  RefPtr<Inner> inner =
+    new ServiceWorkerRegistrationWorkerThread(aWorkerPrivate, aDescriptor);
+
   RefPtr<ServiceWorkerRegistration> registration =
-    new ServiceWorkerRegistrationWorkerThread(aWorkerPrivate, aScope);
+    new ServiceWorkerRegistration(aWorkerPrivate->GlobalScope(), aDescriptor,
+                                  inner);
 
   return registration.forget();
 }
 
+void
+ServiceWorkerRegistration::DisconnectFromOwner()
+{
+  mInner->ClearServiceWorkerRegistration(this);
+  mInner = nullptr;
+  DOMEventTargetHelper::DisconnectFromOwner();
+}
+
+already_AddRefed<ServiceWorker>
+ServiceWorkerRegistration::GetInstalling() const
+{
+  RefPtr<ServiceWorker> ref = mInstallingWorker;
+  return ref.forget();
+}
+
+already_AddRefed<ServiceWorker>
+ServiceWorkerRegistration::GetWaiting() const
+{
+  RefPtr<ServiceWorker> ref = mWaitingWorker;
+  return ref.forget();
+}
+
+already_AddRefed<ServiceWorker>
+ServiceWorkerRegistration::GetActive() const
+{
+  RefPtr<ServiceWorker> ref = mActiveWorker;
+  return ref.forget();
+}
+
+void
+ServiceWorkerRegistration::UpdateState(const ServiceWorkerRegistrationDescriptor& aDescriptor)
+{
+  MOZ_DIAGNOSTIC_ASSERT(MatchesDescriptor(aDescriptor));
+
+  mDescriptor = aDescriptor;
+
+  nsCOMPtr<nsIGlobalObject> global = GetParentObject();
+  if (!global) {
+    mInstallingWorker = nullptr;
+    mWaitingWorker = nullptr;
+    mActiveWorker = nullptr;
+    return;
+  }
+
+  Maybe<ServiceWorkerDescriptor> active = aDescriptor.GetActive();
+  if (active.isSome()) {
+    mActiveWorker = global->GetOrCreateServiceWorker(active.ref());
+  } else {
+    mActiveWorker = nullptr;
+  }
+
+  Maybe<ServiceWorkerDescriptor> waiting = aDescriptor.GetWaiting();
+  if (waiting.isSome()) {
+    mWaitingWorker = global->GetOrCreateServiceWorker(waiting.ref());
+  } else {
+    mWaitingWorker = nullptr;
+  }
+
+  Maybe<ServiceWorkerDescriptor> installing = aDescriptor.GetInstalling();
+  if (installing.isSome()) {
+    mInstallingWorker = global->GetOrCreateServiceWorker(installing.ref());
+  } else {
+    mInstallingWorker = nullptr;
+  }
+}
+
+bool
+ServiceWorkerRegistration::MatchesDescriptor(const ServiceWorkerRegistrationDescriptor& aDescriptor) const
+{
+  return aDescriptor.PrincipalInfo() == mDescriptor.PrincipalInfo() &&
+         aDescriptor.Scope() == mDescriptor.Scope();
+}
+
+void
+ServiceWorkerRegistration::GetScope(nsAString& aScope) const
+{
+  CopyUTF8toUTF16(mDescriptor.Scope(), aScope);
+}
+
+ServiceWorkerUpdateViaCache
+ServiceWorkerRegistration::GetUpdateViaCache(ErrorResult& aRv) const
+{
+  return mDescriptor.UpdateViaCache();
+}
+
+already_AddRefed<Promise>
+ServiceWorkerRegistration::Update(ErrorResult& aRv)
+{
+  if (!mInner) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return nullptr;
+  }
+  return mInner->Update(aRv);
+}
+
+already_AddRefed<Promise>
+ServiceWorkerRegistration::Unregister(ErrorResult& aRv)
+{
+  if (!mInner) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return nullptr;
+  }
+  return mInner->Unregister(aRv);
+}
+
+already_AddRefed<PushManager>
+ServiceWorkerRegistration::GetPushManager(JSContext* aCx, ErrorResult& aRv)
+{
+  if (!mInner) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return nullptr;
+  }
+  if (!mPushManager) {
+    mPushManager = mInner->GetPushManager(aCx, aRv);
+    if (aRv.Failed()) {
+      return nullptr;
+    }
+  }
+  RefPtr<PushManager> ret = mPushManager;
+  return ret.forget();
+}
+
+already_AddRefed<Promise>
+ServiceWorkerRegistration::ShowNotification(JSContext* aCx,
+                                            const nsAString& aTitle,
+                                            const NotificationOptions& aOptions,
+                                            ErrorResult& aRv)
+{
+  if (!mInner) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return nullptr;
+  }
+  return mInner->ShowNotification(aCx, aTitle, aOptions, aRv);
+}
+
+already_AddRefed<Promise>
+ServiceWorkerRegistration::GetNotifications(const GetNotificationOptions& aOptions,
+                                            ErrorResult& aRv)
+{
+  if (!mInner) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return nullptr;
+  }
+  return mInner->GetNotifications(aOptions, aRv);
+}
+
 } // dom namespace
 } // mozilla namespace
--- a/dom/serviceworkers/ServiceWorkerRegistration.h
+++ b/dom/serviceworkers/ServiceWorkerRegistration.h
@@ -5,114 +5,136 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_ServiceWorkerRegistration_h
 #define mozilla_dom_ServiceWorkerRegistration_h
 
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/DOMPrefs.h"
 #include "mozilla/dom/ServiceWorkerBinding.h"
-#include "mozilla/dom/ServiceWorkerCommon.h"
 #include "mozilla/dom/ServiceWorkerRegistrationBinding.h"
-#include "mozilla/dom/WorkerHolder.h"
+#include "mozilla/dom/ServiceWorkerRegistrationDescriptor.h"
 
 // Support for Notification API extension.
 #include "mozilla/dom/NotificationBinding.h"
 
-class nsPIDOMWindowInner;
+class nsIGlobalObject;
 
 namespace mozilla {
 namespace dom {
 
 class Promise;
 class PushManager;
-class WorkerListener;
+class WorkerPrivate;
+class ServiceWorker;
 
-// Used by ServiceWorkerManager to notify ServiceWorkerRegistrations of
-// updatefound event and invalidating ServiceWorker instances.
-class ServiceWorkerRegistrationListener
+class ServiceWorkerRegistration final : public DOMEventTargetHelper
 {
 public:
-  NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
+  class Inner
+  {
+  public:
+    NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
 
-  virtual void
-  UpdateFound() = 0;
+    virtual void
+    SetServiceWorkerRegistration(ServiceWorkerRegistration* aReg) = 0;
 
-  virtual void
-  InvalidateWorkers(WhichServiceWorker aWhichOnes) = 0;
+    virtual void
+    ClearServiceWorkerRegistration(ServiceWorkerRegistration* aReg) = 0;
 
-  virtual void
-  TransitionWorker(WhichServiceWorker aWhichOne) = 0;
+    virtual already_AddRefed<Promise>
+    Update(ErrorResult& aRv) = 0;
+
+    virtual already_AddRefed<Promise>
+    Unregister(ErrorResult& aRv) = 0;
 
-  virtual void
-  RegistrationRemoved() = 0;
+    virtual already_AddRefed<Promise>
+    ShowNotification(JSContext* aCx,
+                     const nsAString& aTitle,
+                     const NotificationOptions& aOptions,
+                     ErrorResult& aRv) = 0;
 
-  virtual void
-  GetScope(nsAString& aScope) const = 0;
-};
+    virtual already_AddRefed<Promise>
+    GetNotifications(const GetNotificationOptions& aOptions,
+                     ErrorResult& aRv) = 0;
 
-class ServiceWorkerRegistration : public DOMEventTargetHelper
-{
-public:
+    virtual already_AddRefed<PushManager>
+    GetPushManager(JSContext* aCx, ErrorResult& aRv) = 0;
+  };
+
   NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorkerRegistration, DOMEventTargetHelper)
 
   IMPL_EVENT_HANDLER(updatefound)
 
   static already_AddRefed<ServiceWorkerRegistration>
   CreateForMainThread(nsPIDOMWindowInner* aWindow,
-                      const nsAString& aScope);
+                      const ServiceWorkerRegistrationDescriptor& aDescriptor);
 
   static already_AddRefed<ServiceWorkerRegistration>
   CreateForWorker(WorkerPrivate* aWorkerPrivate,
-                  const nsAString& aScope);
+                  const ServiceWorkerRegistrationDescriptor& aDescriptor);
 
   JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
-  virtual already_AddRefed<ServiceWorker>
-  GetInstalling() = 0;
+  void DisconnectFromOwner() override;
 
-  virtual already_AddRefed<ServiceWorker>
-  GetWaiting() = 0;
+  already_AddRefed<ServiceWorker>
+  GetInstalling() const;
+
+  already_AddRefed<ServiceWorker>
+  GetWaiting() const;
 
-  virtual already_AddRefed<ServiceWorker>
-  GetActive() = 0;
+  already_AddRefed<ServiceWorker>
+  GetActive() const;
 
-  virtual void
-  GetScope(nsAString& aScope) const = 0;
+  void
+  UpdateState(const ServiceWorkerRegistrationDescriptor& aDescriptor);
+
+  bool
+  MatchesDescriptor(const ServiceWorkerRegistrationDescriptor& aDescriptor) const;
 
-  virtual ServiceWorkerUpdateViaCache
-  GetUpdateViaCache(ErrorResult& aRv) const = 0;
+  void
+  GetScope(nsAString& aScope) const;
 
-  virtual already_AddRefed<Promise>
-  Update(ErrorResult& aRv) = 0;
+  ServiceWorkerUpdateViaCache
+  GetUpdateViaCache(ErrorResult& aRv) const;
 
-  virtual already_AddRefed<Promise>
-  Unregister(ErrorResult& aRv) = 0;
+  already_AddRefed<Promise>
+  Update(ErrorResult& aRv);
 
-  virtual already_AddRefed<PushManager>
-  GetPushManager(JSContext* aCx, ErrorResult& aRv) = 0;
+  already_AddRefed<Promise>
+  Unregister(ErrorResult& aRv);
 
-  virtual already_AddRefed<Promise>
+  already_AddRefed<PushManager>
+  GetPushManager(JSContext* aCx, ErrorResult& aRv);
+
+  already_AddRefed<Promise>
   ShowNotification(JSContext* aCx,
                    const nsAString& aTitle,
                    const NotificationOptions& aOptions,
-                   ErrorResult& aRv) = 0;
+                   ErrorResult& aRv);
 
-  virtual already_AddRefed<Promise>
+  already_AddRefed<Promise>
   GetNotifications(const GetNotificationOptions& aOptions,
-                   ErrorResult& aRv) = 0;
+                   ErrorResult& aRv);
 
-protected:
-  ServiceWorkerRegistration(nsPIDOMWindowInner* aWindow,
-                            const nsAString& aScope);
+private:
+  ServiceWorkerRegistration(nsIGlobalObject* aGlobal,
+                            const ServiceWorkerRegistrationDescriptor& aDescriptor,
+                            Inner* aInner);
+
+  ~ServiceWorkerRegistration();
 
-  virtual ~ServiceWorkerRegistration()
-  { }
-
-  const nsString mScope;
+  ServiceWorkerRegistrationDescriptor mDescriptor;
+  RefPtr<Inner> mInner;
+  RefPtr<ServiceWorker> mInstallingWorker;
+  RefPtr<ServiceWorker> mWaitingWorker;
+  RefPtr<ServiceWorker> mActiveWorker;
+  RefPtr<PushManager> mPushManager;
 };
 
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_ServiceWorkerRegistration_h */
--- a/dom/serviceworkers/ServiceWorkerRegistrationDescriptor.cpp
+++ b/dom/serviceworkers/ServiceWorkerRegistrationDescriptor.cpp
@@ -1,15 +1,17 @@
 /* -*- 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/dom/ServiceWorkerRegistrationDescriptor.h"
+
+#include "mozilla/dom/IPCServiceWorkerRegistrationDescriptor.h"
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "ServiceWorkerInfo.h"
 
 namespace mozilla {
 namespace dom {
 
 Maybe<IPCServiceWorkerDescriptor>
 ServiceWorkerRegistrationDescriptor::NewestInternal() const
@@ -66,16 +68,19 @@ ServiceWorkerRegistrationDescriptor::Ser
   // on default copy construction.  Use the assignment operator to
   // minimize duplication.
   operator=(aRight);
 }
 
 ServiceWorkerRegistrationDescriptor&
 ServiceWorkerRegistrationDescriptor::operator=(const ServiceWorkerRegistrationDescriptor& aRight)
 {
+  if (this == &aRight) {
+    return *this;
+  }
   mData.reset();
   mData = MakeUnique<IPCServiceWorkerRegistrationDescriptor>(*aRight.mData);
   MOZ_DIAGNOSTIC_ASSERT(IsValid());
   return *this;
 }
 
 ServiceWorkerRegistrationDescriptor::ServiceWorkerRegistrationDescriptor(ServiceWorkerRegistrationDescriptor&& aRight)
   : mData(Move(aRight.mData))
@@ -87,16 +92,21 @@ ServiceWorkerRegistrationDescriptor&
 ServiceWorkerRegistrationDescriptor::operator=(ServiceWorkerRegistrationDescriptor&& aRight)
 {
   mData.reset();
   mData = Move(aRight.mData);
   MOZ_DIAGNOSTIC_ASSERT(IsValid());
   return *this;
 }
 
+ServiceWorkerRegistrationDescriptor::~ServiceWorkerRegistrationDescriptor()
+{
+  // Non-default destructor to avoid exposing the IPC type in the header.
+}
+
 bool
 ServiceWorkerRegistrationDescriptor::operator==(const ServiceWorkerRegistrationDescriptor& aRight) const
 {
   return *mData == *aRight.mData;
 }
 
 ServiceWorkerUpdateViaCache
 ServiceWorkerRegistrationDescriptor::UpdateViaCache() const
@@ -244,19 +254,19 @@ ServiceWorkerRegistrationDescriptor::Set
   } else {
     mData->active() = void_t();
   }
 
   MOZ_DIAGNOSTIC_ASSERT(IsValid());
 }
 
 void
-ServiceWorkerRegistrationDescriptor::SetWorkers(OptionalIPCServiceWorkerDescriptor& aInstalling,
-                                                OptionalIPCServiceWorkerDescriptor& aWaiting,
-                                                OptionalIPCServiceWorkerDescriptor& aActive)
+ServiceWorkerRegistrationDescriptor::SetWorkers(const OptionalIPCServiceWorkerDescriptor& aInstalling,
+                                                const OptionalIPCServiceWorkerDescriptor& aWaiting,
+                                                const OptionalIPCServiceWorkerDescriptor& aActive)
 {
   mData->installing() = aInstalling;
   mData->waiting() = aWaiting;
   mData->active() = aActive;
   MOZ_DIAGNOSTIC_ASSERT(IsValid());
 }
 
 const IPCServiceWorkerRegistrationDescriptor&
--- a/dom/serviceworkers/ServiceWorkerRegistrationDescriptor.h
+++ b/dom/serviceworkers/ServiceWorkerRegistrationDescriptor.h
@@ -2,29 +2,29 @@
 /* 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 _mozilla_dom_ServiceWorkerRegistrationDescriptor_h
 #define _mozilla_dom_ServiceWorkerRegistrationDescriptor_h
 
 #include "mozilla/Maybe.h"
-#include "mozilla/dom/IPCServiceWorkerRegistrationDescriptor.h"
 #include "mozilla/dom/ServiceWorkerDescriptor.h"
 #include "mozilla/UniquePtr.h"
 
 namespace mozilla {
 
 namespace ipc {
 class PrincipalInfo;
 } // namespace ipc
 
 namespace dom {
 
 class IPCServiceWorkerRegistrationDescriptor;
+class OptionalIPCServiceWorkerDescriptor;
 class ServiceWorkerInfo;
 enum class ServiceWorkerUpdateViaCache : uint8_t;
 
 // This class represents a snapshot of a particular
 // ServiceWorkerRegistrationInfo object. It is threadsafe and can be
 // transferred across processes.
 class ServiceWorkerRegistrationDescriptor final
 {
@@ -52,17 +52,17 @@ public:
   ServiceWorkerRegistrationDescriptor&
   operator=(const ServiceWorkerRegistrationDescriptor& aRight);
 
   ServiceWorkerRegistrationDescriptor(ServiceWorkerRegistrationDescriptor&& aRight);
 
   ServiceWorkerRegistrationDescriptor&
   operator=(ServiceWorkerRegistrationDescriptor&& aRight);
 
-  ~ServiceWorkerRegistrationDescriptor() = default;
+  ~ServiceWorkerRegistrationDescriptor();
 
   bool
   operator==(const ServiceWorkerRegistrationDescriptor& aRight) const;
 
   ServiceWorkerUpdateViaCache
   UpdateViaCache() const;
 
   const mozilla::ipc::PrincipalInfo&
@@ -90,19 +90,19 @@ public:
   SetUpdateViaCache(ServiceWorkerUpdateViaCache aUpdateViaCache);
 
   void
   SetWorkers(ServiceWorkerInfo* aInstalling,
              ServiceWorkerInfo* aWaiting,
              ServiceWorkerInfo* aActive);
 
   void
-  SetWorkers(OptionalIPCServiceWorkerDescriptor& aInstalling,
-             OptionalIPCServiceWorkerDescriptor& aWaiting,
-             OptionalIPCServiceWorkerDescriptor& aActive);
+  SetWorkers(const OptionalIPCServiceWorkerDescriptor& aInstalling,
+             const OptionalIPCServiceWorkerDescriptor& aWaiting,
+             const OptionalIPCServiceWorkerDescriptor& aActive);
 
   // Expose the underlying IPC type so that it can be passed via IPC.
   const IPCServiceWorkerRegistrationDescriptor&
   ToIPC() const;
 };
 
 } // namespace dom
 } // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/serviceworkers/ServiceWorkerRegistrationImpl.cpp
@@ -0,0 +1,1029 @@
+/* -*- 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 "ServiceWorkerRegistrationImpl.h"
+
+#include "ipc/ErrorIPCUtils.h"
+#include "mozilla/dom/DOMPrefs.h"
+#include "mozilla/dom/Notification.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/PromiseWindowProxy.h"
+#include "mozilla/dom/PromiseWorkerProxy.h"
+#include "mozilla/dom/PushManagerBinding.h"
+#include "mozilla/dom/PushManager.h"
+#include "mozilla/dom/ServiceWorkerRegistrationBinding.h"
+#include "mozilla/dom/WorkerPrivate.h"
+#include "mozilla/dom/WorkerCommon.h"
+#include "mozilla/dom/WorkerScope.h"
+#include "mozilla/Services.h"
+#include "mozilla/Unused.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsNetUtil.h"
+#include "nsServiceManagerUtils.h"
+#include "ServiceWorker.h"
+#include "ServiceWorkerManager.h"
+#include "ServiceWorkerRegistration.h"
+
+#include "nsIDocument.h"
+#include "nsIServiceWorkerManager.h"
+#include "nsISupportsPrimitives.h"
+#include "nsPIDOMWindow.h"
+#include "nsContentUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+////////////////////////////////////////////////////
+// Main Thread implementation
+
+ServiceWorkerRegistrationMainThread::ServiceWorkerRegistrationMainThread(const ServiceWorkerRegistrationDescriptor& aDescriptor)
+  : mOuter(nullptr)
+  , mScope(NS_ConvertUTF8toUTF16(aDescriptor.Scope()))
+  , mListeningForEvents(false)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+}
+
+ServiceWorkerRegistrationMainThread::~ServiceWorkerRegistrationMainThread()
+{
+  MOZ_DIAGNOSTIC_ASSERT(!mListeningForEvents);
+  MOZ_DIAGNOSTIC_ASSERT(!mOuter);
+}
+
+// XXXnsm, maybe this can be optimized to only add when a event handler is
+// registered.
+void
+ServiceWorkerRegistrationMainThread::StartListeningForEvents()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!mListeningForEvents);
+  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+  if (swm) {
+    swm->AddRegistrationEventListener(mScope, this);
+    mListeningForEvents = true;
+  }
+}
+
+void
+ServiceWorkerRegistrationMainThread::StopListeningForEvents()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (!mListeningForEvents) {
+    return;
+  }
+
+  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+  if (swm) {
+    swm->RemoveRegistrationEventListener(mScope, this);
+  }
+  mListeningForEvents = false;
+}
+
+void
+ServiceWorkerRegistrationMainThread::UpdateFound()
+{
+  mOuter->DispatchTrustedEvent(NS_LITERAL_STRING("updatefound"));
+}
+
+void
+ServiceWorkerRegistrationMainThread::UpdateState(const ServiceWorkerRegistrationDescriptor& aDescriptor)
+{
+  mOuter->UpdateState(aDescriptor);
+}
+
+void
+ServiceWorkerRegistrationMainThread::RegistrationRemoved()
+{
+  // If the registration is being removed completely, remove it from the
+  // window registration hash table so that a new registration would get a new
+  // wrapper JS object.
+  if (nsCOMPtr<nsPIDOMWindowInner> window = mOuter->GetOwner()) {
+    window->InvalidateServiceWorkerRegistration(mScope);
+  }
+}
+
+bool
+ServiceWorkerRegistrationMainThread::MatchesDescriptor(const ServiceWorkerRegistrationDescriptor& aDescriptor)
+{
+  return mOuter->MatchesDescriptor(aDescriptor);
+}
+
+void
+ServiceWorkerRegistrationMainThread::SetServiceWorkerRegistration(ServiceWorkerRegistration* aReg)
+{
+  MOZ_DIAGNOSTIC_ASSERT(aReg);
+  MOZ_DIAGNOSTIC_ASSERT(!mOuter);
+  mOuter = aReg;
+  StartListeningForEvents();
+}
+
+void
+ServiceWorkerRegistrationMainThread::ClearServiceWorkerRegistration(ServiceWorkerRegistration* aReg)
+{
+  MOZ_DIAGNOSTIC_ASSERT(mOuter);
+  MOZ_DIAGNOSTIC_ASSERT(mOuter == aReg);
+  StopListeningForEvents();
+  mOuter = nullptr;
+}
+
+namespace {
+
+void
+UpdateInternal(nsIPrincipal* aPrincipal,
+               const nsAString& aScope,
+               ServiceWorkerUpdateFinishCallback* aCallback)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aPrincipal);
+  MOZ_ASSERT(aCallback);
+
+  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+  if (!swm) {
+    // browser shutdown
+    return;
+  }
+
+  swm->Update(aPrincipal, NS_ConvertUTF16toUTF8(aScope), aCallback);
+}
+
+class MainThreadUpdateCallback final : public ServiceWorkerUpdateFinishCallback
+{
+  PromiseWindowProxy mPromise;
+
+  ~MainThreadUpdateCallback()
+  { }
+
+public:
+  explicit MainThreadUpdateCallback(nsPIDOMWindowInner* aWindow,
+                                    Promise* aPromise)
+    : mPromise(aWindow, aPromise)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+  }
+
+  void
+  UpdateSucceeded(ServiceWorkerRegistrationInfo* aRegistration) override
+  {
+    RefPtr<Promise> promise = mPromise.Get();
+    nsCOMPtr<nsPIDOMWindowInner> win = mPromise.GetWindow();
+    if (!promise || !win) {
+      return;
+    }
+
+    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
+      "MainThreadUpdateCallback::UpdateSucceeded",
+      [promise = Move(promise)] () {
+        promise->MaybeResolveWithUndefined();
+      });
+    MOZ_ALWAYS_SUCCEEDS(
+      win->EventTargetFor(TaskCategory::Other)->Dispatch(r.forget()));
+  }
+
+  void
+  UpdateFailed(ErrorResult& aStatus) override
+  {
+    if (RefPtr<Promise> promise = mPromise.Get()) {
+      promise->MaybeReject(aStatus);
+    }
+  }
+};
+
+class UpdateResultRunnable final : public WorkerRunnable
+{
+  RefPtr<PromiseWorkerProxy> mPromiseProxy;
+  IPC::Message mSerializedErrorResult;
+
+  ~UpdateResultRunnable()
+  {}
+
+public:
+  UpdateResultRunnable(PromiseWorkerProxy* aPromiseProxy, ErrorResult& aStatus)
+    : WorkerRunnable(aPromiseProxy->GetWorkerPrivate())
+    , mPromiseProxy(aPromiseProxy)
+  {
+    // ErrorResult is not thread safe.  Serialize it for transfer across
+    // threads.
+    IPC::WriteParam(&mSerializedErrorResult, aStatus);
+    aStatus.SuppressException();
+  }
+
+  bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+  {
+    // Deserialize the ErrorResult now that we are back in the worker
+    // thread.
+    ErrorResult status;
+    PickleIterator iter = PickleIterator(mSerializedErrorResult);
+    Unused << IPC::ReadParam(&mSerializedErrorResult, &iter, &status);
+
+    Promise* promise = mPromiseProxy->WorkerPromise();
+    if (status.Failed()) {
+      promise->MaybeReject(status);
+    } else {
+      promise->MaybeResolveWithUndefined();
+    }
+    status.SuppressException();
+    mPromiseProxy->CleanUp();
+    return true;
+  }
+};
+
+class WorkerThreadUpdateCallback final : public ServiceWorkerUpdateFinishCallback
+{
+  RefPtr<PromiseWorkerProxy> mPromiseProxy;
+
+  ~WorkerThreadUpdateCallback()
+  {
+  }
+
+public:
+  explicit WorkerThreadUpdateCallback(PromiseWorkerProxy* aPromiseProxy)
+    : mPromiseProxy(aPromiseProxy)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+  }
+
+  void
+  UpdateSucceeded(ServiceWorkerRegistrationInfo* aRegistration) override
+  {
+    ErrorResult rv(NS_OK);
+    Finish(rv);
+  }
+
+  void
+  UpdateFailed(ErrorResult& aStatus) override
+  {
+    Finish(aStatus);
+  }
+
+  void
+  Finish(ErrorResult& aStatus)
+  {
+    if (!mPromiseProxy) {
+      return;
+    }
+
+    RefPtr<PromiseWorkerProxy> proxy = mPromiseProxy.forget();
+
+    MutexAutoLock lock(proxy->Lock());
+    if (proxy->CleanedUp()) {
+      return;
+    }
+
+    RefPtr<UpdateResultRunnable> r =
+      new UpdateResultRunnable(proxy, aStatus);
+    r->Dispatch();
+  }
+};
+
+class SWRUpdateRunnable final : public Runnable
+{
+public:
+  SWRUpdateRunnable(PromiseWorkerProxy* aPromiseProxy, const nsAString& aScope)
+    : Runnable("dom::SWRUpdateRunnable")
+    , mPromiseProxy(aPromiseProxy)
+    , mScope(aScope)
+  {}
+
+  NS_IMETHOD
+  Run() override
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    ErrorResult result;
+
+    nsCOMPtr<nsIPrincipal> principal;
+    // UpdateInternal may try to reject the promise synchronously leading
+    // to a deadlock.
+    {
+      MutexAutoLock lock(mPromiseProxy->Lock());
+      if (mPromiseProxy->CleanedUp()) {
+        return NS_OK;
+      }
+
+      principal = mPromiseProxy->GetWorkerPrivate()->GetPrincipal();
+    }
+    MOZ_ASSERT(principal);
+
+    RefPtr<WorkerThreadUpdateCallback> cb =
+      new WorkerThreadUpdateCallback(mPromiseProxy);
+    UpdateInternal(principal, mScope, cb);
+    return NS_OK;
+  }
+
+private:
+  ~SWRUpdateRunnable()
+  {}
+
+  RefPtr<PromiseWorkerProxy> mPromiseProxy;
+  const nsString mScope;
+};
+
+class UnregisterCallback final : public nsIServiceWorkerUnregisterCallback
+{
+  PromiseWindowProxy mPromise;
+
+public:
+  NS_DECL_ISUPPORTS
+
+  explicit UnregisterCallback(nsPIDOMWindowInner* aWindow,
+                              Promise* aPromise)
+    : mPromise(aWindow, aPromise)
+  {
+    MOZ_ASSERT(aPromise);
+  }
+
+  NS_IMETHOD
+  UnregisterSucceeded(bool aState) override
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    RefPtr<Promise> promise = mPromise.Get();
+    nsCOMPtr<nsPIDOMWindowInner> win = mPromise.GetWindow();
+    if (!promise || !win) {
+      return NS_OK;
+    }
+
+    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
+      "UnregisterCallback::UnregisterSucceeded",
+      [promise = Move(promise), aState] () {
+        promise->MaybeResolve(aState);
+      });
+    MOZ_ALWAYS_SUCCEEDS(
+      win->EventTargetFor(TaskCategory::Other)->Dispatch(r.forget()));
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  UnregisterFailed() override
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    if (RefPtr<Promise> promise = mPromise.Get()) {
+      promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
+    }
+    return NS_OK;
+  }
+
+private:
+  ~UnregisterCallback()
+  { }
+};
+
+NS_IMPL_ISUPPORTS(UnregisterCallback, nsIServiceWorkerUnregisterCallback)
+
+class FulfillUnregisterPromiseRunnable final : public WorkerRunnable
+{
+  RefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
+  Maybe<bool> mState;
+public:
+  FulfillUnregisterPromiseRunnable(PromiseWorkerProxy* aProxy,
+                                   const Maybe<bool>& aState)
+    : WorkerRunnable(aProxy->GetWorkerPrivate())
+    , mPromiseWorkerProxy(aProxy)
+    , mState(aState)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(mPromiseWorkerProxy);
+  }
+
+  bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+  {
+    RefPtr<Promise> promise = mPromiseWorkerProxy->WorkerPromise();
+    if (mState.isSome()) {
+      promise->MaybeResolve(mState.value());
+    } else {
+      promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
+    }
+
+    mPromiseWorkerProxy->CleanUp();
+    return true;
+  }
+};
+
+class WorkerUnregisterCallback final : public nsIServiceWorkerUnregisterCallback
+{
+  RefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
+public:
+  NS_DECL_ISUPPORTS
+
+  explicit WorkerUnregisterCallback(PromiseWorkerProxy* aProxy)
+    : mPromiseWorkerProxy(aProxy)
+  {
+    MOZ_ASSERT(aProxy);
+  }
+
+  NS_IMETHOD
+  UnregisterSucceeded(bool aState) override
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    Finish(Some(aState));
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  UnregisterFailed() override
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    Finish(Nothing());
+    return NS_OK;
+  }
+
+private:
+  ~WorkerUnregisterCallback()
+  {}
+
+  void
+  Finish(const Maybe<bool>& aState)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    if (!mPromiseWorkerProxy) {
+      return;
+    }
+
+    RefPtr<PromiseWorkerProxy> proxy = mPromiseWorkerProxy.forget();
+    MutexAutoLock lock(proxy->Lock());
+    if (proxy->CleanedUp()) {
+      return;
+    }
+
+    RefPtr<WorkerRunnable> r =
+      new FulfillUnregisterPromiseRunnable(proxy, aState);
+
+    r->Dispatch();
+  }
+};
+
+NS_IMPL_ISUPPORTS(WorkerUnregisterCallback, nsIServiceWorkerUnregisterCallback);
+
+/*
+ * If the worker goes away, we still continue to unregister, but we don't try to
+ * resolve the worker Promise (which doesn't exist by that point).
+ */
+class StartUnregisterRunnable final : public Runnable
+{
+  RefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
+  const nsString mScope;
+
+public:
+  StartUnregisterRunnable(PromiseWorkerProxy* aProxy, const nsAString& aScope)
+    : Runnable("dom::StartUnregisterRunnable")
+    , mPromiseWorkerProxy(aProxy)
+    , mScope(aScope)
+  {
+    MOZ_ASSERT(aProxy);
+  }
+
+  NS_IMETHOD
+  Run() override
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    // XXXnsm: There is a rare chance of this failing if the worker gets
+    // destroyed. In that case, unregister() called from a SW is no longer
+    // guaranteed to run. We should fix this by having a main thread proxy
+    // maintain a strongref to ServiceWorkerRegistrationInfo and use its
+    // principal. Can that be trusted?
+    nsCOMPtr<nsIPrincipal> principal;
+    {
+      MutexAutoLock lock(mPromiseWorkerProxy->Lock());
+      if (mPromiseWorkerProxy->CleanedUp()) {
+        return NS_OK;
+      }
+
+      WorkerPrivate* worker = mPromiseWorkerProxy->GetWorkerPrivate();
+      MOZ_ASSERT(worker);
+      principal = worker->GetPrincipal();
+    }
+    MOZ_ASSERT(principal);
+
+    RefPtr<WorkerUnregisterCallback> cb =
+      new WorkerUnregisterCallback(mPromiseWorkerProxy);
+    nsCOMPtr<nsIServiceWorkerManager> swm =
+      mozilla::services::GetServiceWorkerManager();
+    nsresult rv = swm->Unregister(principal, cb, mScope);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      cb->UnregisterFailed();
+    }
+
+    return NS_OK;
+  }
+};
+} // namespace
+
+already_AddRefed<Promise>
+ServiceWorkerRegistrationMainThread::Update(ErrorResult& aRv)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(mOuter->GetOwner());
+  if (!go) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  RefPtr<Promise> promise = Promise::Create(go, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIDocument> doc = mOuter->GetOwner()->GetExtantDoc();
+  MOZ_ASSERT(doc);
+
+  RefPtr<MainThreadUpdateCallback> cb =
+    new MainThreadUpdateCallback(mOuter->GetOwner(), promise);
+  UpdateInternal(doc->NodePrincipal(), mScope, cb);
+
+  return promise.forget();
+}
+
+already_AddRefed<Promise>
+ServiceWorkerRegistrationMainThread::Unregister(ErrorResult& aRv)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(mOuter->GetOwner());
+  if (!go) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  // Although the spec says that the same-origin checks should also be done
+  // asynchronously, we do them in sync because the Promise created by the
+  // WebIDL infrastructure due to a returned error will be resolved
+  // asynchronously. We aren't making any internal state changes in these
+  // checks, so ordering of multiple calls is not affected.
+  nsCOMPtr<nsIDocument> document = mOuter->GetOwner()->GetExtantDoc();
+  if (!document) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIURI> scopeURI;
+  nsCOMPtr<nsIURI> baseURI = document->GetBaseURI();
+  // "If the origin of scope is not client's origin..."
+  nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), mScope, nullptr, baseURI);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIPrincipal> documentPrincipal = document->NodePrincipal();
+  rv = documentPrincipal->CheckMayLoad(scopeURI, true /* report */,
+                                       false /* allowIfInheritsPrinciple */);
+  if (NS_FAILED(rv)) {
+    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return nullptr;
+  }
+
+  nsAutoCString uriSpec;
+  aRv = scopeURI->GetSpecIgnoringRef(uriSpec);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIServiceWorkerManager> swm =
+    mozilla::services::GetServiceWorkerManager();
+
+  RefPtr<Promise> promise = Promise::Create(go, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  RefPtr<UnregisterCallback> cb = new UnregisterCallback(mOuter->GetOwner(), promise);
+
+  NS_ConvertUTF8toUTF16 scope(uriSpec);
+  aRv = swm->Unregister(documentPrincipal, cb, scope);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  return promise.forget();
+}
+
+// Notification API extension.
+already_AddRefed<Promise>
+ServiceWorkerRegistrationMainThread::ShowNotification(JSContext* aCx,
+                                                      const nsAString& aTitle,
+                                                      const NotificationOptions& aOptions,
+                                                      ErrorResult& aRv)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  nsCOMPtr<nsPIDOMWindowInner> window = mOuter->GetOwner();
+  if (NS_WARN_IF(!window)) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
+  if (NS_WARN_IF(!doc)) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  RefPtr<ServiceWorker> worker = mOuter->GetActive();
+  if (!worker) {
+    aRv.ThrowTypeError<MSG_NO_ACTIVE_WORKER>(mScope);
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(window);
+  RefPtr<Promise> p =
+    Notification::ShowPersistentNotification(aCx, global, mScope, aTitle,
+                                             aOptions, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  return p.forget();
+}
+
+already_AddRefed<Promise>
+ServiceWorkerRegistrationMainThread::GetNotifications(const GetNotificationOptions& aOptions, ErrorResult& aRv)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  nsCOMPtr<nsPIDOMWindowInner> window = mOuter->GetOwner();
+  if (NS_WARN_IF(!window)) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return nullptr;
+  }
+  return Notification::Get(window, aOptions, mScope, aRv);
+}
+
+already_AddRefed<PushManager>
+ServiceWorkerRegistrationMainThread::GetPushManager(JSContext* aCx,
+                                                    ErrorResult& aRv)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsCOMPtr<nsIGlobalObject> globalObject = do_QueryInterface(mOuter->GetOwner());
+
+  if (!globalObject) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  GlobalObject global(aCx, globalObject->GetGlobalJSObject());
+  RefPtr<PushManager> ret = PushManager::Constructor(global, mScope, aRv);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  return ret.forget();
+}
+
+////////////////////////////////////////////////////
+// Worker Thread implementation
+
+class WorkerListener final : public ServiceWorkerRegistrationListener
+{
+  // Accessed on the main thread.
+  WorkerPrivate* mWorkerPrivate;
+  const nsString mScope;
+  bool mListeningForEvents;
+
+  // Accessed on the worker thread.
+  ServiceWorkerRegistrationWorkerThread* mRegistration;
+
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WorkerListener, override)
+
+  WorkerListener(WorkerPrivate* aWorkerPrivate,
+                 ServiceWorkerRegistrationWorkerThread* aReg,
+                 const nsAString& aScope)
+    : mWorkerPrivate(aWorkerPrivate)
+    , mScope(aScope)
+    , mListeningForEvents(false)
+    , mRegistration(aReg)
+  {
+    MOZ_ASSERT(mWorkerPrivate);
+    mWorkerPrivate->AssertIsOnWorkerThread();
+    MOZ_ASSERT(mRegistration);
+  }
+
+  void
+  StartListeningForEvents()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(!mListeningForEvents);
+    MOZ_ASSERT(mWorkerPrivate);
+    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+    if (swm) {
+      // FIXME(nsm): Maybe the function shouldn't take an explicit scope.
+      swm->AddRegistrationEventListener(mScope, this);
+      mListeningForEvents = true;
+    }
+  }
+
+  void
+  StopListeningForEvents()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    MOZ_ASSERT(mListeningForEvents);
+
+    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+
+    // We aren't going to need this anymore and we shouldn't hold on since the
+    // worker will go away soon.
+    mWorkerPrivate = nullptr;
+
+    if (swm) {
+      // FIXME(nsm): Maybe the function shouldn't take an explicit scope.
+      swm->RemoveRegistrationEventListener(mScope, this);
+      mListeningForEvents = false;
+    }
+  }
+
+  // ServiceWorkerRegistrationListener
+  void
+  UpdateFound() override;
+
+  void
+  UpdateState(const ServiceWorkerRegistrationDescriptor& aDescriptor) override
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    // TODO: Not implemented
+  }
+
+  void
+  RegistrationRemoved() override
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+  }
+
+  void
+  GetScope(nsAString& aScope) const override
+  {
+    aScope = mScope;
+  }
+
+  bool
+  MatchesDescriptor(const ServiceWorkerRegistrationDescriptor& aDescriptor) override
+  {
+    // TODO: Not implemented
+    return false;
+  }
+
+  ServiceWorkerRegistrationWorkerThread*
+  GetRegistration() const
+  {
+    if (mWorkerPrivate) {
+      mWorkerPrivate->AssertIsOnWorkerThread();
+    }
+    return mRegistration;
+  }
+
+  void
+  ClearRegistration()
+  {
+    if (mWorkerPrivate) {
+      mWorkerPrivate->AssertIsOnWorkerThread();
+    }
+    mRegistration = nullptr;
+  }
+
+private:
+  ~WorkerListener()
+  {
+    MOZ_ASSERT(!mListeningForEvents);
+  }
+};
+
+ServiceWorkerRegistrationWorkerThread::ServiceWorkerRegistrationWorkerThread(WorkerPrivate* aWorkerPrivate,
+                                                                             const ServiceWorkerRegistrationDescriptor& aDescriptor)
+  : WorkerHolder("ServiceWorkerRegistrationWorkerThread")
+  , mOuter(nullptr)
+  , mWorkerPrivate(aWorkerPrivate)
+  , mScope(NS_ConvertUTF8toUTF16(aDescriptor.Scope()))
+{
+}
+
+ServiceWorkerRegistrationWorkerThread::~ServiceWorkerRegistrationWorkerThread()
+{
+  MOZ_DIAGNOSTIC_ASSERT(!mListener);
+  MOZ_DIAGNOSTIC_ASSERT(!mOuter);
+}
+
+void
+ServiceWorkerRegistrationWorkerThread::SetServiceWorkerRegistration(ServiceWorkerRegistration* aReg)
+{
+  MOZ_DIAGNOSTIC_ASSERT(aReg);
+  MOZ_DIAGNOSTIC_ASSERT(!mOuter);
+  mOuter = aReg;
+  InitListener();
+}
+
+void
+ServiceWorkerRegistrationWorkerThread::ClearServiceWorkerRegistration(ServiceWorkerRegistration* aReg)
+{
+  MOZ_DIAGNOSTIC_ASSERT(mOuter);
+  MOZ_DIAGNOSTIC_ASSERT(mOuter == aReg);
+  ReleaseListener();
+  mOuter = nullptr;
+}
+
+already_AddRefed<Promise>
+ServiceWorkerRegistrationWorkerThread::Update(ErrorResult& aRv)
+{
+  WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
+  MOZ_ASSERT(worker);
+  worker->AssertIsOnWorkerThread();
+
+  RefPtr<Promise> promise = Promise::Create(worker->GlobalScope(), aRv);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  // Avoid infinite update loops by ignoring update() calls during top
+  // level script evaluation.  See:
+  // https://github.com/slightlyoff/ServiceWorker/issues/800
+  if (worker->LoadScriptAsPartOfLoadingServiceWorkerScript()) {
+    promise->MaybeResolveWithUndefined();
+    return promise.forget();
+  }
+
+  RefPtr<PromiseWorkerProxy> proxy = PromiseWorkerProxy::Create(worker, promise);
+  if (!proxy) {
+    aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
+    return nullptr;
+  }
+
+  RefPtr<SWRUpdateRunnable> r = new SWRUpdateRunnable(proxy, mScope);
+  MOZ_ALWAYS_SUCCEEDS(worker->DispatchToMainThread(r.forget()));
+
+  return promise.forget();
+}
+
+already_AddRefed<Promise>
+ServiceWorkerRegistrationWorkerThread::Unregister(ErrorResult& aRv)
+{
+  WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
+  MOZ_ASSERT(worker);
+  worker->AssertIsOnWorkerThread();
+
+  if (!worker->IsServiceWorker()) {
+    // For other workers, the registration probably originated from
+    // getRegistration(), so we may have to validate origin etc. Let's do this
+    // this later.
+    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return nullptr;
+  }
+
+  RefPtr<Promise> promise = Promise::Create(worker->GlobalScope(), aRv);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  RefPtr<PromiseWorkerProxy> proxy = PromiseWorkerProxy::Create(worker, promise);
+  if (!proxy) {
+    aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
+    return nullptr;
+  }
+
+  RefPtr<StartUnregisterRunnable> r = new StartUnregisterRunnable(proxy, mScope);
+  MOZ_ALWAYS_SUCCEEDS(worker->DispatchToMainThread(r.forget()));
+
+  return promise.forget();
+}
+
+void
+ServiceWorkerRegistrationWorkerThread::InitListener()
+{
+  MOZ_ASSERT(!mListener);
+  WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
+  MOZ_ASSERT(worker);
+  worker->AssertIsOnWorkerThread();
+
+  mListener = new WorkerListener(worker, this, mScope);
+  if (!HoldWorker(worker, Closing)) {
+    mListener = nullptr;
+    NS_WARNING("Could not add feature");
+    return;
+  }
+
+  nsCOMPtr<nsIRunnable> r =
+    NewRunnableMethod("dom::WorkerListener::StartListeningForEvents",
+                      mListener,
+                      &WorkerListener::StartListeningForEvents);
+  MOZ_ALWAYS_SUCCEEDS(worker->DispatchToMainThread(r.forget()));
+}
+
+void
+ServiceWorkerRegistrationWorkerThread::ReleaseListener()
+{
+  if (!mListener) {
+    return;
+  }
+
+  // We can assert worker here, because:
+  // 1) We always HoldWorker, so if the worker has shutdown already, we'll
+  //    have received Notify and removed it. If HoldWorker had failed,
+  //    mListener will be null and we won't reach here.
+  // 2) Otherwise, worker is still around even if we are going away.
+  mWorkerPrivate->AssertIsOnWorkerThread();
+  ReleaseWorker();
+
+  mListener->ClearRegistration();
+
+  nsCOMPtr<nsIRunnable> r =
+    NewRunnableMethod("dom::WorkerListener::StopListeningForEvents",
+                      mListener,
+                      &WorkerListener::StopListeningForEvents);
+  MOZ_ALWAYS_SUCCEEDS(mWorkerPrivate->DispatchToMainThread(r.forget()));
+
+  mListener = nullptr;
+  mWorkerPrivate = nullptr;
+}
+
+bool
+ServiceWorkerRegistrationWorkerThread::Notify(WorkerStatus aStatus)
+{
+  ReleaseListener();
+  return true;
+}
+
+class FireUpdateFoundRunnable final : public WorkerRunnable
+{
+  RefPtr<WorkerListener> mListener;
+public:
+  FireUpdateFoundRunnable(WorkerPrivate* aWorkerPrivate,
+                          WorkerListener* aListener)
+    : WorkerRunnable(aWorkerPrivate)
+    , mListener(aListener)
+  {
+    // Need this assertion for now since runnables which modify busy count can
+    // only be dispatched from parent thread to worker thread and we don't deal
+    // with nested workers. SW threads can't be nested.
+    MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
+  }
+
+  bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+  {
+    MOZ_ASSERT(aWorkerPrivate);
+    aWorkerPrivate->AssertIsOnWorkerThread();
+
+    ServiceWorkerRegistrationWorkerThread* reg = mListener->GetRegistration();
+    if (reg) {
+      reg->UpdateFound();
+    }
+    return true;
+  }
+};
+
+void
+WorkerListener::UpdateFound()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (mWorkerPrivate) {
+    RefPtr<FireUpdateFoundRunnable> r =
+      new FireUpdateFoundRunnable(mWorkerPrivate, this);
+    Unused << NS_WARN_IF(!r->Dispatch());
+  }
+}
+
+// Notification API extension.
+already_AddRefed<Promise>
+ServiceWorkerRegistrationWorkerThread::ShowNotification(JSContext* aCx,
+                                                        const nsAString& aTitle,
+                                                        const NotificationOptions& aOptions,
+                                                        ErrorResult& aRv)
+{
+
+  // Until Bug 1131324 exposes ServiceWorkerContainer on workers,
+  // ShowPersistentNotification() checks for valid active worker while it is
+  // also verifying scope so that we block the worker on the main thread only
+  // once.
+  RefPtr<Promise> p =
+    Notification::ShowPersistentNotification(aCx, mWorkerPrivate->GlobalScope(),
+                                             mScope, aTitle, aOptions, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  return p.forget();
+}
+
+already_AddRefed<Promise>
+ServiceWorkerRegistrationWorkerThread::GetNotifications(const GetNotificationOptions& aOptions,
+                                                        ErrorResult& aRv)
+{
+  return Notification::WorkerGet(mWorkerPrivate, aOptions, mScope, aRv);
+}
+
+already_AddRefed<PushManager>
+ServiceWorkerRegistrationWorkerThread::GetPushManager(JSContext* aCx, ErrorResult& aRv)
+{
+  RefPtr<PushManager> ret = new PushManager(mScope);
+  return ret.forget();
+}
+
+void
+ServiceWorkerRegistrationWorkerThread::UpdateFound()
+{
+  mOuter->DispatchTrustedEvent(NS_LITERAL_STRING("updatefound"));
+}
+
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/serviceworkers/ServiceWorkerRegistrationImpl.h
@@ -0,0 +1,156 @@
+/* -*- 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/dom/WorkerPrivate.h"
+#include "mozilla/Unused.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsIDocument.h"
+#include "nsPIDOMWindow.h"
+#include "ServiceWorkerManager.h"
+#include "ServiceWorkerRegistration.h"
+#include "ServiceWorkerRegistrationListener.h"
+
+namespace mozilla {
+namespace dom {
+
+class Promise;
+class PushManager;
+class ServiceWorker;
+
+////////////////////////////////////////////////////
+// Main Thread implementation
+
+class ServiceWorkerRegistrationMainThread final : public ServiceWorkerRegistration::Inner
+                                                , public ServiceWorkerRegistrationListener
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(ServiceWorkerRegistrationMainThread, override)
+
+  explicit ServiceWorkerRegistrationMainThread(const ServiceWorkerRegistrationDescriptor& aDescriptor);
+
+  // ServiceWorkerRegistration::Inner
+  void
+  SetServiceWorkerRegistration(ServiceWorkerRegistration* aReg) override;
+
+  void
+  ClearServiceWorkerRegistration(ServiceWorkerRegistration* aReg) override;
+
+  already_AddRefed<Promise>
+  Update(ErrorResult& aRv) override;
+
+  already_AddRefed<Promise>
+  Unregister(ErrorResult& aRv) override;
+
+  already_AddRefed<Promise>
+  ShowNotification(JSContext* aCx,
+                   const nsAString& aTitle,
+                   const NotificationOptions& aOptions,
+                   ErrorResult& aRv) override;
+
+  already_AddRefed<Promise>
+  GetNotifications(const GetNotificationOptions& aOptions,
+                   ErrorResult& aRv) override;
+
+  already_AddRefed<PushManager>
+  GetPushManager(JSContext* aCx, ErrorResult& aRv) override;
+
+  // ServiceWorkerRegistrationListener
+  void
+  UpdateFound() override;
+
+  void
+  UpdateState(const ServiceWorkerRegistrationDescriptor& aDescriptor) override;
+
+  void
+  RegistrationRemoved() override;
+
+  void
+  GetScope(nsAString& aScope) const override
+  {
+    aScope = mScope;
+  }
+
+  bool
+  MatchesDescriptor(const ServiceWorkerRegistrationDescriptor& aDescriptor) override;
+
+private:
+  ~ServiceWorkerRegistrationMainThread();
+
+  void
+  StartListeningForEvents();
+
+  void
+  StopListeningForEvents();
+
+  ServiceWorkerRegistration* mOuter;
+  const nsString mScope;
+  bool mListeningForEvents;
+};
+
+////////////////////////////////////////////////////
+// Worker Thread implementation
+
+class WorkerListener;
+
+class ServiceWorkerRegistrationWorkerThread final : public ServiceWorkerRegistration::Inner
+                                                  , public WorkerHolder
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(ServiceWorkerRegistrationWorkerThread, override)
+
+  ServiceWorkerRegistrationWorkerThread(WorkerPrivate* aWorkerPrivate,
+                                        const ServiceWorkerRegistrationDescriptor& aDescriptor);
+
+  // ServiceWorkerRegistration::Inner
+  void
+  SetServiceWorkerRegistration(ServiceWorkerRegistration* aReg) override;
+
+  void
+  ClearServiceWorkerRegistration(ServiceWorkerRegistration* aReg) override;
+
+  already_AddRefed<Promise>
+  Update(ErrorResult& aRv) override;
+
+  already_AddRefed<Promise>
+  Unregister(ErrorResult& aRv) override;
+
+  already_AddRefed<Promise>
+  ShowNotification(JSContext* aCx,
+                   const nsAString& aTitle,
+                   const NotificationOptions& aOptions,
+                   ErrorResult& aRv) override;
+
+  already_AddRefed<Promise>
+  GetNotifications(const GetNotificationOptions& aOptions,
+                   ErrorResult& aRv) override;
+
+  already_AddRefed<PushManager>
+  GetPushManager(JSContext* aCx, ErrorResult& aRv) override;
+
+  // WorkerHolder
+  bool
+  Notify(WorkerStatus aStatus) override;
+
+  void
+  UpdateFound();
+
+private:
+  ~ServiceWorkerRegistrationWorkerThread();
+
+  void
+  InitListener();
+
+  void
+  ReleaseListener();
+
+  ServiceWorkerRegistration* mOuter;
+  WorkerPrivate* mWorkerPrivate;
+  const nsString mScope;
+  RefPtr<WorkerListener> mListener;
+};
+
+} // dom namespace
+} // mozilla namespace
--- a/dom/serviceworkers/ServiceWorkerRegistrationInfo.cpp
+++ b/dom/serviceworkers/ServiceWorkerRegistrationInfo.cpp
@@ -47,44 +47,41 @@ public:
 
 void
 ServiceWorkerRegistrationInfo::Clear()
 {
   if (mEvaluatingWorker) {
     mEvaluatingWorker = nullptr;
   }
 
-  UpdateRegistrationStateProperties(WhichServiceWorker::INSTALLING_WORKER |
-                                    WhichServiceWorker::WAITING_WORKER |
-                                    WhichServiceWorker::ACTIVE_WORKER, Invalidate);
+  RefPtr<ServiceWorkerInfo> installing = mInstallingWorker.forget();
+  RefPtr<ServiceWorkerInfo> waiting = mWaitingWorker.forget();
+  RefPtr<ServiceWorkerInfo> active = mActiveWorker.forget();
 
-  if (mInstallingWorker) {
-    mInstallingWorker->UpdateState(ServiceWorkerState::Redundant);
-    mInstallingWorker->UpdateRedundantTime();
-    mInstallingWorker->WorkerPrivate()->NoteDeadServiceWorkerInfo();
-    mInstallingWorker = nullptr;
+  UpdateRegistrationState();
+
+  if (installing) {
+    installing->UpdateState(ServiceWorkerState::Redundant);
+    installing->UpdateRedundantTime();
+    installing->WorkerPrivate()->NoteDeadServiceWorkerInfo();
     // FIXME(nsm): Abort any inflight requests from installing worker.
   }
 
-  if (mWaitingWorker) {
-    mWaitingWorker->UpdateState(ServiceWorkerState::Redundant);
-    mWaitingWorker->UpdateRedundantTime();
-    mWaitingWorker->WorkerPrivate()->NoteDeadServiceWorkerInfo();
-    mWaitingWorker = nullptr;
+  if (waiting) {
+    waiting->UpdateState(ServiceWorkerState::Redundant);
+    waiting->UpdateRedundantTime();
+    waiting->WorkerPrivate()->NoteDeadServiceWorkerInfo();
   }
 
-  if (mActiveWorker) {
-    mActiveWorker->UpdateState(ServiceWorkerState::Redundant);
-    mActiveWorker->UpdateRedundantTime();
-    mActiveWorker->WorkerPrivate()->NoteDeadServiceWorkerInfo();
-    mActiveWorker = nullptr;
+  if (active) {
+    active->UpdateState(ServiceWorkerState::Redundant);
+    active->UpdateRedundantTime();
+    active->WorkerPrivate()->NoteDeadServiceWorkerInfo();
   }
 
-  mDescriptor.SetWorkers(mInstallingWorker, mWaitingWorker, mActiveWorker);
-
   NotifyChromeRegistrationListeners();
 }
 
 ServiceWorkerRegistrationInfo::ServiceWorkerRegistrationInfo(
     const nsACString& aScope,
     nsIPrincipal* aPrincipal,
     ServiceWorkerUpdateViaCache aUpdateViaCache)
   : mPrincipal(aPrincipal)
@@ -386,53 +383,32 @@ ServiceWorkerRegistrationInfo::IsLastUpd
   if (nowMicros < mLastUpdateTime ||
       (nowMicros - mLastUpdateTime) / PR_USEC_PER_SEC > kSecondsPerDay) {
     return true;
   }
   return false;
 }
 
 void
-ServiceWorkerRegistrationInfo::AsyncUpdateRegistrationStateProperties(WhichServiceWorker aWorker,
-                                                                      TransitionType aTransition)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
-  if (!swm) {
-    // browser shutdown started during this async step
-    return;
-  }
-
-  if (aTransition == Invalidate) {
-    swm->InvalidateServiceWorkerRegistrationWorker(this, aWorker);
-  } else {
-    MOZ_ASSERT(aTransition == TransitionToNextState);
-    swm->TransitionServiceWorkerRegistrationWorker(this, aWorker);
-
-    if (aWorker == WhichServiceWorker::WAITING_WORKER) {
-      swm->CheckPendingReadyPromises();
-    }
-  }
-}
-
-void
-ServiceWorkerRegistrationInfo::UpdateRegistrationStateProperties(WhichServiceWorker aWorker,
-                                                                 TransitionType aTransition)
+ServiceWorkerRegistrationInfo::UpdateRegistrationState()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  nsCOMPtr<nsIRunnable> runnable =
-    NewRunnableMethod<WhichServiceWorker, TransitionType>(
-      "dom::ServiceWorkerRegistrationInfo::"
-      "AsyncUpdateRegistrationStateProperties",
-      this,
-      &ServiceWorkerRegistrationInfo::AsyncUpdateRegistrationStateProperties,
-      aWorker,
-      aTransition);
-  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable.forget()));
+  mDescriptor.SetWorkers(mInstallingWorker, mWaitingWorker, mActiveWorker);
+
+  RefPtr<ServiceWorkerRegistrationInfo> self(this);
+  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
+    "ServiceWorkerRegistrationInfo::UpdateRegistrationState",
+    [self = Move(self)] {
+      RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+      if (swm) {
+        swm->UpdateRegistrationListeners(self);
+      }
+    });
+  MOZ_ALWAYS_SUCCEEDS(SystemGroup::Dispatch(TaskCategory::Other, r.forget()));
 }
 
 void
 ServiceWorkerRegistrationInfo::NotifyChromeRegistrationListeners()
 {
   nsTArray<nsCOMPtr<nsIServiceWorkerRegistrationInfoListener>> listeners(mListeners);
   for (size_t index = 0; index < listeners.Length(); ++index) {
     listeners[index]->OnChange();
@@ -565,38 +541,38 @@ void
 ServiceWorkerRegistrationInfo::ClearInstalling()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!mInstallingWorker) {
     return;
   }
 
-  UpdateRegistrationStateProperties(WhichServiceWorker::INSTALLING_WORKER,
-                                    Invalidate);
-  mInstallingWorker->UpdateState(ServiceWorkerState::Redundant);
-  mInstallingWorker->UpdateRedundantTime();
-  mInstallingWorker = nullptr;
+  RefPtr<ServiceWorkerInfo> installing = mInstallingWorker.forget();
 
-  mDescriptor.SetWorkers(mInstallingWorker, mWaitingWorker, mActiveWorker);
+  UpdateRegistrationState();
+
+  installing->UpdateState(ServiceWorkerState::Redundant);
+  installing->UpdateRedundantTime();
 
   NotifyChromeRegistrationListeners();
 }
 
 void
 ServiceWorkerRegistrationInfo::TransitionEvaluatingToInstalling()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mEvaluatingWorker);
   MOZ_ASSERT(!mInstallingWorker);
 
   mInstallingWorker = mEvaluatingWorker.forget();
-  mInstallingWorker->UpdateState(ServiceWorkerState::Installing);
 
-  mDescriptor.SetWorkers(mInstallingWorker, mWaitingWorker, mActiveWorker);
+  UpdateRegistrationState();
+
+  mInstallingWorker->UpdateState(ServiceWorkerState::Installing);
 
   NotifyChromeRegistrationListeners();
 }
 
 void
 ServiceWorkerRegistrationInfo::TransitionInstallingToWaiting()
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -604,23 +580,22 @@ ServiceWorkerRegistrationInfo::Transitio
 
   if (mWaitingWorker) {
     MOZ_ASSERT(mInstallingWorker->CacheName() != mWaitingWorker->CacheName());
     mWaitingWorker->UpdateState(ServiceWorkerState::Redundant);
     mWaitingWorker->UpdateRedundantTime();
   }
 
   mWaitingWorker = mInstallingWorker.forget();
-  UpdateRegistrationStateProperties(WhichServiceWorker::INSTALLING_WORKER,
-                                    TransitionToNextState);
+
+  UpdateRegistrationState();
+
   mWaitingWorker->UpdateState(ServiceWorkerState::Installed);
   mWaitingWorker->UpdateInstalledTime();
 
-  mDescriptor.SetWorkers(mInstallingWorker, mWaitingWorker, mActiveWorker);
-
   NotifyChromeRegistrationListeners();
 
   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
   if (!swm) {
     // browser shutdown began
     return;
   }
   swm->StoreRegistration(mPrincipal, this);
@@ -646,21 +621,20 @@ ServiceWorkerRegistrationInfo::SetActive
     mActiveWorker->UpdateRedundantTime();
   }
 
   // The active worker is being overriden due to initial load or
   // another process activating a worker.  Move straight to the
   // Activated state.
   mActiveWorker = aServiceWorker;
   mActiveWorker->SetActivateStateUncheckedWithoutEvent(ServiceWorkerState::Activated);
+
   // We don't need to update activated time when we load registration from
   // registrar.
-  UpdateRegistrationStateProperties(WhichServiceWorker::ACTIVE_WORKER, Invalidate);
-
-  mDescriptor.SetWorkers(mInstallingWorker, mWaitingWorker, mActiveWorker);
+  UpdateRegistrationState();
 
   NotifyChromeRegistrationListeners();
 }
 
 void
 ServiceWorkerRegistrationInfo::TransitionWaitingToActive()
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -670,21 +644,30 @@ ServiceWorkerRegistrationInfo::Transitio
     MOZ_ASSERT(mWaitingWorker->CacheName() != mActiveWorker->CacheName());
     mActiveWorker->UpdateState(ServiceWorkerState::Redundant);
     mActiveWorker->UpdateRedundantTime();
   }
 
   // We are transitioning from waiting to active normally, so go to
   // the activating state.
   mActiveWorker = mWaitingWorker.forget();
-  UpdateRegistrationStateProperties(WhichServiceWorker::WAITING_WORKER,
-                                    TransitionToNextState);
+
+  UpdateRegistrationState();
+
   mActiveWorker->UpdateState(ServiceWorkerState::Activating);
 
-  mDescriptor.SetWorkers(mInstallingWorker, mWaitingWorker, mActiveWorker);
+  nsCOMPtr<nsIRunnable> r =
+    NS_NewRunnableFunction("ServiceWorkerRegistrationInfo::TransitionWaitingToActive",
+    [] {
+      RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+      if (swm) {
+        swm->CheckPendingReadyPromises();
+      }
+    });
+  MOZ_ALWAYS_SUCCEEDS(SystemGroup::Dispatch(TaskCategory::Other, r.forget()));
 
   NotifyChromeRegistrationListeners();
 }
 
 bool
 ServiceWorkerRegistrationInfo::IsIdle() const
 {
   return !mActiveWorker || mActiveWorker->WorkerPrivate()->IsIdle();
@@ -696,16 +679,17 @@ ServiceWorkerRegistrationInfo::GetUpdate
   return mDescriptor.UpdateViaCache();
 }
 
 void
 ServiceWorkerRegistrationInfo::SetUpdateViaCache(
     ServiceWorkerUpdateViaCache aUpdateViaCache)
 {
   mDescriptor.SetUpdateViaCache(aUpdateViaCache);
+  UpdateRegistrationState();
 }
 
 int64_t
 ServiceWorkerRegistrationInfo::GetLastUpdateTime() const
 {
   return mLastUpdateTime;
 }
 
--- a/dom/serviceworkers/ServiceWorkerRegistrationInfo.h
+++ b/dom/serviceworkers/ServiceWorkerRegistrationInfo.h
@@ -3,17 +3,16 @@
 /* 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 mozilla_dom_serviceworkerregistrationinfo_h
 #define mozilla_dom_serviceworkerregistrationinfo_h
 
 #include "mozilla/dom/ServiceWorkerInfo.h"
-#include "mozilla/dom/ServiceWorkerCommon.h"
 #include "mozilla/dom/ServiceWorkerRegistrationBinding.h"
 #include "mozilla/dom/ServiceWorkerRegistrationDescriptor.h"
 #include "nsProxyRelease.h"
 
 namespace mozilla {
 namespace dom {
 
 class ServiceWorkerRegistrationInfo final
@@ -208,30 +207,21 @@ public:
 
   void
   SetLastUpdateTime(const int64_t aTime);
 
   const ServiceWorkerRegistrationDescriptor&
   Descriptor() const;
 
 private:
-  enum TransitionType {
-    TransitionToNextState = 0,
-    Invalidate
-  };
-
-  // Queued as a runnable from UpdateRegistrationStateProperties.
-  void
-  AsyncUpdateRegistrationStateProperties(WhichServiceWorker aWorker, TransitionType aType);
-
   // Roughly equivalent to [[Update Registration State algorithm]]. Make sure
   // this is called *before* updating SW instances' state, otherwise they
   // may get CC-ed.
   void
-  UpdateRegistrationStateProperties(WhichServiceWorker aWorker, TransitionType aType);
+  UpdateRegistrationState();
 
   // Used by devtools to track changes to the properties of *nsIServiceWorkerRegistrationInfo*.
   // Note, this doesn't necessarily need to be in sync with the DOM registration objects, but
   // it does need to be called in the same task that changed |mInstallingWorker|,
   // |mWaitingWorker| or |mActiveWorker|.
   void
   NotifyChromeRegistrationListeners();
 };
new file mode 100644
--- /dev/null
+++ b/dom/serviceworkers/ServiceWorkerRegistrationListener.h
@@ -0,0 +1,42 @@
+/* -*- 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 mozilla_dom_ServiceWorkerRegistrationListener_h
+#define mozilla_dom_ServiceWorkerRegistrationListener_h
+
+namespace mozilla {
+namespace dom {
+
+class ServiceWorkerRegistrationDescriptor;
+
+// Used by ServiceWorkerManager to notify ServiceWorkerRegistrations of
+// updatefound event and invalidating ServiceWorker instances.
+class ServiceWorkerRegistrationListener
+{
+public:
+  NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
+
+  virtual void
+  UpdateFound() = 0;
+
+  virtual void
+  UpdateState(const ServiceWorkerRegistrationDescriptor& aDescriptor) = 0;
+
+  virtual void
+  RegistrationRemoved() = 0;
+
+  virtual void
+  GetScope(nsAString& aScope) const = 0;
+
+  virtual bool
+  MatchesDescriptor(const ServiceWorkerRegistrationDescriptor& aDescriptor) = 0;
+};
+
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_ServiceWorkerRegistrationListener_h */
--- a/dom/serviceworkers/moz.build
+++ b/dom/serviceworkers/moz.build
@@ -5,17 +5,16 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 with Files("**"):
     BUG_COMPONENT = ("Core", "DOM: Service Workers")
 
 # Public stuff.
 EXPORTS.mozilla.dom += [
     'ServiceWorker.h',
-    'ServiceWorkerCommon.h',
     'ServiceWorkerContainer.h',
     'ServiceWorkerDescriptor.h',
     'ServiceWorkerEvents.h',
     'ServiceWorkerInfo.h',
     'ServiceWorkerInterceptController.h',
     'ServiceWorkerIPCUtils.h',
     'ServiceWorkerManager.h',
     'ServiceWorkerManagerChild.h',
@@ -40,16 +39,17 @@ UNIFIED_SOURCES += [
     'ServiceWorkerManagerChild.cpp',
     'ServiceWorkerManagerParent.cpp',
     'ServiceWorkerManagerService.cpp',
     'ServiceWorkerPrivate.cpp',
     'ServiceWorkerRegisterJob.cpp',
     'ServiceWorkerRegistrar.cpp',
     'ServiceWorkerRegistration.cpp',
     'ServiceWorkerRegistrationDescriptor.cpp',
+    'ServiceWorkerRegistrationImpl.cpp',
     'ServiceWorkerRegistrationInfo.cpp',
     'ServiceWorkerScriptCache.cpp',
     'ServiceWorkerUnregisterJob.cpp',
     'ServiceWorkerUpdateJob.cpp',
     'ServiceWorkerUpdaterChild.cpp',
     'ServiceWorkerUpdaterParent.cpp',
     'ServiceWorkerUtils.cpp',
 ]
--- a/dom/serviceworkers/test/test_bug1408734.html
+++ b/dom/serviceworkers/test/test_bug1408734.html
@@ -43,23 +43,16 @@ add_task(async () => {
   registration = await navigator.serviceWorker.getRegistration("./");
   ok(registration, "should get the registration under scope './'");
 
   // call unregister()
   await registration.unregister();
 
   // access registration.updateViaCache to trigger the bug
   // we really care that we don't crash. In the future we will fix
-  // updateViaCache to be accessible after unregister().
-  try {
-    if (registration.updateViaCache) {
-      ok(false,
-         "Expected InvalidStateError when accessing registration.updateViaCache after unregister()");
-    }
-  } catch (err) {
-    is(err.name, "InvalidStateError", "Expected InvalidStateError.");
-  }
+  is(registration.updateViaCache, "imports",
+     "registration.updateViaCache should work after unregister()");
 });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/webidl/TreeBoxObject.webidl
+++ b/dom/webidl/TreeBoxObject.webidl
@@ -22,17 +22,17 @@ interface TreeBoxObject : BoxObject {
    */
   readonly attribute TreeColumns? columns;
 
   /**
    * The view that backs the tree and that supplies it with its data.
    * It is dynamically settable, either using a view attribute on the
    * tree tag or by setting this attribute to a new value.
    */
-  [SetterThrows]
+  [SetterThrows, NeedsCallerType]
   attribute MozTreeView? view;
 
   /**
    * Whether or not we are currently focused.
    */
   attribute boolean focused;
 
   /**
--- a/dom/workers/WorkerLoadInfo.cpp
+++ b/dom/workers/WorkerLoadInfo.cpp
@@ -141,16 +141,17 @@ WorkerLoadInfo::StealFrom(WorkerLoadInfo
 
   MOZ_ASSERT(!mPrincipalInfo);
   mPrincipalInfo = aOther.mPrincipalInfo.forget();
 
   mDomain = aOther.mDomain;
   mOrigin = aOther.mOrigin;
   mServiceWorkerCacheName = aOther.mServiceWorkerCacheName;
   mServiceWorkerDescriptor = aOther.mServiceWorkerDescriptor;
+  mServiceWorkerRegistrationDescriptor = aOther.mServiceWorkerRegistrationDescriptor;
   mLoadFlags = aOther.mLoadFlags;
   mWindowID = aOther.mWindowID;
   mReferrerPolicy = aOther.mReferrerPolicy;
   mFromWindow = aOther.mFromWindow;
   mEvalAllowed = aOther.mEvalAllowed;
   mReportCSPViolations = aOther.mReportCSPViolations;
   mXHRParamsAllowed = aOther.mXHRParamsAllowed;
   mPrincipalIsSystem = aOther.mPrincipalIsSystem;
--- a/dom/workers/WorkerLoadInfo.h
+++ b/dom/workers/WorkerLoadInfo.h
@@ -3,16 +3,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/. */
 
 #ifndef mozilla_dom_workers_WorkerLoadInfo_h
 #define mozilla_dom_workers_WorkerLoadInfo_h
 
 #include "mozilla/dom/ChannelInfo.h"
+#include "mozilla/dom/ServiceWorkerRegistrationDescriptor.h"
 #include "mozilla/dom/WorkerCommon.h"
 #include "mozilla/net/ReferrerPolicy.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsILoadContext.h"
 #include "nsIRequest.h"
 #include "nsISupportsImpl.h"
 #include "nsIWeakReferenceUtils.h"
 
@@ -87,16 +88,17 @@ struct WorkerLoadInfo
   RefPtr<InterfaceRequestor> mInterfaceRequestor;
 
   nsAutoPtr<mozilla::ipc::PrincipalInfo> mPrincipalInfo;
   nsCString mDomain;
   nsString mOrigin; // Derived from mPrincipal; can be used on worker thread.
 
   nsString mServiceWorkerCacheName;
   Maybe<ServiceWorkerDescriptor> mServiceWorkerDescriptor;
+  Maybe<ServiceWorkerRegistrationDescriptor> mServiceWorkerRegistrationDescriptor;
 
   Maybe<ServiceWorkerDescriptor> mParentController;
 
   ChannelInfo mChannelInfo;
   nsLoadFlags mLoadFlags;
 
   uint64_t mWindowID;
 
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -5135,17 +5135,19 @@ WorkerPrivate::GetOrCreateGlobalScope(JS
 {
   AssertIsOnWorkerThread();
 
   if (!mScope) {
     RefPtr<WorkerGlobalScope> globalScope;
     if (IsSharedWorker()) {
       globalScope = new SharedWorkerGlobalScope(this, WorkerName());
     } else if (IsServiceWorker()) {
-      globalScope = new ServiceWorkerGlobalScope(this, ServiceWorkerScope());
+      globalScope =
+        new ServiceWorkerGlobalScope(this,
+                                     GetServiceWorkerRegistrationDescriptor());
     } else {
       globalScope = new DedicatedWorkerGlobalScope(this, WorkerName());
     }
 
     JS::Rooted<JSObject*> global(aCx);
     NS_ENSURE_TRUE(globalScope->WrapGlobalObject(aCx, &global), nullptr);
 
     JSAutoCompartment ac(aCx, global);
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -837,16 +837,24 @@ public:
   const ServiceWorkerDescriptor&
   GetServiceWorkerDescriptor() const
   {
     MOZ_DIAGNOSTIC_ASSERT(IsServiceWorker());
     MOZ_DIAGNOSTIC_ASSERT(mLoadInfo.mServiceWorkerDescriptor.isSome());
     return mLoadInfo.mServiceWorkerDescriptor.ref();
   }
 
+  const ServiceWorkerRegistrationDescriptor&
+  GetServiceWorkerRegistrationDescriptor() const
+  {
+    MOZ_DIAGNOSTIC_ASSERT(IsServiceWorker());
+    MOZ_DIAGNOSTIC_ASSERT(mLoadInfo.mServiceWorkerRegistrationDescriptor.isSome());
+    return mLoadInfo.mServiceWorkerRegistrationDescriptor.ref();
+  }
+
   void
   UpdateServiceWorkerState(ServiceWorkerState aState)
   {
     MOZ_DIAGNOSTIC_ASSERT(IsServiceWorker());
     MOZ_DIAGNOSTIC_ASSERT(mLoadInfo.mServiceWorkerDescriptor.isSome());
     return mLoadInfo.mServiceWorkerDescriptor.ref().SetState(aState);
   }
 
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -628,19 +628,25 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(Servi
                                    mClients, mRegistration)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorkerGlobalScope)
 NS_INTERFACE_MAP_END_INHERITING(WorkerGlobalScope)
 
 NS_IMPL_ADDREF_INHERITED(ServiceWorkerGlobalScope, WorkerGlobalScope)
 NS_IMPL_RELEASE_INHERITED(ServiceWorkerGlobalScope, WorkerGlobalScope)
 
 ServiceWorkerGlobalScope::ServiceWorkerGlobalScope(WorkerPrivate* aWorkerPrivate,
-                                                   const nsACString& aScope)
-  : WorkerGlobalScope(aWorkerPrivate),
-    mScope(NS_ConvertUTF8toUTF16(aScope))
+                                                   const ServiceWorkerRegistrationDescriptor& aRegistrationDescriptor)
+  : WorkerGlobalScope(aWorkerPrivate)
+  , mScope(NS_ConvertUTF8toUTF16(aRegistrationDescriptor.Scope()))
+
+  // Eagerly create the registration because we will need to receive updates
+  // about the state of the registration.  We can't wait until first access
+  // to start receiving these.
+  , mRegistration(ServiceWorkerRegistration::CreateForWorker(aWorkerPrivate,
+                                                             aRegistrationDescriptor))
 {
 }
 
 ServiceWorkerGlobalScope::~ServiceWorkerGlobalScope()
 {
 }
 
 bool
@@ -667,21 +673,16 @@ ServiceWorkerGlobalScope::GetClients()
 
   RefPtr<Clients> ref = mClients;
   return ref.forget();
 }
 
 ServiceWorkerRegistration*
 ServiceWorkerGlobalScope::Registration()
 {
-  if (!mRegistration) {
-    mRegistration =
-      ServiceWorkerRegistration::CreateForWorker(mWorkerPrivate, mScope);
-  }
-
   return mRegistration;
 }
 
 EventHandlerNonNull*
 ServiceWorkerGlobalScope::GetOnfetch()
 {
   MOZ_ASSERT(mWorkerPrivate);
   mWorkerPrivate->AssertIsOnWorkerThread();
--- a/dom/workers/WorkerScope.h
+++ b/dom/workers/WorkerScope.h
@@ -298,17 +298,18 @@ class ServiceWorkerGlobalScope final : p
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorkerGlobalScope,
                                            WorkerGlobalScope)
   IMPL_EVENT_HANDLER(notificationclick)
   IMPL_EVENT_HANDLER(notificationclose)
 
-  ServiceWorkerGlobalScope(WorkerPrivate* aWorkerPrivate, const nsACString& aScope);
+  ServiceWorkerGlobalScope(WorkerPrivate* aWorkerPrivate,
+                           const ServiceWorkerRegistrationDescriptor& aRegistrationDescriptor);
 
   virtual bool
   WrapGlobalObject(JSContext* aCx,
                    JS::MutableHandle<JSObject*> aReflector) override;
 
   void
   GetScope(nsString& aScope) const
   {
deleted file mode 100644
--- a/gfx/cairo/libpixman/src/Makefile.in
+++ /dev/null
@@ -1,10 +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 $(topsrcdir)/config/rules.mk
-
-# The ARM asm functions here don't appreciate being called by functions
-# compiled with -mapcs-frame.  See bug 832752.
-CXXFLAGS := $(filter-out -mapcs-frame,$(CXXFLAGS))
-CFLAGS := $(filter-out -mapcs-frame,$(CFLAGS))
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -534,19 +534,18 @@ bool
 ImageBridgeChild::InitForContent(Endpoint<PImageBridgeChild>&& aEndpoint, uint32_t aNamespace)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   gfxPlatform::GetPlatform();
 
   if (!sImageBridgeChildThread) {
     sImageBridgeChildThread = new Thread("ImageBridgeChild");
-    if (!sImageBridgeChildThread->Start()) {
-      return false;
-    }
+    bool success = sImageBridgeChildThread->Start();
+    MOZ_RELEASE_ASSERT(success, "Failed to start ImageBridgeChild thread!");
   }
 
   RefPtr<ImageBridgeChild> child = new ImageBridgeChild(aNamespace);
 
   RefPtr<Runnable> runnable = NewRunnableMethod<Endpoint<PImageBridgeChild>&&>(
     "layers::ImageBridgeChild::Bind",
     child,
     &ImageBridgeChild::Bind,
--- a/gfx/vr/ipc/VRManagerChild.cpp
+++ b/gfx/vr/ipc/VRManagerChild.cpp
@@ -85,17 +85,17 @@ VRManagerChild::IsCreated()
 /* static */ bool
 VRManagerChild::InitForContent(Endpoint<PVRManagerChild>&& aEndpoint)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!sVRManagerChildSingleton);
 
   RefPtr<VRManagerChild> child(new VRManagerChild());
   if (!aEndpoint.Bind(child)) {
-    MOZ_CRASH("Couldn't Open() Compositor channel.");
+    return false;
   }
   sVRManagerChildSingleton = child;
   return true;
 }
 
 /* static */ bool
 VRManagerChild::ReinitForContent(Endpoint<PVRManagerChild>&& aEndpoint)
 {
--- a/gfx/webrender_bindings/RenderThread.cpp
+++ b/gfx/webrender_bindings/RenderThread.cpp
@@ -234,31 +234,31 @@ NotifyDidRender(layers::CompositorBridge
   while (wr_pipeline_info_next_removed_pipeline(aInfo, &pipeline)) {
     aBridge->NotifyPipelineRemoved(pipeline);
   }
 
   wr_pipeline_info_delete(aInfo);
 }
 
 void
-RenderThread::UpdateAndRender(wr::WindowId aWindowId)
+RenderThread::UpdateAndRender(wr::WindowId aWindowId, bool aReadback)
 {
   AUTO_PROFILER_TRACING("Paint", "Composite");
   MOZ_ASSERT(IsInRenderThread());
 
   auto it = mRenderers.find(aWindowId);
   MOZ_ASSERT(it != mRenderers.end());
   if (it == mRenderers.end()) {
     return;
   }
 
   auto& renderer = it->second;
   TimeStamp start = TimeStamp::Now();
 
-  bool ret = renderer->UpdateAndRender();
+  bool ret = renderer->UpdateAndRender(aReadback);
   if (!ret) {
     // Render did not happen, do not call NotifyDidRender.
     return;
   }
 
   TimeStamp end = TimeStamp::Now();
 
   auto info = renderer->FlushPipelineInfo();
--- a/gfx/webrender_bindings/RenderThread.h
+++ b/gfx/webrender_bindings/RenderThread.h
@@ -122,17 +122,17 @@ public:
 
   /// Automatically forwarded to the render thread.
   void PipelineSizeChanged(wr::WindowId aWindowId, uint64_t aPipelineId, float aWidth, float aHeight);
 
   /// Automatically forwarded to the render thread.
   void RunEvent(wr::WindowId aWindowId, UniquePtr<RendererEvent> aCallBack);
 
   /// Can only be called from the render thread.
-  void UpdateAndRender(wr::WindowId aWindowId);
+  void UpdateAndRender(wr::WindowId aWindowId, bool aReadback = false);
 
   void Pause(wr::WindowId aWindowId);
   bool Resume(wr::WindowId aWindowId);
 
   /// Can be called from any thread.
   void RegisterExternalImage(uint64_t aExternalImageId, already_AddRefed<RenderTextureHost> aTexture);
 
   /// Can be called from any thread.
--- a/gfx/webrender_bindings/RendererOGL.cpp
+++ b/gfx/webrender_bindings/RendererOGL.cpp
@@ -83,19 +83,23 @@ RendererOGL::GetExternalImageHandler()
 
 void
 RendererOGL::Update()
 {
   wr_renderer_update(mRenderer);
 }
 
 bool
-RendererOGL::UpdateAndRender()
+RendererOGL::UpdateAndRender(bool aReadback)
 {
   uint32_t flags = gfx::gfxVars::WebRenderDebugFlags();
+  // Disable debug flags during readback
+  if (aReadback) {
+    flags = 0;
+  }
 
   if (mDebugFlags.mBits != flags) {
     mDebugFlags.mBits = flags;
     wr_renderer_set_debug_flags(mRenderer, mDebugFlags);
   }
 
   mozilla::widget::WidgetRenderingContext widgetContext;
 
--- a/gfx/webrender_bindings/RendererOGL.h
+++ b/gfx/webrender_bindings/RendererOGL.h
@@ -48,17 +48,17 @@ class RendererOGL
 
 public:
   wr::WrExternalImageHandler GetExternalImageHandler();
 
   /// This can be called on the render thread only.
   void Update();
 
   /// This can be called on the render thread only.
-  bool UpdateAndRender();
+  bool UpdateAndRender(bool aReadback);
 
   /// This can be called on the render thread only.
   bool RenderToTarget(gfx::DrawTarget& aTarget);
 
   /// This can be called on the render thread only.
   void SetProfilerEnabled(bool aEnabled);
 
   /// This can be called on the render thread only.
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -395,17 +395,17 @@ WebRenderAPI::Readback(gfx::IntSize size
 
             ~Readback()
             {
                 MOZ_COUNT_DTOR(Readback);
             }
 
             virtual void Run(RenderThread& aRenderThread, WindowId aWindowId) override
             {
-                aRenderThread.UpdateAndRender(aWindowId);
+                aRenderThread.UpdateAndRender(aWindowId, /* aReadback */ true);
                 wr_renderer_readback(aRenderThread.GetRenderer(aWindowId)->GetRenderer(),
                                      mSize.width, mSize.height, mBuffer, mBufferSize);
                 layers::AutoCompleteTask complete(mTask);
             }
 
             layers::SynchronousTask* mTask;
             gfx::IntSize mSize;
             uint8_t *mBuffer;
--- a/js/public/Conversions.h
+++ b/js/public/Conversions.h
@@ -16,18 +16,16 @@
 
 #include <math.h>
 
 #include "jspubtd.h"
 
 #include "js/RootingAPI.h"
 #include "js/Value.h"
 
-struct JSContext;
-
 namespace js {
 
 /* DO NOT CALL THIS. Use JS::ToBoolean. */
 extern JS_PUBLIC_API(bool)
 ToBooleanSlow(JS::HandleValue v);
 
 /* DO NOT CALL THIS.  Use JS::ToNumber. */
 extern JS_PUBLIC_API(bool)
--- a/js/public/Date.h
+++ b/js/public/Date.h
@@ -30,18 +30,16 @@
  */
 
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/MathAlgorithms.h"
 
 #include "js/Conversions.h"
 #include "js/Value.h"
 
-struct JSContext;
-
 namespace JS {
 
 /**
  * Re-query the system to determine the current time zone adjustment from UTC,
  * including any component due to DST.  If the time zone has changed, this will
  * cause all Date object non-UTC methods and formatting functions to produce
  * appropriately adjusted results.
  *
--- a/js/public/GCAPI.h
+++ b/js/public/GCAPI.h
@@ -10,25 +10,21 @@
 
 #ifndef js_GCAPI_h
 #define js_GCAPI_h
 
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Vector.h"
 
 #include "js/GCAnnotations.h"
+#include "js/TypeDecls.h"
 #include "js/UniquePtr.h"
 #include "js/Utility.h"
 
-struct JSCompartment;
-struct JSContext;
 struct JSFreeOp;
-class JSObject;
-struct JSRuntime;
-class JSString;
 
 #ifdef JS_BROKEN_GCC_ATTRIBUTE_WARNING
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wattributes"
 #endif // JS_BROKEN_GCC_ATTRIBUTE_WARNING
 
 class JS_PUBLIC_API(JSTracer);
 
@@ -321,18 +317,16 @@ typedef void
  * can be called off the main thread.
  */
 struct JSStringFinalizer {
     void (*finalize)(const JSStringFinalizer* fin, char16_t* chars);
 };
 
 namespace JS {
 
-struct Zone;
-
 #define GCREASONS(D)                            \
     /* Reasons internal to the JS engine */     \
     D(API)                                      \
     D(EAGER_ALLOC_TRIGGER)                      \
     D(DESTROY_RUNTIME)                          \
     D(ROOTS_REMOVED)                            \
     D(LAST_DITCH)                               \
     D(TOO_MUCH_MALLOC)                          \
--- a/js/public/GCPolicyAPI.h
+++ b/js/public/GCPolicyAPI.h
@@ -40,16 +40,17 @@
 #ifndef GCPolicyAPI_h
 #define GCPolicyAPI_h
 
 #include "mozilla/Maybe.h"
 #include "mozilla/UniquePtr.h"
 
 #include "js/TraceKind.h"
 #include "js/TracingAPI.h"
+#include "js/TypeDecls.h"
 
 // Expand the given macro D for each public GC pointer.
 #define FOR_EACH_PUBLIC_GC_POINTER_TYPE(D) \
     D(JS::Symbol*) \
     D(JSAtom*) \
     D(JSFunction*) \
     D(JSObject*) \
     D(JSScript*) \
@@ -58,25 +59,16 @@
 // Expand the given macro D for each public tagged GC pointer type.
 #define FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(D) \
     D(JS::Value) \
     D(jsid)
 
 #define FOR_EACH_PUBLIC_AGGREGATE_GC_POINTER_TYPE(D) \
     D(JSPropertyDescriptor)
 
-class JSAtom;
-class JSFunction;
-class JSObject;
-class JSScript;
-class JSString;
-namespace JS {
-class Symbol;
-}
-
 namespace JS {
 
 // Defines a policy for container types with non-GC, i.e. C storage. This
 // policy dispatches to the underlying struct for GC interactions.
 template <typename T>
 struct StructGCPolicy
 {
     static T initial() {
--- a/js/public/HeapAPI.h
+++ b/js/public/HeapAPI.h
@@ -19,16 +19,22 @@ namespace js {
 
 JS_FRIEND_API(bool)
 CurrentThreadCanAccessZone(JS::Zone* zone);
 
 namespace gc {
 
 struct Cell;
 
+/*
+ * The low bit is set so this should never equal a normal pointer, and the high
+ * bit is set so this should never equal the upper 32 bits of a 64-bit pointer.
+ */
+const uint32_t Relocated = uintptr_t(0xbad0bad1);
+
 const size_t ArenaShift = 12;
 const size_t ArenaSize = size_t(1) << ArenaShift;
 const size_t ArenaMask = ArenaSize - 1;
 
 #ifdef JS_GC_SMALL_CHUNK_SIZE
 const size_t ChunkShift = 18;
 #else
 const size_t ChunkShift = 20;
@@ -56,17 +62,17 @@ const size_t ChunkMarkBitmapOffset = 258
 const size_t ChunkMarkBitmapBits = 31744;
 #else
 const size_t ChunkMarkBitmapOffset = 1032352;
 const size_t ChunkMarkBitmapBits = 129024;
 #endif
 const size_t ChunkRuntimeOffset = ChunkSize - sizeof(void*);
 const size_t ChunkTrailerSize = 2 * sizeof(uintptr_t) + sizeof(uint64_t);
 const size_t ChunkLocationOffset = ChunkSize - ChunkTrailerSize;
-const size_t ChunkStoreBufferOffset = ChunkLocationOffset + sizeof(uint64_t);
+const size_t ChunkStoreBufferOffset = ChunkSize - ChunkTrailerSize + sizeof(uint64_t);
 const size_t ArenaZoneOffset = sizeof(size_t);
 const size_t ArenaHeaderSize = sizeof(size_t) + 2 * sizeof(uintptr_t) +
                                sizeof(size_t) + sizeof(uintptr_t);
 
 /*
  * Live objects are marked black or gray. Everything reachable from a JS root is
  * marked black. Objects marked gray are eligible for cycle collection.
  *
@@ -455,20 +461,25 @@ namespace JS {
 
 static MOZ_ALWAYS_INLINE Zone*
 GetTenuredGCThingZone(GCCellPtr thing)
 {
     MOZ_ASSERT(!js::gc::IsInsideNursery(thing.asCell()));
     return js::gc::detail::GetGCThingZone(thing.unsafeAsUIntPtr());
 }
 
+extern JS_PUBLIC_API(Zone*)
+GetNurseryStringZone(JSString* str);
+
 static MOZ_ALWAYS_INLINE Zone*
 GetStringZone(JSString* str)
 {
-    return js::gc::detail::GetGCThingZone(uintptr_t(str));
+    if (!js::gc::IsInsideNursery(reinterpret_cast<js::gc::Cell*>(str)))
+        return js::gc::detail::GetGCThingZone(reinterpret_cast<uintptr_t>(str));
+    return GetNurseryStringZone(str);
 }
 
 extern JS_PUBLIC_API(Zone*)
 GetObjectZone(JSObject* obj);
 
 extern JS_PUBLIC_API(Zone*)
 GetValueZone(const Value& value);
 
@@ -478,16 +489,22 @@ GCThingIsMarkedGray(GCCellPtr thing)
     if (thing.mayBeOwnedByOtherRuntime())
         return false;
     return js::gc::detail::CellIsMarkedGrayIfKnown(thing.asCell());
 }
 
 extern JS_PUBLIC_API(JS::TraceKind)
 GCThingTraceKind(void* thing);
 
+extern JS_PUBLIC_API(void)
+EnableNurseryStrings(JSContext* cx);
+
+extern JS_PUBLIC_API(void)
+DisableNurseryStrings(JSContext* cx);
+
 /*
  * Returns true when writes to GC thing pointers (and reads from weak pointers)
  * must call an incremental barrier. This is generally only true when running
  * mutator code in-between GC slices. At other times, the barrier may be elided
  * for performance.
  */
 extern JS_PUBLIC_API(bool)
 IsIncrementalBarrierNeeded(JSContext* cx);
--- a/js/public/ProfilingFrameIterator.h
+++ b/js/public/ProfilingFrameIterator.h
@@ -10,20 +10,16 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/Maybe.h"
 
 #include "jsbytecode.h"
 #include "js/GCAPI.h"
 #include "js/TypeDecls.h"
 #include "js/Utility.h"
 
-struct JSContext;
-struct JSRuntime;
-class JSScript;
-
 namespace js {
     class Activation;
     namespace jit {
         class JitActivation;
         class JSJitProfilingFrameIterator;
         class JitcodeGlobalEntry;
     } // namespace jit
     namespace wasm {
--- a/js/public/ProfilingStack.h
+++ b/js/public/ProfilingStack.h
@@ -10,19 +10,17 @@
 #include <algorithm>
 #include <stdint.h>
 
 #include "jsbytecode.h"
 #include "jstypes.h"
 #include "js/TypeDecls.h"
 #include "js/Utility.h"
 
-struct JSRuntime;
 class JSTracer;
-
 class PseudoStack;
 
 // This file defines the classes PseudoStack and ProfileEntry.
 // The PseudoStack manages an array of ProfileEntries.
 // Usage:
 //
 //  PseudoStack* pseudoStack = ...;
 //
--- a/js/public/Result.h
+++ b/js/public/Result.h
@@ -113,18 +113,16 @@
  * exist.
  */
 
 #ifndef js_Result_h
 #define js_Result_h
 
 #include "mozilla/Result.h"
 
-struct JSContext;
-
 /**
  * Evaluate the boolean expression expr. If it's true, do nothing.
  * If it's false, return an error result.
  */
 #define JS_TRY_BOOL_TO_RESULT(cx, expr) \
     do { \
         bool ok_ = (expr); \
         if (!ok_) \
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -195,31 +195,32 @@ namespace JS {
 
 template <typename T> class Rooted;
 template <typename T> class PersistentRooted;
 
 /* This is exposing internal state of the GC for inlining purposes. */
 JS_FRIEND_API(bool) isGCEnabled();
 
 JS_FRIEND_API(void) HeapObjectPostBarrier(JSObject** objp, JSObject* prev, JSObject* next);
+JS_FRIEND_API(void) HeapStringPostBarrier(JSString** objp, JSString* prev, JSString* next);
 
 #ifdef JS_DEBUG
 /**
  * For generational GC, assert that an object is in the tenured generation as
  * opposed to being in the nursery.
  */
 extern JS_FRIEND_API(void)
 AssertGCThingMustBeTenured(JSObject* obj);
 extern JS_FRIEND_API(void)
-AssertGCThingIsNotAnObjectSubclass(js::gc::Cell* cell);
+AssertGCThingIsNotNurseryAllocable(js::gc::Cell* cell);
 #else
 inline void
 AssertGCThingMustBeTenured(JSObject* obj) {}
 inline void
-AssertGCThingIsNotAnObjectSubclass(js::gc::Cell* cell) {}
+AssertGCThingIsNotNurseryAllocable(js::gc::Cell* cell) {}
 #endif
 
 /**
  * The Heap<T> class is a heap-stored reference to a JS GC thing. All members of
  * heap classes that refer to GC things should use Heap<T> (or possibly
  * TenuredHeap<T>, described below).
  *
  * Heap<T> is an abstraction that hides some of the complexity required to
@@ -620,17 +621,17 @@ struct BarrierMethods<T*>
     static gc::Cell* asGCThingOrNull(T* v) {
         if (!v)
             return nullptr;
         MOZ_ASSERT(uintptr_t(v) > 32);
         return reinterpret_cast<gc::Cell*>(v);
     }
     static void postBarrier(T** vp, T* prev, T* next) {
         if (next)
-            JS::AssertGCThingIsNotAnObjectSubclass(reinterpret_cast<js::gc::Cell*>(next));
+            JS::AssertGCThingIsNotNurseryAllocable(reinterpret_cast<js::gc::Cell*>(next));
     }
     static void exposeToJS(T* t) {
         if (t)
             js::gc::ExposeGCThingToActiveJS(JS::GCCellPtr(t));
     }
 };
 
 template <>
@@ -668,16 +669,31 @@ struct BarrierMethods<JSFunction*>
                                   reinterpret_cast<JSObject*>(next));
     }
     static void exposeToJS(JSFunction* fun) {
         if (fun)
             JS::ExposeObjectToActiveJS(reinterpret_cast<JSObject*>(fun));
     }
 };
 
+template <>
+struct BarrierMethods<JSString*>
+{
+    static JSString* initial() { return nullptr; }
+    static gc::Cell* asGCThingOrNull(JSString* v) {
+        if (!v)
+            return nullptr;
+        MOZ_ASSERT(uintptr_t(v) > 32);
+        return reinterpret_cast<gc::Cell*>(v);
+    }
+    static void postBarrier(JSString** vp, JSString* prev, JSString* next) {
+        JS::HeapStringPostBarrier(vp, prev, next);
+    }
+};
+
 // Provide hash codes for Cell kinds that may be relocated and, thus, not have
 // a stable address to use as the base for a hash code. Instead of the address,
 // this hasher uses Cell::getUniqueId to provide exact matches and as a base
 // for generating hash codes.
 //
 // Note: this hasher, like PointerHasher can "hash" a nullptr. While a nullptr
 // would not likely be a useful key, there are some cases where being able to
 // hash a nullptr is useful, either on purpose or because of bugs:
--- a/js/public/StructuredClone.h
+++ b/js/public/StructuredClone.h
@@ -15,17 +15,16 @@
 
 #include "jstypes.h"
 
 #include "js/RootingAPI.h"
 #include "js/TypeDecls.h"
 #include "js/Value.h"
 #include "js/Vector.h"
 
-struct JSRuntime;
 struct JSStructuredCloneReader;
 struct JSStructuredCloneWriter;
 
 // API for the HTML5 internal structured cloning algorithm.
 
 namespace JS {
 
 enum class StructuredCloneScope : uint32_t {
--- a/js/public/TypeDecls.h
+++ b/js/public/TypeDecls.h
@@ -17,33 +17,39 @@
 #ifndef js_TypeDecls_h
 #define js_TypeDecls_h
 
 #include <stddef.h>
 #include <stdint.h>
 
 #include "js-config.h"
 
+class JSAtom;
+struct JSCompartment;
 struct JSContext;
 class JSFunction;
 class JSObject;
+struct JSRuntime;
 class JSScript;
 class JSString;
 class JSAddonId;
 struct JSFreeOp;
 
 struct jsid;
 
 namespace JS {
 
 typedef unsigned char Latin1Char;
 
 class Symbol;
 class Value;
 class Realm;
+struct Runtime;
+struct Zone;
+
 template <typename T> class Handle;
 template <typename T> class MutableHandle;
 template <typename T> class Rooted;
 template <typename T> class PersistentRooted;
 
 typedef Handle<JSFunction*> HandleFunction;
 typedef Handle<jsid>        HandleId;
 typedef Handle<JSObject*>   HandleObject;
--- a/js/public/UbiNode.h
+++ b/js/public/UbiNode.h
@@ -158,18 +158,16 @@
 // When ubi::Nodes refer to nodes deserialized from a heap snapshot, analyses
 // must be even more careful: since snapshots often come from potentially
 // compromised e10s content processes, even properties normally guaranteed by
 // the platform (the proper linking of DOM nodes, for example) might be
 // corrupted. While it is the deserializer's responsibility to check the basic
 // structure of the snapshot file, the analyses should be prepared for ubi::Node
 // graphs constructed from snapshots to be even more bizarre.
 
-class JSAtom;
-
 namespace JS {
 namespace ubi {
 
 class Edge;
 class EdgeRange;
 class StackFrame;
 
 } // namespace ubi
--- a/js/rust/src/conversions.rs
+++ b/js/rust/src/conversions.rs
@@ -629,53 +629,56 @@ impl<T: ToJSValConvertible> ToJSValConve
                 JSPROP_ENUMERATE as _
             ));
         }
 
         rval.set(ObjectValue(js_array.handle().get()));
     }
 }
 
-/// Rooting guard for the iterator field of ForOfIterator.
+/// Rooting guard for the iterator and nextMethod fields of ForOfIterator.
 /// Behaves like RootedGuard (roots on creation, unroots on drop),
 /// but borrows and allows access to the whole ForOfIterator, so
 /// that methods on ForOfIterator can still be used through it.
 struct ForOfIteratorGuard<'a> {
     root: &'a mut JS::ForOfIterator
 }
 
 impl<'a> ForOfIteratorGuard<'a> {
     fn new(cx: *mut JSContext, root: &'a mut JS::ForOfIterator) -> Self {
         unsafe {
             root.iterator.register_with_root_lists(cx);
+            root.nextMethod.register_with_root_lists(cx);
         }
         ForOfIteratorGuard {
             root: root
         }
     }
 }
 
 impl<'a> Drop for ForOfIteratorGuard<'a> {
     fn drop(&mut self) {
         unsafe {
+            self.root.nextMethod.remove_from_root_stack();
             self.root.iterator.remove_from_root_stack();
         }
     }
 }
 
 impl<C: Clone, T: FromJSValConvertible<Config=C>> FromJSValConvertible for Vec<T> {
     type Config = C;
 
     unsafe fn from_jsval(cx: *mut JSContext,
                          value: JS::HandleValue,
                          option: C)
                          -> Result<ConversionResult<Vec<T>>, ()> {
         let mut iterator = JS::ForOfIterator {
             cx_: cx,
             iterator: JS::RootedObject::new_unrooted(),
+            nextMethod: JS::RootedValue::new_unrooted(),
             index: ::std::u32::MAX, // NOT_ARRAY
         };
         let iterator = ForOfIteratorGuard::new(cx, &mut iterator);
         let iterator = &mut *iterator.root;
 
         if !iterator.init(value, JS::ForOfIterator_NonIterableBehavior::AllowNonIterable) {
             return Err(())
         }
--- a/js/src/NamespaceImports.h
+++ b/js/src/NamespaceImports.h
@@ -43,17 +43,16 @@ template<typename T> class AutoHashSetRo
 
 class MOZ_STACK_CLASS SourceBufferHolder;
 
 class HandleValueArray;
 
 class ObjectOpResult;
 class PropertyResult;
 
-class Symbol;
 enum class SymbolCode: uint32_t;
 
 } // namespace JS
 
 // Do the importing.
 namespace js {
 
 using JS::Value;
--- a/js/src/builtin/Array.js
+++ b/js/src/builtin/Array.js
@@ -690,27 +690,24 @@ function ArrayIncludes(searchElement, fr
         k++;
     }
 
     // Step 11.
     return false;
 }
 
 // ES6 draft specification, section 22.1.5.1, version 2013-09-05.
-function CreateArrayIteratorAt(obj, kind, n) {
+function CreateArrayIterator(obj, kind) {
     var iteratedObject = ToObject(obj);
     var iterator = NewArrayIterator();
     UnsafeSetReservedSlot(iterator, ITERATOR_SLOT_TARGET, iteratedObject);
-    UnsafeSetReservedSlot(iterator, ITERATOR_SLOT_NEXT_INDEX, n);
+    UnsafeSetReservedSlot(iterator, ITERATOR_SLOT_NEXT_INDEX, 0);
     UnsafeSetReservedSlot(iterator, ITERATOR_SLOT_ITEM_KIND, kind);
     return iterator;
 }
-function CreateArrayIterator(obj, kind) {
-    return CreateArrayIteratorAt(obj, kind, 0);
-}
 
 // ES6, 22.1.5.2.1
 // http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%arrayiteratorprototype%.next
 function ArrayIteratorNext() {
     // Step 1-3.
     if (!IsObject(this) || !IsArrayIterator(this)) {
         return callFunction(CallArrayIteratorMethodIfWrapped, this,
                             "ArrayIteratorNext");
@@ -771,20 +768,16 @@ function ArrayIteratorNext() {
     }
 
     // Step 12.
     assert(itemKind === ITEM_KIND_KEY, itemKind);
     result.value = index;
     return result;
 }
 
-function ArrayValuesAt(n) {
-    return CreateArrayIteratorAt(this, ITEM_KIND_VALUE, n);
-}
-
 function ArrayValues() {
     return CreateArrayIterator(this, ITEM_KIND_VALUE);
 }
 _SetCanonicalName(ArrayValues, "values");
 
 function ArrayEntries() {
     return CreateArrayIterator(this, ITEM_KIND_KEY_AND_VALUE);
 }
--- a/js/src/builtin/AtomicsObject.cpp
+++ b/js/src/builtin/AtomicsObject.cpp
@@ -61,17 +61,17 @@
 #include "jit/AtomicOperations.h"
 #include "jit/InlinableNatives.h"
 #include "js/Class.h"
 #include "vm/GlobalObject.h"
 #include "vm/Time.h"
 #include "vm/TypedArrayObject.h"
 #include "wasm/WasmInstance.h"
 
-#include "jsobjinlines.h"
+#include "vm/JSObject-inl.h"
 
 using namespace js;
 
 const Class AtomicsObject::class_ = {
     "Atomics",
     JSCLASS_HAS_CACHED_PROTO(JSProto_Atomics)
 };
 
--- a/js/src/builtin/AtomicsObject.h
+++ b/js/src/builtin/AtomicsObject.h
@@ -5,19 +5,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef builtin_AtomicsObject_h
 #define builtin_AtomicsObject_h
 
 #include "mozilla/Maybe.h"
 #include "mozilla/TimeStamp.h"
 
-#include "jsobj.h"
-
 #include "threading/ConditionVariable.h"
+#include "vm/JSObject.h"
 #include "vm/MutexIDs.h"
 #include "vm/NativeObject.h"
 
 namespace js {
 
 class AtomicsObject : public NativeObject
 {
   public:
--- a/js/src/builtin/DataViewObject.cpp
+++ b/js/src/builtin/DataViewObject.cpp
@@ -8,29 +8,29 @@
 
 #include "mozilla/Alignment.h"
 #include "mozilla/Casting.h"
 
 #include <string.h>
 
 #include "jsapi.h"
 #include "jsarray.h"
-#include "jscntxt.h"
 #include "jsnum.h"
-#include "jsobj.h"
 #ifdef XP_WIN
 # include "jswin.h"
 #endif
 #include "jswrapper.h"
 
 #include "jit/AtomicOperations.h"
 #include "js/Conversions.h"
 #include "vm/ArrayBufferObject.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
+#include "vm/JSContext.h"
+#include "vm/JSObject.h"
 #include "vm/SharedMem.h"
 #include "vm/WrapperObject.h"
 
 #include "gc/Nursery-inl.h"
 #include "gc/StoreBuffer-inl.h"
 #include "vm/ArrayBufferObject-inl.h"
 #include "vm/NativeObject-inl.h"
 
--- a/js/src/builtin/DataViewObject.h
+++ b/js/src/builtin/DataViewObject.h
@@ -4,21 +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/. */
 
 #ifndef vm_DataViewObject_h
 #define vm_DataViewObject_h
 
 #include "mozilla/Attributes.h"
 
-#include "jsobj.h"
-
 #include "gc/Barrier.h"
 #include "js/Class.h"
 #include "vm/ArrayBufferObject.h"
+#include "vm/JSObject.h"
 #include "vm/SharedArrayObject.h"
 #include "vm/TypedArrayObject.h"
 
 namespace js {
 
 // In the DataViewObject, the private slot contains a raw pointer into
 // the buffer.  The buffer may be shared memory and the raw pointer
 // should not be exposed without sharedness information accompanying
--- a/js/src/builtin/Eval.cpp
+++ b/js/src/builtin/Eval.cpp
@@ -4,22 +4,22 @@
  * 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 "builtin/Eval.h"
 
 #include "mozilla/HashFunctions.h"
 #include "mozilla/Range.h"
 
-#include "jscntxt.h"
 #include "jshashutil.h"
 
 #include "frontend/BytecodeCompiler.h"
 #include "vm/Debugger.h"
 #include "vm/GlobalObject.h"
+#include "vm/JSContext.h"
 #include "vm/JSONParser.h"
 
 #include "vm/Interpreter-inl.h"
 
 using namespace js;
 
 using mozilla::AddToHash;
 using mozilla::HashString;
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -1,25 +1,25 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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 "builtin/MapObject.h"
 
-#include "jscntxt.h"
 #include "jsiter.h"
-#include "jsobj.h"
 
 #include "ds/OrderedHashTable.h"
 #include "gc/FreeOp.h"
 #include "js/Utility.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
+#include "vm/JSContext.h"
+#include "vm/JSObject.h"
 #include "vm/SelfHosting.h"
 #include "vm/Symbol.h"
 
 #include "gc/Marking-inl.h"
 #include "vm/Interpreter-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
--- a/js/src/builtin/MapObject.h
+++ b/js/src/builtin/MapObject.h
@@ -2,20 +2,19 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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 builtin_MapObject_h
 #define builtin_MapObject_h
 
-#include "jsobj.h"
-
 #include "builtin/SelfHostingDefines.h"
 #include "vm/GlobalObject.h"
+#include "vm/JSObject.h"
 #include "vm/NativeObject.h"
 #include "vm/PIC.h"
 #include "vm/Runtime.h"
 
 namespace js {
 
 /*
  * Comparing two ropes for equality can fail. The js::HashTable template
--- a/js/src/builtin/ModuleObject.cpp
+++ b/js/src/builtin/ModuleObject.cpp
@@ -12,18 +12,18 @@
 #include "frontend/ParseNode.h"
 #include "frontend/SharedContext.h"
 #include "gc/FreeOp.h"
 #include "gc/Policy.h"
 #include "gc/Tracer.h"
 #include "vm/AsyncFunction.h"
 #include "vm/AsyncIteration.h"
 
-#include "jsobjinlines.h"
-#include "jsscriptinlines.h"
+#include "vm/JSObject-inl.h"
+#include "vm/JSScript-inl.h"
 
 using namespace js;
 using namespace js::frontend;
 
 static_assert(MODULE_STATUS_UNINSTANTIATED < MODULE_STATUS_INSTANTIATING &&
               MODULE_STATUS_INSTANTIATING < MODULE_STATUS_INSTANTIATED &&
               MODULE_STATUS_INSTANTIATED < MODULE_STATUS_EVALUATED &&
               MODULE_STATUS_EVALUATED < MODULE_STATUS_EVALUATED_ERROR,
--- a/js/src/builtin/ModuleObject.h
+++ b/js/src/builtin/ModuleObject.h
@@ -10,16 +10,17 @@
 #include "mozilla/Maybe.h"
 
 #include "jsapi.h"
 
 #include "builtin/SelfHostingDefines.h"
 #include "js/GCVector.h"
 #include "js/Id.h"
 #include "js/UniquePtr.h"
+#include "vm/JSAtom.h"
 #include "vm/NativeObject.h"
 #include "vm/ProxyObject.h"
 
 namespace js {
 
 class ModuleEnvironmentObject;
 class ModuleObject;
 
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -4,30 +4,29 @@
  * 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 "builtin/Object.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/MaybeOneOf.h"
 
-#include "jscntxt.h"
 #include "jsstr.h"
 
 #include "builtin/Eval.h"
 #include "builtin/SelfHostingDefines.h"
 #include "frontend/BytecodeCompiler.h"
 #include "jit/InlinableNatives.h"
 #include "js/UniquePtr.h"
 #include "vm/AsyncFunction.h"
+#include "vm/JSContext.h"
 #include "vm/RegExpObject.h"
 #include "vm/StringBuffer.h"
 
-#include "jsobjinlines.h"
-
+#include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/Shape-inl.h"
 #include "vm/UnboxedObject-inl.h"
 
 #ifdef FUZZING
 #include "builtin/TestingFunctions.h"
 #endif
 
--- a/js/src/builtin/Profilers.cpp
+++ b/js/src/builtin/Profilers.cpp
@@ -24,17 +24,17 @@
 
 #ifdef XP_WIN
 # include <process.h>
 # define getpid _getpid
 #endif
 
 #include "vm/Probes.h"
 
-#include "jscntxtinlines.h"
+#include "vm/JSContext-inl.h"
 
 using namespace js;
 
 using mozilla::ArrayLength;
 
 /* Thread-unsafe error management */
 
 static char gLastError[2000];
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -5,30 +5,29 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 #include "builtin/Promise.h"
 
 #include "mozilla/Atomics.h"
 #include "mozilla/TimeStamp.h"
 
-#include "jscntxt.h"
 #include "jsexn.h"
 #include "jsfriendapi.h"
 #include "jsiter.h"
 
 #include "gc/Heap.h"
 #include "js/Debug.h"
 #include "vm/AsyncFunction.h"
 #include "vm/AsyncIteration.h"
 #include "vm/Debugger.h"
-
-#include "jsobjinlines.h"
+#include "vm/JSContext.h"
 
 #include "vm/Debugger-inl.h"
+#include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
 static double
 MillisecondsSinceStartup()
 {
     auto now = mozilla::TimeStamp::Now();
@@ -2629,16 +2628,21 @@ js::AsyncFromSyncIteratorMethod(JSContex
 
     // Step 2.
     RootedObject resultPromise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx));
     if (!resultPromise)
         return false;
 
     // Step 3.
     if (!thisVal.isObject() || !thisVal.toObject().is<AsyncFromSyncIteratorObject>()) {
+        // NB: See https://github.com/tc39/proposal-async-iteration/issues/105
+        // for why this check shouldn't be necessary as long as we can ensure
+        // the Async-from-Sync iterator can't be accessed directly by user
+        // code.
+
         // Step 3.a.
         RootedValue badGeneratorError(cx);
         if (!GetTypeError(cx, JSMSG_NOT_AN_ASYNC_ITERATOR, &badGeneratorError))
             return false;
 
         // Step 3.b.
         if (!RejectMaybeWrappedPromise(cx, resultPromise, badGeneratorError))
             return false;
@@ -2653,18 +2657,17 @@ js::AsyncFromSyncIteratorMethod(JSContex
 
     // Step 4.
     RootedObject iter(cx, asyncIter->iterator());
 
     RootedValue resultVal(cx);
     RootedValue func(cx);
     if (completionKind == CompletionKind::Normal) {
         // 11.1.3.2.1 steps 5-6 (partially).
-        if (!GetProperty(cx, iter, iter, cx->names().next, &func))
-            return AbruptRejectPromise(cx, args, resultPromise, nullptr);
+        func.set(asyncIter->nextMethod());
     } else if (completionKind == CompletionKind::Return) {
         // 11.1.3.2.2 steps 5-6.
         if (!GetProperty(cx, iter, iter, cx->names().return_, &func))
             return AbruptRejectPromise(cx, args, resultPromise, nullptr);
 
         // Step 7.
         if (func.isNullOrUndefined()) {
             // Step 7.a.
--- a/js/src/builtin/Reflect.cpp
+++ b/js/src/builtin/Reflect.cpp
@@ -2,20 +2,20 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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 "builtin/Reflect.h"
 
 #include "jsarray.h"
-#include "jscntxt.h"
 
 #include "jit/InlinableNatives.h"
 #include "vm/ArgumentsObject.h"
+#include "vm/JSContext.h"
 #include "vm/Stack.h"
 
 #include "vm/Interpreter-inl.h"
 
 using namespace js;
 
 
 /*** Reflect methods *****************************************************************************/
--- a/js/src/builtin/Reflect.h
+++ b/js/src/builtin/Reflect.h
@@ -2,17 +2,17 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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 builtin_Reflect_h
 #define builtin_Reflect_h
 
-#include "jsobj.h"
+#include "vm/JSObject.h"
 
 namespace js {
 
 extern JSObject*
 InitReflect(JSContext* cx, js::HandleObject obj);
 
 }
 
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -8,28 +8,28 @@
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Move.h"
 
 #include <stdlib.h>
 
 #include "jsarray.h"
-#include "jsobj.h"
 #include "jspubtd.h"
 
 #include "builtin/Reflect.h"
 #include "frontend/Parser.h"
 #include "frontend/TokenStream.h"
 #include "js/CharacterEncoding.h"
+#include "vm/JSAtom.h"
+#include "vm/JSObject.h"
 #include "vm/RegExpObject.h"
 
-#include "jsobjinlines.h"
-
 #include "frontend/ParseNode-inl.h"
+#include "vm/JSObject-inl.h"
 
 using namespace js;
 using namespace js::frontend;
 
 using JS::AutoValueArray;
 using mozilla::ArrayLength;
 using mozilla::DebugOnly;
 using mozilla::Forward;
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -4,28 +4,26 @@
  * 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 "builtin/RegExp.h"
 
 #include "mozilla/CheckedInt.h"
 #include "mozilla/TypeTraits.h"
 
-#include "jscntxt.h"
-
 #include "frontend/TokenStream.h"
 #include "irregexp/RegExpParser.h"
 #include "jit/InlinableNatives.h"
+#include "vm/JSContext.h"
 #include "vm/RegExpStatics.h"
 #include "vm/SelfHosting.h"
 #include "vm/StringBuffer.h"
 #include "vm/Unicode.h"
 
-#include "jsobjinlines.h"
-
+#include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/UnboxedObject-inl.h"
 
 using namespace js;
 using namespace js::unicode;
 
 using mozilla::ArrayLength;
 using mozilla::CheckedInt;
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -23,17 +23,17 @@
 #include "jsnum.h"
 #include "jsprf.h"
 
 #include "builtin/TypedObject.h"
 #include "jit/AtomicOperations.h"
 #include "jit/InlinableNatives.h"
 #include "js/Value.h"
 
-#include "jsobjinlines.h"
+#include "vm/JSObject-inl.h"
 
 using namespace js;
 
 using mozilla::ArrayLength;
 using mozilla::IsFinite;
 using mozilla::IsNaN;
 using mozilla::FloorLog2;
 using mozilla::NumberIsInt32;
--- a/js/src/builtin/Stream.cpp
+++ b/js/src/builtin/Stream.cpp
@@ -3,19 +3,18 @@
  * 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 "builtin/Stream.h"
 
 #include "js/Stream.h"
 
-#include "jscntxt.h"
-
 #include "gc/Heap.h"
+#include "vm/JSContext.h"
 #include "vm/SelfHosting.h"
 
 #include "vm/List-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
 enum StreamSlots {
--- a/js/src/builtin/SymbolObject.cpp
+++ b/js/src/builtin/SymbolObject.cpp
@@ -4,18 +4,17 @@
  * 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 "builtin/SymbolObject.h"
 
 #include "vm/StringBuffer.h"
 #include "vm/Symbol.h"
 
-#include "jsobjinlines.h"
-
+#include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using JS::Symbol;
 using namespace js;
 
 const Class SymbolObject::class_ = {
     "Symbol",
     JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_Symbol)
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -12,20 +12,18 @@
 #include "mozilla/Sprintf.h"
 #include "mozilla/Unused.h"
 
 #include <cmath>
 #include <cstdlib>
 #include <ctime>
 
 #include "jsapi.h"
-#include "jscntxt.h"
 #include "jsfriendapi.h"
 #include "jsiter.h"
-#include "jsobj.h"
 #include "jsprf.h"
 #include "jswrapper.h"
 
 #include "builtin/Promise.h"
 #include "builtin/SelfHostingDefines.h"
 #ifdef DEBUG
 #include "frontend/TokenStream.h"
 #include "irregexp/RegExpAST.h"
@@ -39,34 +37,35 @@
 #include "js/UbiNode.h"
 #include "js/UbiNodeBreadthFirst.h"
 #include "js/UbiNodeShortestPaths.h"
 #include "js/UniquePtr.h"
 #include "js/Vector.h"
 #include "vm/Debugger.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
+#include "vm/JSContext.h"
+#include "vm/JSObject.h"
 #include "vm/ProxyObject.h"
 #include "vm/SavedStacks.h"
 #include "vm/Stack.h"
 #include "vm/StringBuffer.h"
 #include "vm/TraceLogging.h"
 #include "wasm/AsmJS.h"
 #include "wasm/WasmBinaryToText.h"
 #include "wasm/WasmJS.h"
 #include "wasm/WasmModule.h"
 #include "wasm/WasmSignalHandlers.h"
 #include "wasm/WasmTextToBinary.h"
 #include "wasm/WasmTypes.h"
 
-#include "jscntxtinlines.h"
-#include "jsobjinlines.h"
-
 #include "vm/Debugger-inl.h"
 #include "vm/EnvironmentObject-inl.h"
+#include "vm/JSContext-inl.h"
+#include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
 using mozilla::ArrayLength;
 using mozilla::Move;
 
 // If fuzzingSafe is set, remove functionality that could cause problems with
--- a/js/src/builtin/TypedArray.js
+++ b/js/src/builtin/TypedArray.js
@@ -1557,30 +1557,42 @@ function TypedArrayToStringTag() {
         return undefined;
 
     // Steps 4-6.
     // Modified to retrieve the [[TypedArrayName]] from the constructor.
     return _NameForTypedArray(O);
 }
 _SetCanonicalName(TypedArrayToStringTag, "get [Symbol.toStringTag]");
 
-// ES2017 draft rev 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e
+// ES2018 draft rev 0525bb33861c7f4e9850f8a222c89642947c4b9c
 // 22.2.2.1.1 Runtime Semantics: IterableToList( items, method )
 function IterableToList(items, method) {
-    // Step 1.
-    var iterator = GetIterator(items, method);
+    // Step 1 (Inlined GetIterator).
+
+    // 7.4.1 GetIterator, step 1.
+    assert(IsCallable(method), "method argument is a function");
+
+    // 7.4.1 GetIterator, step 2.
+    var iterator = callContentFunction(method, items);
+
+    // 7.4.1 GetIterator, step 3.
+    if (!IsObject(iterator))
+        ThrowTypeError(JSMSG_GET_ITER_RETURNED_PRIMITIVE);
+
+    // 7.4.1 GetIterator, step 4.
+    var nextMethod = iterator.next;
 
     // Step 2.
     var values = [];
 
     // Steps 3-4.
     var i = 0;
     while (true) {
         // Step 4.a.
-        var next = callContentFunction(iterator.next, iterator);
+        var next = callContentFunction(nextMethod, iterator);
         if (!IsObject(next))
             ThrowTypeError(JSMSG_ITER_METHOD_RETURNED_PRIMITIVE, "next");
 
         // Step 4.b.
         if (next.done)
             break;
         _DefineDataProperty(values, i++, next.value);
     }
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -4,32 +4,32 @@
  * 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 "builtin/TypedObject-inl.h"
 
 #include "mozilla/Casting.h"
 #include "mozilla/CheckedInt.h"
 
-#include "jscompartment.h"
-#include "jsfun.h"
 #include "jsutil.h"
 
 #include "builtin/SIMD.h"
 #include "gc/Marking.h"
 #include "js/Vector.h"
 #include "vm/GlobalObject.h"
+#include "vm/JSCompartment.h"
+#include "vm/JSFunction.h"
 #include "vm/String.h"
 #include "vm/StringBuffer.h"
 #include "vm/TypedArrayObject.h"
 
-#include "jsobjinlines.h"
-
 #include "gc/Nursery-inl.h"
 #include "gc/StoreBuffer-inl.h"
+#include "vm/JSAtom-inl.h"
+#include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/Shape-inl.h"
 
 using mozilla::AssertedCast;
 using mozilla::CheckedInt32;
 using mozilla::DebugOnly;
 using mozilla::IsPowerOfTwo;
 using mozilla::PodCopy;
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -2,22 +2,22 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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 builtin_TypedObject_h
 #define builtin_TypedObject_h
 
-#include "jsobj.h"
 #include "jsweakmap.h"
 
 #include "builtin/TypedObjectConstants.h"
 #include "js/Conversions.h"
 #include "vm/ArrayBufferObject.h"
+#include "vm/JSObject.h"
 #include "vm/ShapedObject.h"
 
 /*
  * -------------
  * Typed Objects
  * -------------
  *
  * Typed objects are a special kind of JS object where the data is
--- a/js/src/builtin/Utilities.js
+++ b/js/src/builtin/Utilities.js
@@ -152,32 +152,16 @@ function GetMethod(V, P) {
 }
 
 /* Spec: ECMAScript Draft, 6th edition Dec 24, 2014, 7.2.7 */
 function IsPropertyKey(argument) {
     var type = typeof argument;
     return type === "string" || type === "symbol";
 }
 
-/* Spec: ECMAScript Draft, 6th edition Dec 24, 2014, 7.4.1 */
-function GetIterator(obj, method) {
-    // Steps 1-2.
-    assert(IsCallable(method), "method argument is not optional");
-
-    // Steps 3-4.
-    var iterator = callContentFunction(method, obj);
-
-    // Step 5.
-    if (!IsObject(iterator))
-        ThrowTypeError(JSMSG_GET_ITER_RETURNED_PRIMITIVE);
-
-    // Step 6.
-    return iterator;
-}
-
 #define TO_PROPERTY_KEY(name) \
 (typeof name !== "string" && typeof name !== "number" && typeof name !== "symbol" ? ToPropertyKey(name) : name)
 
 var _builtinCtorsCache = {__proto__: null};
 
 function GetBuiltinConstructor(builtinName) {
     var ctor = _builtinCtorsCache[builtinName] ||
                (_builtinCtorsCache[builtinName] = GetBuiltinConstructorImpl(builtinName));
--- a/js/src/builtin/WeakMapObject.cpp
+++ b/js/src/builtin/WeakMapObject.cpp
@@ -2,20 +2,20 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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 "builtin/WeakMapObject-inl.h"
 
 #include "jsapi.h"
-#include "jscntxt.h"
 
 #include "builtin/WeakSetObject.h"
 #include "gc/FreeOp.h"
+#include "vm/JSContext.h"
 #include "vm/SelfHosting.h"
 
 #include "vm/Interpreter-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
 MOZ_ALWAYS_INLINE bool
--- a/js/src/builtin/WeakMapObject.h
+++ b/js/src/builtin/WeakMapObject.h
@@ -2,19 +2,20 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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 builtin_WeakMapObject_h
 #define builtin_WeakMapObject_h
 
-#include "jsobj.h"
 #include "jsweakmap.h"
 
+#include "vm/JSObject.h"
+
 namespace js {
 
 // Abstract base class for WeakMapObject and WeakSetObject.
 class WeakCollectionObject : public NativeObject
 {
   public:
     ObjectValueMap* getMap() { return static_cast<ObjectValueMap*>(getPrivate()); }
 
--- a/js/src/builtin/WeakSetObject.cpp
+++ b/js/src/builtin/WeakSetObject.cpp
@@ -2,27 +2,26 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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 "builtin/WeakSetObject.h"
 
 #include "jsapi.h"
-#include "jscntxt.h"
 #include "jsiter.h"
 
 #include "builtin/MapObject.h"
 #include "vm/GlobalObject.h"
+#include "vm/JSContext.h"
 #include "vm/SelfHosting.h"
 
-#include "jsobjinlines.h"
-
 #include "builtin/WeakMapObject-inl.h"
 #include "vm/Interpreter-inl.h"
+#include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
 MOZ_ALWAYS_INLINE bool
 IsWeakSet(HandleValue v)
 {
     return v.isObject() && v.toObject().is<WeakSetObject>();
--- a/js/src/builtin/intl/Collator.cpp
+++ b/js/src/builtin/intl/Collator.cpp
@@ -6,29 +6,29 @@
 
 /* Intl.Collator implementation. */
 
 #include "builtin/intl/Collator.h"
 
 #include "mozilla/Assertions.h"
 
 #include "jsapi.h"
-#include "jscntxt.h"
 
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "builtin/intl/SharedIntlData.h"
 #include "gc/FreeOp.h"
 #include "js/TypeDecls.h"
 #include "vm/GlobalObject.h"
+#include "vm/JSContext.h"
 #include "vm/Runtime.h"
 #include "vm/String.h"
 
-#include "jsobjinlines.h"
+#include "vm/JSObject-inl.h"
 
 using namespace js;
 
 using js::intl::GetAvailableLocales;
 using js::intl::IcuLocale;
 using js::intl::ReportInternalError;
 using js::intl::SharedIntlData;
 using js::intl::StringsAreEqual;
--- a/js/src/builtin/intl/CommonFunctions.cpp
+++ b/js/src/builtin/intl/CommonFunctions.cpp
@@ -5,25 +5,25 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Operations used to implement multiple Intl.* classes. */
 
 #include "builtin/intl/CommonFunctions.h"
 
 #include "mozilla/Assertions.h"
 
-#include "jscntxt.h"
 #include "jsfriendapi.h" // for GetErrorMessage, JSMSG_INTERNAL_INTL_ERROR
-#include "jsobj.h"
 
 #include "js/Value.h"
+#include "vm/JSContext.h"
+#include "vm/JSObject.h"
 #include "vm/SelfHosting.h"
 #include "vm/Stack.h"
 
-#include "jsobjinlines.h"
+#include "vm/JSObject-inl.h"
 
 bool
 js::intl::InitializeObject(JSContext* cx, JS::Handle<JSObject*> obj,
                            JS::Handle<PropertyName*> initializer, JS::Handle<JS::Value> locales,
                            JS::Handle<JS::Value> options)
 {
     FixedInvokeArgs<3> args(cx);
 
--- a/js/src/builtin/intl/CommonFunctions.h
+++ b/js/src/builtin/intl/CommonFunctions.h
@@ -14,20 +14,16 @@
 #include <stdint.h>
 #include <string.h>
 
 #include "builtin/intl/ICUStubs.h"
 #include "js/RootingAPI.h"
 #include "js/Vector.h"
 #include "vm/String.h"
 
-namespace JS { class Value; }
-
-class JSObject;
-
 namespace js {
 
 namespace intl {
 
 /**
  * Initialize a new Intl.* object using the named self-hosted function.
  */
 extern bool
--- a/js/src/builtin/intl/DateTimeFormat.cpp
+++ b/js/src/builtin/intl/DateTimeFormat.cpp
@@ -6,30 +6,29 @@
 
 /* Intl.DateTimeFormat implementation. */
 
 #include "builtin/intl/DateTimeFormat.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Range.h"
 
-#include "jscntxt.h"
 #include "jsfriendapi.h"
 
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "builtin/intl/SharedIntlData.h"
 #include "builtin/intl/TimeZoneDataGenerated.h"
 #include "gc/FreeOp.h"
 #include "vm/GlobalObject.h"
+#include "vm/JSContext.h"
 #include "vm/Runtime.h"
 
-#include "jsobjinlines.h"
-
+#include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
 using JS::ClippedTime;
 using JS::TimeClip;
 
 using js::intl::CallICU;
--- a/js/src/builtin/intl/IntlObject.cpp
+++ b/js/src/builtin/intl/IntlObject.cpp
@@ -8,31 +8,31 @@
 
 #include "builtin/intl/IntlObject.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Likely.h"
 #include "mozilla/Range.h"
 
 #include "jsapi.h"
-#include "jscntxt.h"
-#include "jsobj.h"
 
 #include "builtin/intl/Collator.h"
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/DateTimeFormat.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/NumberFormat.h"
 #include "builtin/intl/PluralRules.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "js/Class.h"
 #include "vm/GlobalObject.h"
+#include "vm/JSContext.h"
+#include "vm/JSObject.h"
 #include "vm/String.h"
 
-#include "jsobjinlines.h"
+#include "vm/JSObject-inl.h"
 
 using namespace js;
 
 using mozilla::Range;
 using mozilla::RangedPtr;
 
 using js::intl::CallICU;
 using js::intl::DateTimeFormatOptions;
--- a/js/src/builtin/intl/IntlObject.h
+++ b/js/src/builtin/intl/IntlObject.h
@@ -5,21 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef builtin_intl_IntlObject_h
 #define builtin_intl_IntlObject_h
 
 #include "mozilla/Attributes.h"
 
 #include "js/RootingAPI.h"
-
-struct JSContext;
-class JSObject;
-
-namespace JS { class Value; }
+#include "js/TypeDecls.h"
 
 namespace js {
 
 /**
  * Initializes the Intl Object and its standard built-in properties.
  * Spec: ECMAScript Internationalization API Specification, 8.0, 8.1
  */
 extern JSObject*
--- a/js/src/builtin/intl/NumberFormat.cpp
+++ b/js/src/builtin/intl/NumberFormat.cpp
@@ -10,29 +10,28 @@
 
 #include "mozilla/Assertions.h"
 #include "mozilla/FloatingPoint.h"
 
 #include <algorithm>
 #include <stddef.h>
 #include <stdint.h>
 
-#include "jscntxt.h"
-
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "ds/Sort.h"
 #include "gc/FreeOp.h"
 #include "js/RootingAPI.h"
 #include "js/TypeDecls.h"
+#include "vm/JSContext.h"
 #include "vm/SelfHosting.h"
 #include "vm/Stack.h"
 
-#include "jsobjinlines.h"
+#include "vm/JSObject-inl.h"
 
 using namespace js;
 
 using mozilla::AssertedCast;
 using mozilla::IsFinite;
 using mozilla::IsNaN;
 using mozilla::IsNegativeZero;
 
--- a/js/src/builtin/intl/PluralRules.cpp
+++ b/js/src/builtin/intl/PluralRules.cpp
@@ -6,27 +6,25 @@
 
 /* Implementation of the Intl.PluralRules proposal. */
 
 #include "builtin/intl/PluralRules.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Casting.h"
 
-#include "jscntxt.h"
-
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "gc/FreeOp.h"
 #include "vm/GlobalObject.h"
+#include "vm/JSContext.h"
 #include "vm/String.h"
 
-#include "jsobjinlines.h"
-
+#include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
 using mozilla::AssertedCast;
 
 using js::intl::CallICU;
 using js::intl::DateTimeFormatOptions;
--- a/js/src/builtin/intl/RelativeTimeFormat.cpp
+++ b/js/src/builtin/intl/RelativeTimeFormat.cpp
@@ -6,23 +6,22 @@
 
 /* Implementation of the Intl.RelativeTimeFormat proposal. */
 
 #include "builtin/intl/RelativeTimeFormat.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Casting.h"
 
-#include "jscntxt.h"
-
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "gc/FreeOp.h"
 #include "vm/GlobalObject.h"
+#include "vm/JSContext.h"
 
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
 using mozilla::IsNegativeZero;
 using mozilla::Range;
 using mozilla::RangedPtr;
--- a/js/src/builtin/intl/SharedIntlData.cpp
+++ b/js/src/builtin/intl/SharedIntlData.cpp
@@ -8,24 +8,24 @@
 
 #include "builtin/intl/SharedIntlData.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/HashFunctions.h"
 
 #include <stdint.h>
 
-#include "jsatom.h"
 #include "jsstr.h"
 
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "builtin/intl/TimeZoneDataGenerated.h"
 #include "js/Utility.h"
+#include "vm/JSAtom.h"
 
 using js::HashNumber;
 using js::intl::StringsAreEqual;
 
 template<typename Char>
 static constexpr Char
 ToUpperASCII(Char c)
 {
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -26,32 +26,32 @@
 #ifdef HAVE_SSIZE_T
 #include <sys/types.h>
 #endif
 
 #if defined(XP_UNIX)
 #include <errno.h>
 #endif
 
-#include "jscntxt.h"
 #include "jsexn.h"
-#include "jsfun.h"
 #include "jsnum.h"
 #include "jsprf.h"
 #include "jswin.h"
 
 #include "builtin/TypedObject.h"
 #include "ctypes/Library.h"
 #include "gc/FreeOp.h"
 #include "gc/Policy.h"
 #include "gc/Zone.h"
 #include "jit/AtomicOperations.h"
 #include "js/Vector.h"
-
-#include "jsobjinlines.h"
+#include "vm/JSContext.h"
+#include "vm/JSFunction.h"
+
+#include "vm/JSObject-inl.h"
 
 using namespace std;
 
 using JS::AutoCheckCannotGC;
 
 namespace js {
 namespace ctypes {
 
--- a/js/src/devtools/automation/autospider.py
+++ b/js/src/devtools/automation/autospider.py
@@ -274,17 +274,17 @@ if word_bits == 32:
         else:
             sse_flags = '-msse -msse2 -mfpmath=sse'
         env['CCFLAGS'] = '{0} {1}'.format(env.get('CCFLAGS', ''), sse_flags)
         env['CXXFLAGS'] = '{0} {1}'.format(env.get('CXXFLAGS', ''), sse_flags)
 else:
     if platform.system() == 'Windows':
         CONFIGURE_ARGS += ' --target=x86_64-pc-mingw32 --host=x86_64-pc-mingw32'
 
-if platform.system() == 'Linux':
+if platform.system() == 'Linux' and AUTOMATION:
     CONFIGURE_ARGS = '--enable-stdcxx-compat ' + CONFIGURE_ARGS
 
 # Timeouts.
 ACTIVE_PROCESSES = set()
 
 
 def killall():
     for proc in ACTIVE_PROCESSES:
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -4,33 +4,31 @@
  * 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 "frontend/BytecodeCompiler.h"
 
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/Maybe.h"
 
-#include "jscntxt.h"
-#include "jsscript.h"
-
 #include "builtin/ModuleObject.h"
 #include "frontend/BytecodeEmitter.h"
 #include "frontend/ErrorReporter.h"
 #include "frontend/FoldConstants.h"
 #include "frontend/NameFunctions.h"
 #include "frontend/Parser.h"
 #include "vm/GlobalObject.h"
+#include "vm/JSContext.h"
+#include "vm/JSScript.h"
 #include "vm/TraceLogging.h"
 #include "wasm/AsmJS.h"
 
-#include "jsobjinlines.h"
-#include "jsscriptinlines.h"
-
 #include "vm/EnvironmentObject-inl.h"
+#include "vm/JSObject-inl.h"
+#include "vm/JSScript-inl.h"
 
 using namespace js;
 using namespace js::frontend;
 using mozilla::Maybe;
 using mozilla::Nothing;
 
 // The BytecodeCompiler class contains resources common to compiling scripts and
 // function bodies.
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -14,36 +14,37 @@
 #include "mozilla/DebugOnly.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/PodOperations.h"
 
 #include <string.h>
 
 #include "jsapi.h"
-#include "jscntxt.h"
-#include "jsfun.h"
 #include "jsnum.h"
 #include "jsopcode.h"
-#include "jsscript.h"
 #include "jstypes.h"
 #include "jsutil.h"
 
 #include "ds/Nestable.h"
 #include "frontend/Parser.h"
 #include "frontend/TokenStream.h"
 #include "vm/Debugger.h"
 #include "vm/GeneratorObject.h"
+#include "vm/JSAtom.h"
+#include "vm/JSContext.h"
+#include "vm/JSFunction.h"
+#include "vm/JSScript.h"
 #include "vm/Stack.h"
 #include "wasm/AsmJS.h"
 
-#include "jsscriptinlines.h"
-
 #include "frontend/ParseNode-inl.h"
 #include "vm/EnvironmentObject-inl.h"
+#include "vm/JSAtom-inl.h"
+#include "vm/JSScript-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 using namespace js::gc;
 using namespace js::frontend;
 
 using mozilla::AssertedCast;
 using mozilla::DebugOnly;
@@ -247,22 +248,31 @@ class LoopControl : public BreakableCont
         MOZ_ASSERT(is<LoopControl>());
 
         LoopControl* enclosingLoop = findNearest<LoopControl>(enclosing());
 
         stackDepth_ = bce->stackDepth;
         loopDepth_ = enclosingLoop ? enclosingLoop->loopDepth_ + 1 : 1;
 
         int loopSlots;
-        if (loopKind == StatementKind::Spread)
+        if (loopKind == StatementKind::Spread) {
+            // The iterator next method, the iterator, the result array, and
+            // the current array index are on the stack.
+            loopSlots = 4;
+        } else if (loopKind == StatementKind::ForOfLoop) {
+            // The iterator next method, the iterator, and the current value
+            // are on the stack.
             loopSlots = 3;
-        else if (loopKind == StatementKind::ForInLoop || loopKind == StatementKind::ForOfLoop)
+        } else if (loopKind == StatementKind::ForInLoop) {
+            // The iterator and the current value are on the stack.
             loopSlots = 2;
-        else
+        } else {
+            // No additional loop values are on the stack.
             loopSlots = 0;
+        }
 
         MOZ_ASSERT(loopSlots <= stackDepth_);
 
         if (enclosingLoop) {
             canIonOsr_ = (enclosingLoop->canIonOsr_ &&
                           stackDepth_ == enclosingLoop->stackDepth_ + loopSlots);
         } else {
             canIonOsr_ = stackDepth_ == loopSlots;
@@ -2092,35 +2102,43 @@ class ForOfLoopControl : public LoopCont
         return bce->tryNoteList.append(JSTRY_FOR_OF_ITERCLOSE, 0, start, end);
     }
 
     bool emitPrepareForNonLocalJump(BytecodeEmitter* bce, bool isTarget) {
         // Pop unnecessary value from the stack.  Effectively this means
         // leaving try-catch block.  However, the performing IteratorClose can
         // reach the depth for try-catch, and effectively re-enter the
         // try-catch block.
+        if (!bce->emit1(JSOP_POP))                        // NEXT ITER
+            return false;
+
+        // Pop the iterator's next method.
+        if (!bce->emit1(JSOP_SWAP))                       // ITER NEXT
+            return false;
         if (!bce->emit1(JSOP_POP))                        // ITER
             return false;
 
         // Clear ITER slot on the stack to tell catch block to avoid performing
         // IteratorClose again.
         if (!bce->emit1(JSOP_UNDEFINED))                  // ITER UNDEF
             return false;
         if (!bce->emit1(JSOP_SWAP))                       // UNDEF ITER
             return false;
 
         if (!emitIteratorClose(bce))                      // UNDEF
             return false;
 
         if (isTarget) {
             // At the level of the target block, there's bytecode after the
-            // loop that will pop the iterator and the value, so push
-            // an undefined to balance the stack.
+            // loop that will pop the next method, the iterator, and the
+            // value, so push two undefineds to balance the stack.
             if (!bce->emit1(JSOP_UNDEFINED))              // UNDEF UNDEF
                 return false;
+            if (!bce->emit1(JSOP_UNDEFINED))              // UNDEF UNDEF UNDEF
+                return false;
         } else {
             if (!bce->emit1(JSOP_POP))                    //
                 return false;
         }
 
         return true;
     }
 };
@@ -2783,17 +2801,19 @@ NonLocalExitControl::prepareForNonLocalJ
             if (emitIteratorClose) {
                 if (!flushPops(bce_))
                     return false;
 
                 ForOfLoopControl& loopinfo = control->as<ForOfLoopControl>();
                 if (!loopinfo.emitPrepareForNonLocalJump(bce_, /* isTarget = */ false)) // ...
                     return false;
             } else {
-                npops += 2;
+                // The iterator next method, the iterator, and the current
+                // value are on the stack.
+                npops += 3;
             }
             break;
 
           case StatementKind::ForInLoop:
             if (!flushPops(bce_))
                 return false;
 
             // The iterator and the current value are on the stack.
@@ -2808,17 +2828,17 @@ NonLocalExitControl::prepareForNonLocalJ
         }
     }
 
     if (!flushPops(bce_))
         return false;
 
     if (target && emitIteratorCloseAtTarget && target->is<ForOfLoopControl>()) {
         ForOfLoopControl& loopinfo = target->as<ForOfLoopControl>();
-        if (!loopinfo.emitPrepareForNonLocalJump(bce_, /* isTarget = */ true)) // ... UNDEF UNDEF
+        if (!loopinfo.emitPrepareForNonLocalJump(bce_, /* isTarget = */ true)) // ... UNDEF UNDEF UNDEF
             return false;
     }
 
     EmitterScope* targetEmitterScope = target ? target->emitterScope() : bce_->varEmitterScope;
     for (; es != targetEmitterScope; es = es->enclosingInFrame()) {
         if (!leaveScope(es))
             return false;
     }
@@ -5222,22 +5242,18 @@ BytecodeEmitter::emitSetOrInitializeDest
 bool
 BytecodeEmitter::emitIteratorNext(ParseNode* pn, IteratorKind iterKind /* = IteratorKind::Sync */,
                                   bool allowSelfHosted /* = false */)
 {
     MOZ_ASSERT(allowSelfHosted || emitterMode != BytecodeEmitter::SelfHosting,
                ".next() iteration is prohibited in self-hosted code because it "
                "can run user-modifiable iteration code");
 
-    if (!emit1(JSOP_DUP))                                 // ... ITER ITER
-        return false;
-    if (!emitAtomOp(cx->names().next, JSOP_CALLPROP))     // ... ITER NEXT
-        return false;
-    if (!emit1(JSOP_SWAP))                                // ... NEXT ITER
-        return false;
+    MOZ_ASSERT(this->stackDepth >= 2);                    // ... NEXT ITER
+
     if (!emitCall(JSOP_CALL, 0, pn))                      // ... RESULT
         return false;
 
     if (iterKind == IteratorKind::Async) {
         if (!emitAwait())                                 // ... RESULT
             return false;
     }
 
@@ -5539,68 +5555,69 @@ BytecodeEmitter::emitDestructuringOpsArr
 
     // Here's pseudo code for |let [a, b, , c=y, ...d] = x;|
     //
     // Lines that are annotated "covered by trynote" mean that upon throwing
     // an exception, IteratorClose is called on iter only if done is false.
     //
     //   let x, y;
     //   let a, b, c, d;
-    //   let iter, lref, result, done, value; // stack values
+    //   let iter, next, lref, result, done, value; // stack values
     //
     //   iter = x[Symbol.iterator]();
+    //   next = iter.next;
     //
     //   // ==== emitted by loop for a ====
     //   lref = GetReference(a);              // covered by trynote
     //
-    //   result = iter.next();
+    //   result = Call(next, iter);
     //   done = result.done;
     //
     //   if (done)
     //     value = undefined;
     //   else
     //     value = result.value;
     //
     //   SetOrInitialize(lref, value);        // covered by trynote
     //
     //   // ==== emitted by loop for b ====
     //   lref = GetReference(b);              // covered by trynote
     //
     //   if (done) {
     //     value = undefined;
     //   } else {
-    //     result = iter.next();
+    //     result = Call(next, iter);
     //     done = result.done;
     //     if (done)
     //       value = undefined;
     //     else
     //       value = result.value;
     //   }
     //
     //   SetOrInitialize(lref, value);        // covered by trynote
     //
     //   // ==== emitted by loop for elision ====
     //   if (done) {
     //     value = undefined;
     //   } else {
-    //     result = iter.next();
+    //     result = Call(next, iter);
     //     done = result.done;
     //     if (done)
     //       value = undefined;
     //     else
     //       value = result.value;
     //   }
     //
     //   // ==== emitted by loop for c ====
     //   lref = GetReference(c);              // covered by trynote
     //
     //   if (done) {
     //     value = undefined;
     //   } else {
-    //     result = iter.next();
+    //     result = Call(next, iter);
     //     done = result.done;
     //     if (done)
     //       value = undefined;
     //     else
     //       value = result.value;
     //   }
     //
     //   if (value === undefined)
@@ -5621,26 +5638,32 @@ BytecodeEmitter::emitDestructuringOpsArr
     //   // === emitted after loop ===
     //   if (!done)
     //      IteratorClose(iter);
 
     // Use an iterator to destructure the RHS, instead of index lookup. We
     // must leave the *original* value on the stack.
     if (!emit1(JSOP_DUP))                                         // ... OBJ OBJ
         return false;
-    if (!emitIterator())                                          // ... OBJ ITER
+    if (!emitIterator())                                          // ... OBJ NEXT ITER
         return false;
 
     // For an empty pattern [], call IteratorClose unconditionally. Nothing
     // else needs to be done.
-    if (!pattern->pn_head)
+    if (!pattern->pn_head) {
+        if (!emit1(JSOP_SWAP))                                    // ... OBJ ITER NEXT
+            return false;
+        if (!emit1(JSOP_POP))                                     // ... OBJ ITER
+            return false;
+
         return emitIteratorClose();                               // ... OBJ
+    }
 
     // Push an initial FALSE value for DONE.
-    if (!emit1(JSOP_FALSE))                                       // ... OBJ ITER FALSE
+    if (!emit1(JSOP_FALSE))                                       // ... OBJ NEXT ITER FALSE
         return false;
 
     // JSTRY_DESTRUCTURING_ITERCLOSE expects the iterator and the done value
     // to be the second to top and the top of the stack, respectively.
     // IteratorClose is called upon exception only if done is false.
     int32_t tryNoteDepth = stackDepth;
 
     for (ParseNode* member = pattern->pn_head; member; member = member->pn_next) {
@@ -5652,186 +5675,194 @@ BytecodeEmitter::emitDestructuringOpsArr
         // Spec requires LHS reference to be evaluated first.
         ParseNode* lhsPattern = member;
         if (lhsPattern->isKind(ParseNodeKind::Assign))
             lhsPattern = lhsPattern->pn_left;
 
         bool isElision = lhsPattern->isKind(ParseNodeKind::Elision);
         if (!isElision) {
             auto emitLHSRef = [lhsPattern, &emitted](BytecodeEmitter* bce) {
-                return bce->emitDestructuringLHSRef(lhsPattern, &emitted); // ... OBJ ITER DONE *LREF
+                return bce->emitDestructuringLHSRef(lhsPattern, &emitted); // ... OBJ NEXT ITER DONE *LREF
             };
             if (!wrapWithDestructuringIteratorCloseTryNote(tryNoteDepth, emitLHSRef))
                 return false;
         }
 
         // Pick the DONE value to the top of the stack.
         if (emitted) {
-            if (!emit2(JSOP_PICK, emitted))                       // ... OBJ ITER *LREF DONE
+            if (!emit2(JSOP_PICK, emitted))                       // ... OBJ NEXT ITER *LREF DONE
                 return false;
         }
 
         if (isFirst) {
             // If this element is the first, DONE is always FALSE, so pop it.
             //
             // Non-first elements should emit if-else depending on the
             // member pattern, below.
-            if (!emit1(JSOP_POP))                                 // ... OBJ ITER *LREF
+            if (!emit1(JSOP_POP))                                 // ... OBJ NEXT ITER *LREF
                 return false;
         }
 
         if (member->isKind(ParseNodeKind::Spread)) {
             IfThenElseEmitter ifThenElse(this);
             if (!isFirst) {
                 // If spread is not the first element of the pattern,
                 // iterator can already be completed.
-                                                                  // ... OBJ ITER *LREF DONE
-                if (!ifThenElse.emitIfElse())                     // ... OBJ ITER *LREF
+                                                                  // ... OBJ NEXT ITER *LREF DONE
+                if (!ifThenElse.emitIfElse())                     // ... OBJ NEXT ITER *LREF
                     return false;
 
-                if (!emitUint32Operand(JSOP_NEWARRAY, 0))         // ... OBJ ITER *LREF ARRAY
+                if (!emitUint32Operand(JSOP_NEWARRAY, 0))         // ... OBJ NEXT ITER *LREF ARRAY
                     return false;
-                if (!ifThenElse.emitElse())                       // ... OBJ ITER *LREF
+                if (!ifThenElse.emitElse())                       // ... OBJ NEXT ITER *LREF
                     return false;
             }
 
             // If iterator is not completed, create a new array with the rest
             // of the iterator.
-            if (!emitDupAt(emitted))                              // ... OBJ ITER *LREF ITER
-                return false;
-            if (!emitUint32Operand(JSOP_NEWARRAY, 0))             // ... OBJ ITER *LREF ITER ARRAY
-                return false;
-            if (!emitNumberOp(0))                                 // ... OBJ ITER *LREF ITER ARRAY INDEX
-                return false;
-            if (!emitSpread())                                    // ... OBJ ITER *LREF ARRAY INDEX
-                return false;
-            if (!emit1(JSOP_POP))                                 // ... OBJ ITER *LREF ARRAY
+            if (!emitDupAt(emitted + 1))                          // ... OBJ NEXT ITER *LREF NEXT
+                return false;
+            if (!emitDupAt(emitted + 1))                          // ... OBJ NEXT ITER *LREF NEXT ITER
+                return false;
+            if (!emitUint32Operand(JSOP_NEWARRAY, 0))             // ... OBJ NEXT ITER *LREF NEXT ITER ARRAY
+                return false;
+            if (!emitNumberOp(0))                                 // ... OBJ NEXT ITER *LREF NEXT ITER ARRAY INDEX
+                return false;
+            if (!emitSpread())                                    // ... OBJ NEXT ITER *LREF ARRAY INDEX
+                return false;
+            if (!emit1(JSOP_POP))                                 // ... OBJ NEXT ITER *LREF ARRAY
                 return false;
 
             if (!isFirst) {
                 if (!ifThenElse.emitEnd())
                     return false;
                 MOZ_ASSERT(ifThenElse.pushed() == 1);
             }
 
             // At this point the iterator is done. Unpick a TRUE value for DONE above ITER.
-            if (!emit1(JSOP_TRUE))                                // ... OBJ ITER *LREF ARRAY TRUE
-                return false;
-            if (!emit2(JSOP_UNPICK, emitted + 1))                 // ... OBJ ITER TRUE *LREF ARRAY
+            if (!emit1(JSOP_TRUE))                                // ... OBJ NEXT ITER *LREF ARRAY TRUE
+                return false;
+            if (!emit2(JSOP_UNPICK, emitted + 1))                 // ... OBJ NEXT ITER TRUE *LREF ARRAY
                 return false;
 
             auto emitAssignment = [member, flav](BytecodeEmitter* bce) {
-                return bce->emitSetOrInitializeDestructuring(member, flav); // ... OBJ ITER TRUE
+                return bce->emitSetOrInitializeDestructuring(member, flav); // ... OBJ NEXT ITER TRUE
             };
             if (!wrapWithDestructuringIteratorCloseTryNote(tryNoteDepth, emitAssignment))
                 return false;
 
             MOZ_ASSERT(!hasNext);
             break;
         }
 
         ParseNode* pndefault = nullptr;
         if (member->isKind(ParseNodeKind::Assign))
             pndefault = member->pn_right;
 
         MOZ_ASSERT(!member->isKind(ParseNodeKind::Spread));
 
         IfThenElseEmitter ifAlreadyDone(this);
         if (!isFirst) {
-                                                                  // ... OBJ ITER *LREF DONE
-            if (!ifAlreadyDone.emitIfElse())                      // ... OBJ ITER *LREF
-                return false;
-
-            if (!emit1(JSOP_UNDEFINED))                           // ... OBJ ITER *LREF UNDEF
-                return false;
-            if (!emit1(JSOP_NOP_DESTRUCTURING))                   // ... OBJ ITER *LREF UNDEF
+                                                                  // ... OBJ NEXT ITER *LREF DONE
+            if (!ifAlreadyDone.emitIfElse())                      // ... OBJ NEXT ITER *LREF
+                return false;
+
+            if (!emit1(JSOP_UNDEFINED))                           // ... OBJ NEXT ITER *LREF UNDEF
+                return false;
+            if (!emit1(JSOP_NOP_DESTRUCTURING))                   // ... OBJ NEXT ITER *LREF UNDEF
                 return false;
 
             // The iterator is done. Unpick a TRUE value for DONE above ITER.
-            if (!emit1(JSOP_TRUE))                                // ... OBJ ITER *LREF UNDEF TRUE
-                return false;
-            if (!emit2(JSOP_UNPICK, emitted + 1))                 // ... OBJ ITER TRUE *LREF UNDEF
-                return false;
-
-            if (!ifAlreadyDone.emitElse())                        // ... OBJ ITER *LREF
-                return false;
-        }
-
-        if (!emitDupAt(emitted))                                  // ... OBJ ITER *LREF ITER
-            return false;
-        if (!emitIteratorNext(pattern))                           // ... OBJ ITER *LREF RESULT
-            return false;
-        if (!emit1(JSOP_DUP))                                     // ... OBJ ITER *LREF RESULT RESULT
-            return false;
-        if (!emitAtomOp(cx->names().done, JSOP_GETPROP))          // ... OBJ ITER *LREF RESULT DONE
-            return false;
-
-        if (!emit1(JSOP_DUP))                                     // ... OBJ ITER *LREF RESULT DONE DONE
-            return false;
-        if (!emit2(JSOP_UNPICK, emitted + 2))                     // ... OBJ ITER DONE *LREF RESULT DONE
+            if (!emit1(JSOP_TRUE))                                // ... OBJ NEXT ITER *LREF UNDEF TRUE
+                return false;
+            if (!emit2(JSOP_UNPICK, emitted + 1))                 // ... OBJ NEXT ITER TRUE *LREF UNDEF
+                return false;
+
+            if (!ifAlreadyDone.emitElse())                        // ... OBJ NEXT ITER *LREF
+                return false;
+        }
+
+        if (!emitDupAt(emitted + 1))                              // ... OBJ NEXT ITER *LREF NEXT
+            return false;
+        if (!emitDupAt(emitted + 1))                              // ... OBJ NEXT ITER *LREF NEXT ITER
+            return false;
+        if (!emitIteratorNext(pattern))                           // ... OBJ NEXT ITER *LREF RESULT
+            return false;
+        if (!emit1(JSOP_DUP))                                     // ... OBJ NEXT ITER *LREF RESULT RESULT
+            return false;
+        if (!emitAtomOp(cx->names().done, JSOP_GETPROP))          // ... OBJ NEXT ITER *LREF RESULT DONE
+            return false;
+
+        if (!emit1(JSOP_DUP))                                     // ... OBJ NEXT ITER *LREF RESULT DONE DONE
+            return false;
+        if (!emit2(JSOP_UNPICK, emitted + 2))                     // ... OBJ NEXT ITER DONE *LREF RESULT DONE
             return false;
 
         IfThenElseEmitter ifDone(this);
-        if (!ifDone.emitIfElse())                                 // ... OBJ ITER DONE *LREF RESULT
-            return false;
-
-        if (!emit1(JSOP_POP))                                     // ... OBJ ITER DONE *LREF
-            return false;
-        if (!emit1(JSOP_UNDEFINED))                               // ... OBJ ITER DONE *LREF UNDEF
-            return false;
-        if (!emit1(JSOP_NOP_DESTRUCTURING))                       // ... OBJ ITER DONE *LREF UNDEF
-            return false;
-
-        if (!ifDone.emitElse())                                   // ... OBJ ITER DONE *LREF RESULT
-            return false;
-
-        if (!emitAtomOp(cx->names().value, JSOP_GETPROP))         // ... OBJ ITER DONE *LREF VALUE
+        if (!ifDone.emitIfElse())                                 // ... OBJ NEXT ITER DONE *LREF RESULT
+            return false;
+
+        if (!emit1(JSOP_POP))                                     // ... OBJ NEXT ITER DONE *LREF
+            return false;
+        if (!emit1(JSOP_UNDEFINED))                               // ... OBJ NEXT ITER DONE *LREF UNDEF
+            return false;
+        if (!emit1(JSOP_NOP_DESTRUCTURING))                       // ... OBJ NEXT ITER DONE *LREF UNDEF
+            return false;
+
+        if (!ifDone.emitElse())                                   // ... OBJ NEXT ITER DONE *LREF RESULT
+            return false;
+
+        if (!emitAtomOp(cx->names().value, JSOP_GETPROP))         // ... OBJ NEXT ITER DONE *LREF VALUE
             return false;
 
         if (!ifDone.emitEnd())
             return false;
         MOZ_ASSERT(ifDone.pushed() == 0);
 
         if (!isFirst) {
             if (!ifAlreadyDone.emitEnd())
                 return false;
             MOZ_ASSERT(ifAlreadyDone.pushed() == 2);
         }
 
         if (pndefault) {
             auto emitDefault = [pndefault, lhsPattern](BytecodeEmitter* bce) {
-                return bce->emitDefault(pndefault, lhsPattern);    // ... OBJ ITER DONE *LREF VALUE
+                return bce->emitDefault(pndefault, lhsPattern);    // ... OBJ NEXT ITER DONE *LREF VALUE
             };
 
             if (!wrapWithDestructuringIteratorCloseTryNote(tryNoteDepth, emitDefault))
                 return false;
         }
 
         if (!isElision) {
             auto emitAssignment = [lhsPattern, flav](BytecodeEmitter* bce) {
-                return bce->emitSetOrInitializeDestructuring(lhsPattern, flav); // ... OBJ ITER DONE
+                return bce->emitSetOrInitializeDestructuring(lhsPattern, flav); // ... OBJ NEXT ITER DONE
             };
 
             if (!wrapWithDestructuringIteratorCloseTryNote(tryNoteDepth, emitAssignment))
                 return false;
         } else {
-            if (!emit1(JSOP_POP))                                 // ... OBJ ITER DONE
+            if (!emit1(JSOP_POP))                                 // ... OBJ NEXT ITER DONE
                 return false;
         }
     }
 
     // The last DONE value is on top of the stack. If not DONE, call
     // IteratorClose.
-                                                                  // ... OBJ ITER DONE
+                                                                  // ... OBJ NEXT ITER DONE
     IfThenElseEmitter ifDone(this);
-    if (!ifDone.emitIfElse())                                     // ... OBJ ITER
-        return false;
-    if (!emit1(JSOP_POP))                                         // ... OBJ
-        return false;
-    if (!ifDone.emitElse())                                       // ... OBJ ITER
+    if (!ifDone.emitIfElse())                                     // ... OBJ NEXT ITER
+        return false;
+    if (!emitPopN(2))                                             // ... OBJ
+        return false;
+    if (!ifDone.emitElse())                                       // ... OBJ NEXT ITER
+        return false;
+    if (!emit1(JSOP_SWAP))                                        // ... OBJ ITER NEXT
+        return false;
+    if (!emit1(JSOP_POP))                                         // ... OBJ ITER
         return false;
     if (!emitIteratorClose())                                     // ... OBJ
         return false;
     if (!ifDone.emitEnd())
         return false;
 
     return true;
 }
@@ -6926,16 +6957,22 @@ BytecodeEmitter::emitIterator()
         return false;
     if (!emit1(JSOP_SWAP))                                        // ITERFN OBJ
         return false;
     if (!emitCall(JSOP_CALLITER, 0))                              // ITER
         return false;
     checkTypeSet(JSOP_CALLITER);
     if (!emitCheckIsObj(CheckIsObjectKind::GetIterator))          // ITER
         return false;
+    if (!emit1(JSOP_DUP))                                         // ITER ITER
+        return false;
+    if (!emitAtomOp(cx->names().next, JSOP_GETPROP))              // ITER NEXT
+        return false;
+    if (!emit1(JSOP_SWAP))                                        // NEXT ITER
+        return false;
     return true;
 }
 
 bool
 BytecodeEmitter::emitAsyncIterator()
 {
     // Convert iterable to iterator.
     if (!emit1(JSOP_DUP))                                         // OBJ OBJ
@@ -6964,16 +7001,21 @@ BytecodeEmitter::emitAsyncIterator()
     if (!emit1(JSOP_SWAP))                                        // ITERFN OBJ
         return false;
     if (!emitCall(JSOP_CALLITER, 0))                              // ITER
         return false;
     checkTypeSet(JSOP_CALLITER);
     if (!emitCheckIsObj(CheckIsObjectKind::GetIterator))          // ITER
         return false;
 
+    if (!emit1(JSOP_DUP))                                         // ITER ITER
+        return false;
+    if (!emitAtomOp(cx->names().next, JSOP_GETPROP))              // ITER SYNCNEXT
+        return false;
+
     if (!emit1(JSOP_TOASYNCITER))                                 // ITER
         return false;
 
     if (!ifAsyncIterIsUndefined.emitElse())                       // OBJ ITERFN
         return false;
 
     if (!emit1(JSOP_SWAP))                                        // ITERFN OBJ
         return false;
@@ -6981,16 +7023,23 @@ BytecodeEmitter::emitAsyncIterator()
         return false;
     checkTypeSet(JSOP_CALLITER);
     if (!emitCheckIsObj(CheckIsObjectKind::GetIterator))          // ITER
         return false;
 
     if (!ifAsyncIterIsUndefined.emitEnd())                        // ITER
         return false;
 
+    if (!emit1(JSOP_DUP))                                         // ITER ITER
+        return false;
+    if (!emitAtomOp(cx->names().next, JSOP_GETPROP))              // ITER NEXT
+        return false;
+    if (!emit1(JSOP_SWAP))                                        // NEXT ITER
+        return false;
+
     return true;
 }
 
 bool
 BytecodeEmitter::emitSpread(bool allowSelfHosted)
 {
     LoopControl loopInfo(this, StatementKind::Spread);
 
@@ -7000,81 +7049,85 @@ BytecodeEmitter::emitSpread(bool allowSe
     unsigned noteIndex;
     if (!newSrcNote(SRC_FOR_OF, &noteIndex))
         return false;
 
     // Jump down to the loop condition to minimize overhead, assuming at least
     // one iteration.  (This is also what we do for loops; whether this
     // assumption holds for spreads is an unanswered question.)
     JumpList initialJump;
-    if (!emitJump(JSOP_GOTO, &initialJump))               // ITER ARR I (during the goto)
+    if (!emitJump(JSOP_GOTO, &initialJump))               // NEXT ITER ARR I (during the goto)
         return false;
 
     JumpTarget top{ -1 };
-    if (!emitLoopHead(nullptr, &top))                     // ITER ARR I
-        return false;
-
-    // When we enter the goto above, we have ITER ARR I on the stack.  But when
-    // we reach this point on the loop backedge (if spreading produces at least
-    // one value), we've additionally pushed a RESULT iteration value.
+    if (!emitLoopHead(nullptr, &top))                     // NEXT ITER ARR I
+        return false;
+
+    // When we enter the goto above, we have NEXT ITER ARR I on the stack. But
+    // when we reach this point on the loop backedge (if spreading produces at
+    // least one value), we've additionally pushed a RESULT iteration value.
     // Increment manually to reflect this.
     this->stackDepth++;
 
     JumpList beq;
     JumpTarget breakTarget{ -1 };
     {
 #ifdef DEBUG
         auto loopDepth = this->stackDepth;
 #endif
 
         // Emit code to assign result.value to the iteration variable.
-        if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // ITER ARR I VALUE
-            return false;
-        if (!emit1(JSOP_INITELEM_INC))                    // ITER ARR (I+1)
+        if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // NEXT ITER ARR I VALUE
+            return false;
+        if (!emit1(JSOP_INITELEM_INC))                    // NEXT ITER ARR (I+1)
             return false;
 
         MOZ_ASSERT(this->stackDepth == loopDepth - 1);
 
         // Spread operations can't contain |continue|, so don't bother setting loop
         // and enclosing "update" offsets, as we do with for-loops.
 
         // COME FROM the beginning of the loop to here.
-        if (!emitLoopEntry(nullptr, initialJump))         // ITER ARR I
-            return false;
-
-        if (!emitDupAt(2))                                // ITER ARR I ITER
+        if (!emitLoopEntry(nullptr, initialJump))         // NEXT ITER ARR I
+            return false;
+
+        if (!emitDupAt(3))                                // NEXT ITER ARR I NEXT
+            return false;
+        if (!emitDupAt(3))                                // NEXT ITER ARR I NEXT ITER
             return false;
         if (!emitIteratorNext(nullptr, IteratorKind::Sync, allowSelfHosted))  // ITER ARR I RESULT
             return false;
-        if (!emit1(JSOP_DUP))                             // ITER ARR I RESULT RESULT
-            return false;
-        if (!emitAtomOp(cx->names().done, JSOP_GETPROP))  // ITER ARR I RESULT DONE
-            return false;
-
-        if (!emitBackwardJump(JSOP_IFEQ, top, &beq, &breakTarget)) // ITER ARR I RESULT
+        if (!emit1(JSOP_DUP))                             // NEXT ITER ARR I RESULT RESULT
+            return false;
+        if (!emitAtomOp(cx->names().done, JSOP_GETPROP))  // NEXT ITER ARR I RESULT DONE
+            return false;
+
+        if (!emitBackwardJump(JSOP_IFEQ, top, &beq, &breakTarget)) // NEXT ITER ARR I RESULT
             return false;
 
         MOZ_ASSERT(this->stackDepth == loopDepth);
     }
 
     // Let Ion know where the closing jump of this loop is.
     if (!setSrcNoteOffset(noteIndex, 0, beq.offset - initialJump.offset))
         return false;
 
     // No breaks or continues should occur in spreads.
     MOZ_ASSERT(loopInfo.breaks.offset == -1);
     MOZ_ASSERT(loopInfo.continues.offset == -1);
 
     if (!tryNoteList.append(JSTRY_FOR_OF, stackDepth, top.offset, breakTarget.offset))
         return false;
 
-    if (!emit2(JSOP_PICK, 3))                             // ARR FINAL_INDEX RESULT ITER
-        return false;
-
-    return emitPopN(2);                                   // ARR FINAL_INDEX
+    if (!emit2(JSOP_PICK, 4))                             // ITER ARR FINAL_INDEX RESULT NEXT
+        return false;
+    if (!emit2(JSOP_PICK, 4))                             // ARR FINAL_INDEX RESULT NEXT ITER
+        return false;
+
+    return emitPopN(3);                                   // ARR FINAL_INDEX
 }
 
 bool
 BytecodeEmitter::emitInitializeForInOrOfTarget(ParseNode* forHead)
 {
     MOZ_ASSERT(forHead->isKind(ParseNodeKind::ForIn) ||
                forHead->isKind(ParseNodeKind::ForOf));
     MOZ_ASSERT(forHead->isArity(PN_TERNARY));
@@ -7164,43 +7217,44 @@ BytecodeEmitter::emitForOf(ParseNode* fo
     }
 
     // Evaluate the expression being iterated. The forHeadExpr should use a
     // distinct TDZCheckCache to evaluate since (abstractly) it runs in its own
     // LexicalEnvironment.
     if (!emitTreeInBranch(forHeadExpr))                   // ITERABLE
         return false;
     if (iterKind == IteratorKind::Async) {
-        if (!emitAsyncIterator())                         // ITER
+        if (!emitAsyncIterator())                         // NEXT ITER
             return false;
     } else {
-        if (!emitIterator())                              // ITER
+        if (!emitIterator())                              // NEXT ITER
             return false;
     }
 
     int32_t iterDepth = stackDepth;
 
-    // For-of loops have both the iterator and the result.value on the stack.
+    // For-of loops have the iterator next method, the iterator itself, and
+    // the result.value on the stack.
     // Push an undefined to balance the stack.
-    if (!emit1(JSOP_UNDEFINED))                           // ITER UNDEF
+    if (!emit1(JSOP_UNDEFINED))                           // NEXT ITER UNDEF
         return false;
 
     ForOfLoopControl loopInfo(this, iterDepth, allowSelfHostedIter, iterKind);
 
     // Annotate so IonMonkey can find the loop-closing jump.
     unsigned noteIndex;
     if (!newSrcNote(SRC_FOR_OF, &noteIndex))
         return false;
 
     JumpList initialJump;
-    if (!emitJump(JSOP_GOTO, &initialJump))               // ITER UNDEF
+    if (!emitJump(JSOP_GOTO, &initialJump))               // NEXT ITER UNDEF
         return false;
 
     JumpTarget top{ -1 };
-    if (!emitLoopHead(nullptr, &top))                     // ITER UNDEF
+    if (!emitLoopHead(nullptr, &top))                     // NEXT ITER UNDEF
         return false;
 
     // If the loop had an escaping lexical declaration, replace the current
     // environment with an dead zoned one to implement TDZ semantics.
     if (headLexicalEmitterScope) {
         // The environment chain only includes an environment for the for-of
         // loop head *if* a scope binding is captured, thereby requiring
         // recreation each iteration. If a lexical scope exists for the head,
@@ -7208,17 +7262,17 @@ BytecodeEmitter::emitForOf(ParseNode* fo
         // bindings inducing an environment, recreate the current environment.
         DebugOnly<ParseNode*> forOfTarget = forOfHead->pn_kid1;
         MOZ_ASSERT(forOfTarget->isKind(ParseNodeKind::Let) ||
                    forOfTarget->isKind(ParseNodeKind::Const));
         MOZ_ASSERT(headLexicalEmitterScope == innermostEmitterScope);
         MOZ_ASSERT(headLexicalEmitterScope->scope(this)->kind() == ScopeKind::Lexical);
 
         if (headLexicalEmitterScope->hasEnvironment()) {
-            if (!emit1(JSOP_RECREATELEXICALENV))          // ITER UNDEF
+            if (!emit1(JSOP_RECREATELEXICALENV))          // NEXT ITER UNDEF
                 return false;
         }
 
         // For uncaptured bindings, put them back in TDZ.
         if (!headLexicalEmitterScope->deadZoneFrameSlots(this))
             return false;
     }
 
@@ -7228,107 +7282,107 @@ BytecodeEmitter::emitForOf(ParseNode* fo
 #ifdef DEBUG
         auto loopDepth = this->stackDepth;
 #endif
 
         // Make sure this code is attributed to the "for".
         if (!updateSourceCoordNotes(forOfHead->pn_pos.begin))
             return false;
 
-        if (!emit1(JSOP_POP))                             // ITER
-            return false;
-        if (!emit1(JSOP_DUP))                             // ITER ITER
+        if (!emit1(JSOP_POP))                             // NEXT ITER
+            return false;
+        if (!emit1(JSOP_DUP2))                            // NEXT ITER NEXT ITER
             return false;
 
         if (!emitIteratorNext(forOfHead, iterKind, allowSelfHostedIter))
-            return false;                                 // ITER RESULT
-
-        if (!emit1(JSOP_DUP))                             // ITER RESULT RESULT
-            return false;
-        if (!emitAtomOp(cx->names().done, JSOP_GETPROP))  // ITER RESULT DONE
+            return false;                                 // NEXT ITER RESULT
+
+        if (!emit1(JSOP_DUP))                             // NEXT ITER RESULT RESULT
+            return false;
+        if (!emitAtomOp(cx->names().done, JSOP_GETPROP))  // NEXT ITER RESULT DONE
             return false;
 
         IfThenElseEmitter ifDone(this);
 
-        if (!ifDone.emitIf())                             // ITER RESULT
+        if (!ifDone.emitIf())                             // NEXT ITER RESULT
             return false;
 
         // Remove RESULT from the stack to release it.
-        if (!emit1(JSOP_POP))                             // ITER
-            return false;
-        if (!emit1(JSOP_UNDEFINED))                       // ITER UNDEF
+        if (!emit1(JSOP_POP))                             // NEXT ITER
+            return false;
+        if (!emit1(JSOP_UNDEFINED))                       // NEXT ITER UNDEF
             return false;
 
         // If the iteration is done, leave loop here, instead of the branch at
         // the end of the loop.
-        if (!loopInfo.emitSpecialBreakForDone(this))      // ITER UNDEF
-            return false;
-
-        if (!ifDone.emitEnd())                            // ITER RESULT
+        if (!loopInfo.emitSpecialBreakForDone(this))      // NEXT ITER UNDEF
+            return false;
+
+        if (!ifDone.emitEnd())                            // NEXT ITER RESULT
             return false;
 
         // Emit code to assign result.value to the iteration variable.
         //
         // Note that ES 13.7.5.13, step 5.c says getting result.value does not
         // call IteratorClose, so start JSTRY_ITERCLOSE after the GETPROP.
-        if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // ITER VALUE
+        if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // NEXT ITER VALUE
             return false;
 
         if (!loopInfo.emitBeginCodeNeedingIteratorClose(this))
             return false;
 
-        if (!emitInitializeForInOrOfTarget(forOfHead))    // ITER VALUE
+        if (!emitInitializeForInOrOfTarget(forOfHead))    // NEXT ITER VALUE
             return false;
 
         MOZ_ASSERT(stackDepth == loopDepth,
                    "the stack must be balanced around the initializing "
                    "operation");
 
         // Remove VALUE from the stack to release it.
-        if (!emit1(JSOP_POP))                             // ITER
-            return false;
-        if (!emit1(JSOP_UNDEFINED))                       // ITER UNDEF
+        if (!emit1(JSOP_POP))                             // NEXT ITER
+            return false;
+        if (!emit1(JSOP_UNDEFINED))                       // NEXT ITER UNDEF
             return false;
 
         // Perform the loop body.
         ParseNode* forBody = forOfLoop->pn_right;
-        if (!emitTree(forBody))                           // ITER UNDEF
+        if (!emitTree(forBody))                           // NEXT ITER UNDEF
             return false;
 
         MOZ_ASSERT(stackDepth == loopDepth,
                    "the stack must be balanced around the for-of body");
 
         if (!loopInfo.emitEndCodeNeedingIteratorClose(this))
             return false;
 
         // Set offset for continues.
         loopInfo.continueTarget = { offset() };
 
-        if (!emitLoopEntry(forHeadExpr, initialJump))     // ITER UNDEF
-            return false;
-
-        if (!emit1(JSOP_FALSE))                           // ITER UNDEF FALSE
+        if (!emitLoopEntry(forHeadExpr, initialJump))     // NEXT ITER UNDEF
+            return false;
+
+        if (!emit1(JSOP_FALSE))                           // NEXT ITER UNDEF FALSE
             return false;
         if (!emitBackwardJump(JSOP_IFEQ, top, &beq, &breakTarget))
-            return false;                                 // ITER UNDEF
+            return false;                                 // NEXT ITER UNDEF
 
         MOZ_ASSERT(this->stackDepth == loopDepth);
     }
 
     // Let Ion know where the closing jump of this loop is.
     if (!setSrcNoteOffset(noteIndex, 0, beq.offset - initialJump.offset))
         return false;
 
     if (!loopInfo.patchBreaksAndContinues(this))
         return false;
 
     if (!tryNoteList.append(JSTRY_FOR_OF, stackDepth, top.offset, breakTarget.offset))
         return false;
 
-    return emitPopN(2);                                   //
+    return emitPopN(3);                                   //
 }
 
 bool
 BytecodeEmitter::emitForIn(ParseNode* forInLoop, EmitterScope* headLexicalEmitterScope)
 {
     MOZ_ASSERT(forInLoop->isKind(ParseNodeKind::For));
     MOZ_ASSERT(forInLoop->isArity(PN_BINARY));
     MOZ_ASSERT(forInLoop->isOp(JSOP_ITER));
@@ -8377,113 +8431,113 @@ BytecodeEmitter::emitYieldStar(ParseNode
     MOZ_ASSERT(sc->isFunctionBox());
     MOZ_ASSERT(sc->asFunctionBox()->isGenerator());
 
     bool isAsyncGenerator = sc->asFunctionBox()->isAsync();
 
     if (!emitTree(iter))                                  // ITERABLE
         return false;
     if (isAsyncGenerator) {
-        if (!emitAsyncIterator())                         // ITER
+        if (!emitAsyncIterator())                         // NEXT ITER
             return false;
     } else {
-        if (!emitIterator())                              // ITER
+        if (!emitIterator())                              // NEXT ITER
             return false;
     }
 
     // Initial send value is undefined.
-    if (!emit1(JSOP_UNDEFINED))                           // ITER RECEIVED
+    if (!emit1(JSOP_UNDEFINED))                           // NEXT ITER RECEIVED
         return false;
 
     int32_t savedDepthTemp;
     int32_t startDepth = stackDepth;
-    MOZ_ASSERT(startDepth >= 2);
+    MOZ_ASSERT(startDepth >= 3);
 
     TryEmitter tryCatch(this, TryEmitter::TryCatchFinally, TryEmitter::DontUseRetVal,
                         TryEmitter::DontUseControl);
-    if (!tryCatch.emitJumpOverCatchAndFinally())          // ITER RESULT
+    if (!tryCatch.emitJumpOverCatchAndFinally())          // NEXT ITER RESULT
         return false;
 
     JumpTarget tryStart{ offset() };
-    if (!tryCatch.emitTry())                              // ITER RESULT
+    if (!tryCatch.emitTry())                              // NEXT ITER RESULT
         return false;
 
     MOZ_ASSERT(this->stackDepth == startDepth);
 
     // 11.4.3.7 AsyncGeneratorYield step 5.
     if (isAsyncGenerator) {
-        if (!emitAwait())                                 // ITER RESULT
+        if (!emitAwait())                                 // NEXT ITER RESULT
             return false;
     }
 
     // Load the generator object.
-    if (!emitGetDotGenerator())                           // ITER RESULT GENOBJ
+    if (!emitGetDotGenerator())                           // NEXT ITER RESULT GENOBJ
         return false;
 
     // Yield RESULT as-is, without re-boxing.
-    if (!emitYieldOp(JSOP_YIELD))                         // ITER RECEIVED
-        return false;
-
-    if (!tryCatch.emitCatch())                            // ITER RESULT
-        return false;
-
-    stackDepth = startDepth;                              // ITER RESULT
-    if (!emit1(JSOP_EXCEPTION))                           // ITER RESULT EXCEPTION
-        return false;
-    if (!emitDupAt(2))                                    // ITER RESULT EXCEPTION ITER
-        return false;
-    if (!emit1(JSOP_DUP))                                 // ITER RESULT EXCEPTION ITER ITER
-        return false;
-    if (!emitAtomOp(cx->names().throw_, JSOP_CALLPROP))   // ITER RESULT EXCEPTION ITER THROW
-        return false;
-    if (!emit1(JSOP_DUP))                                 // ITER RESULT EXCEPTION ITER THROW THROW
-        return false;
-    if (!emit1(JSOP_UNDEFINED))                           // ITER RESULT EXCEPTION ITER THROW THROW UNDEFINED
-        return false;
-    if (!emit1(JSOP_EQ))                                  // ITER RESULT EXCEPTION ITER THROW ?EQL
+    if (!emitYieldOp(JSOP_YIELD))                         // NEXT ITER RECEIVED
+        return false;
+
+    if (!tryCatch.emitCatch())                            // NEXT ITER RESULT
+        return false;
+
+    stackDepth = startDepth;                              // NEXT ITER RESULT
+    if (!emit1(JSOP_EXCEPTION))                           // NEXT ITER RESULT EXCEPTION
+        return false;
+    if (!emitDupAt(2))                                    // NEXT ITER RESULT EXCEPTION ITER
+        return false;
+    if (!emit1(JSOP_DUP))                                 // NEXT ITER RESULT EXCEPTION ITER ITER
+        return false;
+    if (!emitAtomOp(cx->names().throw_, JSOP_CALLPROP))   // NEXT ITER RESULT EXCEPTION ITER THROW
+        return false;
+    if (!emit1(JSOP_DUP))                                 // NEXT ITER RESULT EXCEPTION ITER THROW THROW
+        return false;
+    if (!emit1(JSOP_UNDEFINED))                           // NEXT ITER RESULT EXCEPTION ITER THROW THROW UNDEFINED
+        re