Merge mozilla-inbound to mozilla-central a=merge
authorAndreea Pavel <apavel@mozilla.com>
Thu, 15 Feb 2018 12:24:21 +0200
changeset 403921 9b69cc60e5848f2f8802c911fd00771b50eed41f
parent 403920 026401920e32e641eb42b068a16d7dd9e86c66a4 (current diff)
parent 403789 e0ac728caeaa88cddaf54f0bd0936587158151f3 (diff)
child 403922 99495614cba7396c358b78b3e50aea1b38937c59
child 404012 3e332972119f8f76c5c523157c7e5cb2b987fab2
push id99885
push userapavel@mozilla.com
push dateThu, 15 Feb 2018 10:38:09 +0000
treeherdermozilla-inbound@99495614cba7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone60.0a1
first release with
nightly linux32
9b69cc60e584 / 60.0a1 / 20180215103933 / files
nightly linux64
9b69cc60e584 / 60.0a1 / 20180215103933 / files
nightly mac
9b69cc60e584 / 60.0a1 / 20180215103933 / files
nightly win32
9b69cc60e584 / 60.0a1 / 20180215103933 / files
nightly win64
9b69cc60e584 / 60.0a1 / 20180215103933 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-inbound to mozilla-central a=merge
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
devtools/client/framework/toolbox.js
dom/base/ShadowRoot.cpp
dom/base/nsGlobalWindowInner.cpp
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
js/src/vm/JSCompartment.cpp
js/src/vm/JSCompartment.h
js/src/vm/JSScript.cpp
js/src/vm/JSScript.h
js/src/vm/MemoryMetrics.cpp
mobile/android/installer/package-manifest.in
testing/geckodriver/Cargo.lock
testing/mozharness/mozharness/mozilla/proxxy.py
testing/web-platform/meta/shadow-dom/event-composed-path-with-related-target.html.ini
testing/web-platform/meta/shadow-dom/event-post-dispatch.html.ini
testing/web-platform/meta/shadow-dom/event-with-related-target.html.ini
testing/web-platform/meta/shadow-dom/untriaged/events/retargeting-relatedtarget/test-001.html.ini
testing/web-platform/meta/shadow-dom/untriaged/events/retargeting-relatedtarget/test-002.html.ini
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
toolkit/modules/LightweightThemeConsumer.jsm
--- 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