Merge mozilla-central to mozilla-inbound. a=merge
authorDaniel Varga <dvarga@mozilla.com>
Wed, 03 Oct 2018 01:17:27 +0300
changeset 497773 e6820b5308f7d9c008b79113983c9a975ea44d6f
parent 497772 e85309ff67f47c2ee52944499f6ababe1278fabd (current diff)
parent 497716 0d4e73bc2cd705d7a021c75a0e8aeb174ab4db59 (diff)
child 497774 d04fa630f0fac22c8fd7ece46c3a6ac9a6dfaba6
push id1864
push userffxbld-merge
push dateMon, 03 Dec 2018 15:51:40 +0000
treeherdermozilla-release@f040763d99ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to mozilla-inbound. a=merge
browser/components/search/test/browser_abouthome_behavior.js
devtools/client/debugger/test/mochitest/browser.ini
devtools/client/debugger/test/mochitest/browser_dbg_clean-exit-window.js
devtools/client/debugger/test/mochitest/browser_dbg_split-console-keypress.js
taskcluster/ci/test/talos.yml
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -721,31 +721,31 @@ name = "ena"
 version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "encoding_c"
 version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "encoding_rs 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "encoding_rs 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "encoding_glue"
 version = "0.1.0"
 dependencies = [
- "encoding_rs 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "encoding_rs 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "nserror 0.1.0",
  "nsstring 0.1.0",
 ]
 
 [[package]]
 name = "encoding_rs"
-version = "0.8.7"
+version = "0.8.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "simd 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "env_logger"
@@ -1599,17 +1599,17 @@ dependencies = [
  "nsstring 0.1.0",
 ]
 
 [[package]]
 name = "nsstring"
 version = "0.1.0"
 dependencies = [
  "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "encoding_rs 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "encoding_rs 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "nsstring-gtest"
 version = "0.1.0"
 dependencies = [
  "nsstring 0.1.0",
 ]
@@ -3064,17 +3064,17 @@ dependencies = [
 "checksum diff 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "3c2b69f912779fbb121ceb775d74d51e915af17aaebc38d28a592843a2dd0a3a"
 "checksum docopt 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d8acd393692c503b168471874953a2531df0e9ab77d0b6bbc582395743300a4a"
 "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab"
 "checksum dtoa-short 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "068d4026697c1a18f0b0bb8cfcad1b0c151b90d8edb9bf4c235ad68128920d1d"
 "checksum dwrote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "30a998e9ff70cd208ccdc4f864e998688bf61d7b897dccec8e17a884d17358bf"
 "checksum either 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18785c1ba806c258137c937e44ada9ee7e69a37e3c72077542cd2f069d78562a"
 "checksum ena 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cabe5a5078ac8c506d3e4430763b1ba9b609b1286913e7d08e581d1c2de9b7e5"
 "checksum encoding_c 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "769ecb8b33323998e482b218c0d13cd64c267609023b4b7ec3ee740714c318ee"
-"checksum encoding_rs 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)" = "21a550ec129ca2f8593227888625c7c5331c6ad878e2cee6b7ac25e1c7d05746"
+"checksum encoding_rs 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)" = "cc9945e460ad969220c1061b9574fb02ed097c6f0704ce2f3e336cb443c40c73"
 "checksum env_logger 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0561146661ae44c579e993456bc76d11ce1e0c7d745e57b2fa7146b6e49fa2ad"
 "checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3"
 "checksum euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "70a2ebdf55fb9d6329046e026329a55ef8fbaae5ea833f56e170beb3125a8a5f"
 "checksum failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7efb22686e4a466b1ec1a15c2898f91fa9cb340452496dca654032de20ff95b9"
 "checksum failure_derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "946d0e98a50d9831f5d589038d2ca7f8f455b1c21028c0db0e84116a12696426"
 "checksum fixedbitset 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "85cb8fec437468d86dc7c83ca7cfc933341d561873275f22dd5eedefa63a6478"
 "checksum flate2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9fac2277e84e5e858483756647a9d0aa8d9a2b7cba517fd84325a0aaa69a0909"
 "checksum fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344"
--- a/browser/actors/BlockedSiteChild.jsm
+++ b/browser/actors/BlockedSiteChild.jsm
@@ -70,17 +70,17 @@ class BlockedSiteChild extends ActorChil
     /**
     * Set error description link in error details.
     * For example, the "reported as a deceptive site" link for
     * blocked phishing pages.
     */
     let desc = Services.prefs.getCharPref(
       "browser.safebrowsing.provider." + provider + ".reportURL", "");
     if (desc) {
-      doc.getElementById("error_desc_link").setAttribute("href", desc + aEvent.detail.url);
+      doc.getElementById("error_desc_link").setAttribute("href", desc + encodeURIComponent(aEvent.detail.url));
     }
 
     // Set other links in error details.
     switch (aEvent.detail.err) {
       case "malware":
         doc.getElementById("report_detection").setAttribute("href",
           (SafeBrowsing.getReportURL("MalwareMistake", blockedInfo) ||
            "https://www.stopbadware.org/firefox"));
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -424,21 +424,17 @@ pref("browser.link.open_newwindow.restri
 // different.
 #ifdef XP_MACOSX
 pref("browser.link.open_newwindow.disabled_in_fullscreen", true);
 #else
 pref("browser.link.open_newwindow.disabled_in_fullscreen", false);
 #endif
 
 // Tabbed browser
-#ifdef EARLY_BETA_OR_EARLIER
 pref("browser.tabs.multiselect", true);
-#else
-pref("browser.tabs.multiselect", false);
-#endif
 pref("browser.tabs.20FpsThrobber", false);
 pref("browser.tabs.30FpsThrobber", false);
 pref("browser.tabs.closeTabByDblclick", false);
 pref("browser.tabs.closeWindowWithLastTab", true);
 // Open related links to a tab, e.g., link in current tab, at next to the
 // current tab if |insertRelatedAfterCurrent| is true.  Otherwise, always
 // append new tab to the end.
 pref("browser.tabs.insertRelatedAfterCurrent", true);
@@ -1250,16 +1246,24 @@ pref("browser.newtabpage.activity-stream
 pref("browser.newtabpage.activity-stream.debug", false);
 #endif
 
 pref("browser.library.activity-stream.enabled", true);
 
 // The remote FxA root content URL for the Activity Stream firstrun page.
 pref("browser.newtabpage.activity-stream.fxaccounts.endpoint", "https://accounts.firefox.com/");
 
+// The pref that controls if the search shortcuts experiment is on
+#ifdef EARLY_BETA_OR_EARLIER
+pref("browser.newtabpage.activity-stream.improvesearch.topSiteSearchShortcuts", true);
+#else
+pref("browser.newtabpage.activity-stream.improvesearch.topSiteSearchShortcuts", false);
+#else
+#endif
+
 // Enable the DOM fullscreen API.
 pref("full-screen-api.enabled", true);
 
 // Startup Crash Tracking
 // number of startup crashes that can occur before starting into safe mode automatically
 // (this pref has no effect if more than 6 hours have passed since the last crash)
 pref("toolkit.startup.max_resumed_crashes", 3);
 
--- a/browser/base/content/browser-fullScreenAndPointerLock.js
+++ b/browser/base/content/browser-fullScreenAndPointerLock.js
@@ -372,18 +372,17 @@ var FullScreen = {
         // browser, the target would be the browser which was the parameter of
         // `remoteFrameFullscreenChanged` call. If the fullscreen
         // request was initiated from an in-process browser, we need
         // to get its corresponding browser here.
         let browser;
         if (event.target.ownerGlobal == window) {
           browser = event.target;
         } else {
-          let topWin = event.target.ownerGlobal.top;
-          browser = gBrowser.getBrowserForContentWindow(topWin);
+          browser = event.target.ownerGlobal.docShell.chromeEventHandler;
         }
         TelemetryStopwatch.start("FULLSCREEN_CHANGE_MS");
         this.enterDomFullscreen(browser);
         break;
       }
       case "MozDOMFullscreen:Exited":
         TelemetryStopwatch.start("FULLSCREEN_CHANGE_MS");
         this.cleanupDomFullscreen();
--- a/browser/base/content/browser-fullZoom.js
+++ b/browser/base/content/browser-fullZoom.js
@@ -452,18 +452,19 @@ var FullZoom = {
     const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
     if (target instanceof window.XULElement &&
         target.localName == "browser" &&
         target.namespaceURI == XUL_NS)
       return target;
 
     // With in-process content browsers, the event's target is the content
     // document.
-    if (target.nodeType == Node.DOCUMENT_NODE)
-      return gBrowser.getBrowserForDocument(target);
+    if (target.nodeType == Node.DOCUMENT_NODE) {
+      return target.ownerGlobal.docShell.chromeEventHandler;
+    }
 
     throw new Error("Unexpected ZoomChangeUsingMouseWheel event source");
   },
 
   /**
    * Increments the zoom change token for the given browser so that pending
    * async operations know that it may be unsafe to access they zoom when they
    * finish.
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -485,37 +485,31 @@ var PlacesCommandHook = {
   get uniqueSelectedPages() {
     return this.getUniquePages(gBrowser.selectedTabs);
   },
 
   /**
    * Adds a folder with bookmarks to URIList given in param.
    */
   bookmarkPages(URIList) {
-    if (URIList.length > 1) {
-      PlacesUIUtils.showBookmarkDialog({ action: "add",
-                                         type: "folder",
-                                         URIList,
-                                       }, window);
+    if (!URIList.length) {
+      return;
     }
-  },
 
-  /**
-   * Updates disabled state for the "Bookmark All Tabs" command.
-   */
-  updateBookmarkAllTabsCommand:
-  function PCH_updateBookmarkAllTabsCommand() {
-    // There's nothing to do in non-browser windows.
-    if (window.location.href != AppConstants.BROWSER_CHROME_URL)
-      return;
+    let bookmarkDialogInfo = {action: "add"};
+    if (URIList.length > 1) {
+      bookmarkDialogInfo.type = "folder";
+      bookmarkDialogInfo.URIList = URIList;
+    } else {
+      bookmarkDialogInfo.type = "bookmark";
+      bookmarkDialogInfo.title = URIList[0].title;
+      bookmarkDialogInfo.uri = URIList[0].uri;
+    }
 
-    // Disable "Bookmark All Tabs" if there are less than two
-    // "unique current pages".
-    goSetCommandEnabled("Browser:BookmarkAllTabs",
-                        this.uniqueCurrentPages.length >= 2);
+    PlacesUIUtils.showBookmarkDialog(bookmarkDialogInfo, window);
   },
 
   /**
    * Adds a Live Bookmark to a feed associated with the current page.
    * @param     url
    *            The nsIURI of the page the feed was attached to
    * @title     title
    *            The title of the feed. Optional.
@@ -1564,17 +1558,16 @@ var BookmarkingUI = {
   },
 
   onMainMenuPopupShowing: function BUI_onMainMenuPopupShowing(event) {
     // Don't handle events for submenus.
     if (event.target != event.currentTarget)
       return;
 
     this.updateBookmarkPageMenuItem();
-    PlacesCommandHook.updateBookmarkAllTabsCommand();
     this._initMobileBookmarks(document.getElementById("menu_mobileBookmarks"));
   },
 
   showSubView(anchor) {
     this._showSubView(null, anchor);
   },
 
   _showSubView(event, anchor = document.getElementById(this.BOOKMARK_BUTTON_ID)) {
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -50,18 +50,16 @@
     <command id="cmd_findAgain" oncommand="gLazyFindCommand('onFindAgainCommand', false)"/>
     <command id="cmd_findPrevious" oncommand="gLazyFindCommand('onFindAgainCommand', true)"/>
 #ifdef XP_MACOSX
     <command id="cmd_findSelection" oncommand="gLazyFindCommand('onFindSelectionCommand')"/>
 #endif
     <!-- work-around bug 392512 -->
     <command id="Browser:AddBookmarkAs"
              oncommand="PlacesCommandHook.bookmarkPage();"/>
-    <!-- The command disabled state must be manually updated through
-         PlacesCommandHook.updateBookmarkAllTabsCommand() -->
     <command id="Browser:BookmarkAllTabs"
              oncommand="PlacesCommandHook.bookmarkPages(PlacesCommandHook.uniqueCurrentPages);"/>
     <command id="Browser:Back"    oncommand="BrowserBack();" disabled="true"/>
     <command id="Browser:BackOrBackDuplicate" oncommand="BrowserBack(event);" disabled="true">
       <observes element="Browser:Back" attribute="disabled"/>
     </command>
     <command id="Browser:Forward" oncommand="BrowserForward();" disabled="true"/>
     <command id="Browser:ForwardOrForwardDuplicate" oncommand="BrowserForward(event);" disabled="true">
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1437,28 +1437,16 @@ var gBrowserInit = {
     TelemetryTimestamps.add("delayedStartupStarted");
 
     this._cancelDelayedStartup();
 
     // We need to set the OfflineApps message listeners up before we
     // load homepages, which might need them.
     OfflineApps.init();
 
-    gBrowser.addEventListener("AboutTabCrashedLoad", function(event) {
-      let ownerDoc = event.originalTarget;
-
-      if (!ownerDoc.documentURI.startsWith("about:tabcrashed")) {
-        return;
-      }
-
-      let browser = gBrowser.getBrowserForDocument(event.target);
-      // Reset the zoom for the tabcrashed page.
-      ZoomManager.setZoomForBrowser(browser, 1);
-    }, false, true);
-
     gBrowser.addEventListener("InsecureLoginFormsStateChange", function() {
       gIdentityHandler.refreshForInsecureLoginForms();
     }, true);
 
     gBrowser.addEventListener("PermissionStateChange", function() {
       gIdentityHandler.refreshIdentityBlock();
     }, true);
 
@@ -5701,18 +5689,16 @@ function onViewToolbarsPopupShowing(aEve
     node.hidden = showTabStripItems;
   }
 
   for (let node of popup.querySelectorAll('menuitem[contexttype="tabbar"]')) {
     node.hidden = !showTabStripItems;
   }
 
   if (showTabStripItems) {
-    PlacesCommandHook.updateBookmarkAllTabsCommand();
-
     let haveMultipleTabs = gBrowser.visibleTabs.length > 1;
     document.getElementById("toolbar-context-reloadAllTabs").disabled = !haveMultipleTabs;
 
     document.getElementById("toolbar-context-selectAllTabs").disabled = gBrowser.allTabsSelected();
     document.getElementById("toolbar-context-undoCloseTab").disabled =
       SessionStore.getClosedTabCount(window) == 0;
     return;
   }
@@ -6860,17 +6846,17 @@ var CanvasPermissionPromptHelper = {
   observe(aSubject, aTopic, aData) {
     if (aTopic != this._permissionsPrompt) {
       return;
     }
 
     let browser;
     if (aSubject instanceof Ci.nsIDOMWindow) {
       let contentWindow = aSubject.QueryInterface(Ci.nsIDOMWindow);
-      browser = gBrowser.getBrowserForContentWindow(contentWindow);
+      browser = contentWindow.docShell.chromeEventHandler;
     } else {
       browser = aSubject.QueryInterface(Ci.nsIBrowser);
     }
 
     let uri = Services.io.newURI(aData);
     if (gBrowser.selectedBrowser !== browser) {
       // Must belong to some other window.
       return;
@@ -7214,18 +7200,18 @@ function BrowserOpenAddonsMgr(aView) {
     Services.obs.addObserver(receivePong, "EM-pong");
     Services.obs.notifyObservers(null, "EM-ping");
     Services.obs.removeObserver(receivePong, "EM-pong");
 
     if (emWindow) {
       if (aView) {
         emWindow.loadView(aView);
       }
-      browserWindow.gBrowser.selectedTab =
-        browserWindow.gBrowser._getTabForContentWindow(emWindow);
+      let tab = browserWindow.gBrowser.getTabForBrowser(emWindow.docShell.chromeEventHandler);
+      browserWindow.gBrowser.selectedTab = tab;
       emWindow.focus();
       resolve(emWindow);
       return;
     }
 
     // This must be a new load, else the ping/pong would have
     // found the window above.
     let whereToOpen = (window.gBrowser && isTabEmpty(gBrowser.selectedTab)) ?
@@ -7442,23 +7428,16 @@ const gRemoteControl = {
     if (enabled) {
       mainWindow.setAttribute("remotecontrol", "true");
     } else {
       mainWindow.removeAttribute("remotecontrol");
     }
   },
 };
 
-function getTabModalPromptBox(aWindow) {
-  var foundBrowser = gBrowser.getBrowserForDocument(aWindow.document);
-  if (foundBrowser)
-    return gBrowser.getTabModalPromptBox(foundBrowser);
-  return null;
-}
-
 /* DEPRECATED */
 function getBrowser() {
   return gBrowser;
 }
 
 const gAccessibilityServiceIndicator = {
   init() {
     // Pref to enable accessibility service indicator.
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -112,16 +112,25 @@ xmlns="http://www.w3.org/1999/xhtml"
                 accesskey="&reloadSelectedTabs.accesskey;"
                 oncommand="gBrowser.reloadMultiSelectedTabs();"/>
       <menuitem id="context_toggleMuteTab" oncommand="TabContextMenu.contextTab.toggleMuteAudio();"/>
       <menuitem id="context_toggleMuteSelectedTabs" hidden="true"
                 oncommand="gBrowser.toggleMuteAudioOnMultiSelectedTabs(TabContextMenu.contextTab);"/>
       <menuseparator/>
       <menuitem id="context_selectAllTabs" label="&selectAllTabs.label;" accesskey="&selectAllTabs.accesskey;"
                 oncommand="gBrowser.selectAllTabs();"/>
+      <menuitem id="context_bookmarkSelectedTabs"
+                hidden="true"
+                label="&bookmarkSelectedTabs.label;"
+                accesskey="&bookmarkSelectedTabs.accesskey;"
+                oncommand="PlacesCommandHook.bookmarkPages(PlacesCommandHook.uniqueSelectedPages);"/>
+      <menuitem id="context_bookmarkTab"
+                label="&bookmarkTab.label;"
+                accesskey="&bookmarkTab.accesskey;"
+                oncommand="PlacesCommandHook.bookmarkPages(PlacesCommandHook.getUniquePages([TabContextMenu.contextTab]));"/>
       <menuitem id="context_pinTab" label="&pinTab.label;"
                 accesskey="&pinTab.accesskey;"
                 oncommand="gBrowser.pinTab(TabContextMenu.contextTab);"/>
       <menuitem id="context_unpinTab" label="&unpinTab.label;" hidden="true"
                 accesskey="&unpinTab.accesskey;"
                 oncommand="gBrowser.unpinTab(TabContextMenu.contextTab);"/>
       <menuitem id="context_pinSelectedTabs" label="&pinSelectedTabs.label;" hidden="true"
                 accesskey="&pinSelectedTabs.accesskey;"
@@ -148,25 +157,16 @@ xmlns="http://www.w3.org/1999/xhtml"
             class="sync-ui-item">
         <menupopup id="context_sendTabToDevicePopupMenu"
                    onpopupshowing="gSync.populateSendTabToDevicesMenu(event.target, TabContextMenu.contextTab);"/>
       </menu>
       <menuseparator/>
       <menuitem id="context_reloadAllTabs" label="&reloadAllTabs.label;" accesskey="&reloadAllTabs.accesskey;"
                 tbattr="tabbrowser-multiple-visible"
                 oncommand="gBrowser.reloadAllTabs();"/>
-       <menuitem id="context_bookmarkSelectedTabs"
-                hidden="true"
-                label="&bookmarkSelectedTabs.label;"
-                accesskey="&bookmarkSelectedTabs.accesskey;"
-                oncommand="PlacesCommandHook.bookmarkPages(PlacesCommandHook.uniqueSelectedPages);"/>
-      <menuitem id="context_bookmarkAllTabs"
-                label="&bookmarkAllTabs.label;"
-                accesskey="&bookmarkAllTabs.accesskey;"
-                command="Browser:BookmarkAllTabs"/>
       <menuitem id="context_closeTabsToTheEnd" label="&closeTabsToTheEnd.label;" accesskey="&closeTabsToTheEnd.accesskey;"
                 oncommand="gBrowser.removeTabsToTheEndFrom(TabContextMenu.contextTab, {animate: true});"/>
       <menuitem id="context_closeOtherTabs" label="&closeOtherTabs.label;" accesskey="&closeOtherTabs.accesskey;"
                 oncommand="gBrowser.removeAllTabsBut(TabContextMenu.contextTab);"/>
       <menuseparator/>
       <menuitem id="context_undoCloseTab"
                 label="&undoCloseTab.label;"
                 accesskey="&undoCloseTab.accesskey;"
@@ -412,22 +412,22 @@ xmlns="http://www.w3.org/1999/xhtml"
                 contexttype="toolbaritem"
                 class="customize-context-removeFromToolbar"/>
       <menuitem id="toolbar-context-reloadAllTabs"
                 class="toolbaritem-tabsmenu"
                 contexttype="tabbar"
                 oncommand="gBrowser.reloadAllTabs();"
                 label="&toolbarContextMenu.reloadAllTabs.label;"
                 accesskey="&toolbarContextMenu.reloadAllTabs.accesskey;"/>
-      <menuitem id="toolbar-context-bookmarkAllTabs"
+      <menuitem id="toolbar-context-bookmarkSelectedTabs"
                 class="toolbaritem-tabsmenu"
                 contexttype="tabbar"
-                command="Browser:BookmarkAllTabs"
-                label="&toolbarContextMenu.bookmarkAllTabs.label;"
-                accesskey="&toolbarContextMenu.bookmarkAllTabs.accesskey;"/>
+                oncommand="PlacesCommandHook.bookmarkPages(PlacesCommandHook.uniqueSelectedPages);"
+                label="&toolbarContextMenu.bookmarkSelectedTabs.label;"
+                accesskey="&toolbarContextMenu.bookmarkSelectedTabs.accesskey;"/>
       <menuitem id="toolbar-context-selectAllTabs"
                 class="toolbaritem-tabsmenu"
                 contexttype="tabbar"
                 oncommand="gBrowser.selectAllTabs();"
                 label="&toolbarContextMenu.selectAllTabs.label;"
                 accesskey="&toolbarContextMenu.selectAllTabs.accesskey;"/>
       <menuitem id="toolbar-context-undoCloseTab"
                 class="toolbaritem-tabsmenu"
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -639,53 +639,20 @@ window._gBrowser = {
       });
     });
   },
 
   getBrowserAtIndex(aIndex) {
     return this.browsers[aIndex];
   },
 
-  getBrowserIndexForDocument(aDocument) {
-    var tab = this._getTabForContentWindow(aDocument.defaultView);
-    return tab ? tab._tPos : -1;
-  },
-
-  getBrowserForDocument(aDocument) {
-    var tab = this._getTabForContentWindow(aDocument.defaultView);
-    return tab ? tab.linkedBrowser : null;
-  },
-
-  getBrowserForContentWindow(aWindow) {
-    var tab = this._getTabForContentWindow(aWindow);
-    return tab ? tab.linkedBrowser : null;
-  },
-
   getBrowserForOuterWindowID(aID) {
     return this._outerWindowIDBrowserMap.get(aID);
   },
 
-  _getTabForContentWindow(aWindow) {
-    // When not using remote browsers, we can take a fast path by getting
-    // directly from the content window to the browser without looping
-    // over all browsers.
-    if (!gMultiProcessBrowser) {
-      let browser = aWindow.docShell.chromeEventHandler;
-      return this.getTabForBrowser(browser);
-    }
-
-    for (let i = 0; i < this.browsers.length; i++) {
-      // NB: We use contentWindowAsCPOW so that this code works both
-      // for remote browsers as well. aWindow may be a CPOW.
-      if (this.browsers[i].contentWindowAsCPOW == aWindow)
-        return this.tabs[i];
-    }
-    return null;
-  },
-
   getTabForBrowser(aBrowser) {
     return this._tabForBrowser.get(aBrowser);
   },
 
   getNotificationBox(aBrowser) {
     return this.getSidebarContainer(aBrowser).parentNode;
   },
 
@@ -4444,17 +4411,18 @@ window._gBrowser = {
       if (this.tabs.length == 1) {
         // We already did PermitUnload in nsGlobalWindow::Close
         // for this tab. There are no other tabs we need to do
         // PermitUnload for.
         window.skipNextCanClose = true;
         return;
       }
 
-      var tab = this._getTabForContentWindow(event.target);
+      let browser = event.target.docShell.chromeEventHandler;
+      let tab = this.getTabForBrowser(browser);
       if (tab) {
         // Skip running PermitUnload since it already happened.
         this.removeTab(tab, { skipPermitUnload: true });
         event.preventDefault();
       }
     }, true);
 
     this.addEventListener("DOMWillOpenModalDialog", (event) => {
@@ -4464,17 +4432,17 @@ window._gBrowser = {
       let targetIsWindow = event.target instanceof Window;
 
       // We're about to open a modal dialog, so figure out for which tab:
       // If this is a same-process modal dialog, then we're given its DOM
       // window as the event's target. For remote dialogs, we're given the
       // browser, but that's in the originalTarget and not the target,
       // because it's across the tabbrowser's XBL boundary.
       let tabForEvent = targetIsWindow ?
-        this._getTabForContentWindow(event.target.top) :
+        this.getTabForBrowser(event.target.docShell.chromeEventHandler) :
         this.getTabForBrowser(event.originalTarget);
 
       // Focus window for beforeunload dialog so it is seen but don't
       // steal focus from other applications.
       if (event.detail &&
           event.detail.tabPrompt &&
           event.detail.inPermitUnload &&
           Services.focus.activeWindow)
@@ -4527,17 +4495,18 @@ window._gBrowser = {
     this.addEventListener("DOMTitleChanged", (event) => {
       if (!event.isTrusted)
         return;
 
       var contentWin = event.target.defaultView;
       if (contentWin != contentWin.top)
         return;
 
-      var tab = this._getTabForContentWindow(contentWin);
+      let browser = contentWin.docShell.chromeEventHandler;
+      var tab = this.getTabForBrowser(browser);
       if (!tab || tab.hasAttribute("pending"))
         return;
 
       var titleChanged = this.setTabTitle(tab);
       if (titleChanged && !tab.selected && !tab.hasAttribute("busy"))
         tab.setAttribute("titlechanged", "true");
     });
 
@@ -5338,23 +5307,20 @@ var TabContextMenu = {
       gBrowser.visibleTabs.filter(t => !t.multiselected && !t.pinned).length :
       gBrowser.visibleTabs.filter(t => t != this.contextTab && !t.pinned).length;
     document.getElementById("context_closeOtherTabs").disabled = unpinnedTabsToClose < 1;
 
     // Only one of close_tab/close_selected_tabs should be visible
     document.getElementById("context_closeTab").hidden = multiselectionContext;
     document.getElementById("context_closeSelectedTabs").hidden = !multiselectionContext;
 
-    // Hide "Bookmark All Tabs" for a pinned tab or multiselection.
+    // Hide "Bookmark Tab" for multiselection.
     // Update its state if visible.
-    let bookmarkAllTabs = document.getElementById("context_bookmarkAllTabs");
-    bookmarkAllTabs.hidden = this.contextTab.pinned || multiselectionContext;
-    if (!bookmarkAllTabs.hidden) {
-      PlacesCommandHook.updateBookmarkAllTabsCommand();
-    }
+    let bookmarkTab = document.getElementById("context_bookmarkTab");
+    bookmarkTab.hidden = multiselectionContext;
 
     // Show "Bookmark Selected Tabs" in a multiselect context and hide it otherwise.
     let bookmarkMultiSelectedTabs = document.getElementById("context_bookmarkSelectedTabs");
     bookmarkMultiSelectedTabs.hidden = !multiselectionContext;
 
     let toggleMute = document.getElementById("context_toggleMuteTab");
     let toggleMultiSelectMute = document.getElementById("context_toggleMuteSelectedTabs");
 
--- a/browser/base/content/test/general/browser_keywordSearch_postData.js
+++ b/browser/base/content/test/general/browser_keywordSearch_postData.js
@@ -11,83 +11,59 @@ var gTests = [
   },
   {
     name: "?-prefixed search (search service)",
     testText: "?   foo  ",
     expectText: "foo",
   },
 ];
 
-function test() {
-  waitForExplicitFinish();
-
-  let tab = gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
-
-  let searchObserver = function search_observer(aSubject, aTopic, aData) {
-    let engine = aSubject.QueryInterface(Ci.nsISearchEngine);
-    info("Observer: " + aData + " for " + engine.name);
-
-    if (aData != "engine-added")
-      return;
-
-    if (engine.name != "POST Search")
-      return;
-
-    Services.search.defaultEngine = engine;
-
-    registerCleanupFunction(function() {
-      Services.search.removeEngine(engine);
-    });
-
-    // ready to execute the tests!
-    executeSoon(nextTest);
-  };
-
-  Services.obs.addObserver(searchObserver, "browser-search-engine-modified");
-
-  registerCleanupFunction(function() {
-    gBrowser.removeTab(tab);
-
-    Services.obs.removeObserver(searchObserver, "browser-search-engine-modified");
+add_task(async function setup() {
+  let engineAddedPromise = TestUtils.topicObserved("browser-search-engine-modified", (subject, data) => {
+    return data == "engine-added";
   });
 
   Services.search.addEngine("http://test:80/browser/browser/base/content/test/general/POSTSearchEngine.xml",
                             null, null, false);
-}
+
+  let [subject, data] = await engineAddedPromise;
+
+  let engine = subject.QueryInterface(Ci.nsISearchEngine);
+  info("Observer: " + data + " for " + engine.name);
+
+  if (engine.name != "POST Search") {
+    Assert.ok(false, "Wrong search engine added");
+  }
 
-var gCurrTest;
-function nextTest() {
-  if (gTests.length) {
-    gCurrTest = gTests.shift();
-    doTest();
-  } else {
-    finish();
-  }
-}
+  Services.search.defaultEngine = engine;
 
-function doTest() {
-  info("Running test: " + gCurrTest.name);
+  registerCleanupFunction(function() {
+    Services.search.removeEngine(engine);
+  });
+});
+
+add_task(async function() {
+  for (let test of gTests) {
+    let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
+    let browser = tab.linkedBrowser;
 
-  waitForLoad(function() {
-    let loadedText = gBrowser.contentDocumentAsCPOW.body.textContent;
-    ok(loadedText, "search page loaded");
-    let needle = "searchterms=" + gCurrTest.expectText;
-    is(loadedText, needle, "The query POST data should be returned in the response");
-    nextTest();
-  });
+    // Simulate a user entering search terms
+    gURLBar.value = test.testText;
+    gURLBar.focus();
+    EventUtils.synthesizeKey("KEY_Enter");
+
+    await BrowserTestUtils.browserLoaded(browser, false, url => {
+      return url != "about:blank";
+    });
+
+    info("Page loaded: " + browser.currentURI.spec);
 
-  // Simulate a user entering search terms
-  gURLBar.value = gCurrTest.testText;
-  gURLBar.focus();
-  EventUtils.synthesizeKey("KEY_Enter");
-}
-
+    let textContent = await ContentTask.spawn(browser, null, async () => {
+      return content.document.body.textContent;
+    });
 
-function waitForLoad(cb) {
-  let browser = gBrowser.selectedBrowser;
-  function wantLoad(url) {
-    return url != "about:blank";
+    Assert.ok(textContent, "search page loaded");
+    let needle = "searchterms=" + test.expectText;
+    Assert.equal(textContent, needle, "The query POST data should be returned in the response");
+
+    BrowserTestUtils.removeTab(tab);
   }
-  BrowserTestUtils.browserLoaded(browser, false, wantLoad).then(() => {
-    info("Page loaded: " + browser.currentURI.spec);
-    cb();
-  }, true);
-}
+});
--- a/browser/base/content/test/general/browser_tab_dragdrop2.js
+++ b/browser/base/content/test/general/browser_tab_dragdrop2.js
@@ -34,17 +34,20 @@ add_task(async function() {
   await Promise.all([tabClosed, promiseDelayedStartupFinished(win2)]);
 
   // Remove the 'TestsDone' event listener as now
   // we're kicking off a new test run manually.
   win2.removeEventListener("TestsDone", onTestsDone);
 
   // Run tests once again.
   let promise = promiseTestsDone(win2);
-  win2.gBrowser.contentWindowAsCPOW.test_panels();
+  let browser2 = win2.gBrowser.selectedBrowser;
+  await ContentTask.spawn(browser2, null, async () => {
+    content.test_panels();
+  });
   await promise;
   ok(true, "tests succeeded a second time");
 
   // Cleanup.
   await promiseWindowClosed(win2);
   await promiseWindowClosed(win);
 });
 
--- a/browser/base/content/test/pageinfo/browser_pageinfo_image_info.js
+++ b/browser/base/content/test/pageinfo/browser_pageinfo_image_info.js
@@ -1,50 +1,46 @@
-/* Make sure that "View Image Info" loads the correct image data */
-function getImageInfo(imageElement) {
-  return {
-    currentSrc: imageElement.currentSrc,
-    width: imageElement.width,
-    height: imageElement.height,
-    imageText: imageElement.title || imageElement.alt,
-  };
-}
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ **/
 
 const URI =
   "data:text/html," +
   "<style type='text/css'>%23test-image,%23not-test-image {background-image: url('about:logo?c');}</style>" +
   "<img src='about:logo?b' height=300 width=350 alt=2 id='not-test-image'>" +
   "<img src='about:logo?b' height=300 width=350 alt=2>" +
   "<img src='about:logo?a' height=200 width=250>" +
   "<img src='about:logo?b' height=200 width=250 alt=1>" +
   "<img src='about:logo?b' height=100 width=150 alt=2 id='test-image'>";
 
-function test() {
-  waitForExplicitFinish();
-
-  gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
+add_task(async function() {
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, URI);
+  let browser = tab.linkedBrowser;
 
-  BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false,
-                                 URI).then(function() {
-    var doc = gBrowser.contentDocumentAsCPOW;
-    var testImg = doc.getElementById("test-image");
-    var pageInfo = BrowserPageInfo(gBrowser.selectedBrowser.currentURI.spec,
-                                   "mediaTab", getImageInfo(testImg));
+  let imageInfo = await ContentTask.spawn(browser, null, async () => {
+    let testImg = content.document.getElementById("test-image");
 
-    pageInfo.addEventListener("load", function() {
-      pageInfo.onFinished.push(function() {
-        var pageInfoImg = pageInfo.document.getElementById("thepreviewimage");
-        pageInfoImg.addEventListener("loadend", function() {
-
-          is(pageInfoImg.src, testImg.src, "selected image has the correct source");
-          is(pageInfoImg.width, testImg.width, "selected image has the correct width");
-          is(pageInfoImg.height, testImg.height, "selected image has the correct height");
-
-          pageInfo.close();
-          gBrowser.removeCurrentTab();
-          finish();
-        });
-      });
-    }, {capture: true, once: true});
+    return {
+      src: testImg.src,
+      currentSrc: testImg.currentSrc,
+      width: testImg.width,
+      height: testImg.height,
+      imageText: testImg.title || testImg.alt,
+    };
   });
 
-  BrowserTestUtils.loadURI(gBrowser, URI);
-}
+  let pageInfo = BrowserPageInfo(browser.currentURI.spec, "mediaTab", imageInfo);
+  await BrowserTestUtils.waitForEvent(pageInfo, "load", true /** capture **/);
+  await new Promise(resolve => {
+    pageInfo.onFinished.push(() => {
+      let pageInfoImg = pageInfo.document.getElementById("thepreviewimage");
+      BrowserTestUtils.waitForEvent(pageInfoImg, "loadend").then(() => {
+        Assert.equal(pageInfoImg.src, imageInfo.src, "selected image has the correct source");
+        Assert.equal(pageInfoImg.width, imageInfo.width, "selected image has the correct width");
+        Assert.equal(pageInfoImg.height, imageInfo.height, "selected image has the correct height");
+        resolve();
+      });
+    });
+  });
+  pageInfo.close();
+  BrowserTestUtils.removeTab(tab);
+});
--- a/browser/base/content/test/siteIdentity/browser_bug906190.js
+++ b/browser/base/content/test/siteIdentity/browser_bug906190.js
@@ -29,26 +29,30 @@ async function doTest(parentTabSpec, chi
     });
 
     // Disable the Mixed Content Blocker for the page, which reloads it.
     let promiseReloaded = BrowserTestUtils.browserLoaded(browser);
     gIdentityHandler.disableMixedContentProtection();
     await promiseReloaded;
 
     // Wait for the script in the page to update the contents of the test div.
-    let testDiv = gBrowser.contentDocumentAsCPOW.getElementById("mctestdiv");
-    await BrowserTestUtils.waitForCondition(
-      () => testDiv.innerHTML == "Mixed Content Blocker disabled");
+    await ContentTask.spawn(browser, childTabSpec, async (childTabSpecContent) => {
+      let testDiv = content.document.getElementById("mctestdiv");
+      await ContentTaskUtils.waitForCondition(() =>
+        testDiv.innerHTML == "Mixed Content Blocker disabled"
+      );
 
-    // Add the link for the child tab to the page.
-    let mainDiv = gBrowser.contentDocumentAsCPOW.createElement("div");
-    // eslint-disable-next-line no-unsanitized/property
-    mainDiv.innerHTML =
-      '<p><a id="linkToOpenInNewTab" href="' + childTabSpec + '">Link</a></p>';
-    gBrowser.contentDocumentAsCPOW.body.appendChild(mainDiv);
+      // Add the link for the child tab to the page.
+      let mainDiv = content.document.createElement("div");
+
+      // eslint-disable-next-line no-unsanitized/property
+      mainDiv.innerHTML =
+        '<p><a id="linkToOpenInNewTab" href="' + childTabSpecContent + '">Link</a></p>';
+      content.document.body.appendChild(mainDiv);
+    });
 
     // Execute the test in the child tabs with the two methods to open it.
     for (let openFn of [simulateCtrlClick, simulateContextMenuOpenInTab]) {
       let promiseTabLoaded = waitForSomeTabToLoad();
       openFn(browser);
       await promiseTabLoaded;
       gBrowser.selectTabAtIndex(2);
 
@@ -111,18 +115,21 @@ add_task(async function test_same_origin
                HTTPS_TEST_ROOT_1 + "file_bug906190_2.html", async function() {
     // The doorhanger should appear but activeBlocked should be >> NOT << true,
     // because our decision of disabling the mixed content blocker is persistent
     // across tabs.
     await assertMixedContentBlockingState(gBrowser, {
       activeLoaded: true, activeBlocked: false, passiveLoaded: false,
     });
 
-    is(gBrowser.contentDocumentAsCPOW.getElementById("mctestdiv").innerHTML,
-       "Mixed Content Blocker disabled", "OK: Executed mixed script");
+    await ContentTask.spawn(gBrowser.selectedBrowser, null, async () => {
+      Assert.equal(content.document.getElementById("mctestdiv").innerHTML,
+                   "Mixed Content Blocker disabled",
+                   "OK: Executed mixed script");
+    });
   });
 });
 
 /**
  * 2. - Load a html page which has mixed content
  *    - Doorhanger to disable protection appears - we disable it
  *    - Load a new page from a different origin in a new tab simulating a click
  *    - Doorhanger >> SHOULD << appear again!
@@ -132,18 +139,21 @@ add_task(async function test_different_o
                HTTPS_TEST_ROOT_2 + "file_bug906190_2.html", async function() {
     // The doorhanger should appear and activeBlocked should be >> TRUE <<,
     // because our decision of disabling the mixed content blocker should only
     // persist if pages are from the same domain.
     await assertMixedContentBlockingState(gBrowser, {
       activeLoaded: false, activeBlocked: true, passiveLoaded: false,
     });
 
-    is(gBrowser.contentDocumentAsCPOW.getElementById("mctestdiv").innerHTML,
-       "Mixed Content Blocker enabled", "OK: Blocked mixed script");
+    await ContentTask.spawn(gBrowser.selectedBrowser, null, async () => {
+      Assert.equal(content.document.getElementById("mctestdiv").innerHTML,
+                   "Mixed Content Blocker enabled",
+                   "OK: Blocked mixed script");
+    });
   });
 });
 
 /**
  * 3. - Load a html page which has mixed content
  *    - Doorhanger to disable protection appears - we disable it
  *    - Load a new page from the same origin in a new tab simulating a click
  *    - Redirect to another page from the same origin using meta-refresh
@@ -153,18 +163,21 @@ add_task(async function test_same_origin
   // file_bug906190_3_4.html redirects to page test1.example.com/* using meta-refresh
   await doTest(HTTPS_TEST_ROOT_1 + "file_bug906190_1.html",
                HTTPS_TEST_ROOT_1 + "file_bug906190_3_4.html", async function() {
     // The doorhanger should appear but activeBlocked should be >> NOT << true!
     await assertMixedContentBlockingState(gBrowser, {
       activeLoaded: true, activeBlocked: false, passiveLoaded: false,
     });
 
-    is(gBrowser.contentDocumentAsCPOW.getElementById("mctestdiv").innerHTML,
-       "Mixed Content Blocker disabled", "OK: Executed mixed script");
+    await ContentTask.spawn(gBrowser.selectedBrowser, null, async () => {
+      Assert.equal(content.document.getElementById("mctestdiv").innerHTML,
+                   "Mixed Content Blocker disabled",
+                   "OK: Executed mixed script");
+    });
   }, true);
 });
 
 /**
  * 4. - Load a html page which has mixed content
  *    - Doorhanger to disable protection appears - we disable it
  *    - Load a new page from the same origin in a new tab simulating a click
  *    - Redirect to another page from a different origin using meta-refresh
@@ -173,38 +186,44 @@ add_task(async function test_same_origin
 add_task(async function test_same_origin_metarefresh_different_origin() {
   await doTest(HTTPS_TEST_ROOT_2 + "file_bug906190_1.html",
                HTTPS_TEST_ROOT_2 + "file_bug906190_3_4.html", async function() {
     // The doorhanger should appear and activeBlocked should be >> TRUE <<.
     await assertMixedContentBlockingState(gBrowser, {
       activeLoaded: false, activeBlocked: true, passiveLoaded: false,
     });
 
-    is(gBrowser.contentDocumentAsCPOW.getElementById("mctestdiv").innerHTML,
-       "Mixed Content Blocker enabled", "OK: Blocked mixed script");
+    await ContentTask.spawn(gBrowser.selectedBrowser, null, async () => {
+      Assert.equal(content.document.getElementById("mctestdiv").innerHTML,
+                   "Mixed Content Blocker enabled",
+                   "OK: Blocked mixed script");
+    });
   }, true);
 });
 
 /**
  * 5. - Load a html page which has mixed content
  *    - Doorhanger to disable protection appears - we disable it
  *    - Load a new page from the same origin in a new tab simulating a click
  *    - Redirect to another page from the same origin using 302 redirect
  */
 add_task(async function test_same_origin_302redirect_same_origin() {
   // the sjs files returns a 302 redirect- note, same origins
   await doTest(HTTPS_TEST_ROOT_1 + "file_bug906190_1.html",
-               HTTPS_TEST_ROOT_1 + "file_bug906190.sjs", function() {
+               HTTPS_TEST_ROOT_1 + "file_bug906190.sjs", async function() {
     // The doorhanger should appear but activeBlocked should be >> NOT << true.
     // Currently it is >> TRUE << - see follow up bug 914860
     ok(!gIdentityHandler._identityBox.classList.contains("mixedActiveBlocked"),
        "OK: Mixed Content is NOT being blocked");
 
-    is(gBrowser.contentDocumentAsCPOW.getElementById("mctestdiv").innerHTML,
-       "Mixed Content Blocker disabled", "OK: Executed mixed script");
+    await ContentTask.spawn(gBrowser.selectedBrowser, null, async () => {
+      Assert.equal(content.document.getElementById("mctestdiv").innerHTML,
+                   "Mixed Content Blocker disabled",
+                   "OK: Executed mixed script");
+    });
   });
 });
 
 /**
  * 6. - Load a html page which has mixed content
  *    - Doorhanger to disable protection appears - we disable it
  *    - Load a new page from the same origin in a new tab simulating a click
  *    - Redirect to another page from a different origin using 302 redirect
@@ -213,18 +232,21 @@ add_task(async function test_same_origin
   // the sjs files returns a 302 redirect - note, different origins
   await doTest(HTTPS_TEST_ROOT_2 + "file_bug906190_1.html",
                HTTPS_TEST_ROOT_2 + "file_bug906190.sjs", async function() {
     // The doorhanger should appear and activeBlocked should be >> TRUE <<.
     await assertMixedContentBlockingState(gBrowser, {
       activeLoaded: false, activeBlocked: true, passiveLoaded: false,
     });
 
-    is(gBrowser.contentDocumentAsCPOW.getElementById("mctestdiv").innerHTML,
-       "Mixed Content Blocker enabled", "OK: Blocked mixed script");
+    await ContentTask.spawn(gBrowser.selectedBrowser, null, async () => {
+      Assert.equal(content.document.getElementById("mctestdiv").innerHTML,
+                   "Mixed Content Blocker enabled",
+                   "OK: Blocked mixed script");
+    });
   });
 });
 
 /**
  * 7. - Test memory leak issue on redirection error. See Bug 1269426.
  */
 add_task(async function test_bad_redirection() {
   // the sjs files returns a 302 redirect - note, different origins
--- a/browser/base/content/test/tabs/browser_multiselect_tabs_bookmark.js
+++ b/browser/base/content/test/tabs/browser_multiselect_tabs_bookmark.js
@@ -18,47 +18,47 @@ add_task(async function setPref() {
   });
 });
 
 add_task(async function test() {
   let tab1 = await addTab();
   let tab2 = await addTab();
   let tab3 = await addTab();
 
-  let menuItemBookmarkAllTabs = document.getElementById("context_bookmarkAllTabs");
+  let menuItemBookmarkTab = document.getElementById("context_bookmarkTab");
   let menuItemBookmarkSelectedTabs = document.getElementById("context_bookmarkSelectedTabs");
 
   is(gBrowser.multiSelectedTabsCount, 0, "Zero multiselected tabs");
 
   await BrowserTestUtils.switchTab(gBrowser, tab1);
   await triggerClickOn(tab2, { ctrlKey: true });
 
   ok(tab1.multiselected, "Tab1 is multiselected");
   ok(tab2.multiselected, "Tab2 is multiselected");
   ok(!tab3.multiselected, "Tab3 is not multiselected");
 
   // Check the context menu with a non-multiselected tab
   updateTabContextMenu(tab3);
-  is(menuItemBookmarkAllTabs.hidden, false, "Bookmark All Tabs is visible");
+  is(menuItemBookmarkTab.hidden, false, "Bookmark Tab is visible");
   is(menuItemBookmarkSelectedTabs.hidden, true, "Bookmark Selected Tabs is hidden");
 
   // Check the context menu with a multiselected tab and one unique page in the selection.
   updateTabContextMenu(tab2);
-  is(menuItemBookmarkAllTabs.hidden, true, "Bookmark All Tabs is visible");
+  is(menuItemBookmarkTab.hidden, true, "Bookmark Tab is visible");
   is(menuItemBookmarkSelectedTabs.hidden, false, "Bookmark Selected Tabs is hidden");
   is(PlacesCommandHook.uniqueSelectedPages.length, 1, "No more than one unique selected page");
 
   info("Add a different page to selection");
   let tab4 = await addTab_example_com();
   await triggerClickOn(tab4, { ctrlKey: true });
 
   ok(tab4.multiselected, "Tab4 is multiselected");
   is(gBrowser.multiSelectedTabsCount, 3, "Three multiselected tabs");
 
   // Check the context menu with a multiselected tab and two unique pages in the selection.
   updateTabContextMenu(tab2);
-  is(menuItemBookmarkAllTabs.hidden, true, "Bookmark All Tabs is visible");
+  is(menuItemBookmarkTab.hidden, true, "Bookmark Tab is visible");
   is(menuItemBookmarkSelectedTabs.hidden, false, "Bookmark Selected Tabs is hidden");
   is(PlacesCommandHook.uniqueSelectedPages.length, 2, "More than one unique selected page");
 
   for (let tab of [tab1, tab2, tab3, tab4])
     BrowserTestUtils.removeTab(tab);
 });
--- a/browser/base/content/test/tabs/browser_visibleTabs_bookmarkAllTabs.js
+++ b/browser/base/content/test/tabs/browser_visibleTabs_bookmarkAllTabs.js
@@ -3,63 +3,50 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 function test() {
   waitForExplicitFinish();
 
   // There should be one tab when we start the test
   let [origTab] = gBrowser.visibleTabs;
   is(gBrowser.visibleTabs.length, 1, "1 tab should be open");
-  is(Disabled(), true, "Bookmark All Tabs should be disabled");
 
   // Add a tab
   let testTab1 = BrowserTestUtils.addTab(gBrowser);
   is(gBrowser.visibleTabs.length, 2, "2 tabs should be open");
-  is(Disabled(), true, "Bookmark All Tabs should be disabled since there are two tabs with the same address");
 
   let testTab2 = BrowserTestUtils.addTab(gBrowser, "about:mozilla");
   is(gBrowser.visibleTabs.length, 3, "3 tabs should be open");
   // Wait for tab load, the code checks for currentURI.
   testTab2.linkedBrowser.addEventListener("load", function() {
-    is(Disabled(), false, "Bookmark All Tabs should be enabled since there are two tabs with different addresses");
-
     // Hide the original tab
     gBrowser.selectedTab = testTab2;
     gBrowser.showOnlyTheseTabs([testTab2]);
     is(gBrowser.visibleTabs.length, 1, "1 tab should be visible");
-    is(Disabled(), true, "Bookmark All Tabs should be disabled as there is only one visible tab");
 
     // Add a tab that will get pinned
     let pinned = BrowserTestUtils.addTab(gBrowser);
     is(gBrowser.visibleTabs.length, 2, "2 tabs should be visible now");
-    is(Disabled(), false, "Bookmark All Tabs should be available as there are two visible tabs");
     gBrowser.pinTab(pinned);
-    is(Hidden(), false, "Bookmark All Tabs should be visible on a normal tab");
-    is(Disabled(), true, "Bookmark All Tabs should not be available since one tab is pinned");
+    is(BookmarkTabHidden(), false, "Bookmark Tab should be visible on a normal tab");
     gBrowser.selectedTab = pinned;
-    is(Hidden(), true, "Bookmark All Tabs should be hidden on a pinned tab");
+    is(BookmarkTabHidden(), false, "Bookmark Tab should be visible on a pinned tab");
 
     // Show all tabs
     let allTabs = Array.from(gBrowser.tabs);
     gBrowser.showOnlyTheseTabs(allTabs);
 
     // reset the environment
     gBrowser.removeTab(testTab2);
     gBrowser.removeTab(testTab1);
     gBrowser.removeTab(pinned);
     is(gBrowser.visibleTabs.length, 1, "only orig is left and visible");
     is(gBrowser.tabs.length, 1, "sanity check that it matches");
-    is(Disabled(), true, "Bookmark All Tabs should be hidden");
     is(gBrowser.selectedTab, origTab, "got the orig tab");
     is(origTab.hidden, false, "and it's not hidden -- visible!");
     finish();
   }, {capture: true, once: true});
 }
 
-function Disabled() {
+function BookmarkTabHidden() {
   updateTabContextMenu();
-  return document.getElementById("Browser:BookmarkAllTabs").getAttribute("disabled") == "true";
+  return document.getElementById("context_bookmarkTab").hidden;
 }
-
-function Hidden() {
-  updateTabContextMenu();
-  return document.getElementById("context_bookmarkAllTabs").hidden;
-}
--- a/browser/base/content/test/urlbar/browser_urlbarHashChangeProxyState.js
+++ b/browser/base/content/test/urlbar/browser_urlbarHashChangeProxyState.js
@@ -84,20 +84,19 @@ add_task(async function() {
   });
 });
 
 /**
  * Check that initial secure loads that swap remoteness
  * get the correct page icon when finished.
  */
 add_task(async function() {
-  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:newtab", false);
-  // NB: CPOW usage because new tab pages can be preloaded, in which case no
-  // load events fire.
-  await BrowserTestUtils.waitForCondition(() => !tab.linkedBrowser.contentDocumentAsCPOW.hidden);
+  // Ensure there's no preloaded newtab browser, since that'll not fire a load event.
+  gBrowser.removePreloadedBrowser();
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:newtab");
   let url = "https://example.org/browser/browser/base/content/test/urlbar/dummy_page.html#foo";
   gURLBar.value = url;
   gURLBar.select();
   EventUtils.sendKey("return");
   await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
 
   is(gURLBar.textValue, url, "URL bar visible value should be correct when the page loads from about:newtab");
   is(gURLBar.value, url, "URL bar value should be correct when the page loads from about:newtab");
--- a/browser/base/content/test/urlbar/browser_urlbarKeepStateAcrossTabSwitches.js
+++ b/browser/base/content/test/urlbar/browser_urlbarKeepStateAcrossTabSwitches.js
@@ -3,19 +3,23 @@
 /**
  * Verify user typed text remains in the URL bar when tab switching, even when
  * loads fail.
  */
 add_task(async function() {
   let input = "i-definitely-dont-exist.example.com";
   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank", false);
   let browser = tab.linkedBrowser;
-  // NB: CPOW usage because new tab pages can be preloaded, in which case no
-  // load events fire.
-  await BrowserTestUtils.waitForCondition(() => browser.contentDocumentAsCPOW && !browser.contentDocumentAsCPOW.hidden);
+  // Note: Waiting on content document not being hidden because new tab pages can be preloaded,
+  // in which case no load events fire.
+  await ContentTask.spawn(browser, null, async () => {
+    await ContentTaskUtils.waitForCondition(() => {
+      return content.document && !content.document.hidden;
+    });
+  });
   let errorPageLoaded = BrowserTestUtils.waitForErrorPage(tab.linkedBrowser);
   gURLBar.value = input;
   gURLBar.select();
   EventUtils.sendKey("return");
   await errorPageLoaded;
   is(gURLBar.textValue, input, "Text is still in URL bar");
   await BrowserTestUtils.switchTab(gBrowser, tab.previousElementSibling);
   await BrowserTestUtils.switchTab(gBrowser, tab);
@@ -27,19 +31,23 @@ add_task(async function() {
  * Invalid URIs fail differently (that is, immediately, in the loadURI call)
  * if keyword searches are turned off. Test that this works, too.
  */
 add_task(async function() {
   let input = "To be or not to be-that is the question";
   await SpecialPowers.pushPrefEnv({set: [["keyword.enabled", false]]});
   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank", false);
   let browser = tab.linkedBrowser;
-  // NB: CPOW usage because new tab pages can be preloaded, in which case no
-  // load events fire.
-  await BrowserTestUtils.waitForCondition(() => browser.contentDocumentAsCPOW && !browser.contentDocumentAsCPOW.hidden);
+  // Note: Waiting on content document not being hidden because new tab pages can be preloaded,
+  // in which case no load events fire.
+  await ContentTask.spawn(browser, null, async () => {
+    await ContentTaskUtils.waitForCondition(() => {
+      return content.document && !content.document.hidden;
+    });
+  });
   let errorPageLoaded = BrowserTestUtils.waitForErrorPage(tab.linkedBrowser);
   gURLBar.value = input;
   gURLBar.select();
   EventUtils.sendKey("return");
   await errorPageLoaded;
   is(gURLBar.textValue, input, "Text is still in URL bar");
   is(tab.linkedBrowser.userTypedValue, input, "Text still stored on browser");
   await BrowserTestUtils.switchTab(gBrowser, tab.previousElementSibling);
--- a/browser/components/contextualidentity/test/browser/browser_usercontext.js
+++ b/browser/components/contextualidentity/test/browser/browser_usercontext.js
@@ -64,17 +64,17 @@ add_task(async function test() {
 
     let tab = openTabInUserContext(BASE_URI, userContextId);
 
     // wait for load
     let browser = gBrowser.getBrowserForTab(tab);
     await BrowserTestUtils.browserLoaded(browser);
 
     // get the title
-    let title = browser.contentDocumentAsCPOW.title.trim().split("|");
+    let title = browser.contentTitle.trim().split("|");
 
     // check each item in the title and validate it meets expectatations
     for (let part of title) {
       let [storageMethodName, value] = part.split("=");
       is(value, expectedContext,
             "the title reflects the expected contextual identity of " +
             expectedContext + " for method " + storageMethodName + ": " + value);
     }
--- a/browser/components/customizableui/test/browser_customization_context_menus.js
+++ b/browser/components/customizableui/test/browser_customization_context_menus.js
@@ -52,17 +52,17 @@ add_task(async function tabstrip_context
   let rect = tabstrip.getBoundingClientRect();
   EventUtils.synthesizeMouse(tabstrip, rect.width - 2, 2, {type: "contextmenu", button: 2 });
   await shownPromise;
 
   let closedTabsAvailable = SessionStore.getClosedTabCount(window) == 0;
   info("Closed tabs: " + closedTabsAvailable);
   let expectedEntries = [
     ["#toolbar-context-reloadAllTabs", true],
-    ["#toolbar-context-bookmarkAllTabs", true],
+    ["#toolbar-context-bookmarkSelectedTabs", true],
     ["#toolbar-context-selectAllTabs", true],
     ["#toolbar-context-undoCloseTab", !closedTabsAvailable],
     ["---"],
   ];
   if (!isOSX) {
     expectedEntries.push(["#toggle_toolbar-menubar", true]);
   }
   expectedEntries.push(
--- a/browser/components/enterprisepolicies/content/aboutPolicies.js
+++ b/browser/components/enterprisepolicies/content/aboutPolicies.js
@@ -156,19 +156,20 @@ function generatePolicy(data, row, depth
           }
         } else if (count == Object.keys(data).length - 1) {
           let last_row = document.createElement("tr");
           for (let i = 0; i < depth; i++) {
             last_row.appendChild(col(""));
           }
 
           last_row.appendChild(col(obj));
+          last_row.classList.add(color_class);
 
           if (arr_sep) {
-            last_row.classList.add(color_class, "arr_sep");
+            last_row.classList.add("arr_sep");
           }
 
           generatePolicy(data[obj], last_row, depth + 1, new_cont, islast ? islast : false, false);
         } else {
           let new_row = document.createElement("tr");
           new_row.classList.add(color_class);
 
           for (let i = 0; i < depth; i++) {
--- a/browser/components/extensions/parent/ext-pageAction.js
+++ b/browser/components/extensions/parent/ext-pageAction.js
@@ -60,16 +60,17 @@ this.pageAction = class extends Extensio
     }
 
     this.defaults = {
       show,
       showMatches,
       hideMatches,
       title: options.default_title || extension.name,
       popup: options.default_popup || "",
+      pinned: options.pinned,
     };
 
     this.browserStyle = options.browser_style;
 
     this.tabContext = new TabContext(tab => this.defaults);
 
     this.tabContext.on("location-change", this.handleLocationChange.bind(this)); // eslint-disable-line mozilla/balanced-listeners
 
@@ -82,17 +83,17 @@ this.pageAction = class extends Extensio
     this.lastValues = new DefaultWeakMap(() => ({}));
 
     if (!this.browserPageAction) {
       this.browserPageAction = PageActions.addAction(new PageActions.Action({
         id: widgetId,
         extensionID: extension.id,
         title: this.defaults.title,
         iconURL: this.defaults.icon,
-        pinnedToUrlbar: true,
+        pinnedToUrlbar: this.defaults.pinned,
         disabled: !this.defaults.show,
         onCommand: (event, buttonNode) => {
           this.handleClick(event.target.ownerGlobal);
         },
         onBeforePlacedInWindow: browserWindow => {
           if (this.extension.hasPermission("menus") ||
               this.extension.hasPermission("contextMenus")) {
             browserWindow.document.addEventListener("popupshowing", this);
--- a/browser/components/extensions/schemas/page_action.json
+++ b/browser/components/extensions/schemas/page_action.json
@@ -39,16 +39,21 @@
                 "minItems": 1,
                 "items": { "$ref": "MatchPattern" }
               },
               "hide_matches": {
                 "type": "array",
                 "optional": true,
                 "minItems": 1,
                 "items": { "$ref": "MatchPatternRestricted" }
+              },
+              "pinned": {
+                "type": "boolean",
+                "optional": true,
+                "default": true
               }
             },
             "optional": true
           }
         }
       }
     ]
   },
--- a/browser/components/extensions/test/browser/browser_ext_pageAction_simple.js
+++ b/browser/components/extensions/test/browser/browser_ext_pageAction_simple.js
@@ -1,12 +1,14 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
+ChromeUtils.import("resource:///modules/PageActions.jsm");
+
 add_task(async function test_pageAction_basic() {
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "page_action": {
         "default_popup": "popup.html",
         "unrecognized_property": "with-a-random-value",
       },
     },
@@ -44,17 +46,63 @@ add_task(async function test_pageAction_
     SimpleTest.monitorConsole(resolve, [{
       message: /Reading manifest: Error processing page_action.unrecognized_property: An unexpected property was found/,
     }]);
   });
 
   await extension.startup();
   await extension.awaitMessage("page-action-shown");
 
+  let elem = await getPageActionButton(extension);
+  let parent = window.document.getElementById("page-action-buttons");
+  is(elem && elem.parentNode, parent, `pageAction pinned to urlbar ${elem.parentNode.getAttribute("id")}`);
+
   clickPageAction(extension);
 
   await extension.awaitMessage("popup");
 
   await extension.unload();
 
   SimpleTest.endMonitorConsole();
   await waitForConsole;
 });
+
+add_task(async function test_pageAction_pinned() {
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "page_action": {
+        "default_popup": "popup.html",
+        "pinned": false,
+      },
+    },
+
+    files: {
+      "popup.html": `
+      <!DOCTYPE html>
+      <html><body>
+      </body></html>
+      `,
+    },
+
+    background: function() {
+      browser.tabs.query({active: true, currentWindow: true}, tabs => {
+        let tabId = tabs[0].id;
+
+        browser.pageAction.show(tabId).then(() => {
+          browser.test.sendMessage("page-action-shown");
+        });
+      });
+    },
+  });
+
+  await extension.startup();
+  await extension.awaitMessage("page-action-shown");
+
+  let elem = await getPageActionButton(extension);
+  is(elem && elem.parentNode, null, "pageAction is not pinned to urlbar");
+
+  // There are plenty of tests for the main action button, we just verify
+  // that we've properly set the pinned value to false.
+  let action = PageActions.actionForID(makeWidgetId(extension.id));
+  ok(action && !action.pinnedToUrlbar, "pageAction is in main pageaction menu");
+
+  await extension.unload();
+});
--- a/browser/components/extensions/test/browser/head.js
+++ b/browser/components/extensions/test/browser/head.js
@@ -1,16 +1,16 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
 /* exported CustomizableUI makeWidgetId focusWindow forceGC
  *          getBrowserActionWidget
  *          clickBrowserAction clickPageAction
- *          getBrowserActionPopup getPageActionPopup
+ *          getBrowserActionPopup getPageActionPopup getPageActionButton
  *          closeBrowserAction closePageAction
  *          promisePopupShown promisePopupHidden
  *          openContextMenu closeContextMenu
  *          openContextMenuInSidebar openContextMenuInPopup
  *          openExtensionContextMenu closeExtensionContextMenu
  *          openActionContextMenu openSubmenu closeActionContextMenu
  *          openTabContextMenu closeTabContextMenu
  *          openToolsMenu closeToolsMenu
@@ -412,17 +412,17 @@ function closeChromeContextMenu(menuId, 
     EventUtils.synthesizeMouseAtCenter(itemToSelect, {}, win);
   } else {
     menu.hidePopup();
   }
   return hidden;
 }
 
 async function openActionContextMenu(extension, kind, win = window) {
-  // See comment from clickPageAction below.
+  // See comment from getPageActionButton below.
   SetPageProxyState("valid");
   await promiseAnimationFrame(win);
   let buttonID;
   let menuID;
   if (kind == "page") {
     buttonID = "#" + BrowserPageActions.urlbarButtonNodeIDForActionID(makeWidgetId(extension.id));
     menuID = "pageActionContextMenu";
   } else {
@@ -445,31 +445,34 @@ function closeTabContextMenu(itemToSelec
   return closeChromeContextMenu("tabContextMenu", itemToSelect, win);
 }
 
 function getPageActionPopup(extension, win = window) {
   let panelId = makeWidgetId(extension.id) + "-panel";
   return win.document.getElementById(panelId);
 }
 
-async function clickPageAction(extension, win = window) {
+async function getPageActionButton(extension, win = window) {
   // This would normally be set automatically on navigation, and cleared
   // when the user types a value into the URL bar, to show and hide page
   // identity info and icons such as page action buttons.
   //
   // Unfortunately, that doesn't happen automatically in browser chrome
   // tests.
   SetPageProxyState("valid");
 
   await promiseAnimationFrame(win);
 
   let pageActionId = BrowserPageActions.urlbarButtonNodeIDForActionID(makeWidgetId(extension.id));
 
-  let elem = win.document.getElementById(pageActionId);
+  return win.document.getElementById(pageActionId);
+}
 
+async function clickPageAction(extension, win = window) {
+  let elem = await getPageActionButton(extension, win);
   EventUtils.synthesizeMouseAtCenter(elem, {}, win);
   return new Promise(SimpleTest.executeSoon);
 }
 
 function closePageAction(extension, win = window) {
   let node = getPageActionPopup(extension, win);
   if (node) {
     return promisePopupShown(node).then(() => {
--- a/browser/components/newtab/lib/ActivityStream.jsm
+++ b/browser/components/newtab/lib/ActivityStream.jsm
@@ -162,20 +162,16 @@ const PREFS_CONFIG = new Map([
   ["sectionOrder", {
     title: "The rendering order for the sections",
     value: "topsites,topstories,highlights",
   }],
   ["improvesearch.noDefaultSearchTile", {
     title: "Experiment to remove tiles that are the same as the default search",
     value: true,
   }],
-  ["improvesearch.topSiteSearchShortcuts", {
-    title: "Experiment to show special top sites that perform keyword searches",
-    value: UpdateUtils.getUpdateChannel(true) !== "release",
-  }],
   ["improvesearch.topSiteSearchShortcuts.searchEngines", {
     title: "An ordered, comma-delimited list of search shortcuts that we should try and pin",
     // This pref is dynamic as the shortcuts vary depending on the region
     getValue: ({geo}) => {
       if (!geo) {
         return "";
       }
       const searchShortcuts = [];
--- a/browser/components/newtab/lib/PrefsFeed.jsm
+++ b/browser/components/newtab/lib/PrefsFeed.jsm
@@ -100,16 +100,23 @@ this.PrefsFeed = class PrefsFeed {
     // computed in main process
     values.isPrivateBrowsingEnabled = PrivateBrowsingUtils.enabled;
     values.platform = AppConstants.platform;
 
     // Get the firefox accounts url for links and to send firstrun metrics to.
     values.fxa_endpoint = Services.prefs.getStringPref(
       "browser.newtabpage.activity-stream.fxaccounts.endpoint", "https://accounts.firefox.com");
 
+    // Read the pref for search shortcuts top sites experiment from firefox.js and store it
+    // in our interal list of prefs to watch
+    let searchTopSiteExperimentPrefValue = Services.prefs.getBoolPref(
+      "browser.newtabpage.activity-stream.improvesearch.topSiteSearchShortcuts");
+    values["improvesearch.topSiteSearchShortcuts"] = searchTopSiteExperimentPrefValue;
+    this._prefMap.set("improvesearch.topSiteSearchShortcuts", searchTopSiteExperimentPrefValue);
+
     // Set the initial state of all prefs in redux
     this.store.dispatch(ac.BroadcastToContent({type: at.PREFS_INITIAL_VALUES, data: values}));
 
     this._migratePrefs();
     this._setPrerenderPref();
     this._initOnboardingPref();
   }
 
--- a/browser/components/search/test/browser.ini
+++ b/browser/components/search/test/browser.ini
@@ -32,18 +32,16 @@ skip-if = (verify && debug && (os == 'wi
 [browser_hiddenOneOffs_cleanup.js]
 [browser_hiddenOneOffs_diacritics.js]
 [browser_oneOffContextMenu.js]
 skip-if = verify
 [browser_oneOffContextMenu_setDefault.js]
 [browser_oneOffHeader.js]
 skip-if = os == "mac" #1421238
 [browser_private_search_perwindowpb.js]
-[browser_abouthome_behavior.js]
-skip-if = true # Bug ??????, Bug 1100301 - leaks windows until shutdown when --run-by-dir
 [browser_aboutSearchReset.js]
 [browser_searchbar_openpopup.js]
 skip-if = os == "linux" # Linux has different focus behaviours.
 [browser_searchbar_keyboard_navigation.js]
 [browser_searchbar_smallpanel_keyboard_navigation.js]
 [browser_searchEngine_behaviors.js]
 skip-if = artifact # bug 1315953
 [browser_webapi.js]
--- a/browser/components/search/test/browser_aboutSearchReset.js
+++ b/browser/components/search/test/browser_aboutSearchReset.js
@@ -59,17 +59,18 @@ var gTests = [
                       uri.spec;
 
     let rawEngine = engine.wrappedJSObject;
     let initialHash = rawEngine.getAttr("loadPathHash");
     rawEngine.setAttr("loadPathHash", "broken");
     Services.prefs.setCharPref(kStatusPref, "pending");
 
     let loadPromise = promiseStoppedLoad(expectedURL);
-    gBrowser.contentDocumentAsCPOW.getElementById("searchResetKeepCurrent").click();
+    await BrowserTestUtils.synthesizeMouseAtCenter("#searchResetKeepCurrent", {},
+                                                   gBrowser.selectedBrowser);
     await loadPromise;
 
     is(engine, Services.search.currentEngine,
        "the custom engine is still default");
     is(rawEngine.getAttr("loadPathHash"), initialHash,
        "the loadPathHash has been fixed");
 
     checkTelemetryRecords(TELEMETRY_RESULT_ENUM.KEPT_CURRENT);
@@ -77,30 +78,39 @@ var gTests = [
   },
 },
 
 {
   desc: "Test the 'Restore Search Defaults' button.",
   async run() {
     let currentEngine = Services.search.currentEngine;
     let originalEngine = Services.search.originalDefaultEngine;
-    let doc = gBrowser.contentDocumentAsCPOW;
-    let defaultEngineSpan = doc.getElementById("defaultEngine");
-    is(defaultEngineSpan.textContent, originalEngine.name,
+    let browser = gBrowser.selectedBrowser;
+    let defaultEngineSpanText =
+      await ContentTask.spawn(browser, null, async () => {
+        return content.document.getElementById("defaultEngine").textContent;
+      });
+
+    is(defaultEngineSpanText, originalEngine.name,
        "the name of the original default engine is displayed");
 
     let expectedURL = originalEngine.
                       getSubmission(kSearchStr, null, kSearchPurpose).
                       uri.spec;
     let loadPromise = promiseStoppedLoad(expectedURL);
-    let button = doc.getElementById("searchResetChangeEngine");
-    is(doc.activeElement, button,
-       "the 'Change Search Engine' button is focused");
+
+    await ContentTask.spawn(browser, null, async () => {
+      let button = content.document.getElementById("searchResetChangeEngine");
+      Assert.equal(content.document.activeElement, button,
+                   "the 'Change Search Engine' button is focused");
+    });
+
     Services.prefs.setCharPref(kStatusPref, "pending");
-    button.click();
+    await BrowserTestUtils.synthesizeMouseAtCenter("#searchResetChangeEngine",
+                                                   {}, browser);
     await loadPromise;
 
     is(originalEngine, Services.search.currentEngine,
        "the default engine is back to the original one");
 
     checkTelemetryRecords(TELEMETRY_RESULT_ENUM.RESTORED_DEFAULT);
     is(Services.prefs.getCharPref(kStatusPref), "accepted");
     Services.search.currentEngine = currentEngine;
@@ -109,17 +119,19 @@ var gTests = [
 
 {
   desc: "Click the settings link.",
   async run() {
     Services.prefs.setCharPref(kStatusPref, "pending");
     let loadPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser,
                                                      false,
                                                      "about:preferences#search");
-    gBrowser.contentDocumentAsCPOW.getElementById("linkSettingsPage").click();
+    let browser = gBrowser.selectedBrowser;
+    await BrowserTestUtils.synthesizeMouseAtCenter("#linkSettingsPage",
+                                                   {}, browser);
     await loadPromise;
 
     checkTelemetryRecords(TELEMETRY_RESULT_ENUM.OPENED_SETTINGS);
     is(Services.prefs.getCharPref(kStatusPref), "customized");
   },
 },
 
 {
deleted file mode 100644
--- a/browser/components/search/test/browser_abouthome_behavior.js
+++ /dev/null
@@ -1,136 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/*
- * Test home page search for all plugin URLs
- */
-
-"use strict";
-
-function test() {
-  // Bug 992270: Ignore uncaught about:home exceptions (related to snippets from IndexedDB)
-  ignoreAllUncaughtExceptions(true);
-
-  let previouslySelectedEngine = Services.search.currentEngine;
-
-  function replaceUrl(base) {
-    return base;
-  }
-
-  let gMutationObserver = null;
-
-  function verify_about_home_search(engine_name) {
-    let engine = Services.search.getEngineByName(engine_name);
-    ok(engine, engine_name + " is installed");
-
-    Services.search.currentEngine = engine;
-
-    // load about:home, but remove the listener first so it doesn't
-    // get in the way
-    gBrowser.removeProgressListener(listener);
-    BrowserTestUtils.loadURI(gBrowser, "about:home");
-    info("Waiting for about:home load");
-    tab.linkedBrowser.addEventListener("load", function load(event) {
-      if (event.originalTarget != tab.linkedBrowser.contentDocumentAsCPOW ||
-          event.target.location.href == "about:blank") {
-        info("skipping spurious load event");
-        return;
-      }
-      tab.linkedBrowser.removeEventListener("load", load, true);
-
-      // Observe page setup
-      let doc = gBrowser.contentDocumentAsCPOW;
-      gMutationObserver = new MutationObserver(function(mutations) {
-        for (let mutation of mutations) {
-          if (mutation.attributeName == "searchEngineName") {
-            // Re-add the listener, and perform a search
-            gBrowser.addProgressListener(listener);
-            gMutationObserver.disconnect();
-            gMutationObserver = null;
-            executeSoon(function() {
-              doc.getElementById("searchText").value = "foo";
-              doc.getElementById("searchSubmit").click();
-            });
-          }
-        }
-      });
-      gMutationObserver.observe(doc.documentElement, { attributes: true });
-    }, true);
-  }
-  waitForExplicitFinish();
-
-  let gCurrTest;
-  let gTests = [
-    {
-      name: "Search with Bing from about:home",
-      searchURL: replaceUrl("http://www.bing.com/search?q=foo&pc=MOZI&form=MOZSPG"),
-      run() {
-        verify_about_home_search("Bing");
-      },
-    },
-    {
-      name: "Search with Google from about:home",
-      searchURL: replaceUrl("https://www.google.com/search?q=foo&ie=utf-8&oe=utf-8"),
-      run() {
-        verify_about_home_search("Google");
-      },
-    },
-    {
-      name: "Search with Amazon.com from about:home",
-      searchURL: replaceUrl("https://www.amazon.com/exec/obidos/external-search/?field-keywords=foo&mode=blended&tag=mozilla-20&sourceid=Mozilla-search"),
-      run() {
-        verify_about_home_search("Amazon.com");
-      },
-    },
-  ];
-
-  function nextTest() {
-    if (gTests.length) {
-      gCurrTest = gTests.shift();
-      info("Running : " + gCurrTest.name);
-      executeSoon(gCurrTest.run);
-    } else {
-      // Make sure we listen again for uncaught exceptions in the next test or cleanup.
-      executeSoon(finish);
-    }
-  }
-
-  let tab = gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
-
-  let listener = {
-    onStateChange: function onStateChange(webProgress, req, flags, status) {
-      info("onStateChange");
-      // Only care about top-level document starts
-      let docStart = Ci.nsIWebProgressListener.STATE_IS_DOCUMENT |
-                     Ci.nsIWebProgressListener.STATE_START;
-      if (!(flags & docStart) || !webProgress.isTopLevel)
-        return;
-
-      if (req.originalURI.spec == "about:blank")
-        return;
-
-      info("received document start");
-
-      ok(req instanceof Ci.nsIChannel, "req is a channel");
-      is(req.originalURI.spec, gCurrTest.searchURL, "search URL was loaded");
-      info("Actual URI: " + req.URI.spec);
-
-      req.cancel(Cr.NS_ERROR_FAILURE);
-
-      executeSoon(nextTest);
-    },
-  };
-
-  registerCleanupFunction(function() {
-    Services.search.currentEngine = previouslySelectedEngine;
-    gBrowser.removeProgressListener(listener);
-    gBrowser.removeTab(tab);
-    if (gMutationObserver)
-      gMutationObserver.disconnect();
-  });
-
-  tab.linkedBrowser.addEventListener("load", function() {
-    gBrowser.addProgressListener(listener);
-    nextTest();
-  }, {capture: true, once: true});
-}
--- a/browser/components/sessionstore/content/aboutSessionRestore.js
+++ b/browser/components/sessionstore/content/aboutSessionRestore.js
@@ -167,19 +167,20 @@ function restoreSession() {
   Services.obs.addObserver(function observe(win, topic) {
     if (win != newWindow) {
       return;
     }
 
     Services.obs.removeObserver(observe, topic);
     SessionStore.setWindowState(newWindow, stateString, true);
 
-    var tabbrowser = top.gBrowser;
-    var tabIndex = tabbrowser.getBrowserIndexForDocument(document);
-    tabbrowser.removeTab(tabbrowser.tabs[tabIndex]);
+    let tabbrowser = top.gBrowser;
+    let browser = window.docShell.chromeEventHandler;
+    let tab = tabbrowser.getTabForBrowser(browser);
+    tabbrowser.removeTab(tab);
   }, "browser-delayed-startup-finished");
 }
 
 function startNewSession() {
   if (Services.prefs.getIntPref("browser.startup.page") == 0)
     getBrowserWindow().gBrowser.loadURI("about:blank", {
       triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}),
     });
--- a/browser/components/shell/test/browser_1119088.js
+++ b/browser/components/shell/test/browser_1119088.js
@@ -1,64 +1,65 @@
 /* eslint-disable mozilla/no-arbitrary-setTimeout */
 let NS_OSX_PICTURE_DOCUMENTS_DIR = "Pct";
 let NS_MAC_USER_LIB_DIR = "ULibDir";
 
-
-function onPageLoad() {
-  let dirSvc = Cc["@mozilla.org/file/directory_service;1"].
-               getService(Ci.nsIDirectoryServiceProvider);
+/**
+ * Tests "Set As Desktop Background" on macOS.
+ */
+add_task(async function() {
+  await BrowserTestUtils.withNewTab({
+    gBrowser,
+    url: "about:logo",
+  }, async (browser) => {
+    let dirSvc = Cc["@mozilla.org/file/directory_service;1"].
+                 getService(Ci.nsIDirectoryServiceProvider);
 
-  let desktopBackgroundDb = dirSvc.getFile(NS_MAC_USER_LIB_DIR, {});
-  desktopBackgroundDb.append("Application Support");
-  desktopBackgroundDb.append("Dock");
-  let desktopBackgroundDbBackup = desktopBackgroundDb.clone();
-  desktopBackgroundDb.append("desktoppicture.db");
-  desktopBackgroundDbBackup.append("desktoppicture.db.backup");
+    let desktopBackgroundDb = dirSvc.getFile(NS_MAC_USER_LIB_DIR, {});
+    desktopBackgroundDb.append("Application Support");
+    desktopBackgroundDb.append("Dock");
+    let desktopBackgroundDbBackup = desktopBackgroundDb.clone();
+    desktopBackgroundDb.append("desktoppicture.db");
+    desktopBackgroundDbBackup.append("desktoppicture.db.backup");
 
-  ok(desktopBackgroundDb.exists(),
-     "Desktop background database must exist for test to run.");
+    ok(desktopBackgroundDb.exists(),
+       "Desktop background database must exist for test to run.");
 
-  if (desktopBackgroundDbBackup.exists()) {
-    desktopBackgroundDbBackup.remove(false);
-  }
+    if (desktopBackgroundDbBackup.exists()) {
+      desktopBackgroundDbBackup.remove(false);
+    }
+
+    desktopBackgroundDb.copyTo(null, desktopBackgroundDbBackup.leafName);
 
-  desktopBackgroundDb.copyTo(null, desktopBackgroundDbBackup.leafName);
+    let wpFile = dirSvc.getFile(NS_OSX_PICTURE_DOCUMENTS_DIR, {});
+    wpFile.append("logo.png");
+    if (wpFile.exists()) {
+      wpFile.remove(false);
+    }
 
-  let wpFile = dirSvc.getFile(NS_OSX_PICTURE_DOCUMENTS_DIR, {});
-  wpFile.append("logo.png");
-  if (wpFile.exists()) {
-    wpFile.remove(false);
-  }
+    let shell = Cc["@mozilla.org/browser/shell-service;1"].
+                getService(Ci.nsIShellService);
 
-  let shell = Cc["@mozilla.org/browser/shell-service;1"].
-              getService(Ci.nsIShellService);
+    // For simplicity, we're going to reach in and access the image on the
+    // page directly, which means the page shouldn't be running in a remote
+    // browser. Thankfully, about:logo runs in the parent process for now.
+    Assert.ok(!gBrowser.selectedBrowser.isRemoteBrowser,
+              "image can be accessed synchronously from the parent process");
 
-  let image = gBrowser.contentDocumentAsCPOW.images[0];
-  shell.setDesktopBackground(image, 0, "logo.png");
+    let image = gBrowser.selectedBrowser.contentDocument.images[0];
+    shell.setDesktopBackground(image, 0, "logo.png");
 
-  setTimeout(function() {
-    ok(wpFile.exists(), "Desktop background was written to disk.");
+    await BrowserTestUtils.waitForCondition(() => wpFile.exists());
+    info("Desktop background was written to disk.");
 
     desktopBackgroundDbBackup.moveTo(null, desktopBackgroundDb.leafName);
     wpFile.remove(false);
 
     // Restart Dock to reload previous background image.
     let killall = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
     killall.initWithPath("/usr/bin/killall");
     let dockArg = ["Dock"];
     let process =
       Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
     process.init(killall);
     process.run(true, dockArg, 1);
-
-    gBrowser.removeCurrentTab();
-    finish();
-  }, 1000);
-}
-
-function test() {
-  gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
-  BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(onPageLoad, false, "about:logo");
-  BrowserTestUtils.loadURI(gBrowser, "about:logo");
-
-  waitForExplicitFinish();
-}
+  });
+});
--- a/browser/components/urlbar/UrlbarController.jsm
+++ b/browser/components/urlbar/UrlbarController.jsm
@@ -2,42 +2,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/. */
 
 "use strict";
 
 var EXPORTED_SYMBOLS = ["QueryContext", "UrlbarController"];
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
-
-// XXX This is a fake manager to provide a basic integration test whilst we
-// are still constructing the manager.
-// eslint-disable-next-line require-jsdoc
-const ProvidersManager = {
-  queryStart(queryContext, controller) {
-    queryContext.results = [];
-    for (let i = 0; i < queryContext.maxResults; i++) {
-      const SWITCH_TO_TAB = Math.random() < .3;
-      let url = "http://www." + queryContext.searchString;
-      while (Math.random() < .9) {
-        url += queryContext.searchString;
-      }
-      let title = queryContext.searchString;
-      while (Math.random() < .5) {
-        title += queryContext.isPrivate ? " private" : " foo bar";
-      }
-      queryContext.results.push({
-        title,
-        type: SWITCH_TO_TAB ? "switchtotab" : "normal",
-        url,
-      });
-    }
-    controller.receiveResults(queryContext);
-  },
-};
+ChromeUtils.import("resource:///modules/UrlbarProvidersManager.jsm");
 
 /**
  * QueryContext defines a user's autocomplete input from within the Address Bar.
  * It supplements it with details of how the search results should be obtained
  * and what they consist of.
  */
 class QueryContext {
   /**
@@ -105,41 +80,42 @@ class UrlbarController {
    *
    * @param {object} [options]
    *   The initial options for UrlbarController.
    * @param {object} [options.manager]
    *   Optional fake providers manager to override the built-in providers manager.
    *   Intended for use in unit tests only.
    */
   constructor(options = {}) {
-    this.manager = options.manager || ProvidersManager;
+    this.manager = options.manager || UrlbarProvidersManager;
 
     this._listeners = new Set();
   }
 
   /**
    * Takes a query context and starts the query based on the user input.
    *
    * @param {QueryContext} queryContext The query details.
    */
-  handleQuery(queryContext) {
+  async startQuery(queryContext) {
     queryContext.autoFill = Services.prefs.getBoolPref("browser.urlbar.autoFill", true);
 
     this._notify("onQueryStarted", queryContext);
 
-    this.manager.queryStart(queryContext, this);
+    await this.manager.startQuery(queryContext, this);
   }
 
   /**
-   * Cancels an in-progress query.
+   * Cancels an in-progress query. Note, queries may continue running if they
+   * can't be canceled.
    *
    * @param {QueryContext} queryContext The query details.
    */
   cancelQuery(queryContext) {
-    this.manager.queryCancel(queryContext);
+    this.manager.cancelQuery(queryContext);
 
     this._notify("onQueryCancelled", queryContext);
   }
 
   /**
    * Receives results from a query.
    *
    * @param {QueryContext} queryContext The query details.
--- a/browser/components/urlbar/UrlbarInput.jsm
+++ b/browser/components/urlbar/UrlbarInput.jsm
@@ -316,17 +316,17 @@ class UrlbarInput {
       event.preventDefault();
     }
   }
 
   _oninput(event) {
     this.valueIsTyped = true;
 
     // XXX Fill in lastKey, and add anything else we need.
-    this.controller.handleQuery(new QueryContext({
+    this.controller.startQuery(new QueryContext({
       searchString: event.target.value,
       lastKey: "",
       maxResults: UrlbarPrefs.get("maxRichResults"),
       isPrivate: this.isPrivate,
     }));
   }
 
   _onselect(event) {
@@ -437,9 +437,8 @@ class CopyCutController {
   isCommandEnabled(command) {
     return this.supportsCommand(command) &&
            (command != "cmd_cut" || !this.urlbar.readOnly) &&
            this.urlbar.selectionStart < this.urlbar.selectionEnd;
   }
 
   onEvent() {}
 }
-
--- a/browser/components/urlbar/UrlbarView.jsm
+++ b/browser/components/urlbar/UrlbarView.jsm
@@ -1,16 +1,21 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 var EXPORTED_SYMBOLS = ["UrlbarView"];
 
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+XPCOMUtils.defineLazyModuleGetters(this, {
+  UrlbarUtils: "resource:///modules/UrlbarUtils.jsm",
+});
+
 /**
  * Receives and displays address bar autocomplete results.
  */
 class UrlbarView {
   /**
    * @param {UrlbarInput} urlbar
    *   The UrlbarInput instance belonging to this UrlbarView instance.
    */
@@ -73,16 +78,19 @@ class UrlbarView {
   }
 
   // UrlbarController listener methods.
   onQueryStarted(queryContext) {
     this._rows.textContent = "";
   }
 
   onQueryResults(queryContext) {
+    // XXX For now, clear the results for each set received. We should really
+    // be updating the existing list.
+    this._rows.textContent = "";
     for (let result of queryContext.results) {
       this._addRow(result);
     }
     this.open();
   }
 
   // Private methods below.
 
@@ -92,17 +100,17 @@ class UrlbarView {
 
   _createElement(name) {
     return this.document.createElementNS("http://www.w3.org/1999/xhtml", name);
   }
 
   _addRow(result) {
     let item = this._createElement("div");
     item.className = "urlbarView-row";
-    if (result.type == "switchtotab") {
+    if (result.type == UrlbarUtils.MATCH_TYPE.TAB_SWITCH) {
       item.setAttribute("action", "switch-to-tab");
     }
 
     let content = this._createElement("span");
     content.className = "urlbarView-row-inner";
     item.appendChild(content);
 
     let actionIcon = this._createElement("span");
@@ -110,22 +118,22 @@ class UrlbarView {
     content.appendChild(actionIcon);
 
     let favicon = this._createElement("span");
     favicon.className = "urlbarView-favicon";
     content.appendChild(favicon);
 
     let title = this._createElement("span");
     title.className = "urlbarView-title";
-    title.textContent = result.title;
+    title.textContent = result.title || result.url;
     content.appendChild(title);
 
     let secondary = this._createElement("span");
     secondary.className = "urlbarView-secondary";
-    if (result.type == "switchtotab") {
+    if (result.type == UrlbarUtils.MATCH_TYPE.TAB_SWITCH) {
       secondary.classList.add("urlbarView-action");
       secondary.textContent = "Switch to Tab";
     } else {
       secondary.classList.add("urlbarView-url");
       secondary.textContent = result.url;
     }
     content.appendChild(secondary);
 
--- a/browser/components/urlbar/tests/browser/browser_UrlbarInput_unit.js
+++ b/browser/components/urlbar/tests/browser/browser_UrlbarInput_unit.js
@@ -35,48 +35,48 @@ function assertContextMatches(context, e
 
   for (let [key, value] of Object.entries(expectedValues)) {
     Assert.equal(context[key], value,
       `Should have the expected value for ${key} in the QueryContext`);
   }
 }
 
 /**
- * Checks the result of a handleQuery call on the controller.
+ * Checks the result of a startQuery call on the controller.
  *
  * @param {object} stub The sinon stub that should have been called with the
  *                      QueryContext.
  * @param {object} expectedQueryContextProps
  *                   An object consisting of name/value pairs to check against the
  *                   QueryContext properties.
  */
-function checkHandleQueryCall(stub, expectedQueryContextProps) {
+function checkStartQueryCall(stub, expectedQueryContextProps) {
   Assert.equal(stub.callCount, 1,
-    "Should have called handleQuery on the controller");
+    "Should have called startQuery on the controller");
 
   let args = stub.args[0];
   Assert.equal(args.length, 1,
-    "Should have called handleQuery with one argument");
+    "Should have called startQuery with one argument");
 
   let queryContext = args[0];
   Assert.ok(queryContext instanceof QueryContext,
     "Should have been passed a QueryContext");
 
   for (let [name, value] of Object.entries(expectedQueryContextProps)) {
     Assert.deepEqual(queryContext[name],
      value, `Should have the correct value for queryContext.${name}`);
   }
 }
 
 add_task(async function setup() {
   sandbox = sinon.sandbox.create();
 
   fakeController = new UrlbarController();
 
-  sandbox.stub(fakeController, "handleQuery");
+  sandbox.stub(fakeController, "startQuery");
   sandbox.stub(PrivateBrowsingUtils, "isWindowPrivate").returns(false);
 
   // Open a new window, so we don't affect other tests by adding extra
   // UrbarInput wrappers around the urlbar.
   let gTestRoot = getRootDirectory(gTestPath);
 
   let win = window.openDialog(gTestRoot + "empty.xul",
                     "", "chrome");
@@ -107,17 +107,17 @@ add_task(async function setup() {
 add_task(function test_input_starts_query() {
   input.handleEvent({
     target: {
       value: "search",
     },
     type: "input",
   });
 
-  checkHandleQueryCall(fakeController.handleQuery, {
+  checkStartQueryCall(fakeController.startQuery, {
     searchString: "search",
     isPrivate: false,
     maxResults: UrlbarPrefs.get("maxRichResults"),
   });
 
   sandbox.resetHistory();
 });
 
@@ -130,15 +130,15 @@ add_task(function test_input_with_privat
 
   privateInput.handleEvent({
     target: {
       value: "search",
     },
     type: "input",
   });
 
-  checkHandleQueryCall(fakeController.handleQuery, {
+  checkStartQueryCall(fakeController.startQuery, {
     searchString: "search",
     isPrivate: true,
   });
 
   sandbox.resetHistory();
 });
--- a/browser/components/urlbar/tests/unit/head.js
+++ b/browser/components/urlbar/tests/unit/head.js
@@ -68,8 +68,52 @@ function promiseControllerNotification(c
           };
         }
         return () => false;
       },
     });
     controller.addQueryListener(proxifiedObserver);
   });
 }
+
+
+/**
+ * Helper function to clear the existing providers and register a basic provider
+ * that returns only the results given.
+ *
+ * @param {array} results The results for the provider to return.
+ * @param {function} [cancelCallback] Optional, called when the query provider
+ *                                    receives a cancel instruction.
+ */
+function registerBasicTestProvider(results, cancelCallback) {
+  // First unregister all the existing providers.
+  for (let providers of UrlbarProvidersManager.providers.values()) {
+    for (let provider of providers.values()) {
+      // While here check all providers have name and type.
+      Assert.ok(Object.values(UrlbarUtils.PROVIDER_TYPE).includes(provider.type),
+        `The provider "${provider.name}" should have a valid type`);
+      Assert.ok(provider.name, "All providers should have a name");
+      UrlbarProvidersManager.unregisterProvider(provider);
+    }
+  }
+  UrlbarProvidersManager.registerProvider({
+    get name() {
+      return "TestProvider";
+    },
+    get type() {
+      return UrlbarUtils.PROVIDER_TYPE.PROFILE;
+    },
+    async startQuery(context, add) {
+      Assert.ok(context, "context is passed-in");
+      Assert.equal(typeof add, "function", "add is a callback");
+      this._context = context;
+      for (const result of results) {
+        add(this, result);
+      }
+    },
+    cancelQuery(context) {
+      Assert.equal(this._context, context, "context is the same");
+      if (cancelCallback) {
+        cancelCallback();
+      }
+    },
+  });
+}
--- a/browser/components/urlbar/tests/unit/test_UrlbarController_integration.js
+++ b/browser/components/urlbar/tests/unit/test_UrlbarController_integration.js
@@ -2,17 +2,20 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * These tests test the UrlbarController in association with the model.
  */
 
 "use strict";
 
+ChromeUtils.import("resource://gre/modules/PromiseUtils.jsm");
+
 const TEST_URL = "http://example.com";
+const match = new UrlbarMatch(UrlbarUtils.MATCH_TYPE.TAB_SWITCH, { url: TEST_URL });
 let controller;
 
 /**
  * Asserts that the query context has the expected values.
  *
  * @param {QueryContext} context
  * @param {object} expectedValues The expected values for the QueryContext.
  */
@@ -22,34 +25,53 @@ function assertContextMatches(context, e
 
   for (let [key, value] of Object.entries(expectedValues)) {
     Assert.equal(context[key], value,
       `Should have the expected value for ${key} in the QueryContext`);
   }
 }
 
 add_task(async function setup() {
-  await PlacesTestUtils.addVisits(TEST_URL);
-
   controller = new UrlbarController();
 });
 
 add_task(async function test_basic_search() {
   const context = createContext(TEST_URL);
 
+  registerBasicTestProvider([match]);
+
   let startedPromise = promiseControllerNotification(controller, "onQueryStarted");
   let resultsPromise = promiseControllerNotification(controller, "onQueryResults");
 
-  controller.handleQuery(context);
+  controller.startQuery(context);
 
   let params = await startedPromise;
 
   Assert.equal(params[0], context);
 
   params = await resultsPromise;
 
-  Assert.equal(params[0].results.length, Services.prefs.getIntPref("browser.urlbar.maxRichResults"),
-    "Should have given the expected amount of results");
+  Assert.deepEqual(params[0].results, [match],
+    "Should have the expected match");
+});
+
+add_task(async function test_cancel_search() {
+  const context = createContext(TEST_URL);
+
+  let providerCanceledPromise = PromiseUtils.defer();
+  registerBasicTestProvider([match], providerCanceledPromise.resolve);
+
+  let startedPromise = promiseControllerNotification(controller, "onQueryStarted");
+  let cancelPromise = promiseControllerNotification(controller, "onQueryResults");
 
-  for (let result of params[0].results) {
-    Assert.ok(result.url.includes(TEST_URL));
-  }
+  await controller.startQuery(context);
+
+  let params = await startedPromise;
+
+  controller.cancelQuery(context);
+
+  Assert.equal(params[0], context);
+
+  info("Should tell the provider the query is canceled");
+  await providerCanceledPromise;
+
+  params = await cancelPromise;
 });
--- a/browser/components/urlbar/tests/unit/test_UrlbarController_unit.js
+++ b/browser/components/urlbar/tests/unit/test_UrlbarController_unit.js
@@ -29,18 +29,18 @@ function assertContextMatches(context, e
       `Should have the expected value for ${key} in the QueryContext`);
   }
 }
 
 add_task(function setup() {
   sandbox = sinon.sandbox.create();
 
   fPM = {
-    queryStart: sandbox.stub(),
-    queryCancel: sandbox.stub(),
+    startQuery: sandbox.stub(),
+    cancelQuery: sandbox.stub(),
   };
 
   generalListener = {
     onQueryStarted: sandbox.stub(),
     onQueryResults: sandbox.stub(),
     onQueryCancelled: sandbox.stub(),
   };
 
@@ -106,67 +106,67 @@ add_task(function test__notify() {
   // This should succeed without errors.
   controller._notify("onNewFake");
 
   sandbox.resetHistory();
 });
 
 add_task(function test_handle_query_starts_search() {
   const context = createContext();
-  controller.handleQuery(context);
+  controller.startQuery(context);
 
-  Assert.equal(fPM.queryStart.callCount, 1,
-    "Should have called queryStart once");
-  Assert.equal(fPM.queryStart.args[0].length, 2,
-    "Should have called queryStart with two arguments");
+  Assert.equal(fPM.startQuery.callCount, 1,
+    "Should have called startQuery once");
+  Assert.equal(fPM.startQuery.args[0].length, 2,
+    "Should have called startQuery with two arguments");
 
-  assertContextMatches(fPM.queryStart.args[0][0], {
+  assertContextMatches(fPM.startQuery.args[0][0], {
     autoFill: true,
   });
-  Assert.equal(fPM.queryStart.args[0][1], controller,
+  Assert.equal(fPM.startQuery.args[0][1], controller,
     "Should have passed the controller as the second argument");
 
 
   Assert.equal(generalListener.onQueryStarted.callCount, 1,
     "Should have called onQueryStarted for the listener");
   Assert.deepEqual(generalListener.onQueryStarted.args[0], [context],
     "Should have called onQueryStarted with the context");
 
   sandbox.resetHistory();
 });
 
 add_task(function test_handle_query_starts_search_sets_autoFill() {
   Services.prefs.setBoolPref("browser.urlbar.autoFill", false);
 
-  controller.handleQuery(createContext());
+  controller.startQuery(createContext());
 
-  Assert.equal(fPM.queryStart.callCount, 1,
-    "Should have called queryStart once");
-  Assert.equal(fPM.queryStart.args[0].length, 2,
-    "Should have called queryStart with two arguments");
+  Assert.equal(fPM.startQuery.callCount, 1,
+    "Should have called startQuery once");
+  Assert.equal(fPM.startQuery.args[0].length, 2,
+    "Should have called startQuery with two arguments");
 
-  assertContextMatches(fPM.queryStart.args[0][0], {
+  assertContextMatches(fPM.startQuery.args[0][0], {
     autoFill: false,
   });
-  Assert.equal(fPM.queryStart.args[0][1], controller,
+  Assert.equal(fPM.startQuery.args[0][1], controller,
     "Should have passed the controller as the second argument");
 
   sandbox.resetHistory();
 
   Services.prefs.clearUserPref("browser.urlbar.autoFill");
 });
 
 add_task(function test_cancel_query() {
   const context = createContext();
   controller.cancelQuery(context);
 
-  Assert.equal(fPM.queryCancel.callCount, 1,
-    "Should have called queryCancel once");
-  Assert.equal(fPM.queryCancel.args[0].length, 1,
-    "Should have called queryCancel with one argument");
+  Assert.equal(fPM.cancelQuery.callCount, 1,
+    "Should have called cancelQuery once");
+  Assert.equal(fPM.cancelQuery.args[0].length, 1,
+    "Should have called cancelQuery with one argument");
 
   Assert.equal(generalListener.onQueryCancelled.callCount, 1,
     "Should have called onQueryCancelled for the listener");
   Assert.deepEqual(generalListener.onQueryCancelled.args[0], [context],
     "Should have called onQueryCancelled with the context");
 
   sandbox.resetHistory();
 });
--- a/browser/components/urlbar/tests/unit/test_providersManager.js
+++ b/browser/components/urlbar/tests/unit/test_providersManager.js
@@ -1,42 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 add_task(async function test_providers() {
-  // First unregister all the existing providers.
-  for (let providers of UrlbarProvidersManager.providers.values()) {
-    for (let provider of providers.values()) {
-      // While here check all providers have name and type.
-      Assert.ok(Object.values(UrlbarUtils.PROVIDER_TYPE).includes(provider.type),
-        `The provider "${provider.name}" should have a valid type`);
-      Assert.ok(provider.name, "All providers should have a name");
-      UrlbarProvidersManager.unregisterProvider(provider);
-    }
-  }
   let match = new UrlbarMatch(UrlbarUtils.MATCH_TYPE.TAB_SWITCH, { url: "http://mozilla.org/foo/" });
-  UrlbarProvidersManager.registerProvider({
-    get name() {
-      return "TestProvider";
-    },
-    get type() {
-      return UrlbarUtils.PROVIDER_TYPE.PROFILE;
-    },
-    async startQuery(context, add) {
-      Assert.ok(context, "context is passed-in");
-      Assert.equal(typeof add, "function", "add is a callback");
-      this._context = context;
-      add(this, match);
-    },
-    cancelQuery(context) {
-      Assert.equal(this._context, context, "context is the same");
-    },
-  });
+  registerBasicTestProvider([match]);
 
   let context = createContext();
   let controller = new UrlbarController();
   let resultsPromise = promiseControllerNotification(controller, "onQueryResults");
 
   await UrlbarProvidersManager.startQuery(context, controller);
   // Sanity check that this doesn't throw. It should be a no-op since we await
   // for startQuery.
--- a/browser/extensions/onboarding/test/browser/browser_onboarding_skip_tour.js
+++ b/browser/extensions/onboarding/test/browser/browser_onboarding_skip_tour.js
@@ -27,16 +27,21 @@ add_task(async function test_skip_onboar
   BrowserTestUtils.removeTab(tab);
 });
 
 add_task(async function test_hide_skip_button_via_perf() {
   resetOnboardingDefaultState();
   Preferences.set("browser.onboarding.skip-tour-button.hide", true);
 
   let tab = await openTab(ABOUT_NEWTAB_URL);
-  await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
-  await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-button", {}, tab.linkedBrowser);
-  await promiseOnboardingOverlayOpened(tab.linkedBrowser);
+  let browser = tab.linkedBrowser;
+  await promiseOnboardingOverlayLoaded(browser);
+  await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-button", {}, browser);
+  await promiseOnboardingOverlayOpened(browser);
 
-  ok(!gBrowser.contentDocumentAsCPOW.querySelector("#onboarding-skip-tour-button"), "should not render the skip button");
+  let hasTourButton = await ContentTask.spawn(browser, null, () => {
+    return content.document.querySelector("#onboarding-skip-tour-button") != null;
+  });
+
+  ok(!hasTourButton, "should not render the skip button");
 
   BrowserTestUtils.removeTab(tab);
 });
--- a/browser/extensions/onboarding/test/browser/browser_onboarding_tourset.js
+++ b/browser/extensions/onboarding/test/browser/browser_onboarding_tourset.js
@@ -1,29 +1,36 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 requestLongerTimeout(2);
 
+async function testTourIDs(browser, tourIDs) {
+  await ContentTask.spawn(browser, tourIDs, async (tourIDsContent) => {
+    let doc = content.document;
+    let doms = doc.querySelectorAll(".onboarding-tour-item");
+    Assert.equal(doms.length, tourIDsContent.length, "has exact tour numbers");
+    doms.forEach((dom, idx) => {
+      Assert.equal(tourIDsContent[idx], dom.id, "contain defined onboarding id");
+    });
+  });
+}
+
 add_task(async function test_onboarding_default_new_tourset() {
   resetOnboardingDefaultState();
 
   let tab = await openTab(ABOUT_NEWTAB_URL);
-  await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
-  await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-button", {}, tab.linkedBrowser);
-  await promiseOnboardingOverlayOpened(tab.linkedBrowser);
+  let browser = tab.linkedBrowser;
+  await promiseOnboardingOverlayLoaded(browser);
+  await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-button", {}, browser);
+  await promiseOnboardingOverlayOpened(browser);
 
-  let doc = gBrowser.contentDocumentAsCPOW;
-  let doms = doc.querySelectorAll(".onboarding-tour-item");
-  is(doms.length, TOUR_IDs.length, "has exact tour numbers");
-  doms.forEach((dom, idx) => {
-    is(TOUR_IDs[idx], dom.id, "contain defined onboarding id");
-  });
+  await testTourIDs(browser, TOUR_IDs);
 
   BrowserTestUtils.removeTab(tab);
 });
 
 add_task(async function test_onboarding_custom_new_tourset() {
   const CUSTOM_NEW_TOURs = [
     "onboarding-tour-private-browsing",
     "onboarding-tour-addons",
@@ -34,26 +41,22 @@ add_task(async function test_onboarding_
   await SpecialPowers.pushPrefEnv({set: [
     ["browser.onboarding.tour-type", "new"],
     ["browser.onboarding.tourset-version", 1],
     ["browser.onboarding.seen-tourset-version", 1],
     ["browser.onboarding.newtour", "private,addons,customize"],
   ]});
 
   let tab = await openTab(ABOUT_NEWTAB_URL);
-  await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
-  await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-button", {}, tab.linkedBrowser);
-  await promiseOnboardingOverlayOpened(tab.linkedBrowser);
+  let browser = tab.linkedBrowser;
+  await promiseOnboardingOverlayLoaded(browser);
+  await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-button", {}, browser);
+  await promiseOnboardingOverlayOpened(browser);
 
-  let doc = gBrowser.contentDocumentAsCPOW;
-  let doms = doc.querySelectorAll(".onboarding-tour-item");
-  is(doms.length, CUSTOM_NEW_TOURs.length, "has exact tour numbers");
-  doms.forEach((dom, idx) => {
-    is(CUSTOM_NEW_TOURs[idx], dom.id, "contain defined onboarding id");
-  });
+  await testTourIDs(browser, CUSTOM_NEW_TOURs);
 
   BrowserTestUtils.removeTab(tab);
 });
 
 add_task(async function test_onboarding_custom_update_tourset() {
   const CUSTOM_UPDATE_TOURs = [
     "onboarding-tour-customize",
     "onboarding-tour-private-browsing",
@@ -63,21 +66,17 @@ add_task(async function test_onboarding_
   await SpecialPowers.pushPrefEnv({set: [
     ["browser.onboarding.tour-type", "update"],
     ["browser.onboarding.tourset-version", 1],
     ["browser.onboarding.seen-tourset-version", 1],
     ["browser.onboarding.updatetour", "customize,private,addons"],
   ]});
 
   let tab = await openTab(ABOUT_NEWTAB_URL);
-  await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
-  await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-button", {}, tab.linkedBrowser);
-  await promiseOnboardingOverlayOpened(tab.linkedBrowser);
+  let browser = tab.linkedBrowser;
+  await promiseOnboardingOverlayLoaded(browser);
+  await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-button", {}, browser);
+  await promiseOnboardingOverlayOpened(browser);
 
-  let doc = gBrowser.contentDocumentAsCPOW;
-  let doms = doc.querySelectorAll(".onboarding-tour-item");
-  is(doms.length, CUSTOM_UPDATE_TOURs.length, "has exact tour numbers");
-  doms.forEach((dom, idx) => {
-    is(CUSTOM_UPDATE_TOURs[idx], dom.id, "contain defined onboarding id");
-  });
+  await testTourIDs(browser, CUSTOM_UPDATE_TOURs);
 
   BrowserTestUtils.removeTab(tab);
 });
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -66,18 +66,18 @@ can reach it easily. -->
 <!ENTITY  sendPageToDevice.label             "Send Page to Device">
 <!ENTITY  sendPageToDevice.accesskey         "n">
 <!ENTITY  sendLinkToDevice.label             "Send Link to Device">
 <!ENTITY  sendLinkToDevice.accesskey         "n">
 <!ENTITY  moveToNewWindow.label              "Move to New Window">
 <!ENTITY  moveToNewWindow.accesskey          "W">
 <!ENTITY  reopenInContainer.label            "Reopen in Container">
 <!ENTITY  reopenInContainer.accesskey        "e">
-<!ENTITY  bookmarkAllTabs.label              "Bookmark All Tabs…">
-<!ENTITY  bookmarkAllTabs.accesskey          "T">
+<!ENTITY  bookmarkTab.label                  "Bookmark Tab">
+<!ENTITY  bookmarkTab.accesskey              "T">
 <!ENTITY  undoCloseTab.label                 "Undo Close Tab">
 <!ENTITY  undoCloseTab.accesskey             "U">
 <!ENTITY  closeTab.label                     "Close Tab">
 <!ENTITY  closeTab.accesskey                 "c">
 <!ENTITY  hiddenTabs.label                   "Hidden Tabs">
 
 <!ENTITY  listAllTabs.label      "List all tabs">
 
@@ -106,18 +106,18 @@ when there are no windows but Firefox is
 <!ENTITY menubarCmd.accesskey "M">
 <!ENTITY navbarCmd.label "Navigation Toolbar">
 <!ENTITY personalbarCmd.label "Bookmarks Toolbar">
 <!ENTITY personalbarCmd.accesskey "B">
 <!ENTITY bookmarksToolbarItem.label "Bookmarks Toolbar Items">
 
 <!ENTITY toolbarContextMenu.reloadAllTabs.label "Reload All Tabs">
 <!ENTITY toolbarContextMenu.reloadAllTabs.accesskey "A">
-<!ENTITY toolbarContextMenu.bookmarkAllTabs.label "Bookmark All Tabs…">
-<!ENTITY toolbarContextMenu.bookmarkAllTabs.accesskey "T">
+<!ENTITY toolbarContextMenu.bookmarkSelectedTabs.label "Bookmark Selected Tabs…">
+<!ENTITY toolbarContextMenu.bookmarkSelectedTabs.accesskey "T">
 <!ENTITY toolbarContextMenu.selectAllTabs.label "Select All Tabs">
 <!ENTITY toolbarContextMenu.selectAllTabs.accesskey "S">
 <!ENTITY toolbarContextMenu.undoCloseTab.label "Undo Close Tab">
 <!ENTITY toolbarContextMenu.undoCloseTab.accesskey "U">
 
 <!ENTITY pageSourceCmd.label "Page Source">
 <!ENTITY pageSourceCmd.accesskey "o">
 <!ENTITY pageSourceCmd.commandkey "u">
--- a/browser/modules/ContentCrashHandlers.jsm
+++ b/browser/modules/ContentCrashHandlers.jsm
@@ -490,16 +490,20 @@ var TabCrashHandler = {
     // how many about:tabcrashed pages exist, so that they
     // can decide whether or not to display the "Restore All
     // Crashed Tabs" button.
     this.pageListener.sendAsyncMessage("UpdateCount", {
       count: this._crashedTabCount,
     });
 
     let browser = message.target.browser;
+    let window = browser.ownerGlobal;
+
+    // Reset the zoom for the tabcrashed page.
+    window.ZoomManager.setZoomForBrowser(browser, 1);
 
     let childID = this.browserMap.get(browser);
     let index = this.unseenCrashedChildIDs.indexOf(childID);
     if (index != -1) {
       this.unseenCrashedChildIDs.splice(index, 1);
     }
 
     let dumpID = this.getDumpID(browser);
--- a/browser/modules/ZoomUI.jsm
+++ b/browser/modules/ZoomUI.jsm
@@ -37,25 +37,24 @@ function onEndSwapDocShells(event) {
   updateZoomUI(event.originalTarget);
 }
 
 function onZoomChange(event) {
   let browser;
   if (event.target.nodeType == event.target.DOCUMENT_NODE) {
     // In non-e10s, the event is dispatched on the contentDocument
     // so we need to jump through some hoops to get to the <xul:browser>.
-    let gBrowser = event.currentTarget.gBrowser;
     let topDoc = event.target.defaultView.top.document;
     if (!topDoc.documentElement) {
       // In some events, such as loading synthetic documents, the
-      // documentElement will be null and getBrowserForDocument will
-      // return null.
+      // documentElement will be null and we won't be able to find
+      // an associated browser.
       return;
     }
-    browser = gBrowser.getBrowserForDocument(topDoc);
+    browser = topDoc.ownerGlobal.docShell.chromeEventHandler;
   } else {
     browser = event.originalTarget;
   }
   updateZoomUI(browser, true);
 }
 
 /**
  * Updates zoom controls.
--- a/build/docs/tup.rst
+++ b/build/docs/tup.rst
@@ -8,17 +8,17 @@ The Tup build backend is an alternative 
 build system <http://gittup.org/tup/>`_ is designed for fast and correct
 incremental builds. A top-level no-op build should be under 2 seconds, and
 clobbers should rarely be required. It is currently only available for Linux
 Desktop builds -- other platforms like Windows or OSX are planned for the
 future.
 
 As part of the mozbuild architecture, the Tup backend shares a significant
 portion of frontend (developer-facing) code in the build system. When using the
-Tup backend, ``mach build`` is still the entry point to run the build system,
+Tup backend, ``./mach build`` is still the entry point to run the build system,
 and moz.build files are still used for the build description. Familiar parts of
 the build system like configure and generating the build files (the
 ``Reticulating splines...`` step) are virtually identical in both backends. The
 difference is that ``mach`` invokes Tup instead of Make under the hood to do
 the actual work of determining what needs to be rebuilt. Tup is able to perform
 this work more efficiently by loading only the parts of the DAG that are
 required for an incremental build. Additionally, Tup instruments processes to
 see what files are read from and written to in order to verify that
@@ -27,40 +27,41 @@ dependencies are correct.
 For more detailed information on the rationale behind Tup, see the `Build
 System Rules and Algorithms
 <http://gittup.org/tup/build_system_rules_and_algorithms.pdf>`_ paper.
 
 Installation
 ============
 
 You'll need to install the Tup executable, as well as the nightly rust/cargo
-toolchain::
+toolchain (Note: Replace $topsrcdir with the path to your mozilla-central
+source tree)::
 
-   cd ~/.mozbuild && mach artifact toolchain --from-build linux64-tup
+   cd ~/.mozbuild && $topsrcdir/mach artifact toolchain --from-build linux64-tup
    rustup install nightly
    rustup default nightly
 
 Configuration
 =============
 
 Your mozconfig needs to describe how to find the executable if it's not in your
 PATH, and enable the Tup backend::
 
    export TUP=~/.mozbuild/tup/tup
    ac_add_options --enable-build-backends=Tup
 
 What Works
 ==========
 
 You should expect a Linux desktop build to generate a working Firefox binary
-from a ``mach build``, and be able to run test suites against it (eg:
+from a ``./mach build``, and be able to run test suites against it (eg:
 mochitests, xpcshell, gtest). Top-level incremental builds should be fast
 enough to use them during a regular compile/edit/test cycle. If you wish to
 stop compilation partway through the build to more quickly iterate on a
-particular file, you can expect ``mach build objdir/path/to/file.o`` to
+particular file, you can expect ``./mach build objdir/path/to/file.o`` to
 correctly produce all inputs required to build file.o before compiling it. For
 example, you don't have to run the build system in various subdirectories to
 get generated headers built in the right order.
 
 Currently Unsupported / Future Work
 ===================================
 
 There are a number of features that you may use in the Make backend that are
--- a/devtools/client/debugger/test/mochitest/browser.ini
+++ b/devtools/client/debugger/test/mochitest/browser.ini
@@ -153,18 +153,16 @@ uses-unsafe-cpows = true
 skip-if = e10s && debug
 [browser_dbg_bug-896139.js]
 uses-unsafe-cpows = true
 skip-if = e10s && debug
 [browser_dbg_chrome-create.js]
 skip-if = (e10s && debug) || (verify && os == "linux") # Exit code mismatch with verify
 [browser_dbg_chrome-debugging.js]
 skip-if = e10s && debug
-[browser_dbg_clean-exit-window.js]
-skip-if = true # Bug 933950 (leaky test)
 [browser_dbg_clean-exit.js]
 skip-if = true # Bug 1044985 (racy test)
 [browser_dbg_closure-inspection.js]
 uses-unsafe-cpows = true
 skip-if = e10s && debug
 [browser_dbg_conditional-breakpoints-01.js]
 uses-unsafe-cpows = true
 skip-if = e10s && debug
--- a/devtools/client/debugger/test/mochitest/browser_dbg_aaa_run_first_leaktest.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_aaa_run_first_leaktest.js
@@ -15,17 +15,17 @@ function test() {
   // Wait longer for this very simple test that comes first, to make sure that
   // GC from previous tests does not interfere with the debugger suite.
   requestLongerTimeout(2);
 
   let options = {
     source: EXAMPLE_URL + "code_script-switching-01.js",
     line: 1
   };
-  initDebugger(TAB_URL, options).then(([aTab,, aPanel]) => {
+  initDebugger(TAB_URL, options).then(([aTab, aPanel]) => {
     ok(aTab, "Should have a tab available.");
     ok(aPanel, "Should have a debugger pane available.");
 
     waitForSourceAndCaretAndScopes(aPanel, "-02.js", 1).then(() => {
       resumeDebuggerThenCloseAndFinish(aPanel);
     });
 
     callInTab(aTab, "firstCall");
--- a/devtools/client/debugger/test/mochitest/browser_dbg_bfcache.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_bfcache.js
@@ -6,43 +6,47 @@
 /**
  * Make sure that the debugger is updated with the correct sources when moving
  * back and forward in the tab.
  */
 
 const TAB_URL_1 = EXAMPLE_URL + "doc_script-switching-01.html";
 const TAB_URL_2 = EXAMPLE_URL + "doc_recursion-stack.html";
 
-var gTab, gDebuggee, gPanel, gDebugger;
+var gTab, gPanel, gDebugger;
 var gSources;
 
 const test = Task.async(function* () {
   info("Starting browser_dbg_bfcache.js's `test`.");
 
   let options = {
     source: EXAMPLE_URL + "code_script-switching-01.js",
     line: 1
   };
-  ([gTab, gDebuggee, gPanel]) = yield initDebugger(TAB_URL_1, options);
+  ([gTab, gPanel]) = yield initDebugger(TAB_URL_1, options);
   gDebugger = gPanel.panelWin;
   gSources = gDebugger.DebuggerView.Sources;
 
   yield testFirstPage();
   yield testLocationChange();
   yield testBack();
   yield testForward();
   return closeDebuggerAndFinish(gPanel);
 });
 
 function testFirstPage() {
   info("Testing first page.");
 
   // Spin the event loop before causing the debuggee to pause, to allow
   // this function to return first.
-  executeSoon(() => gDebuggee.firstCall());
+  executeSoon(() => {
+    ContentTask.spawn(gTab.linkedBrowser, null, async () => {
+      content.wrappedJSObject.firstCall();
+    });
+  });
 
   return waitForSourceAndCaretAndScopes(gPanel, "-02.js", 1)
     .then(validateFirstPage);
 }
 
 function testLocationChange() {
   info("Navigating to a different page.");
 
@@ -83,13 +87,12 @@ function validateSecondPage() {
   is(gSources.itemCount, 1,
     "Found the expected number of sources.");
   ok(gSources.getItemForAttachment(e => e.label == "doc_recursion-stack.html"),
     "Found the single source label.");
 }
 
 registerCleanupFunction(function () {
   gTab = null;
-  gDebuggee = null;
   gPanel = null;
   gDebugger = null;
   gSources = null;
 });
--- a/devtools/client/debugger/test/mochitest/browser_dbg_break-in-anon.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_break-in-anon.js
@@ -10,17 +10,17 @@
 
 const TAB_URL = EXAMPLE_URL + "doc_script-eval.html";
 
 function test() {
   const options = {
     source: EXAMPLE_URL + "code_script-eval.js",
     line: 1
   };
-  initDebugger(TAB_URL, options).then(([aTab,, aPanel]) => {
+  initDebugger(TAB_URL, options).then(([aTab, aPanel]) => {
     const gTab = aTab;
     const gPanel = aPanel;
     const gDebugger = gPanel.panelWin;
     const gSources = gDebugger.DebuggerView.Sources;
 
     return Task.spawn(function* () {
       is(gSources.values.length, 1, "Should have 1 source");
 
--- a/devtools/client/debugger/test/mochitest/browser_dbg_break-on-next-console.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_break-on-next-console.js
@@ -13,17 +13,17 @@ const TAB_URL = EXAMPLE_URL + "doc_scrip
 function test() {
   let gTab, gPanel, gDebugger;
   let gSources, gBreakpoints, gTarget, gResumeButton, gResumeKey, gThreadClient;
 
   let options = {
     source: EXAMPLE_URL + "code_script-eval.js",
     line: 1
   };
-  initDebugger(TAB_URL, options).then(([aTab,, aPanel]) => {
+  initDebugger(TAB_URL, options).then(([aTab, aPanel]) => {
     gTab = aTab;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gSources = gDebugger.DebuggerView.Sources;
     gTarget = gDebugger.gTarget;
     gThreadClient = gDebugger.gThreadClient;
     gResumeButton = gDebugger.document.getElementById("resume");
     gResumeKey = gDebugger.document.getElementById("resumeKey");
--- a/devtools/client/debugger/test/mochitest/browser_dbg_break-on-next.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_break-on-next.js
@@ -13,17 +13,17 @@ const TAB_URL = EXAMPLE_URL + "doc_scrip
 function test() {
   let gTab, gPanel, gDebugger;
   let gSources, gBreakpoints, gTarget, gResumeButton, gResumeKey, gThreadClient;
 
   const options = {
     source: EXAMPLE_URL + "code_script-eval.js",
     line: 1
   };
-  initDebugger(TAB_URL, options).then(([aTab,, aPanel]) => {
+  initDebugger(TAB_URL, options).then(([aTab, aPanel]) => {
     gTab = aTab;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gSources = gDebugger.DebuggerView.Sources;
     gTarget = gDebugger.gTarget;
     gThreadClient = gDebugger.gThreadClient;
     gResumeButton = gDebugger.document.getElementById("resume");
     gResumeKey = gDebugger.document.getElementById("resumeKey");
--- a/devtools/client/debugger/test/mochitest/browser_dbg_breakpoints-editor.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_breakpoints-editor.js
@@ -10,17 +10,17 @@
 
 const TAB_URL = EXAMPLE_URL + "doc_script-switching-01.html";
 
 function test() {
   let options = {
     source: EXAMPLE_URL + "code_script-switching-01.js",
     line: 1
   };
-  initDebugger(TAB_URL, options).then(([aTab,, aPanel]) => {
+  initDebugger(TAB_URL, options).then(([aTab, aPanel]) => {
     const gTab = aTab;
     const gPanel = aPanel;
     const gDebugger = gPanel.panelWin;
     const gEditor = gDebugger.DebuggerView.editor;
     const gSources = gDebugger.DebuggerView.Sources;
     const queries = gDebugger.require("./content/queries");
     const constants = gDebugger.require("./content/constants");
     const actions = bindActionCreators(gPanel);
--- a/devtools/client/debugger/test/mochitest/browser_dbg_breakpoints-eval.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_breakpoints-eval.js
@@ -9,17 +9,17 @@
 
 const TAB_URL = EXAMPLE_URL + "doc_script-eval.html";
 
 function test() {
   let options = {
     source: EXAMPLE_URL + "code_script-eval.js",
     line: 1
   };
-  initDebugger(TAB_URL, options).then(([aTab,, aPanel]) => {
+  initDebugger(TAB_URL, options).then(([aTab, aPanel]) => {
     const gTab = aTab;
     const gPanel = aPanel;
     const gDebugger = gPanel.panelWin;
     const gSources = gDebugger.DebuggerView.Sources;
     const actions = bindActionCreators(gPanel);
 
     Task.spawn(function* () {
       let newSource = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.NEW_SOURCE);
--- a/devtools/client/debugger/test/mochitest/browser_dbg_breakpoints-new-script.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_breakpoints-new-script.js
@@ -10,17 +10,17 @@
 
 const TAB_URL = EXAMPLE_URL + "doc_inline-script.html";
 
 function test() {
   let options = {
     source: TAB_URL,
     line: 1
   };
-  initDebugger(TAB_URL, options).then(([aTab,, aPanel]) => {
+  initDebugger(TAB_URL, options).then(([aTab, aPanel]) => {
     const gTab = aTab;
     const gPanel = aPanel;
     const gDebugger = gPanel.panelWin;
     const gSources = gDebugger.DebuggerView.Sources;
     const queries = gDebugger.require('./content/queries');
     const actions = bindActionCreators(gPanel);
     const getState = gDebugger.DebuggerController.getState;
 
--- a/devtools/client/debugger/test/mochitest/browser_dbg_breakpoints-other-tabs.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_breakpoints-other-tabs.js
@@ -10,18 +10,18 @@
 
 const TAB_URL = EXAMPLE_URL + "doc_breakpoints-other-tabs.html";
 
 var test = Task.async(function* () {
   const options = {
     source: EXAMPLE_URL + "code_breakpoints-other-tabs.js",
     line: 1
   };
-  const [tab1,, panel1] = yield initDebugger(TAB_URL, options);
-  const [tab2,, panel2] = yield initDebugger(TAB_URL, options);
+  const [tab1, panel1] = yield initDebugger(TAB_URL, options);
+  const [tab2, panel2] = yield initDebugger(TAB_URL, options);
   const queries = panel1.panelWin.require("./content/queries");
   const actions = bindActionCreators(panel1);
   const getState = panel1.panelWin.DebuggerController.getState;
 
   const sources = panel1.panelWin.DebuggerView.Sources;
 
   yield actions.addBreakpoint({
     actor: queries.getSelectedSource(getState()).actor,
--- a/devtools/client/debugger/test/mochitest/browser_dbg_bug-896139.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_bug-896139.js
@@ -17,17 +17,17 @@ function test() {
       callInTab(tab, "f");
       return promise.then(() => doResume(panel));
     }
 
     let options = {
       source: SCRIPT_URL,
       line: 1
     };
-    let [tab,, panel] = yield initDebugger(TAB_URL, options);
+    let [tab, panel] = yield initDebugger(TAB_URL, options);
     let win = panel.panelWin;
 
     let Sources = win.DebuggerView.Sources;
 
     yield panel.addBreakpoint({
       actor: getSourceActor(win.DebuggerView.Sources, SCRIPT_URL),
       line: 6
     });
deleted file mode 100644
--- a/devtools/client/debugger/test/mochitest/browser_dbg_clean-exit-window.js
+++ /dev/null
@@ -1,86 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Test that closing a window with the debugger in a paused state exits cleanly.
- */
-
-var gDebuggee, gPanel, gDebugger, gWindow;
-
-const TAB_URL = EXAMPLE_URL + "doc_inline-debugger-statement.html";
-
-function test() {
-  addWindow(TAB_URL)
-    .then(win => initDebugger(TAB_URL, { window: win }))
-    .then(([aTab, aDebuggee, aPanel, aWindow]) => {
-      gDebuggee = aDebuggee;
-      gPanel = aPanel;
-      gDebugger = gPanel.panelWin;
-      gWindow = aWindow;
-
-      return testCleanExit();
-    })
-    .catch(aError => {
-      ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
-    });
-}
-
-function testCleanExit() {
-  let deferred = promise.defer();
-
-  ok(!!gWindow, "Second window created.");
-
-  gWindow.focus();
-
-  is(Services.wm.getMostRecentWindow("navigator:browser"), gWindow,
-    "The second window is on top.");
-
-  let isActive = promise.defer();
-  let isLoaded = promise.defer();
-
-  promise.all([isActive.promise, isLoaded.promise]).then(() => {
-    waitForSourceAndCaretAndScopes(gPanel, ".html", 16).then(() => {
-      is(gDebugger.gThreadClient.paused, true,
-        "Should be paused after the debugger statement.");
-      gWindow.close();
-      deferred.resolve();
-      finish();
-    });
-
-    gDebuggee.runDebuggerStatement();
-  });
-
-  if (Services.focus.activeWindow != gWindow) {
-    gWindow.addEventListener("activate", function onActivate(aEvent) {
-      if (aEvent.target != gWindow) {
-        return;
-      }
-      gWindow.removeEventListener("activate", onActivate, true);
-      isActive.resolve();
-    }, true);
-  } else {
-    isActive.resolve();
-  }
-
-  if (gWindow.content.location.href != TAB_URL) {
-    gWindow.document.addEventListener("load", function onLoad(aEvent) {
-      if (aEvent.target.documentURI != TAB_URL) {
-        return;
-      }
-      gWindow.document.removeEventListener("load", onLoad, true);
-      isLoaded.resolve();
-    }, true);
-  } else {
-    isLoaded.resolve();
-  }
-  return deferred.promise;
-}
-
-registerCleanupFunction(function () {
-  gWindow = null;
-  gDebuggee = null;
-  gPanel = null;
-  gDebugger = null;
-});
--- a/devtools/client/debugger/test/mochitest/browser_dbg_clean-exit.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_clean-exit.js
@@ -11,17 +11,17 @@ var gTab, gPanel, gDebugger;
 
 const TAB_URL = EXAMPLE_URL + "doc_inline-debugger-statement.html";
 
 function test() {
   const options = {
     source: TAB_URL,
     line: 1
   };
-  initDebugger(TAB_URL, options).then(([aTab,, aPanel]) => {
+  initDebugger(TAB_URL, options).then(([aTab, aPanel]) => {
     gTab = aTab;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
 
     testCleanExit();
   });
 }
 
--- a/devtools/client/debugger/test/mochitest/browser_dbg_closure-inspection.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_closure-inspection.js
@@ -9,17 +9,17 @@ const TAB_URL = EXAMPLE_URL + "doc_closu
 
 function test() {
   let gPanel, gTab, gDebugger;
 
   let options = {
     source: TAB_URL,
     line: 1
   };
-  initDebugger(TAB_URL, options).then(([aTab,, aPanel]) => {
+  initDebugger(TAB_URL, options).then(([aTab, aPanel]) => {
     gTab = aTab;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
 
     testClosure()
       .then(() => resumeDebuggerThenCloseAndFinish(gPanel))
       .catch(aError => {
         ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
--- a/devtools/client/debugger/test/mochitest/browser_dbg_conditional-breakpoints-01.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_conditional-breakpoints-01.js
@@ -12,17 +12,17 @@ const TAB_URL = EXAMPLE_URL + "doc_condi
 function test() {
   // Linux debug test slaves are a bit slow at this test sometimes.
   requestLongerTimeout(2);
 
   let options = {
     source: TAB_URL,
     line: 1
   };
-  initDebugger(TAB_URL, options).then(([aTab,, aPanel]) => {
+  initDebugger(TAB_URL, options).then(([aTab, aPanel]) => {
     const gTab = aTab;
     const gPanel = aPanel;
     const gDebugger = gPanel.panelWin;
     const gEditor = gDebugger.DebuggerView.editor;
     const gSources = gDebugger.DebuggerView.Sources;
     const queries = gDebugger.require("./content/queries");
     const constants = gDebugger.require("./content/constants");
     const actions = bindActionCreators(gPanel);
--- a/devtools/client/debugger/test/mochitest/browser_dbg_conditional-breakpoints-02.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_conditional-breakpoints-02.js
@@ -9,17 +9,17 @@
 
 const TAB_URL = EXAMPLE_URL + "doc_conditional-breakpoints.html";
 
 function test() {
   let options = {
     source: TAB_URL,
     line: 1
   };
-  initDebugger(TAB_URL, options).then(([aTab,, aPanel]) => {
+  initDebugger(TAB_URL, options).then(([aTab, aPanel]) => {
     const gTab = aTab;
     const gPanel = aPanel;
     const gDebugger = gPanel.panelWin;
     const gEditor = gDebugger.DebuggerView.editor;
     const gSources = gDebugger.DebuggerView.Sources;
     const queries = gDebugger.require("./content/queries");
     const constants = gDebugger.require("./content/constants");
     const actions = bindActionCreators(gPanel);
--- a/devtools/client/debugger/test/mochitest/browser_dbg_conditional-breakpoints-03.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_conditional-breakpoints-03.js
@@ -5,17 +5,17 @@
 
 /**
  * Tests that conditional breakpoint expressions survive disabled breakpoints.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_conditional-breakpoints.html";
 
 function test() {
-  initDebugger().then(([aTab,, aPanel]) => {
+  initDebugger().then(([aTab, aPanel]) => {
     const gTab = aTab;
     const gPanel = aPanel;
     const gDebugger = gPanel.panelWin;
     const gSources = gDebugger.DebuggerView.Sources;
     const queries = gDebugger.require("./content/queries");
     const constants = gDebugger.require("./content/constants");
     const actions = bindActionCreators(gPanel);
     const getState = gDebugger.DebuggerController.getState;
--- a/devtools/client/debugger/test/mochitest/browser_dbg_conditional-breakpoints-04.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_conditional-breakpoints-04.js
@@ -10,17 +10,17 @@
 
 const TAB_URL = EXAMPLE_URL + "doc_conditional-breakpoints.html";
 
 function test() {
   let options = {
     source: TAB_URL,
     line: 1
   };
-  initDebugger(TAB_URL, options).then(([aTab,, aPanel]) => {
+  initDebugger(TAB_URL, options).then(([aTab, aPanel]) => {
     const gTab = aTab;
     const gPanel = aPanel;
     const gDebugger = gPanel.panelWin;
     const gSources = gDebugger.DebuggerView.Sources;
     const queries = gDebugger.require("./content/queries");
     const constants = gDebugger.require("./content/constants");
     const actions = bindActionCreators(gPanel);
     const getState = gDebugger.DebuggerController.getState;
--- a/devtools/client/debugger/test/mochitest/browser_dbg_conditional-breakpoints-05.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_conditional-breakpoints-05.js
@@ -10,17 +10,17 @@
 
 const TAB_URL = EXAMPLE_URL + "doc_conditional-breakpoints.html";
 
 function test() {
   let options = {
     source: TAB_URL,
     line: 1
   };
-  initDebugger(TAB_URL, options).then(([aTab,, aPanel]) => {
+  initDebugger(TAB_URL, options).then(([aTab, aPanel]) => {
     const gTab = aTab;
     const gPanel = aPanel;
     const gDebugger = gPanel.panelWin;
     const gEditor = gDebugger.DebuggerView.editor;
     const gSources = gDebugger.DebuggerView.Sources;
     const queries = gDebugger.require("./content/queries");
     const constants = gDebugger.require("./content/constants");
     const actions = bindActionCreators(gPanel);
--- a/devtools/client/debugger/test/mochitest/browser_dbg_console-eval.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_console-eval.js
@@ -6,17 +6,17 @@
 /**
  * Breaking in the middle of a script evaluated by the console should
  * work
  */
 
 function test() {
   Task.spawn(function* () {
     let TAB_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
-    let [,, panel] = yield initDebugger(TAB_URL, { source: null });
+    let [, panel] = yield initDebugger(TAB_URL, { source: null });
     let dbgWin = panel.panelWin;
     let sources = dbgWin.DebuggerView.Sources;
     let frames = dbgWin.DebuggerView.StackFrames;
     let editor = dbgWin.DebuggerView.editor;
     let toolbox = gDevTools.getToolbox(panel.target);
 
     let paused = promise.all([
       waitForEditorEvents(panel, "cursorActivity"),
--- a/devtools/client/debugger/test/mochitest/browser_dbg_console-named-eval.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_console-named-eval.js
@@ -9,17 +9,17 @@
  */
 
 function test() {
   Task.spawn(runTests);
 }
 
 function* runTests() {
   let TAB_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
-  let [,, panel] = yield initDebugger(TAB_URL, { source: null });
+  let [, panel] = yield initDebugger(TAB_URL, { source: null });
   let dbgWin = panel.panelWin;
   let sources = dbgWin.DebuggerView.Sources;
   let frames = dbgWin.DebuggerView.StackFrames;
   let editor = dbgWin.DebuggerView.editor;
   let toolbox = gDevTools.getToolbox(panel.target);
 
   let paused = waitForSourceAndCaretAndScopes(panel, "foo.js", 1);
 
--- a/devtools/client/debugger/test/mochitest/browser_dbg_controller-evaluate-01.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_controller-evaluate-01.js
@@ -10,17 +10,17 @@
 const TAB_URL = EXAMPLE_URL + "doc_script-switching-01.html";
 
 function test() {
   Task.spawn(function* () {
     const options = {
       source: EXAMPLE_URL + "code_script-switching-01.js",
       line: 1
     };
-    const [tab,, panel] = yield initDebugger(TAB_URL, options);
+    const [tab, panel] = yield initDebugger(TAB_URL, options);
     const win = panel.panelWin;
     const frames = win.DebuggerController.StackFrames;
     const framesView = win.DebuggerView.StackFrames;
     const sourcesView = win.DebuggerView.Sources;
     const editorView = win.DebuggerView.editor;
     const events = win.EVENTS;
     const queries = win.require("./content/queries");
     const constants = win.require("./content/constants");
--- a/devtools/client/debugger/test/mochitest/browser_dbg_controller-evaluate-02.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_controller-evaluate-02.js
@@ -10,17 +10,17 @@
 const TAB_URL = EXAMPLE_URL + "doc_script-switching-01.html";
 
 function test() {
   Task.spawn(function* () {
     const options = {
       source: EXAMPLE_URL + "code_script-switching-01.js",
       line: 1
     };
-    const [tab,, panel] = yield initDebugger(TAB_URL, options);
+    const [tab, panel] = yield initDebugger(TAB_URL, options);
     const win = panel.panelWin;
     const frames = win.DebuggerController.StackFrames;
     const framesView = win.DebuggerView.StackFrames;
     const sourcesView = win.DebuggerView.Sources;
     const editorView = win.DebuggerView.editor;
     const events = win.EVENTS;
     const queries = win.require("./content/queries");
     const constants = win.require("./content/constants");
--- a/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-01.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-01.js
@@ -4,142 +4,129 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that the eventListeners request works.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_event-listeners-01.html";
 
-var gClient;
-var gTab;
-
-function test() {
+add_task(async function() {
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
 
   let transport = DebuggerServer.connectPipe();
-  gClient = new DebuggerClient(transport);
-  gClient.connect().then(([aType, aTraits]) => {
-    is(aType, "browser",
-      "Root actor should identify itself as a browser.");
+  let client = new DebuggerClient(transport);
+  let [type, traits] = await client.connect();
+
+  Assert.equal(type, "browser",
+    "Root actor should identify itself as a browser.");
 
-    addTab(TAB_URL)
-      .then((aTab) => {
-        gTab = aTab;
-        return attachThreadActorForUrl(gClient, TAB_URL);
-      })
-      .then(pauseDebuggee)
-      .then(testEventListeners)
-      .then(() => gClient.close())
-      .then(finish)
-      .catch(aError => {
-        ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
-      });
-  });
-}
+  let tab = await addTab(TAB_URL);
+  let threadClient = await attachThreadActorForUrl(client, TAB_URL);
+  await pauseDebuggee(tab, client, threadClient);
+  await testEventListeners(client, threadClient);
 
-function pauseDebuggee(aThreadClient) {
+  await client.close();
+});
+
+function pauseDebuggee(aTab, aClient, aThreadClient) {
   let deferred = promise.defer();
 
-  gClient.addOneTimeListener("paused", (aEvent, aPacket) => {
+  aClient.addOneTimeListener("paused", (aEvent, aPacket) => {
     is(aPacket.type, "paused",
       "We should now be paused.");
     is(aPacket.why.type, "debuggerStatement",
       "The debugger statement was hit.");
 
     deferred.resolve(aThreadClient);
   });
 
-  generateMouseClickInTab(gTab, "content.document.querySelector('button')");
+  generateMouseClickInTab(aTab, "content.document.querySelector('button')");
 
   return deferred.promise;
 }
 
-function testEventListeners(aThreadClient) {
-  let deferred = promise.defer();
+async function testEventListeners(aClient, aThreadClient) {
+  let packet = await aThreadClient.eventListeners();
+
+  if (packet.error) {
+    let msg = "Error getting event listeners: " + packet.message;
+    Assert.ok(false, msg);
+    return;
+  }
+
+  is(packet.listeners.length, 3, "Found all event listeners.");
 
-  aThreadClient.eventListeners(aPacket => {
-    if (aPacket.error) {
-      let msg = "Error getting event listeners: " + aPacket.message;
-      ok(false, msg);
-      deferred.reject(msg);
-      return;
+  let listeners = await promise.all(packet.listeners.map(listener => {
+    const lDeferred = promise.defer();
+    aThreadClient.pauseGrip(listener.function).getDefinitionSite(aResponse => {
+      if (aResponse.error) {
+        const msg = "Error getting function definition site: " + aResponse.message;
+        ok(false, msg);
+        lDeferred.reject(msg);
+        return;
+      }
+      listener.function.url = aResponse.source.url;
+      lDeferred.resolve(listener);
+    });
+    return lDeferred.promise;
+  }));
+
+  let types = [];
+
+  for (let l of listeners) {
+    info("Listener for the " + l.type + " event.");
+    let node = l.node;
+    ok(node, "There is a node property.");
+    ok(node.object, "There is a node object property.");
+    if (node.selector != "window") {
+      let nodeCount =
+        await ContentTask.spawn(gBrowser.selectedBrowser, node.selector, async (selector) => {
+          return content.document.querySelectorAll(selector).length;
+        });
+      Assert.equal(nodeCount, 1, "The node property is a unique CSS selector.");
+    } else {
+      Assert.ok(true, "The node property is a unique CSS selector.");
     }
 
-    is(aPacket.listeners.length, 3,
-      "Found all event listeners.");
-
-    promise.all(aPacket.listeners.map(listener => {
-      const lDeferred = promise.defer();
-      aThreadClient.pauseGrip(listener.function).getDefinitionSite(aResponse => {
-        if (aResponse.error) {
-          const msg = "Error getting function definition site: " + aResponse.message;
-          ok(false, msg);
-          lDeferred.reject(msg);
-          return;
-        }
-        listener.function.url = aResponse.source.url;
-        lDeferred.resolve(listener);
-      });
-      return lDeferred.promise;
-    })).then(listeners => {
-      let types = [];
+    let func = l.function;
+    ok(func, "There is a function property.");
+    is(func.type, "object", "The function form is of type 'object'.");
+    is(func.class, "Function", "The function form is of class 'Function'.");
 
-      for (let l of listeners) {
-        info("Listener for the " + l.type + " event.");
-        let node = l.node;
-        ok(node, "There is a node property.");
-        ok(node.object, "There is a node object property.");
-        ok(node.selector == "window" ||
-          gBrowser.contentDocumentAsCPOW.querySelectorAll(node.selector).length == 1,
-          "The node property is a unique CSS selector.");
+    // The onchange handler is an inline string that doesn't have
+    // a URL because it's basically eval'ed
+    if (l.type !== "change") {
+      is(func.url, TAB_URL, "The function url is correct.");
+    }
 
-        let func = l.function;
-        ok(func, "There is a function property.");
-        is(func.type, "object", "The function form is of type 'object'.");
-        is(func.class, "Function", "The function form is of class 'Function'.");
+    is(l.allowsUntrusted, true,
+      "'allowsUntrusted' property has the right value.");
+    is(l.inSystemEventGroup, false,
+      "'inSystemEventGroup' property has the right value.");
 
-        // The onchange handler is an inline string that doesn't have
-        // a URL because it's basically eval'ed
-        if (l.type !== "change") {
-          is(func.url, TAB_URL, "The function url is correct.");
-        }
+    types.push(l.type);
 
-        is(l.allowsUntrusted, true,
-          "'allowsUntrusted' property has the right value.");
-        is(l.inSystemEventGroup, false,
-          "'inSystemEventGroup' property has the right value.");
-
-        types.push(l.type);
+    if (l.type == "keyup") {
+      is(l.capturing, true,
+        "Capturing property has the right value.");
+      is(l.isEventHandler, false,
+        "'isEventHandler' property has the right value.");
+    } else if (l.type == "load") {
+      is(l.capturing, false,
+        "Capturing property has the right value.");
+      is(l.isEventHandler, false,
+        "'isEventHandler' property has the right value.");
+    } else {
+      is(l.capturing, false,
+        "Capturing property has the right value.");
+      is(l.isEventHandler, true,
+        "'isEventHandler' property has the right value.");
+    }
+  }
 
-        if (l.type == "keyup") {
-          is(l.capturing, true,
-            "Capturing property has the right value.");
-          is(l.isEventHandler, false,
-            "'isEventHandler' property has the right value.");
-        } else if (l.type == "load") {
-          is(l.capturing, false,
-            "Capturing property has the right value.");
-          is(l.isEventHandler, false,
-            "'isEventHandler' property has the right value.");
-        } else {
-          is(l.capturing, false,
-            "Capturing property has the right value.");
-          is(l.isEventHandler, true,
-            "'isEventHandler' property has the right value.");
-        }
-      }
+  Assert.ok(types.includes("click"), "Found the click handler.");
+  Assert.ok(types.includes("change"), "Found the change handler.");
+  Assert.ok(types.includes("keyup"), "Found the keyup handler.");
 
-      ok(types.includes("click"), "Found the click handler.");
-      ok(types.includes("change"), "Found the change handler.");
-      ok(types.includes("keyup"), "Found the keyup handler.");
-
-      aThreadClient.resume(deferred.resolve);
-    });
-  });
-
-  return deferred.promise;
+  await aThreadClient.resume();
 }
-
-registerCleanupFunction(function () {
-  gClient = null;
-});
--- a/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-02.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-02.js
@@ -5,117 +5,107 @@
 
 /**
  * Tests that the eventListeners request works when bound functions are used as
  * event listeners.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_event-listeners-03.html";
 
-var gClient;
-var gTab;
 
-function test() {
+add_task(async function() {
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
 
   let transport = DebuggerServer.connectPipe();
-  gClient = new DebuggerClient(transport);
-  gClient.connect().then(([aType, aTraits]) => {
-    is(aType, "browser",
-      "Root actor should identify itself as a browser.");
+  let client = new DebuggerClient(transport);
+  let [type, traits] = await client.connect();
+
+  Assert.equal(type, "browser",
+    "Root actor should identify itself as a browser.");
 
-    addTab(TAB_URL)
-      .then((aTab) => {
-        gTab = aTab;
-        return attachThreadActorForUrl(gClient, TAB_URL);
-      })
-      .then(pauseDebuggee)
-      .then(testEventListeners)
-      .then(() => gClient.close())
-      .then(finish)
-      .catch(aError => {
-        ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
-      });
-  });
-}
+  let tab = await addTab(TAB_URL);
+  let threadClient = await attachThreadActorForUrl(client, TAB_URL);
+  await pauseDebuggee(tab, client, threadClient);
+  await testEventListeners(client, threadClient);
+  await client.close();
+});
 
-function pauseDebuggee(aThreadClient) {
+function pauseDebuggee(aTab, aClient, aThreadClient) {
   let deferred = promise.defer();
 
-  gClient.addOneTimeListener("paused", (aEvent, aPacket) => {
+  aClient.addOneTimeListener("paused", (aEvent, aPacket) => {
     is(aPacket.type, "paused",
       "We should now be paused.");
     is(aPacket.why.type, "debuggerStatement",
       "The debugger statement was hit.");
 
     deferred.resolve(aThreadClient);
   });
 
-  generateMouseClickInTab(gTab, "content.document.querySelector('button')");
+  generateMouseClickInTab(aTab, "content.document.querySelector('button')");
 
   return deferred.promise;
 }
 
-function testEventListeners(aThreadClient) {
-  let deferred = promise.defer();
+async function testEventListeners(aClient, aThreadClient) {
+  let packet = await aThreadClient.eventListeners();
+
+  if (packet.error) {
+    let msg = "Error getting event listeners: " + aPacket.message;
+    ok(false, msg);
+    return;
+  }
+
+  is(packet.listeners.length, 3,
+    "Found all event listeners.");
 
-  aThreadClient.eventListeners(aPacket => {
-    if (aPacket.error) {
-      let msg = "Error getting event listeners: " + aPacket.message;
-      ok(false, msg);
-      deferred.reject(msg);
-      return;
+  let listeners = await promise.all(packet.listeners.map(listener => {
+    const lDeferred = promise.defer();
+    aThreadClient.pauseGrip(listener.function).getDefinitionSite(aResponse => {
+      if (aResponse.error) {
+        const msg = "Error getting function definition site: " + aResponse.message;
+        ok(false, msg);
+        lDeferred.reject(msg);
+        return;
+      }
+      listener.function.url = aResponse.source.url;
+      lDeferred.resolve(listener);
+    });
+    return lDeferred.promise;
+  }));
+
+  Assert.equal(listeners.length, 3, "Found three event listeners.");
+
+  for (let l of listeners) {
+    let node = l.node;
+    ok(node, "There is a node property.");
+    ok(node.object, "There is a node object property.");
+
+    if (node.selector != "window") {
+      let nodeCount =
+        await ContentTask.spawn(gBrowser.selectedBrowser, node.selector, async (selector) => {
+          return content.document.querySelectorAll(selector).length;
+        });
+      Assert.equal(nodeCount, 1, "The node property is a unique CSS selector.");
+    } else {
+      Assert.ok(true, "The node property is a unique CSS selector.");
     }
 
-    is(aPacket.listeners.length, 3,
-      "Found all event listeners.");
-
-    promise.all(aPacket.listeners.map(listener => {
-      const lDeferred = promise.defer();
-      aThreadClient.pauseGrip(listener.function).getDefinitionSite(aResponse => {
-        if (aResponse.error) {
-          const msg = "Error getting function definition site: " + aResponse.message;
-          ok(false, msg);
-          lDeferred.reject(msg);
-          return;
-        }
-        listener.function.url = aResponse.source.url;
-        lDeferred.resolve(listener);
-      });
-      return lDeferred.promise;
-    })).then(listeners => {
-      is(listeners.length, 3, "Found three event listeners.");
-      for (let l of listeners) {
-        let node = l.node;
-        ok(node, "There is a node property.");
-        ok(node.object, "There is a node object property.");
-        ok(node.selector == "window" ||
-          gBrowser.contentDocumentAsCPOW.querySelectorAll(node.selector).length == 1,
-          "The node property is a unique CSS selector.");
+    let func = l.function;
+    ok(func, "There is a function property.");
+    is(func.type, "object", "The function form is of type 'object'.");
+    is(func.class, "Function", "The function form is of class 'Function'.");
+    is(func.url, TAB_URL, "The function url is correct.");
 
-        let func = l.function;
-        ok(func, "There is a function property.");
-        is(func.type, "object", "The function form is of type 'object'.");
-        is(func.class, "Function", "The function form is of class 'Function'.");
-        is(func.url, TAB_URL, "The function url is correct.");
+    is(l.type, "click", "This is a click event listener.");
+    is(l.allowsUntrusted, true,
+      "'allowsUntrusted' property has the right value.");
+    is(l.inSystemEventGroup, false,
+      "'inSystemEventGroup' property has the right value.");
+    is(l.isEventHandler, false,
+      "'isEventHandler' property has the right value.");
+    is(l.capturing, false,
+      "Capturing property has the right value.");
+  }
 
-        is(l.type, "click", "This is a click event listener.");
-        is(l.allowsUntrusted, true,
-          "'allowsUntrusted' property has the right value.");
-        is(l.inSystemEventGroup, false,
-          "'inSystemEventGroup' property has the right value.");
-        is(l.isEventHandler, false,
-          "'isEventHandler' property has the right value.");
-        is(l.capturing, false,
-          "Capturing property has the right value.");
-      }
-
-      aThreadClient.resume(deferred.resolve);
-    });
-  });
-
-  return deferred.promise;
+  await aThreadClient.resume();
 }
-
-registerCleanupFunction(function () {
-  gClient = null;
-});
--- a/devtools/client/debugger/test/mochitest/browser_dbg_file-reload.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_file-reload.js
@@ -10,17 +10,17 @@
 const TAB_URL = EXAMPLE_URL + "doc_random-javascript.html";
 const JS_URL = EXAMPLE_URL + "sjs_random-javascript.sjs";
 
 function test() {
   let options = {
     source: JS_URL,
     line: 1
   };
-  initDebugger(TAB_URL, options).then(([aTab,, aPanel]) => {
+  initDebugger(TAB_URL, options).then(([aTab, aPanel]) => {
     const gPanel = aPanel;
     const gDebugger = aPanel.panelWin;
     const gEditor = gDebugger.DebuggerView.editor;
     const gSources = gDebugger.DebuggerView.Sources;
     const queries = gDebugger.require("./content/queries");
     const constants = gDebugger.require("./content/constants");
     const actions = bindActionCreators(gPanel);
     const getState = gDebugger.DebuggerController.getState;
--- a/devtools/client/debugger/test/mochitest/browser_dbg_global-method-override.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_global-method-override.js
@@ -11,16 +11,16 @@
 
 const TAB_URL = EXAMPLE_URL + "doc_global-method-override.html";
 
 function test() {
   let options = {
     source: TAB_URL,
     line: 1
   };
-  initDebugger(TAB_URL, options).then(([aTab,, aPanel]) => {
+  initDebugger(TAB_URL, options).then(([aTab, aPanel]) => {
     let gDebugger = aPanel.panelWin;
     ok(gDebugger, "Should have a debugger available.");
     is(gDebugger.gThreadClient.state, "attached", "Debugger should be attached.");
 
     closeDebuggerAndFinish(aPanel);
   });
 }
--- a/devtools/client/debugger/test/mochitest/browser_dbg_host-layout.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_host-layout.js
@@ -29,17 +29,17 @@ function test() {
 }
 
 async function testHosts(aHostTypes, aLayoutTypes) {
   let [firstHost, secondHost, thirdHost] = aHostTypes;
   let [firstLayout, secondLayout, thirdLayout] = aLayoutTypes;
 
   Services.prefs.setCharPref("devtools.toolbox.host", getHost(firstHost));
 
-  let [tab, debuggee, panel] = await initDebugger();
+  let [tab, panel] = await initDebugger();
   if (getHost(firstHost) === "window") {
     await resizeToolboxWindow(panel, firstHost);
   }
 
   await testHost(panel, getHost(firstHost), firstLayout);
   await switchAndTestHost(tab, panel, secondHost, secondLayout);
   await switchAndTestHost(tab, panel, thirdHost, thirdLayout);
   await teardown(panel);
--- a/devtools/client/debugger/test/mochitest/browser_dbg_iframes.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_iframes.js
@@ -5,29 +5,27 @@
 
 /**
  * Tests that iframes can be added as debuggees.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_iframes.html";
 
 function test() {
-  let gTab, gDebuggee, gPanel, gDebugger;
-  let gIframe, gEditor, gSources, gFrames;
+  let gTab, gPanel, gDebugger;
+  let gEditor, gSources, gFrames;
 
   let options = {
     source: EXAMPLE_URL + "doc_inline-debugger-statement.html",
     line: 1
   };
-  initDebugger(TAB_URL, options).then(([aTab, aDebuggee, aPanel]) => {
+  initDebugger(TAB_URL, options).then(([aTab, aPanel]) => {
     gTab = aTab;
-    gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
-    gIframe = gDebuggee.frames[0];
     gEditor = gDebugger.DebuggerView.editor;
     gSources = gDebugger.DebuggerView.Sources;
     gFrames = gDebugger.DebuggerView.StackFrames;
 
     checkIframeSource();
     checkIframePause()
       .then(() => resumeDebuggerThenCloseAndFinish(gPanel))
       .catch(aError => {
@@ -52,17 +50,21 @@ function test() {
       "The currently selected source value is incorrect (0).");
     is(gSources.selectedValue, gSources.values[0],
       "The currently selected source value is incorrect (1).");
   }
 
   function checkIframePause() {
     // Spin the event loop before causing the debuggee to pause, to allow
     // this function to return first.
-    executeSoon(() => gIframe.runDebuggerStatement());
+    executeSoon(() => {
+      ContentTask.spawn(gTab.linkedBrowser, null, async () => {
+        content.frames[0].wrappedJSObject.runDebuggerStatement()
+      });
+    });
 
     return waitForCaretAndScopes(gPanel, 16).then(() => {
       is(gDebugger.gThreadClient.paused, true,
         "Should be paused after an interrupt request.");
 
       ok(isCaretPos(gPanel, 16),
         "The source editor caret position was incorrect.");
       is(gFrames.itemCount, 1,
--- a/devtools/client/debugger/test/mochitest/browser_dbg_interrupts.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_interrupts.js
@@ -12,17 +12,17 @@ const TAB_URL = EXAMPLE_URL + "doc_scrip
 function test() {
   let gTab, gPanel, gDebugger;
   let gSources, gBreakpoints, gTarget, gResumeButton, gResumeKey, gThreadClient;
 
   let options = {
     source: EXAMPLE_URL + "code_script-switching-01.js",
     line: 1
   };
-  initDebugger(TAB_URL, options).then(([aTab,, aPanel]) => {
+  initDebugger(TAB_URL, options).then(([aTab, aPanel]) => {
     gTab = aTab;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gSources = gDebugger.DebuggerView.Sources;
     gBreakpoints = gDebugger.DebuggerController.Breakpoints;
     gTarget = gDebugger.gTarget;
     gThreadClient = gDebugger.gThreadClient;
     gResumeButton = gDebugger.document.getElementById("resume");
--- a/devtools/client/debugger/test/mochitest/browser_dbg_jump-to-function-definition.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_jump-to-function-definition.js
@@ -13,17 +13,17 @@ const SCRIPT_URI = EXAMPLE_URL + "code_f
 
 function test() {
   let gTab, gPanel, gDebugger, gSources;
 
   let options = {
     source: EXAMPLE_URL + "code_function-jump-01.js",
     line: 1
   };
-  initDebugger(TAB_URL, options).then(([aTab,, aPanel]) => {
+  initDebugger(TAB_URL, options).then(([aTab, aPanel]) => {
     gTab = aTab;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gSources = gDebugger.DebuggerView.Sources;
 
     jumpToFunctionDefinition()
       .then(() => closeDebuggerAndFinish(gPanel))
       .catch(aError => {
--- a/devtools/client/debugger/test/mochitest/browser_dbg_on-pause-raise.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_on-pause-raise.js
@@ -9,17 +9,17 @@
 
 const TAB_URL = EXAMPLE_URL + "doc_recursion-stack.html";
 
 add_task(async function() {
   let options = {
     source: TAB_URL,
     line: 1
   };
-  let [tab,, panel] = await initDebugger(TAB_URL, options);
+  let [tab, panel] = await initDebugger(TAB_URL, options);
   let panelWin = panel.panelWin;
   let toolbox = panel._toolbox;
   let toolboxTab = toolbox.doc.getElementById("toolbox-tab-jsdebugger");
 
   let newTab = await addTab(TAB_URL);
   isnot(newTab, tab,
     "The newly added tab is different from the debugger's tab.");
   is(gBrowser.selectedTab, newTab,
--- a/devtools/client/debugger/test/mochitest/browser_dbg_optimized-out-vars.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_optimized-out-vars.js
@@ -9,17 +9,17 @@ function test() {
   Task.spawn(function* () {
     const TAB_URL = EXAMPLE_URL + "doc_closure-optimized-out.html";
     let gDebugger, sources;
 
     let options = {
       source: TAB_URL,
       line: 1
     };
-    let [tab,, panel] = yield initDebugger(TAB_URL, options);
+    let [tab, panel] = yield initDebugger(TAB_URL, options);
     gDebugger = panel.panelWin;
     sources = gDebugger.DebuggerView.Sources;
 
     yield panel.addBreakpoint({ actor: sources.values[0],
                                 line: 18 });
     yield ensureThreadClientState(panel, "resumed");
 
     // Spin the event loop before causing the debuggee to pause, to allow
--- a/devtools/client/debugger/test/mochitest/browser_dbg_pause-resume.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_pause-resume.js
@@ -12,17 +12,17 @@ const TAB_URL = EXAMPLE_URL + "doc_pause
 var gTab, gPanel, gDebugger;
 var gResumeButton, gResumeKey, gFrames;
 
 function test() {
   let options = {
     source: TAB_URL,
     line: 1
   };
-  initDebugger(TAB_URL, options).then(([aTab,, aPanel]) => {
+  initDebugger(TAB_URL, options).then(([aTab, aPanel]) => {
     gTab = aTab;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gResumeButton = gDebugger.document.getElementById("resume");
     gResumeKey = gDebugger.document.getElementById("resumeKey");
     gFrames = gDebugger.DebuggerView.StackFrames;
 
     testPause();
--- a/devtools/client/debugger/test/mochitest/browser_dbg_pause-warning.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_pause-warning.js
@@ -12,17 +12,17 @@ const TAB_URL = EXAMPLE_URL + "doc_inlin
 var gTab, gPanel, gDebugger;
 var gTarget, gToolbox;
 
 function test() {
   let options = {
     source: TAB_URL,
     line: 1
   };
-  initDebugger(TAB_URL, options).then(([aTab,, aPanel]) => {
+  initDebugger(TAB_URL, options).then(([aTab, aPanel]) => {
     gTab = aTab;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gTarget = gPanel.target;
     gToolbox = gPanel._toolbox;
 
     testPause();
   });
--- a/devtools/client/debugger/test/mochitest/browser_dbg_paused-keybindings.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_paused-keybindings.js
@@ -10,17 +10,17 @@ function test() {
   Task.spawn(function* () {
     const TAB_URL = EXAMPLE_URL + "doc_inline-script.html";
     let gDebugger, searchBox;
 
     let options = {
       source: TAB_URL,
       line: 1
     };
-    let [tab, debuggee, panel] = yield initDebugger(TAB_URL, options);
+    let [tab, panel] = yield initDebugger(TAB_URL, options);
     gDebugger = panel.panelWin;
     searchBox = gDebugger.DebuggerView.Filtering._searchbox;
 
     let onCaretUpdated = ensureCaretAt(panel, 20, 1, true);
     let onThreadPaused = ensureThreadClientState(panel, "paused");
     ContentTask.spawn(tab.linkedBrowser, {}, function* () {
       content.document.querySelector("button").click();
     });
--- a/devtools/client/debugger/test/mochitest/browser_dbg_post-page.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_post-page.js
@@ -16,17 +16,17 @@ const POST_CONTENT = "<script>\"POST\";<
 add_task(async function() {
   // Disable rcwn to make cache behavior deterministic.
   await pushPref("network.http.rcwn.enabled", false);
 
   let options = {
     source: TAB_URL,
     line: 1
   };
-  let [tab,, panel] = await initDebugger(TAB_URL, options);
+  let [tab, panel] = await initDebugger(TAB_URL, options);
   let win = panel.panelWin;
   let editor = win.DebuggerView.editor;
   let queries = win.require("./content/queries");
   let getState = win.DebuggerController.getState;
 
   let source = queries.getSelectedSource(getState());
 
   is(queries.getSourceCount(getState()), 1,
--- a/devtools/client/debugger/test/mochitest/browser_dbg_progress-listener-bug.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_progress-listener-bug.js
@@ -15,17 +15,17 @@ const TAB_URL = EXAMPLE_URL + "doc_inlin
 
 function test() {
   installListener();
 
   let options = {
     source: TAB_URL,
     line: 1
   };
-  initDebugger(TAB_URL, options).then(([aTab,, aPanel]) => {
+  initDebugger(TAB_URL, options).then(([aTab, aPanel]) => {
     gTab = aTab;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
 
     is(!!gDebugger.DebuggerController._startup, true,
       "Controller should be initialized after starting the test.");
 
     testPause();
--- a/devtools/client/debugger/test/mochitest/browser_dbg_promises-allocation-stack.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_promises-allocation-stack.js
@@ -17,17 +17,17 @@ function test() {
   Task.spawn(function* () {
     DebuggerServer.init();
     DebuggerServer.registerAllActors();
 
     let options = {
       source: TAB_URL,
       line: 1
     };
-    const [ tab,, panel ] = yield initDebugger(TAB_URL, options);
+    const [ tab, panel ] = yield initDebugger(TAB_URL, options);
 
     let client = new DebuggerClient(DebuggerServer.connectPipe());
     yield connect(client);
 
     let { tabs } = yield listTabs(client);
     let targetTab = findTab(tabs, TAB_URL);
     yield attachTarget(client, targetTab);
 
--- a/devtools/client/debugger/test/mochitest/browser_dbg_promises-fulfillment-stack.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_promises-fulfillment-stack.js
@@ -35,17 +35,17 @@ function test() {
   Task.spawn(function* () {
     DebuggerServer.init();
     DebuggerServer.registerAllActors();
 
     let options = {
       source: TAB_URL,
       line: 1
     };
-    const [ tab,, panel ] = yield initDebugger(TAB_URL, options);
+    const [ tab, panel ] = yield initDebugger(TAB_URL, options);
 
     let client = new DebuggerClient(DebuggerServer.connectPipe());
     yield connect(client);
 
     let { tabs } = yield listTabs(client);
     let targetTab = findTab(tabs, TAB_URL);
     yield attachTarget(client, targetTab);
 
--- a/devtools/client/debugger/test/mochitest/browser_dbg_promises-rejection-stack.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_promises-rejection-stack.js
@@ -42,17 +42,17 @@ function test() {
   Task.spawn(function* () {
     DebuggerServer.init();
     DebuggerServer.registerAllActors();
 
     let options = {
       source: TAB_URL,
       line: 1
     };
-    const [ tab,, panel ] = yield initDebugger(TAB_URL, options);
+    const [ tab, panel ] = yield initDebugger(TAB_URL, options);
 
     let client = new DebuggerClient(DebuggerServer.connectPipe());
     yield connect(client);
 
     let { tabs } = yield listTabs(client);
     let targetTab = findTab(tabs, TAB_URL);
     yield attachTarget(client, targetTab);
 
--- a/devtools/client/debugger/test/mochitest/browser_dbg_server-conditional-bp-01.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_server-conditional-bp-01.js
@@ -12,17 +12,17 @@ const TAB_URL = EXAMPLE_URL + "doc_condi
 function test() {
   // Linux debug test slaves are a bit slow at this test sometimes.
   requestLongerTimeout(2);
 
   let options = {
     source: TAB_URL,
     line: 1
   };
-  initDebugger(TAB_URL, options).then(([aTab,, aPanel]) => {
+  initDebugger(TAB_URL, options).then(([aTab, aPanel]) => {
     const gTab = aTab;
     const gPanel = aPanel;
     const gDebugger = gPanel.panelWin;
     const gEditor = gDebugger.DebuggerView.editor;
     const gSources = gDebugger.DebuggerView.Sources;
     const queries = gDebugger.require("./content/queries");
     const constants = gDebugger.require("./content/constants");
     const actions = bindActionCreators(gPanel);
--- a/devtools/client/debugger/test/mochitest/browser_dbg_server-conditional-bp-02.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_server-conditional-bp-02.js
@@ -9,17 +9,17 @@
 
 const TAB_URL = EXAMPLE_URL + "doc_conditional-breakpoints.html";
 
 function test() {
   let options = {
     source: TAB_URL,
     line: 1
   };
-  initDebugger(TAB_URL, options).then(([aTab,, aPanel]) => {
+  initDebugger(TAB_URL, options).then(([aTab, aPanel]) => {
     const gTab = aTab;
     const gPanel = aPanel;
     const gDebugger = gPanel.panelWin;
     const gEditor = gDebugger.DebuggerView.editor;
     const gSources = gDebugger.DebuggerView.Sources;
     const queries = gDebugger.require("./content/queries");
     const constants = gDebugger.require("./content/constants");
     const actions = bindActionCreators(gPanel);
--- a/devtools/client/debugger/test/mochitest/browser_dbg_server-conditional-bp-03.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_server-conditional-bp-03.js
@@ -10,17 +10,17 @@
 
 const TAB_URL = EXAMPLE_URL + "doc_conditional-breakpoints.html";
 
 function test() {
   let options = {
     source: TAB_URL,
     line: 1
   };
-  initDebugger(TAB_URL, options).then(([aTab,, aPanel]) => {
+  initDebugger(TAB_URL, options).then(([aTab, aPanel]) => {
     const gTab = aTab;
     const gPanel = aPanel;
     const gDebugger = gPanel.panelWin;
     const gSources = gDebugger.DebuggerView.Sources;
     const queries = gDebugger.require("./content/queries");
     const constants = gDebugger.require("./content/constants");
     const actions = bindActionCreators(gPanel);
     const getState = gDebugger.DebuggerController.getState;
--- a/devtools/client/debugger/test/mochitest/browser_dbg_server-conditional-bp-04.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_server-conditional-bp-04.js
@@ -11,17 +11,17 @@
 
 const TAB_URL = EXAMPLE_URL + "doc_conditional-breakpoints.html";
 
 function test() {
   let options = {
     source: TAB_URL,
     line: 1
   };
-  initDebugger(TAB_URL, options).then(([aTab,, aPanel]) => {
+  initDebugger(TAB_URL, options).then(([aTab, aPanel]) => {
     const gTab = aTab;
     const gPanel = aPanel;
     const gDebugger = gPanel.panelWin;
     const gSources = gDebugger.DebuggerView.Sources;
     const queries = gDebugger.require("./content/queries");
     const constants = gDebugger.require("./content/constants");
     const actions = bindActionCreators(gPanel);
     const getState = gDebugger.DebuggerController.getState;
--- a/devtools/client/debugger/test/mochitest/browser_dbg_server-conditional-bp-05.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_server-conditional-bp-05.js
@@ -10,17 +10,17 @@
 
 const TAB_URL = EXAMPLE_URL + "doc_conditional-breakpoints.html";
 
 function test() {
   const options = {
     source: TAB_URL,
     line: 1
   };
-  initDebugger(TAB_URL, options).then(([aTab,, aPanel]) => {
+  initDebugger(TAB_URL, options).then(([aTab, aPanel]) => {
     const gTab = aTab;
     const gPanel = aPanel;
     const gDebugger = gPanel.panelWin;
     const gEditor = gDebugger.DebuggerView.editor;
     const gSources = gDebugger.DebuggerView.Sources;
     const queries = gDebugger.require("./content/queries");
     const constants = gDebugger.require("./content/constants");
     const actions = bindActionCreators(gPanel);
--- a/devtools/client/debugger/test/mochitest/browser_dbg_sources-bookmarklet.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_sources-bookmarklet.js
@@ -11,17 +11,17 @@ const TAB_URL = EXAMPLE_URL + "doc_scrip
 
 const BOOKMARKLET_SCRIPT_CODE = "console.log('bookmarklet executed');";
 
 function test() {
   let options = {
     source: TAB_URL,
     line: 1
   };
-  initDebugger(TAB_URL, options).then(([aTab,, aPanel]) => {
+  initDebugger(TAB_URL, options).then(([aTab, aPanel]) => {
     const gTab = aTab;
     const gPanel = aPanel;
     const gDebugger = gPanel.panelWin;
     const gSources = gDebugger.DebuggerView.Sources;
     const gBreakpoints = gDebugger.DebuggerController.Breakpoints;
     const getState = gDebugger.DebuggerController.getState;
     const constants = gDebugger.require("./content/constants");
     const queries = gDebugger.require("./content/queries");
--- a/devtools/client/debugger/test/mochitest/browser_dbg_sources-cache.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_sources-cache.js
@@ -10,19 +10,18 @@
 const TAB_URL = EXAMPLE_URL + "doc_function-search.html";
 const TOTAL_SOURCES = 4;
 
 function test() {
   let options = {
     source: EXAMPLE_URL + "code_function-search-01.js",
     line: 1
   };
-  initDebugger(TAB_URL, options).then(([aTab, aDebuggee, aPanel]) => {
+  initDebugger(TAB_URL, options).then(([aTab, aPanel]) => {
     const gTab = aTab;
-    const gDebuggee = aDebuggee;
     const gPanel = aPanel;
     const gDebugger = gPanel.panelWin;
     const gEditor = gDebugger.DebuggerView.editor;
     const gSources = gDebugger.DebuggerView.Sources;
     const gPrevLabelsCache = gDebugger.SourceUtils._labelsCache;
     const gPrevGroupsCache = gDebugger.SourceUtils._groupsCache;
     const getState = gDebugger.DebuggerController.getState;
     const queries = gDebugger.require("./content/queries");
--- a/devtools/client/debugger/test/mochitest/browser_dbg_sources-iframe-reload.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_sources-iframe-reload.js
@@ -10,17 +10,17 @@
 "use strict";
 
 const IFRAME_URL = "data:text/html;charset=utf-8," +
   "<script>function fn() { console.log('hello'); }</script>" +
   "<div onclick='fn()'>hello</div>";
 const TAB_URL = `data:text/html;charset=utf-8,<iframe src="${IFRAME_URL}"/>`;
 
 add_task(async function() {
-  let [,, panel] = await initDebugger();
+  let [, panel] = await initDebugger();
   let dbg = panel.panelWin;
   let newSource;
 
   newSource = waitForDebuggerEvents(panel, dbg.EVENTS.NEW_SOURCE);
   reload(panel, TAB_URL);
   await newSource;
   ok(true, "Source event fired on initial load");
 
--- a/devtools/client/debugger/test/mochitest/browser_dbg_split-console-keypress.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_split-console-keypress.js
@@ -15,17 +15,17 @@ function test() {
   // tests, so request extra time.
   requestLongerTimeout(2);
 
   let gDebugger, gToolbox, gThreadClient, gTab, gPanel;
   let options = {
     source: TAB_URL,
     line: 1
   };
-  initDebugger(TAB_URL, options).then(([aTab, debuggeeWin, aPanel]) => {
+  initDebugger(TAB_URL, options).then(([aTab, aPanel]) => {
     gPanel = aPanel;
     gDebugger = aPanel.panelWin;
     gToolbox = gDevTools.getToolbox(aPanel.target);
     gTab = aTab;
     gThreadClient = gDebugger.DebuggerController.activeThread;
     testConsole();
   });
   let testConsole = Task.async(function* () {
--- a/devtools/client/debugger/test/mochitest/browser_dbg_split-console-paused-reload.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_split-console-paused-reload.js
@@ -13,17 +13,17 @@ function test() {
 }
 
 function* runTests() {
   let TAB_URL = EXAMPLE_URL + "doc_split-console-paused-reload.html";
   let options = {
     source: TAB_URL,
     line: 1
   };
-  let [,, panel] = yield initDebugger(TAB_URL, options);
+  let [, panel] = yield initDebugger(TAB_URL, options);
   let dbgWin = panel.panelWin;
   let sources = dbgWin.DebuggerView.Sources;
   let frames = dbgWin.DebuggerView.StackFrames;
   let toolbox = gDevTools.getToolbox(panel.target);
 
   yield panel.addBreakpoint({ actor: getSourceActor(sources, TAB_URL), line: 16 });
   info("Breakpoint was set.");
   dbgWin.DebuggerController._target.activeTab.reload();
--- a/devtools/client/debugger/test/mochitest/browser_dbg_terminate-on-tab-close.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_terminate-on-tab-close.js
@@ -16,17 +16,17 @@ if (!gMultiProcessBrowser) {
 
 const TAB_URL = EXAMPLE_URL + "doc_terminate-on-tab-close.html";
 
 function test() {
   let options = {
     source: TAB_URL,
     line: 1
   };
-  initDebugger(TAB_URL, options).then(([aTab,, aPanel]) => {
+  initDebugger(TAB_URL, options).then(([aTab, aPanel]) => {
     const gTab = aTab;
     const gPanel = aPanel;
     const gDebugger = gPanel.panelWin;
 
     gDebugger.gThreadClient.addOneTimeListener("paused", () => {
       resumeDebuggerThenCloseAndFinish(gPanel).then(function () {
         ok(true, "should not throw after this point");
       });
--- a/devtools/client/debugger/test/mochitest/browser_dbg_worker-source-map.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_worker-source-map.js
@@ -21,17 +21,17 @@ function selectWorker(aPanel, aURL) {
 function test() {
   return Task.spawn(function* () {
     yield pushPrefs(["devtools.debugger.workers", true]);
 
     let options = {
       source: TAB_URL,
       line: 1
     };
-    let [tab,, panel] = yield initDebugger(TAB_URL, options);
+    let [tab, panel] = yield initDebugger(TAB_URL, options);
     let toolbox = yield selectWorker(panel, WORKER_URL);
     let workerPanel = toolbox.getCurrentPanel();
     yield waitForSourceShown(workerPanel, ".coffee");
     let panelWin = workerPanel.panelWin;
     let Sources = panelWin.DebuggerView.Sources;
     let editor = panelWin.DebuggerView.editor;
     let threadClient = panelWin.gThreadClient;
 
--- a/devtools/client/debugger/test/mochitest/head.js
+++ b/devtools/client/debugger/test/mochitest/head.js
@@ -548,17 +548,16 @@ let initDebugger = Task.async(function*(
     // `urlOrTab` is an url. Open an empty tab first in order to load the page
     // only once the panel is ready. That to be able to safely catch the
     // SOURCE_SHOWN event.
     tab = yield addTab("about:blank", window);
     url = urlOrTab;
   }
   info("Debugee tab added successfully: " + urlOrTab);
 
-  let debuggee = tab.linkedBrowser.contentWindowAsCPOW.wrappedJSObject;
   let target = yield TargetFactory.forTab(tab);
 
   let toolbox = yield gDevTools.showToolbox(target, "jsdebugger");
   info("Debugger panel shown successfully.");
 
   let debuggerPanel = toolbox.getCurrentPanel();
   let panelWin = debuggerPanel.panelWin;
   let { Sources } = panelWin.DebuggerView;
@@ -588,17 +587,17 @@ let initDebugger = Task.async(function*(
         let onSource = waitForSourceAndCaret(debuggerPanel, source, line ? line : 1);
         Sources.selectedValue = getSourceActor(Sources, source);
         yield onSource;
       }
     }
     yield onCaretUpdated;
   }
 
-  return [tab, debuggee, debuggerPanel, window];
+  return [tab, debuggerPanel, window];
 });
 
 // Creates an add-on debugger for a given add-on. The returned AddonDebugger
 // object must be destroyed before finishing the test
 function initAddonDebugger(aAddonId) {
   let addonDebugger = new AddonDebugger();
   return addonDebugger.init(aAddonId).then(() => addonDebugger);
 }
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -152,16 +152,17 @@ devtools.jar:
     skin/markup.css (themes/markup.css)
     skin/images/editor-error.png (themes/images/editor-error.png)
     skin/images/breakpoint.svg (themes/images/breakpoint.svg)
     skin/webconsole.css (themes/webconsole.css)
     skin/images/webconsole/alert.svg (themes/images/webconsole/alert.svg)
     skin/images/webconsole/info.svg (themes/images/webconsole/info.svg)
     skin/images/webconsole/input.svg (themes/images/webconsole/input.svg)
     skin/images/webconsole/return.svg (themes/images/webconsole/return.svg)
+    skin/images/webconsole/jump.svg (themes/images/webconsole/jump.svg)
     skin/images/breadcrumbs-scrollbutton.svg (themes/images/breadcrumbs-scrollbutton.svg)
     skin/animation.css (themes/animation.css)
     skin/canvasdebugger.css (themes/canvasdebugger.css)
     skin/debugger.css (themes/debugger.css)
     skin/perf.css (themes/perf.css)
     skin/performance.css (themes/performance.css)
     skin/memory.css (themes/memory.css)
     skin/scratchpad.css (themes/scratchpad.css)
new file mode 100755
--- /dev/null
+++ b/devtools/client/themes/images/webconsole/jump.svg
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 51.3 (57544) - http://www.bohemiancoding.com/sketch -->
+    <title>Group 7</title>
+    <desc>Created with Sketch.</desc>
+    <defs></defs>
+    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="Group-7" transform="translate(1.000000, 1.000000)">
+            <g id="Group-6">
+                <circle id="Oval-2" stroke="#0274E8" cx="8" cy="8" r="8"></circle>
+                <polygon id="Triangle" fill="#0274E8" transform="translate(9.000000, 8.000000) rotate(90.000000) translate(-9.000000, -8.000000) " points="9 5 13 11 5 11"></polygon>
+            </g>
+        </g>
+    </g>
+</svg>
\ No newline at end of file
--- a/devtools/client/themes/variables.css
+++ b/devtools/client/themes/variables.css
@@ -212,16 +212,17 @@
   /* Images */
   --select-arrow-image: url(chrome://devtools/skin/images/select-arrow.svg);
   --theme-pane-collapse-image: url(chrome://devtools/skin/images/pane-collapse.svg);
   --theme-pane-expand-image: url(chrome://devtools/skin/images/pane-expand.svg);
   --theme-console-alert-image: url(chrome://devtools/skin/images/webconsole/alert.svg);
   --theme-console-info-image: url(chrome://devtools/skin/images/webconsole/info.svg);
   --theme-console-input-image: url(chrome://devtools/skin/images/webconsole/input.svg);
   --theme-console-return-image: url(chrome://devtools/skin/images/webconsole/return.svg);
+  --theme-console-jump-image: url(chrome://devtools/skin/images/webconsole/jump.svg);
 
   /* Firefox Colors CSS Variables v1.0.3
    * Colors are taken from: https://github.com/FirefoxUX/design-tokens
    * Some intermediate colors were added (names ending in '5').
    */
   --magenta-50: #ff1ad9;
   --magenta-65: #dd00a9;
   --magenta-70: #b5007f;
--- a/devtools/client/themes/webconsole.css
+++ b/devtools/client/themes/webconsole.css
@@ -184,16 +184,28 @@ a {
   background-image: var(--theme-console-alert-image);
 }
 
 .message.warn > .icon {
   color: var(--console-output-icon-warning);
   background-image: var(--theme-console-alert-image);
 }
 
+
+span.icon[title="Jump"] {
+  background-image:var(--theme-console-jump-image);
+  background-size: 14px 14px;
+  cursor: pointer;
+  opacity: 0;
+}
+
+.message:hover span.icon[title="Jump"] {
+  opacity: 1;
+}
+
 .message > .message-body-wrapper {
   flex: auto;
   min-width: 0px;
   margin: var(--console-output-vertical-padding) 0;
 }
 
 .message-body-wrapper .table-widget-body {
   overflow: visible;
--- a/devtools/client/webconsole/components/ConsoleOutput.js
+++ b/devtools/client/webconsole/components/ConsoleOutput.js
@@ -148,17 +148,17 @@ class ConsoleOutput extends Component {
       messageId,
       serviceContainer,
       open: messagesUi.includes(messageId),
       tableData: messagesTableData.get(messageId),
       timestampsVisible,
       repeat: messagesRepeat[messageId],
       networkMessageUpdate: networkMessagesUpdate[messageId],
       networkMessageActiveTabId,
-      getMessage: () => messages.get(messageId),
+      getMessage: () => messages.get(messageId)
     }));
 
     return (
       dom.div({
         className: "webconsole-output",
         onContextMenu: this.onContextMenu,
         ref: node => {
           this.outputNode = node;
--- a/devtools/client/webconsole/components/Message.js
+++ b/devtools/client/webconsole/components/Message.js
@@ -39,16 +39,17 @@ class Message extends Component {
       indent: PropTypes.number.isRequired,
       topLevelClasses: PropTypes.array.isRequired,
       messageBody: PropTypes.any.isRequired,
       repeat: PropTypes.any,
       frame: PropTypes.any,
       attachment: PropTypes.any,
       stacktrace: PropTypes.any,
       messageId: PropTypes.string,
+      executionPoint: PropTypes.string,
       scrollToMessage: PropTypes.bool,
       exceptionDocURL: PropTypes.string,
       request: PropTypes.object,
       dispatch: PropTypes.func,
       timeStamp: PropTypes.number,
       timestampsVisible: PropTypes.bool.isRequired,
       serviceContainer: PropTypes.shape({
         emitNewMessage: PropTypes.func.isRequired,
@@ -57,31 +58,32 @@ class Message extends Component {
         onViewSourceInStyleEditor: PropTypes.func,
         openContextMenu: PropTypes.func.isRequired,
         openLink: PropTypes.func.isRequired,
         sourceMapService: PropTypes.any,
       }),
       notes: PropTypes.arrayOf(PropTypes.shape({
         messageBody: PropTypes.string.isRequired,
         frame: PropTypes.any,
-      })),
+      }))
     };
   }
 
   static get defaultProps() {
     return {
       indent: 0
     };
   }
 
   constructor(props) {
     super(props);
     this.onLearnMoreClick = this.onLearnMoreClick.bind(this);
     this.toggleMessage = this.toggleMessage.bind(this);
     this.onContextMenu = this.onContextMenu.bind(this);
+    this.renderIcon = this.renderIcon.bind(this);
   }
 
   componentDidMount() {
     if (this.messageNode) {
       if (this.props.scrollToMessage) {
         this.messageNode.scrollIntoView();
       }
       // Event used in tests. Some message types don't pass it in because existing tests
@@ -114,16 +116,31 @@ class Message extends Component {
       request,
       messageId,
     };
     serviceContainer.openContextMenu(e, messageInfo);
     e.stopPropagation();
     e.preventDefault();
   }
 
+  renderIcon() {
+    const { level, messageId, executionPoint, serviceContainer } = this.props;
+
+    if (serviceContainer.canRewind()) {
+      return dom.span({
+        className: "icon",
+        title: "Jump",
+        "aria-live": "off",
+        onClick: () => serviceContainer.jumpToExecutionPoint(executionPoint, messageId)
+      });
+    }
+
+    return MessageIcon({ level });
+  }
+
   render() {
     const {
       open,
       collapsible,
       collapseTitle,
       source,
       type,
       level,
@@ -131,32 +148,32 @@ class Message extends Component {
       topLevelClasses,
       messageBody,
       frame,
       stacktrace,
       serviceContainer,
       exceptionDocURL,
       timeStamp = Date.now(),
       timestampsVisible,
-      notes,
+      notes
     } = this.props;
 
     topLevelClasses.push("message", source, type, level);
     if (open) {
       topLevelClasses.push("open");
     }
 
     let timestampEl;
     if (timestampsVisible === true) {
       timestampEl = dom.span({
         className: "timestamp devtools-monospace"
       }, l10n.timestampString(timeStamp));
     }
 
-    const icon = MessageIcon({level});
+    const icon = this.renderIcon();
 
     // Figure out if there is an expandable part to the message.
     let attachment = null;
     if (this.props.attachment) {
       attachment = this.props.attachment;
     } else if (stacktrace && open) {
       attachment = dom.div(
         {
--- a/devtools/client/webconsole/components/MessageContainer.js
+++ b/devtools/client/webconsole/components/MessageContainer.js
@@ -29,17 +29,17 @@ class MessageContainer extends Component
     return {
       messageId: PropTypes.string.isRequired,
       open: PropTypes.bool.isRequired,
       serviceContainer: PropTypes.object.isRequired,
       tableData: PropTypes.object,
       timestampsVisible: PropTypes.bool.isRequired,
       repeat: PropTypes.number,
       networkMessageUpdate: PropTypes.object,
-      getMessage: PropTypes.func.isRequired,
+      getMessage: PropTypes.func.isRequired
     };
   }
 
   static get defaultProps() {
     return {
       open: false,
     };
   }
--- a/devtools/client/webconsole/components/MessageIcon.js
+++ b/devtools/client/webconsole/components/MessageIcon.js
@@ -12,17 +12,17 @@ const {l10n} = require("devtools/client/
 
 // Store common icons so they can be used without recreating the element
 // during render.
 const CONSTANT_ICONS = {
   "error": getIconElement("level.error"),
   "warn": getIconElement("level.warn"),
   "info": getIconElement("level.info"),
   "log": getIconElement("level.log"),
-  "debug": getIconElement("level.debug"),
+  "debug": getIconElement("level.debug")
 };
 
 function getIconElement(level) {
   return dom.span({
     className: "icon",
     title: l10n.getStr(level),
     "aria-live": "off",
   });
--- a/devtools/client/webconsole/components/message-types/ConsoleApiCall.js
+++ b/devtools/client/webconsole/components/message-types/ConsoleApiCall.js
@@ -37,16 +37,17 @@ function ConsoleApiCall(props) {
     open,
     tableData,
     serviceContainer,
     timestampsVisible,
     repeat,
   } = props;
   const {
     id: messageId,
+    executionPoint,
     indent,
     source,
     type,
     level,
     stacktrace,
     frame,
     timeStamp,
     parameters,
@@ -108,16 +109,17 @@ function ConsoleApiCall(props) {
   }
 
   const collapsible = isGroupType(type)
     || (type === "error" && Array.isArray(stacktrace));
   const topLevelClasses = ["cm-s-mozilla"];
 
   return Message({
     messageId,
+    executionPoint,
     open,
     collapsible,
     collapseTitle,
     source,
     type,
     level,
     topLevelClasses,
     messageBody,
--- a/devtools/client/webconsole/webconsole-output-wrapper.js
+++ b/devtools/client/webconsole/webconsole-output-wrapper.js
@@ -60,16 +60,23 @@ WebConsoleOutputWrapper.prototype = {
             messageId,
             timeStamp,
           }]));
         },
         hudProxy: hud.proxy,
         openLink: (url, e) => {
           hud.owner.openLink(url, e);
         },
+        canRewind: () => {
+          if (!hud.owner.target.activeTab) {
+            return false;
+          }
+
+          return hud.owner.target.activeTab.traits.canRewind;
+        },
         createElement: nodename => {
           return this.document.createElement(nodename);
         },
         getLongString: (grip) => {
           return hud.proxy.webConsoleClient.getString(grip);
         },
         requestData(id, type) {
           return hud.proxy.networkDataProvider.requestData(id, type);
@@ -215,17 +222,19 @@ WebConsoleOutputWrapper.prototype = {
               inspector
             ] = await Promise.all([onGripNodeToFront, onSelectInspector]);
 
             const onInspectorUpdated = inspector.once("inspector-updated");
             const onNodeFrontSet = this.toolbox.selection
               .setNodeFront(front, { reason: "console" });
 
             return Promise.all([onNodeFrontSet, onInspectorUpdated]);
-          }
+          },
+          jumpToExecutionPoint: executionPoint =>
+            this.toolbox.threadClient.timeWarp(executionPoint)
         });
       }
 
       store = configureStore(this.hud, {
         // We may not have access to the toolbox (e.g. in the browser console).
         sessionId: this.toolbox && this.toolbox.sessionId || -1,
         telemetry: this.telemetry,
         services: serviceContainer
--- a/dom/base/test/browser_aboutnewtab_process_selection.js
+++ b/dom/base/test/browser_aboutnewtab_process_selection.js
@@ -13,19 +13,20 @@ add_task(async function(){
   ]});
 });
 
 // Ensure that the preloaded browser exists, and it's finished loading.
 async function ensurePreloaded(gBrowser) {
   gBrowser._createPreloadBrowser();
   // We cannot use the regular BrowserTestUtils helper for waiting here, since that
   // would try to insert the preloaded browser, which would only break things.
-  await BrowserTestUtils.waitForCondition( () => {
-    let doc = gBrowser._preloadedBrowser.contentDocumentAsCPOW;
-    return doc && doc.readyState == "complete";
+  await ContentTask.spawn(gBrowser._preloadedBrowser, null, async () => {
+    await ContentTaskUtils.waitForCondition(() => {
+      return content.document && content.document.readyState == "complete";
+    });
   });
 }
 
 add_task(async function(){
   // This test is only relevant in e10s.
   if (!gMultiProcessBrowser)
     return;
 
--- a/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
@@ -792,46 +792,16 @@ MediaEngineRemoteVideoSource::GetBestFit
   if (!candidateSet.Length()) {
     return UINT32_MAX;
   }
   TrimLessFitCandidates(candidateSet);
   return candidateSet[0].mDistance;
 }
 
 static void
-LogConstraints(const NormalizedConstraintSet& aConstraints)
-{
-  auto& c = aConstraints;
-  if (c.mWidth.mIdeal.isSome()) {
-    LOG(("Constraints: width: { min: %d, max: %d, ideal: %d }",
-         c.mWidth.mMin, c.mWidth.mMax,
-         c.mWidth.mIdeal.valueOr(0)));
-  } else {
-    LOG(("Constraints: width: { min: %d, max: %d }",
-         c.mWidth.mMin, c.mWidth.mMax));
-  }
-  if (c.mHeight.mIdeal.isSome()) {
-    LOG(("             height: { min: %d, max: %d, ideal: %d }",
-         c.mHeight.mMin, c.mHeight.mMax,
-         c.mHeight.mIdeal.valueOr(0)));
-  } else {
-    LOG(("             height: { min: %d, max: %d }",
-         c.mHeight.mMin, c.mHeight.mMax));
-  }
-  if (c.mFrameRate.mIdeal.isSome()) {
-    LOG(("             frameRate: { min: %f, max: %f, ideal: %f }",
-         c.mFrameRate.mMin, c.mFrameRate.mMax,
-         c.mFrameRate.mIdeal.valueOr(0)));
-  } else {
-    LOG(("             frameRate: { min: %f, max: %f }",
-         c.mFrameRate.mMin, c.mFrameRate.mMax));
-  }
-}
-
-static void
 LogCapability(const char* aHeader,
               const webrtc::CaptureCapability &aCapability,
               uint32_t aDistance)
 {
   // RawVideoType and VideoCodecType media/webrtc/trunk/webrtc/common_types.h
   static const char* const types[] = {
     "I420",
     "YV12",
@@ -880,21 +850,21 @@ MediaEngineRemoteVideoSource::ChooseCapa
 {
   LOG((__PRETTY_FUNCTION__));
   AssertIsOnOwningThread();
 
   if (MOZ_LOG_TEST(GetMediaManagerLog(), LogLevel::Debug)) {
     LOG(("ChooseCapability: prefs: %dx%d @%dfps",
          aPrefs.GetWidth(), aPrefs.GetHeight(),
          aPrefs.mFPS));
-    LogConstraints(aConstraints);
+    MediaConstraintsHelper::LogConstraints(aConstraints);
     if (!aConstraints.mAdvanced.empty()) {
       LOG(("Advanced array[%zu]:", aConstraints.mAdvanced.size()));
       for (auto& advanced : aConstraints.mAdvanced) {
-        LogConstraints(advanced);
+        MediaConstraintsHelper::LogConstraints(advanced);
       }
     }
   }
 
   switch (mMediaSource) {
     case MediaSourceEnum::Screen:
     case MediaSourceEnum::Window:
     case MediaSourceEnum::Application: {
--- a/dom/media/webrtc/MediaTrackConstraints.cpp
+++ b/dom/media/webrtc/MediaTrackConstraints.cpp
@@ -9,16 +9,20 @@
 #include <algorithm>
 #include <iterator>
 
 #include "MediaEngineSource.h"
 #include "nsIScriptError.h"
 #include "mozilla/dom/MediaStreamTrackBinding.h"
 #include "mozilla/MediaManager.h"
 
+mozilla::LogModule* GetMediaManagerLog();
+#undef LOG
+#define LOG(msg, ...) MOZ_LOG(GetMediaManagerLog(), mozilla::LogLevel::Debug, (msg, ##__VA_ARGS__))
+
 namespace mozilla {
 
 using dom::ConstrainBooleanParameters;
 using dom::OwningLongOrConstrainLongRange;
 
 template<class ValueType>
 template<class ConstrainRange>
 void
@@ -476,16 +480,17 @@ MediaConstraintsHelper::FitnessDistance(
 
 /* static */ const char*
 MediaConstraintsHelper::SelectSettings(
     const NormalizedConstraints& aConstraints,
     nsTArray<RefPtr<MediaDevice>>& aDevices,
     bool aIsChrome)
 {
   auto& c = aConstraints;
+  LogConstraints(c);
 
   // First apply top-level constraints.
 
   // Stack constraintSets that pass, starting with the required one, because the
   // whole stack must be re-satisfied each time a capability-set is ruled out
   // (this avoids storing state or pushing algorithm into the lower-level code).
   nsTArray<RefPtr<MediaDevice>> unsatisfactory;
   nsTArray<const NormalizedConstraintSet*> aggregateConstraints;
@@ -625,9 +630,74 @@ MediaConstraintsHelper::ConvertOldWithWa
     if (old.IsBoolean()) {
       to.SetAsBoolean() = old.GetAsBoolean();
     } else {
       to.SetAsConstrainBooleanParameters() = old.GetAsConstrainBooleanParameters();
     }
   }
 }
 
+static void
+LogConstraintStringRange(const NormalizedConstraintSet::StringRange& aRange)
+{
+  if (aRange.mExact.size() <= 1 && aRange.mIdeal.size() <= 1) {
+    LOG("  %s: { exact: [%s], ideal: [%s] }",
+        aRange.mName,
+        (aRange.mExact.size()? NS_ConvertUTF16toUTF8(*aRange.mExact.begin()).get() : ""),
+        (aRange.mIdeal.size()? NS_ConvertUTF16toUTF8(*aRange.mIdeal.begin()).get() : ""));
+  } else {
+    LOG("  %s: { exact: [", aRange.mName);
+    for (auto& entry : aRange.mExact) {
+      LOG("      %s,", NS_ConvertUTF16toUTF8(entry).get());
+    }
+    LOG("    ], ideal: [");
+    for (auto& entry : aRange.mIdeal) {
+      LOG("      %s,", NS_ConvertUTF16toUTF8(entry).get());
+    }
+    LOG("    ]}");
+  }
 }
+
+template<typename T>
+static void
+LogConstraintRange(const NormalizedConstraintSet::Range<T>& aRange)
+{
+  if (aRange.mIdeal.isSome()) {
+    LOG("  %s: { min: %d, max: %d, ideal: %d }",
+         aRange.mName, aRange.mMin, aRange.mMax, aRange.mIdeal.valueOr(0));
+  } else {
+    LOG("  %s: { min: %d, max: %d }", aRange.mName, aRange.mMin, aRange.mMax);
+  }
+}
+
+template<>
+void
+LogConstraintRange(const NormalizedConstraintSet::Range<double>& aRange)
+{
+  if (aRange.mIdeal.isSome()) {
+    LOG("  %s: { min: %f, max: %f, ideal: %f }",
+         aRange.mName, aRange.mMin, aRange.mMax, aRange.mIdeal.valueOr(0));
+  } else {
+    LOG("  %s: { min: %f, max: %f }", aRange.mName, aRange.mMin, aRange.mMax);
+  }
+}
+
+/* static */ void
+MediaConstraintsHelper::LogConstraints(const NormalizedConstraintSet& aConstraints)
+{
+  auto& c = aConstraints;
+  LOG("Constraints: {");
+  LOG("%s", [&]() {
+    LogConstraintRange(c.mWidth);
+    LogConstraintRange(c.mHeight);
+    LogConstraintRange(c.mFrameRate);
+    LogConstraintStringRange(c.mMediaSource);
+    LogConstraintStringRange(c.mFacingMode);
+    LogConstraintStringRange(c.mDeviceId);
+    LogConstraintRange(c.mEchoCancellation);
+    LogConstraintRange(c.mAutoGainControl);
+    LogConstraintRange(c.mNoiseSuppression);
+    LogConstraintRange(c.mChannelCount);
+    return "}";
+  }());
+}
+
+}
--- a/dom/media/webrtc/MediaTrackConstraints.h
+++ b/dom/media/webrtc/MediaTrackConstraints.h
@@ -339,13 +339,15 @@ public:
 
   // Warn on and convert use of deprecated constraints to new ones
   static void
   ConvertOldWithWarning(
       const dom::OwningBooleanOrConstrainBooleanParameters& old,
       dom::OwningBooleanOrConstrainBooleanParameters& to,
       const char* aMessageName,
       nsPIDOMWindowInner* aWindow);
+
+  static void LogConstraints(const NormalizedConstraintSet& aConstraints);
 };
 
 } // namespace mozilla
 
 #endif /* MEDIATRACKCONSTRAINTS_H_ */
--- a/extensions/permissions/nsContentBlocker.cpp
+++ b/extensions/permissions/nsContentBlocker.cpp
@@ -118,18 +118,17 @@ nsContentBlocker::Init()
     prefBranch->SetIntPref("image", newPref);
     oldPrefBranch->ClearUserPref("network.image.imageBehavior");
   }
 
 
   // The branch is not a copy of the prefservice, but a new object, because
   // it is a non-default branch. Adding obeservers to it will only work if
   // we make sure that the object doesn't die. So, keep a reference to it.
-  mPrefBranchInternal = do_QueryInterface(prefBranch, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
+  mPrefBranchInternal = prefBranch;
 
   rv = mPrefBranchInternal->AddObserver("", this, true);
   PrefChanged(prefBranch, nullptr);
 
   return rv;
 }
 
 #undef  LIMIT
--- a/image/decoders/icon/nsIconURI.cpp
+++ b/image/decoders/icon/nsIconURI.cpp
@@ -507,23 +507,19 @@ nsMozIconURI::SchemeIs(const char* aSche
 
   *aEquals = PL_strcasecmp("moz-icon", aScheme) ? false : true;
   return NS_OK;
 }
 
 nsresult
 nsMozIconURI::Clone(nsIURI** result)
 {
-  nsresult rv;
   nsCOMPtr<nsIURL> newIconURL;
   if (mIconURL) {
-    newIconURL = do_QueryInterface(mIconURL, &rv);
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
+    newIconURL = mIconURL;
   }
 
   RefPtr<nsMozIconURI> uri = new nsMozIconURI();
   newIconURL.swap(uri->mIconURL);
   uri->mSize = mSize;
   uri->mContentType = mContentType;
   uri->mFileName = mFileName;
   uri->mStockIcon = mStockIcon;
--- a/intl/unicharutil/util/nsBidiUtils.h
+++ b/intl/unicharutil/util/nsBidiUtils.h
@@ -260,17 +260,17 @@ typedef enum nsCharType nsCharType;
  *  BMP (Basic Multilingual Plane) and SMP (Supplementary Multilingual Plane)
  *  according to
  *  http://unicode.org/Public/UNIDATA/extracted/DerivedBidiClass.txt and
  *  http://www.unicode.org/roadmaps/
  */
 
 #define IS_IN_BMP_RTL_BLOCK(c) ((0x590 <= (c)) && ((c) <= 0x8ff))
 #define IS_RTL_PRESENTATION_FORM(c) (((0xfb1d <= (c)) && ((c) <= 0xfdff)) || \
-                                     ((0xfe70 <= (c)) && ((c) <= 0xfefc)))
+                                     ((0xfe70 <= (c)) && ((c) <= 0xfefe)))
 #define IS_IN_SMP_RTL_BLOCK(c) (((0x10800 <= (c)) && ((c) <= 0x10fff)) || \
                                 ((0x1e800 <= (c)) && ((c) <= 0x1eFFF)))
 // Due to the supplementary-plane RTL blocks being identifiable from the
 // high surrogate without examining the low surrogate, it is correct to
 // use this by-code-unit check on potentially astral text without doing
 // the math to decode surrogate pairs into code points. However, unpaired
 // high surrogates that are RTL high surrogates then count as RTL even
 // though, if replaced by the REPLACEMENT CHARACTER, it would not be
--- a/js/src/vm/CharacterEncoding.cpp
+++ b/js/src/vm/CharacterEncoding.cpp
@@ -254,79 +254,75 @@ ReportBufferTooSmall(JSContext* cx, uint
 static void
 ReportTooBigCharacter(JSContext* cx, uint32_t v)
 {
     char buffer[10];
     SprintfLiteral(buffer, "0x%x", v + 0x10000);
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_UTF8_CHAR_TOO_LARGE, buffer);
 }
 
-enum InflateUTF8Action {
-    CountAndReportInvalids,
-    CountAndIgnoreInvalids,
-    AssertNoInvalids,
-    Copy,
-    FindEncoding
+enum class LoopDisposition {
+    Break,
+    Continue,
 };
 
-static const char16_t REPLACE_UTF8 = 0xFFFD;
-static const Latin1Char REPLACE_UTF8_LATIN1 = '?';
+enum class OnUTF8Error {
+    InsertReplacementCharacter,
+    InsertQuestionMark,
+    Throw,
+    Crash,
+};
+
+// The Unicode REPLACEMENT CHARACTER, rendered as a diamond with a question
+// mark, meaning "someone screwed up here but it wasn't me".
+static const char16_t REPLACEMENT_CHARACTER = 0xFFFD;
 
 // If making changes to this algorithm, make sure to also update
 // LossyConvertUTF8toUTF16() in dom/wifi/WifiUtils.cpp
-template <InflateUTF8Action Action, typename CharT, class ContextT>
+//
+// Scan UTF8 input and (internally, at least) convert it to a series of UTF-16
+// code units. But you can also do odd things like pass an empty lambda for
+// `dst`, in which case the output is discarded entirely--the only effect of
+// calling the template that way is error-checking.
+template <OnUTF8Error ErrorAction, typename OutputFn>
 static bool
-InflateUTF8StringToBuffer(ContextT* cx, const UTF8Chars src, CharT* dst, size_t* dstlenp,
-                          JS::SmallestEncoding *smallestEncoding)
+InflateUTF8ToUTF16(JSContext* cx, const UTF8Chars src, OutputFn dst)
 {
-    if (Action != AssertNoInvalids) {
-        *smallestEncoding = JS::SmallestEncoding::ASCII;
-    }
-    auto RequireLatin1 = [&smallestEncoding]{
-        *smallestEncoding = std::max(JS::SmallestEncoding::Latin1, *smallestEncoding);
-    };
-    auto RequireUTF16 = [&smallestEncoding]{
-        *smallestEncoding = JS::SmallestEncoding::UTF16;
-    };
-
-    // Count how many code units need to be in the inflated string.
-    // |i| is the index into |src|, and |j| is the the index into |dst|.
     size_t srclen = src.length();
-    uint32_t j = 0;
-    for (uint32_t i = 0; i < srclen; i++, j++) {
+    for (uint32_t i = 0; i < srclen; i++) {
         uint32_t v = uint32_t(src[i]);
         if (!(v & 0x80)) {
             // ASCII code unit.  Simple copy.
-            if (Action == Copy) {
-                dst[j] = CharT(v);
+            if (dst(uint16_t(v)) == LoopDisposition::Break) {
+                break;
             }
-
         } else {
             // Non-ASCII code unit.  Determine its length in bytes (n).
             uint32_t n = 1;
             while (v & (0x80 >> n)) {
                 n++;
             }
 
         #define INVALID(report, arg, n2)                                \
             do {                                                        \
-                if (Action == CountAndReportInvalids) {                 \
+                if (ErrorAction == OnUTF8Error::Throw) {                \
                     report(cx, arg);                                    \
                     return false;                                       \
-                } else if (Action == AssertNoInvalids) {                \
+                } else if (ErrorAction == OnUTF8Error::Crash) {         \
                     MOZ_CRASH("invalid UTF-8 string: " # report);       \
                 } else {                                                \
-                    if (Action == Copy) {                               \
-                        if (std::is_same<decltype(dst[0]), Latin1Char>::value) \
-                            dst[j] = CharT(REPLACE_UTF8_LATIN1);        \
-                        else                                            \
-                            dst[j] = CharT(REPLACE_UTF8);               \
+                    char16_t replacement;                               \
+                    if (ErrorAction == OnUTF8Error::InsertReplacementCharacter) { \
+                        replacement = REPLACEMENT_CHARACTER;            \
                     } else {                                            \
-                        MOZ_ASSERT(Action == CountAndIgnoreInvalids ||  \
-                                   Action == FindEncoding);             \
+                        MOZ_ASSERT(ErrorAction == OnUTF8Error::InsertQuestionMark); \
+                        replacement = '?';                              \
+                    }                                                   \
+                    if (dst(replacement) == LoopDisposition::Break) {   \
+                        break;                                          \
                     }                                                   \
                     n = n2;                                             \
                     goto invalidMultiByteCodeUnit;                      \
                 }                                                       \
             } while (0)
 
             // Check the leading byte.
             if (n < 2 || n > 4) {
@@ -352,164 +348,166 @@ InflateUTF8StringToBuffer(ContextT* cx, 
             for (uint32_t m = 1; m < n; m++) {
                 if ((src[i + m] & 0xC0) != 0x80) {
                     INVALID(ReportInvalidCharacter, i, m);
                 }
             }
 
             // Determine the code unit's length in CharT and act accordingly.
             v = JS::Utf8ToOneUcs4Char((uint8_t*)&src[i], n);
-            if (Action != AssertNoInvalids) {
-                if (v > 0xff) {
-                    RequireUTF16();
-                    if (Action == FindEncoding) {
-                        MOZ_ASSERT(dst == nullptr);
-                        return true;
-                    }
-                } else {
-                    RequireLatin1();
-                }
-            }
             if (v < 0x10000) {
                 // The n-byte UTF8 code unit will fit in a single CharT.
-                if (Action == Copy) {
-                    dst[j] = CharT(v);
+                if (dst(char16_t(v)) == LoopDisposition::Break) {
+                    break;
                 }
             } else {
                 v -= 0x10000;
                 if (v <= 0xFFFFF) {
                     // The n-byte UTF8 code unit will fit in two CharT units.
-                    if (Action == Copy) {
-                        dst[j] = CharT((v >> 10) + 0xD800);
+                    if (dst(char16_t((v >> 10) + 0xD800)) == LoopDisposition::Break) {
+                        break;
                     }
-                    j++;
-                    if (Action == Copy) {
-                        dst[j] = CharT((v & 0x3FF) + 0xDC00);
+                    if (dst(char16_t((v & 0x3FF) + 0xDC00)) == LoopDisposition::Break) {
+                        break;
                     }
-
                 } else {
                     // The n-byte UTF8 code unit won't fit in two CharT units.
                     INVALID(ReportTooBigCharacter, v, 1);
                 }
             }
 
           invalidMultiByteCodeUnit:
-            // Move i to the last byte of the multi-byte code unit;  the loop
+            // Move i to the last byte of the multi-byte code unit; the loop
             // header will do the final i++ to move to the start of the next
             // code unit.
             i += n - 1;
-            if (Action != AssertNoInvalids) {
-                RequireUTF16();
-            }
         }
     }
 
-    if (Action != AssertNoInvalids && Action != FindEncoding) {
-        *dstlenp = j;
-    }
-
     return true;
 }
 
-template <InflateUTF8Action Action, typename CharsT, class ContextT>
+template <OnUTF8Error ErrorAction, typename CharsT>
 static CharsT
-InflateUTF8StringHelper(ContextT* cx, const UTF8Chars src, size_t* outlen)
+InflateUTF8StringHelper(JSContext* cx, const UTF8Chars src, size_t* outlen)
 {
     using CharT = typename CharsT::CharT;
+    static_assert(std::is_same<CharT, char16_t>::value ||
+                  std::is_same<CharT, Latin1Char>::value,
+                  "bad CharT");
+
     *outlen = 0;
 
-    JS::SmallestEncoding encoding;
-    if (!InflateUTF8StringToBuffer<Action, CharT>(cx, src, /* dst = */ nullptr, outlen, &encoding)) {
+    size_t len = 0;
+    bool allASCII = true;
+    auto count = [&](char16_t c) -> LoopDisposition {
+        len++;
+        allASCII &= (c < 0x80);
+        return LoopDisposition::Continue;
+    };
+    if (!InflateUTF8ToUTF16<ErrorAction>(cx, src, count)) {
         return CharsT();
     }
+    *outlen = len;
 
     CharT* dst = cx->template pod_malloc<CharT>(*outlen + 1);  // +1 for NUL
     if (!dst) {
         ReportOutOfMemory(cx);
         return CharsT();
     }
 
-    if (encoding == JS::SmallestEncoding::ASCII) {
+    if (allASCII) {
         size_t srclen = src.length();
         MOZ_ASSERT(*outlen == srclen);
         for (uint32_t i = 0; i < srclen; i++) {
             dst[i] = CharT(src[i]);
         }
     } else {
-        MOZ_ALWAYS_TRUE((InflateUTF8StringToBuffer<Copy, CharT>(cx, src, dst, outlen, &encoding)));
+        constexpr OnUTF8Error errorMode = std::is_same<CharT, Latin1Char>::value
+            ? OnUTF8Error::InsertQuestionMark
+            : OnUTF8Error::InsertReplacementCharacter;
+        size_t j = 0;
+        auto push = [&](char16_t c) -> LoopDisposition {
+            dst[j++] = CharT(c);
+            return LoopDisposition::Continue;
+        };
+        MOZ_ALWAYS_TRUE((InflateUTF8ToUTF16<errorMode>(cx, src, push)));
+        MOZ_ASSERT(j == len);
     }
-
     dst[*outlen] = 0;    // NUL char
 
     return CharsT(dst, *outlen);
 }
 
 TwoByteCharsZ
 JS::UTF8CharsToNewTwoByteCharsZ(JSContext* cx, const UTF8Chars utf8, size_t* outlen)
 {
-    return InflateUTF8StringHelper<CountAndReportInvalids, TwoByteCharsZ>(cx, utf8, outlen);
+    return InflateUTF8StringHelper<OnUTF8Error::Throw, TwoByteCharsZ>(cx, utf8, outlen);
 }
 
 TwoByteCharsZ
 JS::UTF8CharsToNewTwoByteCharsZ(JSContext* cx, const ConstUTF8CharsZ& utf8, size_t* outlen)
 {
     UTF8Chars chars(utf8.c_str(), strlen(utf8.c_str()));
-    return InflateUTF8StringHelper<CountAndReportInvalids, TwoByteCharsZ>(cx, chars, outlen);
+    return InflateUTF8StringHelper<OnUTF8Error::Throw, TwoByteCharsZ>(cx, chars, outlen);
 }
 
 TwoByteCharsZ
 JS::LossyUTF8CharsToNewTwoByteCharsZ(JSContext* cx, const JS::UTF8Chars utf8, size_t* outlen)
 {
-    return InflateUTF8StringHelper<CountAndIgnoreInvalids, TwoByteCharsZ>(cx, utf8, outlen);
+    return InflateUTF8StringHelper<OnUTF8Error::InsertReplacementCharacter, TwoByteCharsZ>(cx, utf8, outlen);
 }
 
 TwoByteCharsZ
 JS::LossyUTF8CharsToNewTwoByteCharsZ(JSContext* cx, const JS::ConstUTF8CharsZ& utf8, size_t* outlen)
 {
     UTF8Chars chars(utf8.c_str(), strlen(utf8.c_str()));
-    return InflateUTF8StringHelper<CountAndIgnoreInvalids, TwoByteCharsZ>(cx, chars, outlen);
+    return InflateUTF8StringHelper<OnUTF8Error::InsertReplacementCharacter, TwoByteCharsZ>(cx, chars, outlen);
 }
 
 JS::SmallestEncoding
 JS::FindSmallestEncoding(UTF8Chars utf8)
 {
-    JS::SmallestEncoding encoding;
-    MOZ_ALWAYS_TRUE((InflateUTF8StringToBuffer<FindEncoding, char16_t, JSContext>(
-                         /* cx = */ nullptr,
-                         utf8,
-                         /* dst = */ nullptr,
-                         /* dstlen = */ nullptr,
-                         &encoding)));
+    JS::SmallestEncoding encoding = JS::SmallestEncoding::ASCII;
+    auto onChar = [&](char16_t c) -> LoopDisposition {
+        if (c >= 0x80) {
+            if (c < 0x100) {
+                encoding = JS::SmallestEncoding::Latin1;
+            } else {
+                encoding = JS::SmallestEncoding::UTF16;
+                return LoopDisposition::Break;
+            }
+        }
+        return LoopDisposition::Continue;
+    };
+    MOZ_ALWAYS_TRUE((InflateUTF8ToUTF16<OnUTF8Error::InsertReplacementCharacter>(
+                         /* cx = */ nullptr, utf8, onChar)));
     return encoding;
 }
 
 Latin1CharsZ
 JS::UTF8CharsToNewLatin1CharsZ(JSContext* cx, const UTF8Chars utf8, size_t* outlen)
 {
-    return InflateUTF8StringHelper<CountAndReportInvalids, Latin1CharsZ>(cx, utf8, outlen);
+    return InflateUTF8StringHelper<OnUTF8Error::Throw, Latin1CharsZ>(cx, utf8, outlen);
 }
 
 Latin1CharsZ
 JS::LossyUTF8CharsToNewLatin1CharsZ(JSContext* cx, const UTF8Chars utf8, size_t* outlen)
 {
-    return InflateUTF8StringHelper<CountAndIgnoreInvalids, Latin1CharsZ>(cx, utf8, outlen);
+    return InflateUTF8StringHelper<OnUTF8Error::InsertQuestionMark, Latin1CharsZ>(cx, utf8, outlen);
 }
 
 #ifdef DEBUG
 void
 JS::ConstUTF8CharsZ::validate(size_t aLength)
 {
     MOZ_ASSERT(data_);
     UTF8Chars chars(data_, aLength);
-    InflateUTF8StringToBuffer<AssertNoInvalids, char16_t, JSContext>(
-        /* cx = */ nullptr,
-        chars,
-        /* dst = */ nullptr,
-        /* dstlen = */ nullptr,
-        /* smallestEncoding = */ nullptr);
+    auto nop = [](char16_t) -> LoopDisposition { return LoopDisposition::Continue; };
+    InflateUTF8ToUTF16<OnUTF8Error::Crash>(/* cx = */ nullptr, chars, nop);
 }
 #endif
 
 bool
 JS::StringIsASCII(const char* s)
 {
     while (*s) {
         if (*s & 0x80) {
--- a/js/xpconnect/src/XPCShellImpl.cpp
+++ b/js/xpconnect/src/XPCShellImpl.cpp
@@ -1219,17 +1219,17 @@ XRE_XPCShellMain(int argc, char** argv, 
         if (argc > 1 && !strcmp(argv[1], "-a")) {
             if (argc < 3) {
                 return usage();
             }
 
             nsCOMPtr<nsIFile> dir;
             rv = XRE_GetFileFromPath(argv[2], getter_AddRefs(dir));
             if (NS_SUCCEEDED(rv)) {
-                appDir = do_QueryInterface(dir, &rv);
+                appDir = dir;
                 dirprovider.SetAppDir(appDir);
             }
             if (NS_FAILED(rv)) {
                 printf("Couldn't use given appdir.\n");
                 return 1;
             }
             argc -= 2;
             argv += 2;
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -8283,33 +8283,27 @@ MinimumFontSizeFor(nsPresContext* aPresC
   nsIPresShell* presShell = aPresContext->PresShell();
 
   uint32_t emPerLine = presShell->FontSizeInflationEmPerLine();
   uint32_t minTwips = presShell->FontSizeInflationMinTwips();
   if (emPerLine == 0 && minTwips == 0) {
     return 0;
   }
 
-  // Clamp the container width to the device dimensions
-  nscoord iFrameISize = aWritingMode.IsVertical()
-    ? aPresContext->GetVisibleArea().height
-    : aPresContext->GetVisibleArea().width;
-  nscoord effectiveContainerISize = std::min(iFrameISize, aContainerISize);
-
   nscoord byLine = 0, byInch = 0;
   if (emPerLine != 0) {
-    byLine = effectiveContainerISize / emPerLine;
+    byLine = aContainerISize / emPerLine;
   }
   if (minTwips != 0) {
     // REVIEW: Is this giving us app units and sizes *not* counting
     // viewport scaling?
     gfxSize screenSize = aPresContext->ScreenSizeInchesForFontInflation();
     float deviceISizeInches = aWritingMode.IsVertical()
       ? screenSize.height : screenSize.width;
-    byInch = NSToCoordRound(effectiveContainerISize /
+    byInch = NSToCoordRound(aContainerISize /
                             (deviceISizeInches * 1440 /
                              minTwips ));
   }
   return std::max(byLine, byInch);
 }
 
 /* static */ float
 nsLayoutUtils::FontSizeInflationInner(const nsIFrame *aFrame,
@@ -8447,17 +8441,17 @@ nsLayoutUtils::InflationMinFontSizeFor(c
       // FIXME: The need to null-check here is sort of a bug, and might
       // lead to incorrect results.
       if (!data || !data->InflationEnabled()) {
         return 0;
       }
 
       return MinimumFontSizeFor(aFrame->PresContext(),
                                 aFrame->GetWritingMode(),
-                                data->EffectiveISize());
+                                data->UsableISize());
     }
   }
 
   MOZ_ASSERT(false, "root should always be container");
 
   return 0;
 }
 
--- a/layout/generic/ReflowInput.cpp
+++ b/layout/generic/ReflowInput.cpp
@@ -344,41 +344,48 @@ ReflowInput::SetComputedHeight(nscoord a
     ComputedHeight() = aComputedHeight;
     LayoutFrameType frameType = mFrame->Type();
     if (frameType != LayoutFrameType::Viewport || !mWritingMode.IsVertical()) {
       InitResizeFlags(mFrame->PresContext(), frameType);
     }
   }
 }
 
+/* static */ void
+ReflowInput::MarkFrameChildrenDirty(nsIFrame* aFrame)
+{
+  if (aFrame->IsXULBoxFrame()) {
+    return;
+  }
+  // Mark all child frames as dirty.
+  //
+  // We don't do this for XUL boxes because they handle their child
+  // reflow separately.
+  for (nsIFrame::ChildListIterator childLists(aFrame); !childLists.IsDone();
+       childLists.Next()) {
+    for (nsIFrame* childFrame : childLists.CurrentList()) {
+      if (!childFrame->IsTableColGroupFrame()) {
+        childFrame->AddStateBits(NS_FRAME_IS_DIRTY);
+      }
+    }
+  }
+}
+
 void
 ReflowInput::Init(nsPresContext*     aPresContext,
                         const LogicalSize* aContainingBlockSize,
                         const nsMargin*    aBorder,
                         const nsMargin*    aPadding)
 {
-  if ((mFrame->GetStateBits() & NS_FRAME_IS_DIRTY) &&
-      !mFrame->IsXULBoxFrame()) {
-    // Mark all child frames as dirty.
-    //
-    // We don't do this for XUL boxes because they handle their child
-    // reflow separately.
-    //
+  if ((mFrame->GetStateBits() & NS_FRAME_IS_DIRTY)) {
     // FIXME (bug 1376530): It would be better for memory locality if we
     // did this as we went.  However, we need to be careful not to do
     // this twice for any particular child if we reflow it twice.  The
     // easiest way to accomplish that is to do it at the start.
-    for (nsIFrame::ChildListIterator childLists(mFrame);
-         !childLists.IsDone(); childLists.Next()) {
-      for (nsIFrame* childFrame : childLists.CurrentList()) {
-        if (!childFrame->IsTableColGroupFrame()) {
-          childFrame->AddStateBits(NS_FRAME_IS_DIRTY);
-        }
-      }
-    }
+    MarkFrameChildrenDirty(mFrame);
   }
 
   if (AvailableISize() == NS_UNCONSTRAINEDSIZE) {
     // Look up the parent chain for an orthogonal inline limit,
     // and reset AvailableISize() if found.
     for (const ReflowInput *parent = mParentReflowInput;
          parent != nullptr; parent = parent->mParentReflowInput) {
       if (parent->GetWritingMode().IsOrthogonalTo(mWritingMode) &&
@@ -633,19 +640,21 @@ ReflowInput::InitResizeFlags(nsPresConte
       // the second time we'd set it to false even without the
       // NS_FRAME_IS_DIRTY bit already set.
       if (mFrame->IsSVGForeignObjectFrame()) {
         // Foreign object frames use dirty bits in a special way.
         mFrame->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
         nsIFrame *kid = mFrame->PrincipalChildList().FirstChild();
         if (kid) {
           kid->AddStateBits(NS_FRAME_IS_DIRTY);
+          MarkFrameChildrenDirty(kid);
         }
       } else {
         mFrame->AddStateBits(NS_FRAME_IS_DIRTY);
+        MarkFrameChildrenDirty(mFrame);
       }
 
       // Mark intrinsic widths on all descendants dirty.  We need to do
       // this (1) since we're changing the size of text and need to
       // clear text runs on text frames and (2) since we actually are
       // changing some intrinsic widths, but only those that live inside
       // of containers.
 
@@ -2585,17 +2594,17 @@ SizeComputationInput::InitOffsets(Writin
   }
   else if (nsSVGUtils::IsInSVGTextSubtree(mFrame)) {
     ComputedPhysicalPadding().SizeTo(0, 0, 0, 0);
     needPaddingProp = false;
   }
   else if (aPadding) { // padding is an input arg
     ComputedPhysicalPadding() = *aPadding;
     needPaddingProp = mFrame->StylePadding()->IsWidthDependent() ||
-	  (mFrame->GetStateBits() & NS_FRAME_REFLOW_ROOT);
+    (mFrame->GetStateBits() & NS_FRAME_REFLOW_ROOT);
   }
   else {
     needPaddingProp = ComputePadding(aWM, aPercentBasis, aFrameType);
   }
 
   // Add [align|justify]-content:baseline padding contribution.
   typedef const FramePropertyDescriptor<SmallValueHolder<nscoord>>* Prop;
   auto ApplyBaselinePadding = [this, &needPaddingProp]
--- a/layout/generic/ReflowInput.h
+++ b/layout/generic/ReflowInput.h
@@ -1004,13 +1004,19 @@ protected:
   // in the aAxis dimension that goes inside the edge given by box-sizing;
   // aOutsideBoxSizing returns the rest.
   void CalculateBorderPaddingMargin(mozilla::LogicalAxis aAxis,
                                     nscoord aContainingBlockSize,
                                     nscoord* aInsideBoxSizing,
                                     nscoord* aOutsideBoxSizing) const;
 
   void CalculateBlockSideMargins(LayoutFrameType aFrameType);
+
+  /**
+   * Make all descendants of this frame dirty.
+   * Exceptions: XULBoxFrame and TabeColGroupFrame children.
+   */
+  static void MarkFrameChildrenDirty(nsIFrame* aFrame);
 };
 
 } // namespace mozilla
 
 #endif // mozilla_ReflowInput_h
--- a/layout/generic/nsFontInflationData.cpp
+++ b/layout/generic/nsFontInflationData.cpp
@@ -22,63 +22,65 @@ NS_DECLARE_FRAME_PROPERTY_DELETABLE(Font
 
 /* static */ nsFontInflationData*
 nsFontInflationData::FindFontInflationDataFor(const nsIFrame *aFrame)
 {
   // We have one set of font inflation data per block formatting context.
   const nsIFrame *bfc = FlowRootFor(aFrame);
   NS_ASSERTION(bfc->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT,
                "should have found a flow root");
+  MOZ_ASSERT(aFrame->GetWritingMode().IsVertical() ==
+               bfc->GetWritingMode().IsVertical(),
+             "current writing mode should match that of our flow root");
 
   return bfc->GetProperty(FontInflationDataProperty());
 }
 
 /* static */ bool
 nsFontInflationData::UpdateFontInflationDataISizeFor(const ReflowInput& aReflowInput)
 {
   nsIFrame *bfc = aReflowInput.mFrame;
   NS_ASSERTION(bfc->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT,
                "should have been given a flow root");
   nsFontInflationData *data = bfc->GetProperty(FontInflationDataProperty());
   bool oldInflationEnabled;
-  nscoord oldNCAISize;
+  nscoord oldUsableISize;
   if (data) {
-    oldNCAISize = data->mNCAISize;
+    oldUsableISize = data->mUsableISize;
     oldInflationEnabled = data->mInflationEnabled;
   } else {
     data = new nsFontInflationData(bfc);
     bfc->SetProperty(FontInflationDataProperty(), data);
-    oldNCAISize = -1;
+    oldUsableISize = -1;
     oldInflationEnabled = true; /* not relevant */
   }
 
   data->UpdateISize(aReflowInput);
 
   if (oldInflationEnabled != data->mInflationEnabled)
     return true;
 
-  return oldInflationEnabled &&
-         oldNCAISize != data->mNCAISize;
+  return oldInflationEnabled && oldUsableISize != data->mUsableISize;
 }
 
 /* static */ void
 nsFontInflationData::MarkFontInflationDataTextDirty(nsIFrame *aBFCFrame)
 {
   NS_ASSERTION(aBFCFrame->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT,
                "should have been given a flow root");
 
   nsFontInflationData *data = aBFCFrame->GetProperty(FontInflationDataProperty());
   if (data) {
     data->MarkTextDirty();
   }
 }
 
 nsFontInflationData::nsFontInflationData(nsIFrame *aBFCFrame)
   : mBFCFrame(aBFCFrame)
-  , mNCAISize(0)
+  , mUsableISize(0)
   , mTextAmount(0)
   , mTextThreshold(0)
   , mInflationEnabled(false)
   , mTextDirty(true)
 {
 }
 
 /**
@@ -209,17 +211,33 @@ nsFontInflationData::UpdateISize(const R
   nscoord newTextThreshold = (newNCAISize * lineThreshold) / 100;
 
   if (mTextThreshold <= mTextAmount && mTextAmount < newTextThreshold) {
     // Because we truncate our scan when we hit sufficient text, we now
     // need to rescan.
     mTextDirty = true;
   }
 
-  mNCAISize = newNCAISize;
+  // Font inflation increases the font size for a given flow root so that the
+  // text is legible when we've zoomed such that the respective nearest common
+  // ancestor's (NCA) full inline-size (ISize) fills the screen. We assume how-
+  // ever that we don't want to zoom out further than the root iframe's ISize
+  // (i.e. the viewport for a top-level document, or the containing iframe
+  // otherwise), since in some cases zooming out further might not even be
+  // possible or make sense.
+  // Hence the ISize assumed to be usable for displaying text is limited to the
+  // visible area.
+  nsPresContext* presContext = bfc->PresContext();
+  MOZ_ASSERT(bfc->GetWritingMode().IsVertical() ==
+               nca->GetWritingMode().IsVertical(),
+             "writing mode of NCA should match that of its flow root");
+  nscoord iFrameISize = bfc->GetWritingMode().IsVertical()
+                          ? presContext->GetVisibleArea().height
+                          : presContext->GetVisibleArea().width;
+  mUsableISize = std::min(iFrameISize, newNCAISize);
   mTextThreshold = newTextThreshold;
   mInflationEnabled = mTextAmount >= mTextThreshold;
 }
 
 /* static */ nsIFrame*
 nsFontInflationData::FindEdgeInflatableFrameIn(nsIFrame* aFrame,
                                                SearchDirection aDirection)
 {
--- a/layout/generic/nsFontInflationData.h
+++ b/layout/generic/nsFontInflationData.h
@@ -14,32 +14,32 @@
 class nsFontInflationData
 {
   using ReflowInput = mozilla::ReflowInput;
 
 public:
 
   static nsFontInflationData* FindFontInflationDataFor(const nsIFrame *aFrame);
 
-  // Returns whether the effective width changed (which requires the
-  // caller to mark its descendants dirty
+  // Returns whether the usable width changed (which requires the
+  // caller to mark its descendants dirty)
   static bool
     UpdateFontInflationDataISizeFor(const ReflowInput& aReflowInput);
 
   static void MarkFontInflationDataTextDirty(nsIFrame *aFrame);
 
   bool InflationEnabled() {
     if (mTextDirty) {
       ScanText();
     }
     return mInflationEnabled;
   }
 
-  nscoord EffectiveISize() const {
-    return mNCAISize;
+  nscoord UsableISize() const {
+    return mUsableISize;
   }
 
 private:
 
   explicit nsFontInflationData(nsIFrame* aBFCFrame);
 
   nsFontInflationData(const nsFontInflationData&) = delete;
   void operator=(const nsFontInflationData&) = delete;
@@ -62,15 +62,15 @@ private:
   {
     while (!(aFrame->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT)) {
       aFrame = aFrame->GetParent();
     }
     return aFrame;
   }
 
   nsIFrame *mBFCFrame;
-  nscoord mNCAISize;
+  nscoord mUsableISize;
   nscoord mTextAmount, mTextThreshold;
   bool mInflationEnabled; // for this BFC
   bool mTextDirty;
 };
 
 #endif /* !defined(nsFontInflationData_h_) */
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-inflation/fixed-width-body-viewport-ref.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<!--
+Our body is 2000px wide, but the effective container size for font inflation will be clamped to the
+viewport width of 1000px (this test uses a custom value for browser.viewport.desktopWidth).
+The minimum font size at 20em per line in an 1000px container is 50px. This means we map 0px-75px
+into 50px-75px, so 12px gets mapped to 54px.
+-->
+<html>
+<head>
+  <style>
+    body {
+      width: 2000px;
+      line-height: 1.1;
+      font-size: 54px;
+      overflow: hidden;
+      -moz-text-size-adjust: none;
+    }
+  </style>
+</head>
+  <body>
+    PERSONS attempting to find a motive in this narrative will be prosecuted;
+    persons attempting to find a moral in it will be banished;
+    persons attempting to find a plot in it will be shot.<br>
+    BY ORDER OF THE AUTHOR,<br>
+    Per G.G., Chief of Ordnance.
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-inflation/fixed-width-body-viewport.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<!--
+Our body is 2000px wide, but the effective container size for font inflation will be clamped to the
+viewport width of 1000px (this test uses a custom value for browser.viewport.desktopWidth).
+The minimum font size at 20em per line in an 1000px container is 50px. This means we map 0px-75px
+into 50px-75px, so 12px gets mapped to 54px.
+-->
+<html>
+<head>
+  <style>
+    body {
+      width: 2000px;
+      line-height: 1.1;
+      font-size: 12px;
+      overflow: hidden;
+    }
+  </style>
+</head>
+  <body>
+    PERSONS attempting to find a motive in this narrative will be prosecuted;
+    persons attempting to find a moral in it will be banished;
+    persons attempting to find a plot in it will be shot.<br>
+    BY ORDER OF THE AUTHOR,<br>
+    Per G.G., Chief of Ordnance.
+  </body>
+</html>
--- a/layout/reftests/font-inflation/reftest.list
+++ b/layout/reftests/font-inflation/reftest.list
@@ -32,18 +32,17 @@ test-pref(font.size.inflation.emPerLine,
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == input-text-2-noheight.html input-text-2-noheight-ref.html
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == input-text-3-height.html input-text-3-height-ref.html
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == input-text-3-noheight.html input-text-3-noheight-ref.html
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == textarea-1.html textarea-1-ref.html
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == textarea-2.html textarea-2-ref.html
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == textarea-3.html textarea-3-ref.html
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == css-transform-1.html css-transform-1-ref.html
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) fuzzy-if(webrender,0-1,0-19) == css-transform-2.html css-transform-2-ref.html
-# skipped - bug 1380830
-fuzzy-if(asyncPan&&!layersGPUAccelerated,0-102,0-1764) skip test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == container-with-clamping.html container-with-clamping-ref.html
+fuzzy-if(asyncPan&&!layersGPUAccelerated,0-102,0-1764) test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == container-with-clamping.html container-with-clamping-ref.html
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) load video-1.html
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == intrinsic-min-1.html intrinsic-min-1-ref.html
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == intrinsic-max-1.html intrinsic-max-1-ref.html
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == intrinsic-fit-1a.html intrinsic-fit-1a-ref.html
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == intrinsic-fit-1b.html intrinsic-fit-1b-ref.html
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == intrinsic-fit-1c.html intrinsic-fit-1c-ref.html
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == intrinsic-fit-2a.html intrinsic-fit-1a-ref.html
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == intrinsic-fit-2b.html intrinsic-fit-1b-ref.html
@@ -63,16 +62,22 @@ test-pref(font.size.inflation.emPerLine,
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == disable-fontinfl-on-mobile-2.html disable-fontinfl-on-mobile-ref.html
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == disable-fontinfl-on-mobile-3.html disable-fontinfl-on-mobile-ref.html
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) != disable-fontinfl-on-mobile-5.html disable-fontinfl-on-mobile-ref.html
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == preformatted-text.html preformatted-text-ref.html
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == fixed-height-body.html fixed-height-body-ref.html # Bug 1392106
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == fixed-height-body-child.html fixed-height-body-child-ref.html # Bug 1392106
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == consecutive-inline.html consecutive-inline-ref.html
 
+# Ordinarily, reftests use a browser.viewport.desktopWidth of 800px, same as the
+# size of the reftest document.  The failure condition of the test below however
+# depends on the initial window size being smaller than the viewport the
+# MobileViewportManager eventually calculates, so we use a bigger value here.
+test-pref(font.size.inflation.emPerLine,20) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) test-pref(browser.viewport.desktopWidth,1000) == fixed-width-body-viewport.html fixed-width-body-viewport-ref.html
+
 # The tests below use nonzero values of the lineThreshold preference.
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,100) == text-1.html text-1.html
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,100) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == list-1.html list-1-ref.html # Bug 1392106
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,100) == threshold-1a.html threshold-1a.html
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,100) == threshold-1b.html threshold-1b-ref.html
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,100) == threshold-1c.html threshold-1c-ref.html
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,100) == threshold-2.html threshold-2-ref.html
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,100) == threshold-3.html threshold-3-ref.html
--- a/layout/tools/reftest/manifest.jsm
+++ b/layout/tools/reftest/manifest.jsm
@@ -542,16 +542,19 @@ sandbox.compareRetainedDisplayLists = g.
     }
 
     // Graphics features
     sandbox.usesRepeatResampling = sandbox.d2d;
 
     // Running in a test-verify session?
     sandbox.verify = prefs.getBoolPref("reftest.verify", false);
 
+    // Running with serviceworker e10s redesign enabled?
+    sandbox.serviceWorkerE10s = prefs.getBoolPref("dom.serviceWorkers.parent_intercept", false);
+
     if (!g.dumpedConditionSandbox) {
         g.logger.info("Dumping JSON representation of sandbox");
         g.logger.info(JSON.stringify(Cu.waiveXrays(sandbox)));
         g.dumpedConditionSandbox = true;
     }
 
     return sandbox;
 }
--- a/modules/libjar/zipwriter/nsZipDataStream.cpp
+++ b/modules/libjar/zipwriter/nsZipDataStream.cpp
@@ -46,18 +46,17 @@ nsresult nsZipDataStream::Init(nsZipWrit
         nsCOMPtr<nsIStreamConverter> converter =
                               new nsDeflateConverter(aCompression);
         NS_ENSURE_TRUE(converter, NS_ERROR_OUT_OF_MEMORY);
 
         rv = converter->AsyncConvertData("uncompressed", "rawdeflate", mOutput,
                                          nullptr);
         NS_ENSURE_SUCCESS(rv, rv);
 
-        mOutput = do_QueryInterface(converter, &rv);
-        NS_ENSURE_SUCCESS(rv, rv);
+        mOutput = converter;
     }
     else {
         mHeader->mMethod = ZIP_METHOD_STORE;
     }
 
     return NS_OK;
 }
 
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -5413,17 +5413,17 @@ pref("network.trr.useGET", false);
 pref("network.trr.confirmationNS", "example.com");
 // hardcode the resolution of the hostname in network.trr.uri instead of
 // relying on the system resolver to do it for you
 pref("network.trr.bootstrapAddress", "");
 // TRR blacklist entry expire time (in seconds). Default is one minute.
 // Meant to survive basically a page load.
 pref("network.trr.blacklist-duration", 60);
 // Single TRR request timeout, in milliseconds
-pref("network.trr.request-timeout", 3000);
+pref("network.trr.request-timeout", 1500);
 // Allow AAAA entries to be used "early", before the A results are in
 pref("network.trr.early-AAAA", false);
 // Explicitly disable ECS (EDNS Client Subnet, RFC 7871)
 pref("network.trr.disable-ECS", true);
 
 pref("captivedetect.canonicalURL", "http://detectportal.firefox.com/success.txt");
 pref("captivedetect.canonicalContent", "success\n");
 pref("captivedetect.maxWaitingTime", 5000);
--- a/netwerk/base/nsStreamListenerTee.cpp
+++ b/netwerk/base/nsStreamListenerTee.cpp
@@ -80,18 +80,17 @@ nsStreamListenerTee::OnDataAvailable(nsI
         mInputTee = do_QueryInterface(tee, &rv);
         if (NS_FAILED(rv)) return rv;
     }
     else {
         // re-initialize the input tee since the input stream may have changed.
         rv = mInputTee->SetSource(input);
         if (NS_FAILED(rv)) return rv;
 
-        tee = do_QueryInterface(mInputTee, &rv);
-        if (NS_FAILED(rv)) return rv;
+        tee = mInputTee;
     }
 
     return mListener->OnDataAvailable(request, context, tee, offset, count);
 }
 
 NS_IMETHODIMP
 nsStreamListenerTee::CheckListenerChain()
 {
--- a/netwerk/cache/nsCacheService.cpp
+++ b/netwerk/cache/nsCacheService.cpp
@@ -741,18 +741,19 @@ nsCacheProfilePrefObserver::ReadPrefs(ns
                                                       "Cache");
             }
         }
         // use file cache in build tree only if asked, to avoid cache dir litter
         if (!directory && PR_GetEnv("NECKO_DEV_ENABLE_DISK_CACHE")) {
             rv = NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR,
                                         getter_AddRefs(directory));
         }
-        if (directory)
-            mDiskCacheParentDirectory = do_QueryInterface(directory, &rv);
+        if (directory) {
+            mDiskCacheParentDirectory = directory;
+        }
     }
     if (mDiskCacheParentDirectory) {
         bool firstSmartSizeRun;
         rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF,
                                  &firstSmartSizeRun);
         if (NS_FAILED(rv))
             firstSmartSizeRun = false;
         if (PermittedToSmartSize(branch, firstSmartSizeRun)) {
@@ -811,18 +812,19 @@ nsCacheProfilePrefObserver::ReadPrefs(ns
         }
 #if DEBUG
         if (!directory) {
             // use current process directory during development
             rv = NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR,
                                         getter_AddRefs(directory));
         }
 #endif
-        if (directory)
-            mOfflineCacheParentDirectory = do_QueryInterface(directory, &rv);
+        if (directory) {
+            mOfflineCacheParentDirectory = directory;
+        }
     }
 
     // read memory cache device prefs
     (void) branch->GetBoolPref(MEMORY_CACHE_ENABLE_PREF, &mMemoryCacheEnabled);
 
     mMemoryCacheCapacity = -1;
     (void) branch->GetIntPref(MEMORY_CACHE_CAPACITY_PREF,
                               &mMemoryCacheCapacity);
--- a/netwerk/streamconv/converters/nsMultiMixedConv.cpp
+++ b/netwerk/streamconv/converters/nsMultiMixedConv.cpp
@@ -991,20 +991,17 @@ nsMultiMixedConv::SendData()
     if (NS_FAILED(rv))
         return rv;
 
     rv = ss->ShareData(rawData, mRawDataLength);
     mRawData = nullptr;
     if (NS_FAILED(rv))
         return rv;
 
-    nsCOMPtr<nsIInputStream> inStream(do_QueryInterface(ss, &rv));
-    if (NS_FAILED(rv)) return rv;
-
-    return mPartChannel->SendOnDataAvailable(mContext, inStream, offset, mRawDataLength);
+    return mPartChannel->SendOnDataAvailable(mContext, ss, offset, mRawDataLength);
 }
 
 void
 nsMultiMixedConv::HeadersToDefault()
 {
     mContentLength = UINT64_MAX;
     mContentType.Truncate();
     mContentDisposition.Truncate();
--- a/netwerk/streamconv/nsStreamConverterService.cpp
+++ b/netwerk/streamconv/nsStreamConverterService.cpp
@@ -494,28 +494,22 @@ nsStreamConverterService::AsyncConvertDa
 
             // connect the converter w/ the listener that should get the converted data.
             rv = converter->AsyncConvertData(fromStr.get(), toStr.get(), finalListener, aContext);
             if (NS_FAILED(rv)) {
                 delete converterChain;
                 return rv;
             }
 
-            nsCOMPtr<nsIStreamListener> chainListener(do_QueryInterface(converter, &rv));
-            if (NS_FAILED(rv)) {
-                delete converterChain;
-                return rv;
-            }
-
             // the last iteration of this loop will result in finalListener
             // pointing to the converter that "starts" the conversion chain.
             // this converter's "from" type is the original "from" type. Prior
             // to the last iteration, finalListener will continuously be wedged
             // into the next listener in the chain, then be updated.
-            finalListener = chainListener;
+            finalListener = converter;
         }
         delete converterChain;
         // return the first listener in the chain.
         finalListener.forget(_retval);
     } else {
         // we're going direct.
         rv = listener->AsyncConvertData(aFromType, aToType, aListener, aContext);
         listener.forget(_retval);
--- a/python/mach/mach/logging.py
+++ b/python/mach/mach/logging.py
@@ -88,17 +88,17 @@ class StructuredHumanFormatter(logging.F
         return t
 
 
 class StructuredTerminalFormatter(StructuredHumanFormatter):
     """Log formatter for structured messages writing to a terminal."""
 
     def set_terminal(self, terminal):
         self.terminal = terminal
-        self._sgr0 = blessings.tigetstr('sgr0') or '' if terminal and blessings else ''
+        self._sgr0 = terminal.normal if terminal and blessings else ''
 
     def format(self, record):
         f = record.msg.format(**record.params)
 
         if not self.write_times:
             return f
 
         t = self.terminal.blue(format_seconds(self._time(record)))
--- a/taskcluster/ci/config.yml
+++ b/taskcluster/ci/config.yml
@@ -14,17 +14,19 @@ treeherder:
         'Fxfn-l': 'Firefox functional tests (local)'
         'Fxfn-l-e10s': 'Firefox functional tests (local) with e10s'
         'Fxfn-r': 'Firefox functional tests (remote)'
         'Fxfn-r-e10s': 'Firefox functional tests (remote) with e10s'
         'M': 'Mochitests'
         'M-e10s': 'Mochitests with e10s'
         'M-V': 'Mochitests on Valgrind'
         'R': 'Reftests'
+        'R-sw': 'Reftests with serviceworker redesign enabled'
         'R-e10s': 'Reftests with e10s'
+        'R-sw-e10s': 'Reftests with serviceworker redesign and e10s'
         'Rap': 'Raptor performance tests on Firefox'
         'Rap-e10s': 'Raptor performance tests on Firefox with e10s'
         'Rap-C': 'Raptor performance tests on Google Chrome'
         'Rap-C-e10s': 'Raptor performance tests on Google Chrome with e10s'
         'T': 'Talos performance tests'
         'Tsd': 'Talos performance tests with Stylo disabled'
         'Tss': 'Talos performance tests with Stylo sequential'
         'T-e10s': 'Talos performance tests with e10s'
--- a/taskcluster/ci/test/reftest.yml
+++ b/taskcluster/ci/test/reftest.yml
@@ -2,16 +2,20 @@ job-defaults:
     target:
         by-test-platform:
             android-em-7.0-x86/opt: geckoview-androidTest.apk
             default: null
     tier:
         by-test-platform:
             android-em-7.0-x86/opt: 3
             default: default
+    serviceworker-e10s:
+        by-test-platform:
+            linux64/debug: both
+            default: false
     mozharness:
         script:
             by-test-platform:
                 android-em.*: android_emulator_unittest.py
                 default: desktop_unittest.py
         config:
             by-test-platform:
                 android-em-7.0-x86/opt:
--- a/taskcluster/ci/test/talos.yml
+++ b/taskcluster/ci/test/talos.yml
@@ -606,18 +606,17 @@ talos-xperf:
     run-as-administrator:
         by-test-platform:
             windows7-32.*: false
             windows10-64.*: true
     run-on-projects:
         by-test-platform:
             windows7-32-msvc/.*: ['mozilla-beta', 'mozilla-central', 'try']
             windows7-32(-pgo)?/.*: ['mozilla-beta', 'mozilla-central', 'mozilla-inbound', 'autoland', 'try']
-            windows10-64(-pgo)?/opt: ['mozilla-beta', 'mozilla-central', 'mozilla-inbound', 'autoland', 'try']
-            .*-qr/.*: []  # this test is not useful with webrender
+            windows10-64.*/opt: ['mozilla-beta', 'mozilla-central', 'mozilla-inbound', 'autoland', 'try']
             default: []
     tier:
         by-test-platform:
             windows7-32.*: default
             windows10-64(-pgo)?/.*: default
             windows10-64-ccov/debug: 3
             default: 3  # this should be disabled but might run via try syntax anyway, so explicitly downgrade to tier-3
     mozharness:
--- a/taskcluster/ci/test/test-sets.yml
+++ b/taskcluster/ci/test/test-sets.yml
@@ -211,16 +211,17 @@ windows-tests:
     - mochitest-webgl2-ext
     - reftest
     - reftest-no-accel
     - test-verify
     - test-verify-gpu
     - test-verify-wpt
     - web-platform-tests
     - web-platform-tests-reftests
+    - web-platform-tests-wdspec
     - xpcshell
 
 windows-talos:
     - talos-bcv
     - talos-chrome
     - talos-damp
     - talos-dromaeojs
     - talos-g1
--- a/taskcluster/docs/attributes.rst
+++ b/taskcluster/docs/attributes.rst
@@ -113,16 +113,22 @@ This is the name used to refer to a "job
 some kinds, ``-j`` also matches against ``build_platform``.
 
 test_chunk
 ==========
 
 This is the chunk number of a chunked test suite (talos or unittest).  Note
 that this is a string!
 
+serviceworker_e10s
+==================
+
+For test suites which distinguish whether or not they run with the serviceworker
+e10s redesign enabled.
+
 e10s
 ====
 
 For test suites which distinguish whether they run with or without e10s, this
 boolean value identifies this particular run.
 
 image_name
 ==========
--- a/taskcluster/docs/versioncontrol.rst
+++ b/taskcluster/docs/versioncontrol.rst
@@ -10,24 +10,30 @@ components.
 
 Vendored robustcheckout
 -----------------------
 
 The ``robustcheckout`` Mercurial extension is used throughout CI to
 perform clones and working directory updates. The canonical home of
 the extension is in the
 https://hg.mozilla.org/hgcustom/version-control-tools repository
-at the path ``hgext/robustcheckout/__init__.py``. A copy of the
-extension is vendored at
-``testing/mozharness/external_tools/robustcheckout.py``.
+at the path ``hgext/robustcheckout/__init__.py``.
+
 
 When upgrading Mercurial, the ``robustcheckout`` extension should also
 be updated to ensure it is compatible with the version of Mercurial
 being upgraded to. Typically, one simply copies the latest version
-from ``version-control-tools`` into the vendored location.
+from ``version-control-tools`` into the vendored locations.
+
+The locations are as follows:
+
+- In-tree: ``testing/mozharness/external_tools/robustcheckout.py``
+- Treescript: ``https://github.com/mozilla-releng/treescript/tree/master/treescript/py2/robustcheckout.py``
+- build-puppet: ``https://github.com/mozilla-releng/build-puppet/blob/master/modules/mercurial/files/robustcheckout.py``
+
 
 Debian Packages for Debian Based Docker Images
 ----------------------------------------------
 
 ``taskcluster/ci/packages/kind.yml`` defines custom Debian packages for
 Mercurial. These are installed in various Docker images.
 
 To upgrade Mercurial, typically you just need to update the source URL
--- a/taskcluster/taskgraph/transforms/tests.py
+++ b/taskcluster/taskgraph/transforms/tests.py
@@ -15,16 +15,17 @@ The test description should be fully for
 transforms, and these transforms should not embody any specific knowledge about
 what should run where. this is the wrong place for special-casing platforms,
 for example - use `all_tests.py` instead.
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 from taskgraph.transforms.base import TransformSequence
+from taskgraph.util.attributes import match_run_on_projects
 from taskgraph.util.schema import resolve_keyed_by, OptimizationSchema
 from taskgraph.util.treeherder import split_symbol, join_symbol, add_suffix
 from taskgraph.util.platforms import platform_family
 from taskgraph.util.schema import (
     validate_schema,
     optionally_keyed_by,
     Schema,
 )
@@ -197,16 +198,24 @@ test_description_schema = Schema({
     Required('chunks'): optionally_keyed_by(
         'test-platform',
         int),
 
     # the time (with unit) after which this task is deleted; default depends on
     # the branch (see below)
     Optional('expires-after'): basestring,
 
+    # Whether to run this task with the serviceworker e10s redesign enabled
+    # (desktop-test only).  If 'both', run one task with and one task without.
+    # Tasks with this enabled have have "-sw" appended to the test name and
+    # treeherder group.
+    Optional('serviceworker-e10s'): optionally_keyed_by(
+        'test-platform', 'project',
+        Any(bool, 'both')),
+
     # Whether to run this task with e10s (desktop-test only).  If false, run
     # without e10s; if true, run with e10s; if 'both', run one task with and
     # one task without e10s.  E10s tasks have "-e10s" appended to the test name
     # and treeherder group.
     Required('e10s'): optionally_keyed_by(
         'test-platform', 'project',
         Any(bool, 'both')),
 
@@ -455,16 +464,17 @@ def set_defaults(config, tests):
         test.setdefault('instance-size', 'default')
         test.setdefault('max-run-time', 3600)
         test.setdefault('reboot', False)
         test.setdefault('virtualization', 'virtual')
         test.setdefault('loopback-audio', False)
         test.setdefault('loopback-video', False)
         test.setdefault('docker-image', {'in-tree': 'desktop1604-test'})
         test.setdefault('checkout', False)
+        test.setdefault('serviceworker-e10s', False)
 
         test['mozharness'].setdefault('extra-options', [])
         test['mozharness'].setdefault('requires-signed-builds', False)
         test['mozharness'].setdefault('tooltool-downloads', False)
         test['mozharness'].setdefault('set-moz-node-path', False)
         test['mozharness'].setdefault('chunked', False)
         test['mozharness'].setdefault('chunking-args', 'this-chunk')
         yield test
@@ -681,16 +691,17 @@ def set_download_symbols(config, tests):
 @transforms.add
 def handle_keyed_by(config, tests):
     """Resolve fields that can be keyed by platform, etc."""
     fields = [
         'instance-size',
         'docker-image',
         'max-run-time',
         'chunks',
+        'serviceworker-e10s',
         'e10s',
         'suite',
         'run-on-projects',
         'os-groups',
         'run-as-administrator',
         'mozharness.chunked',
         'mozharness.config',
         'mozharness.extra-options',
@@ -810,16 +821,47 @@ def handle_run_on_projects(config, tests
     """Handle translating `built-projects` appropriately"""
     for test in tests:
         if test['run-on-projects'] == 'built-projects':
             test['run-on-projects'] = test['build-attributes'].get('run_on_projects', ['all'])
         yield test
 
 
 @transforms.add
+def split_serviceworker_e10s(config, tests):
+    for test in tests:
+        sw = test.pop('serviceworker-e10s')
+
+        test['serviceworker-e10s'] = False
+        test['attributes']['serviceworker_e10s'] = False
+
+        if sw == 'both':
+            yield copy.deepcopy(test)
+            sw = True
+        if sw:
+            if not match_run_on_projects('mozilla-central', test['run-on-projects']):
+                continue
+
+            test['description'] += " with serviceworker-e10s redesign enabled"
+            test['run-on-projects'] = ['mozilla-central']
+            test['test-name'] += '-sw'
+            test['try-name'] += '-sw'
+            test['attributes']['serviceworker_e10s'] = True
+            group, symbol = split_symbol(test['treeherder-symbol'])
+            if group != '?':
+                group += '-sw'
+            else:
+                symbol += '-sw'
+            test['treeherder-symbol'] = join_symbol(group, symbol)
+            test['mozharness']['extra-options'].append(
+                '--setpref="dom.serviceWorkers.parent_intercept=true"')
+        yield test
+
+
+@transforms.add
 def split_e10s(config, tests):
     for test in tests:
         e10s = test['e10s']
 
         test['e10s'] = False
         test['attributes']['e10s'] = False
 
         if e10s == 'both':
--- a/testing/mochitest/tests/SimpleTest/SimpleTest.js
+++ b/testing/mochitest/tests/SimpleTest/SimpleTest.js
@@ -726,17 +726,17 @@ SimpleTest.promiseFocus = function (targ
  * targetWindow should be specified if it is different than 'window'. The actual
  * focused window may be a descendant of targetWindow.
  *
  * @param callback
  *        function called when load and focus are complete
  * @param targetWindow
  *        optional window to be loaded and focused, defaults to 'window'.
  *        This may also be a <browser> element, in which case the window within
- *        that browser will be focused.
+ *        that browser will be focused. This cannot be a window CPOW.
  * @param expectBlankPage
  *        true if targetWindow.location is 'about:blank'. Defaults to false
  */
 SimpleTest.waitForFocus = function (callback, targetWindow, expectBlankPage) {
     // A separate method is used that is serialized and passed to the child
     // process via loadFrameScript. Once the child window is focused, the
     // child will send the WaitForFocus:ChildFocused notification to the parent.
     // If a child frame in a child process must be focused, a
@@ -883,45 +883,25 @@ SimpleTest.waitForFocus = function (call
     var browser = null;
     if (typeof(XULElement) != "undefined" &&
         targetWindow instanceof XULElement &&
         targetWindow.localName == "browser") {
         browser = targetWindow;
     }
 
     var isWrapper = Cu.isCrossProcessWrapper(targetWindow);
-    if (isWrapper || (browser && browser.isRemoteBrowser)) {
-        var mustFocusSubframe = false;
-        if (isWrapper) {
-            // Look for a tabbrowser and see if targetWindow corresponds to one
-            // within that tabbrowser. If not, just return.
-            var tabBrowser = window.gBrowser || null;
-            browser = tabBrowser ? tabBrowser.getBrowserForContentWindow(targetWindow.top) : null;
-            if (!browser) {
-                SimpleTest.info("child process window cannot be focused");
-                return;
-            }
-
-            mustFocusSubframe = (targetWindow != targetWindow.top);
-        }
+    if (isWrapper) {
+        throw new Error("Can't pass CPOW to SimpleTest.focus as the content window.");
+    }
 
-        // If a subframe in a child process needs to be focused, first focus the
-        // parent frame, then send a WaitForFocus:FocusChild message to the child
-        // containing the subframe to focus.
+    if (browser && browser.isRemoteBrowser) {
         browser.messageManager.addMessageListener("WaitForFocus:ChildFocused", function waitTest(msg) {
-            if (mustFocusSubframe) {
-                mustFocusSubframe = false;
-                var mm = gBrowser.selectedBrowser.messageManager;
-                mm.sendAsyncMessage("WaitForFocus:FocusChild", {}, { child: targetWindow } );
-            }
-            else {
-                browser.messageManager.removeMessageListener("WaitForFocus:ChildFocused", waitTest);
-                SimpleTest._pendingWaitForFocusCount--;
-                setTimeout(callback, 0, browser ? browser.contentWindowAsCPOW : targetWindow);
-            }
+            browser.messageManager.removeMessageListener("WaitForFocus:ChildFocused", waitTest);
+            SimpleTest._pendingWaitForFocusCount--;
+            setTimeout(callback, 0, browser);
         });
 
         // Serialize the waitForFocusInner function and run it in the child process.
         var frameScript = "data:,(" + waitForFocusInner.toString() +
                           ")(content, true, " + expectBlankPage + ");";
         browser.messageManager.loadFrameScript(frameScript, true);
         browser.focus();
     }
--- a/testing/mozharness/configs/web_platform_tests/prod_config.py
+++ b/testing/mozharness/configs/web_platform_tests/prod_config.py
@@ -1,24 +1,27 @@
 # ***** BEGIN LICENSE BLOCK *****
 # 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/.
 # ***** END LICENSE BLOCK *****
+
+import os
+
+
 config = {
     "options": [
         "--prefs-root=%(test_path)s/prefs",
         "--processes=1",
         "--config=%(test_path)s/wptrunner.ini",
         "--ca-cert-path=%(test_path)s/tests/tools/certs/cacert.pem",
         "--host-key-path=%(test_path)s/tests/tools/certs/web-platform.test.key",
         "--host-cert-path=%(test_path)s/tests/tools/certs/web-platform.test.pem",
         "--certutil-binary=%(test_install_path)s/bin/certutil",
     ],
 
     "download_minidump_stackwalk": True,
 
     # this would normally be in "exes", but "exes" is clobbered by remove_executables
-    "geckodriver": "%(abs_test_bin_dir)s/geckodriver",
+    "geckodriver": os.path.join("%(abs_test_bin_dir)s", "geckodriver"),
 
     "per_test_category": "web-platform",
 }
-
--- a/testing/mozharness/configs/web_platform_tests/prod_config_windows.py
+++ b/testing/mozharness/configs/web_platform_tests/prod_config_windows.py
@@ -1,30 +1,35 @@
-# ***** BEGIN LICENSE BLOCK *****
-# 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/.
-# ***** END LICENSE BLOCK *****
-
-# This is a template config file for web-platform-tests test.
-
-import sys
-
-config = {
-    "options": [
-        "--prefs-root=%(test_path)s/prefs",
-        "--processes=1",
-        "--config=%(test_path)s/wptrunner.ini",
-        "--ca-cert-path=%(test_path)s/tests/tools/certs/cacert.pem",
-        "--host-key-path=%(test_path)s/tests/tools/certs/web-platform.test.key",
-        "--host-cert-path=%(test_path)s/tests/tools/certs/web-platform.test.pem",
-        "--certutil-binary=%(test_install_path)s/bin/certutil",
-    ],
-
-    "exes": {
-        'python': sys.executable,
-        'hg': 'c:/mozilla-build/hg/hg',
-    },
-
-    "download_minidump_stackwalk": True,
-
-    "per_test_category": "web-platform",
-}
+# ***** BEGIN LICENSE BLOCK *****
+# 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/.
+# ***** END LICENSE BLOCK *****
+
+# This is a template config file for web-platform-tests test.
+
+import os
+import sys
+
+
+config = {
+    "options": [
+        "--prefs-root=%(test_path)s/prefs",
+        "--processes=1",
+        "--config=%(test_path)s/wptrunner.ini",
+        "--ca-cert-path=%(test_path)s/tests/tools/certs/cacert.pem",
+        "--host-key-path=%(test_path)s/tests/tools/certs/web-platform.test.key",
+        "--host-cert-path=%(test_path)s/tests/tools/certs/web-platform.test.pem",
+        "--certutil-binary=%(test_install_path)s/bin/certutil",
+    ],
+
+    "exes": {
+        'python': sys.executable,
+        'hg': 'c:/mozilla-build/hg/hg',
+    },
+
+    "download_minidump_stackwalk": True,
+
+    # this would normally be in "exes", but "exes" is clobbered by remove_executables
+    "geckodriver": os.path.join("%(abs_test_bin_dir)s", "geckodriver.exe"),
+
+    "per_test_category": "web-platform",
+}
--- a/testing/mozharness/configs/web_platform_tests/prod_config_windows_taskcluster.py
+++ b/testing/mozharness/configs/web_platform_tests/prod_config_windows_taskcluster.py
@@ -4,16 +4,17 @@
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 # ***** END LICENSE BLOCK *****
 
 # This is a template config file for web-platform-tests test.
 
 import os
 import sys
 
+
 config = {
     "options": [
         "--prefs-root=%(test_path)s/prefs",
         "--processes=1",
         "--config=%(test_path)s/wptrunner.ini",
         "--ca-cert-path=%(test_path)s/tests/tools/certs/cacert.pem",
         "--host-key-path=%(test_path)s/tests/tools/certs/web-platform.test.key",
         "--host-cert-path=%(test_path)s/tests/tools/certs/web-platform.test.pem",
@@ -47,10 +48,13 @@ config = {
             'architectures': ['32bit', '64bit'],
             'halt_on_failure': True,
             'enabled': True
         }
     ],
 
     "download_minidump_stackwalk": True,
 
+    # this would normally be in "exes", but "exes" is clobbered by remove_executables
+    "geckodriver": os.path.join("%(abs_test_bin_dir)s", "geckodriver.exe"),
+
     "per_test_category": "web-platform",
 }
--- a/testing/mozharness/mozharness/mozilla/testing/talos.py
+++ b/testing/mozharness/mozharness/mozilla/testing/talos.py
@@ -151,16 +151,23 @@ class Talos(TestingMixin, MercurialScrip
             "--e10s to help"
         }],
         [["--enable-webrender"], {
             "action": "store_true",
             "dest": "enable_webrender",
             "default": False,
             "help": "Tries to enable the WebRender compositor.",
         }],
+        [["--setpref"], {
+            "action": "append",
+            "metavar": "PREF=VALUE",
+            "dest": "extra_prefs",
+            "default": [],
+            "help": "Defines an extra user preference."}
+         ],
     ] + testing_config_options + copy.deepcopy(code_coverage_config_options)
 
     def __init__(self, **kwargs):
         kwargs.setdefault('config_options', self.config_options)
         kwargs.setdefault('all_actions', ['clobber',
                                           'download-and-extract',
                                           'populate-webroot',
                                           'create-virtualenv',
@@ -351,16 +358,18 @@ class Talos(TestingMixin, MercurialScrip
         options.extend(self.query_gecko_profile_options())
         # extra arguments
         if args is not None:
             options += args
         if 'talos_extra_options' in self.config:
             options += self.config['talos_extra_options']
         if self.config.get('code_coverage', False):
             options.extend(['--code-coverage'])
+        if self.config['extra_prefs']:
+            options.extend(['--setpref={}'.format(p) for p in self.config['extra_prefs']])
         return options
 
     def populate_webroot(self):
         """Populate the production test slaves' webroots"""
         self.talos_path = os.path.join(
             self.query_abs_dirs()['abs_test_install_dir'], 'talos'
         )
 
--- a/testing/mozharness/scripts/desktop_unittest.py
+++ b/testing/mozharness/scripts/desktop_unittest.py
@@ -160,16 +160,23 @@ class DesktopUnittest(TestingMixin, Merc
             "help": "Tries to enable the WebRender compositor."}
          ],
         [["--gpu-required"], {
             "action": "store_true",
             "dest": "gpu_required",
             "default": False,
             "help": "Run additional verification on modified tests using gpu instances."}
          ],
+        [["--setpref"], {
+            "action": "append",
+            "metavar": "PREF=VALUE",
+            "dest": "extra_prefs",
+            "default": [],
+            "help": "Defines an extra user preference."}
+         ],
     ] + copy.deepcopy(testing_config_options) + \
         copy.deepcopy(code_coverage_config_options)
 
     def __init__(self, require_config_file=True):
         # abs_dirs defined already in BaseScript but is here to make pylint happy
         self.abs_dirs = None
         super(DesktopUnittest, self).__init__(
             config_options=self.config_options,
@@ -402,16 +409,19 @@ class DesktopUnittest(TestingMixin, Merc
                     base_cmd.append('--bisect-chunk=default')
                 else:
                     self.warning("--no-random does not currently work with suites other than "
                                  "mochitest.")
 
             if c['headless']:
                 base_cmd.append('--headless')
 
+            if c['extra_prefs']:
+                base_cmd.extend(['--setpref={}'.format(p) for p in c['extra_prefs']])
+
             # set pluginsPath
             abs_res_plugins_dir = os.path.join(abs_res_dir, 'plugins')
             str_format_values['test_plugin_path'] = abs_res_plugins_dir
 
             if suite_category not in c["suite_definitions"]:
                 self.fatal("'%s' not defined in the config!")
 
             if suite in ('browser-chrome-coverage', 'xpcshell-coverage',
--- a/testing/mozharness/scripts/web_platform_tests.py
+++ b/testing/mozharness/scripts/web_platform_tests.py
@@ -84,16 +84,23 @@ class WebPlatformTest(TestingMixin, Merc
             "help": "Specify headless fake screen height (default: 1200)."}
          ],
         [["--single-stylo-traversal"], {
             "action": "store_true",
             "dest": "single_stylo_traversal",
             "default": False,
             "help": "Forcibly enable single thread traversal in Stylo with STYLO_THREADS=1"}
          ],
+        [["--setpref"], {
+            "action": "append",
+            "metavar": "PREF=VALUE",
+            "dest": "extra_prefs",
+            "default": [],
+            "help": "Defines an extra user preference."}
+         ],
     ] + copy.deepcopy(testing_config_options) + \
         copy.deepcopy(code_coverage_config_options)
 
     def __init__(self, require_config_file=True):
         super(WebPlatformTest, self).__init__(
             config_options=self.config_options,
             all_actions=[
                 'clobber',
@@ -166,19 +173,27 @@ class WebPlatformTest(TestingMixin, Merc
         return path
 
     def _query_cmd(self, test_types):
         if not self.binary_path:
             self.fatal("Binary path could not be determined")
             # And exit
 
         c = self.config
+        run_file_name = "runtests.py"
+
         dirs = self.query_abs_dirs()
         abs_app_dir = self.query_abs_app_dir()
-        run_file_name = "runtests.py"
+        str_format_values = {
+            'binary_path': self.binary_path,
+            'test_path': dirs["abs_wpttest_dir"],
+            'test_install_path': dirs["abs_test_install_dir"],
+            'abs_app_dir': abs_app_dir,
+            'abs_work_dir': dirs["abs_work_dir"]
+        }
 
         cmd = [self.query_python_path('python'), '-u']
         cmd.append(os.path.join(dirs["abs_wpttest_dir"], run_file_name))
 
         # Make sure that the logging directory exists
         if self.mkdir_p(dirs["abs_blob_upload_dir"]) == -1:
             self.fatal("Could not create blobber upload directory")
             # Exit
@@ -200,16 +215,19 @@ class WebPlatformTest(TestingMixin, Merc
                 "--no-pause-after-test"]
 
         if not sys.platform.startswith("linux"):
             cmd += ["--exclude=css"]
 
         for test_type in test_types:
             cmd.append("--test-type=%s" % test_type)
 
+        if c['extra_prefs']:
+            cmd.extend(['--setpref={}'.format(p) for p in c['extra_prefs']])
+
         if not c["e10s"]:
             cmd.append("--disable-e10s")
 
         if c["single_stylo_traversal"]:
             cmd.append("--stylo-threads=1")
         else:
             cmd.append("--stylo-threads=4")
 
@@ -221,34 +239,26 @@ class WebPlatformTest(TestingMixin, Merc
                          for p in paths if p.startswith(prefix)]
                 cmd.extend(paths)
             else:
                 for opt in ["total_chunks", "this_chunk"]:
                     val = c.get(opt)
                     if val:
                         cmd.append("--%s=%s" % (opt.replace("_", "-"), val))
 
+        options = list(c.get("options", []))
+
         if "wdspec" in test_types:
             geckodriver_path = self._query_geckodriver()
             if not geckodriver_path or not os.path.isfile(geckodriver_path):
                 self.fatal("Unable to find geckodriver binary "
                            "in common test package: %s" % str(geckodriver_path))
             cmd.append("--webdriver-binary=%s" % geckodriver_path)
             cmd.append("--webdriver-arg=-vv")  # enable trace logs
 
-        options = list(c.get("options", []))
-
-        str_format_values = {
-            'binary_path': self.binary_path,
-            'test_path': dirs["abs_wpttest_dir"],
-            'test_install_path': dirs["abs_test_install_dir"],
-            'abs_app_dir': abs_app_dir,
-            'abs_work_dir': dirs["abs_work_dir"]
-        }
-
         test_type_suite = {
             "testharness": "web-platform-tests",
             "reftest": "web-platform-tests-reftests",
             "wdspec": "web-platform-tests-wdspec",
         }
         for test_type in test_types:
             try_options, try_tests = self.try_args(test_type_suite[test_type])
 
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/selectors/floating-first-letter-05d0.html.ini
@@ -0,0 +1,4 @@
+[floating-first-letter-05d0.html]
+  type: reftest
+  expected: FAIL
+  bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1495674
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/webdriver/tests/element_send_keys/scroll_into_view.py.ini
@@ -0,0 +1,4 @@
+[scroll_into_view.py]
+  [test_contenteditable_element_outside_of_scrollable_viewport]
+    disabled:
+      if (os == "win"): https://bugzilla.mozilla.org/show_bug.cgi?id=1495521
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/webdriver/tests/get_current_url/get.py.ini
@@ -0,0 +1,4 @@
+[get.py]
+  expected:
+    if (os == "win"): TIMEOUT
+  bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1495513
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/selectors/floating-first-letter-05d0.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Drop cap with U+05D0 in the document</title>
+<meta name="assert" content="The text placement within :first-line should not be affected by later presence of a right-to-left character.">
+<link rel=help href=https://drafts.csswg.org/css-pseudo-4/#first-line-styling>
+<link rel=match href=/css/selectors/floating-first-letter-ref.html>
+<style>
+  p:first-line {
+    background: lightblue;
+  }
+
+  p::first-letter {
+    float: left;
+    font-size: 4rem;
+  }
+  div {
+  	color: transparent;
+  }
+</style>
+<p>Ab</p><div>&#x05D0;</div>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/selectors/floating-first-letter-feff.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Drop cap with U+FEFF in the document</title>
+<meta name="assert" content="The text placement within :first-line should not be affected by later presence of U+FEFF.">
+<link rel=help href=https://drafts.csswg.org/css-pseudo-4/#first-line-styling>
+<link rel=match href=/css/selectors/floating-first-letter-ref.html>
+<style>
+  p:first-line {
+    background: lightblue;
+  }
+
+  p::first-letter {
+    float: left;
+    font-size: 4rem;
+  }
+</style>
+<p>Ab</p>&#xFEFF;
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/selectors/floating-first-letter-ref.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Drop cap with no bidi in the document</title>
+<style>
+  p:first-line {
+    background: lightblue;
+  }
+
+  p::first-letter {
+    float: left;
+    font-size: 4rem;
+  }
+</style>
+<p>Ab</p>
\ No newline at end of file
--- a/third_party/rust/encoding_rs/.cargo-checksum.json
+++ b/third_party/rust/encoding_rs/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{".cargo_vcs_info.json":"3e41f4891f008acf5f8efe42c51111397526d9ee968b3c9f82f8d89a69a915ce",".travis.yml":"262b9925451db996c31e4bb8bb6bbdb36c3f6009f407db5ed4b0ed25cd72d8c8","CONTRIBUTING.md":"0d4bfb1068c2b2f32b7bc7833662dfff628d86b0f31068266027c719fb727360","COPYRIGHT":"20d4fff11cca11529df3f02096fbe8ffe350219cdb07cdedea34e6a762866da5","Cargo.toml":"20ecd9cc4ef07cb595a7f9ae583621dfd0fe89ea27b615303b411f26a8bd568f","Ideas.md":"7fbeddb0f8ba7b233673ee705997adc2fddb1636a17fe662532b35ef2810a51d","LICENSE-APACHE":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30","LICENSE-MIT":"74aa8b6d04c36bb640ee81187a3f24a2fa94e36d4c1d4f2ca164c3784ae87a83","README.md":"808f2a6a9a9798f495ec7607e5cfc7563115a11978acbdcc671d1faea8201e21","doc/Big5.txt":"f73a2edc5cb6c2d140ba6e07f4542e1c4a234950378acde1df93480f0ca0be0b","doc/EUC-JP.txt":"ee2818b907d0137f40a9ab9fd525fc700a44dbdddb6cf0c157a656566bae4bf1","doc/EUC-KR.txt":"71d9e2ccf3b124e8bdfb433c8cf2773fd878077038d0cec3c7237a50f4a78a30","doc/GBK.txt":"c1b522b5a799884e5001da661f42c5a8f4d0acb9ef1d74b206f22b5f65365606","doc/IBM866.txt":"a5a433e804d0f83af785015179fbc1d9b0eaf1f7960efcd04093e136b51fbd0e","doc/ISO-2022-JP.txt":"af86684f5a8f0e2868d7b2c292860140c3d2e5527530ca091f1b28198e8e2fe6","doc/ISO-8859-10.txt":"6d3949ad7c81ca176895101ed81a1db7df1060d64e262880b94bd31bb344ab4d","doc/ISO-8859-13.txt":"3951dd89cf93f7729148091683cf8511f4529388b7dc8dcd0d62eaed55be93fa","doc/ISO-8859-14.txt":"3d330784a0374fd255a38b47949675cc7168c800530534b0a01cac6edc623adc","doc/ISO-8859-15.txt":"24b1084aab5127a85aab99153f86e24694d0a3615f53b5ce23683f97cf66c47a","doc/ISO-8859-16.txt":"ce0272559b92ba76d7a7e476f6424ae4a5cc72e75b183611b08392e44add4d25","doc/ISO-8859-2.txt":"18ceff88c13d1b5ba455a3919b1e3de489045c4c3d2dd7e8527c125c75d54aad","doc/ISO-8859-3.txt":"21798404c68f4f5db59223362f24999da96968c0628427321fccce7d2849a130","doc/ISO-8859-4.txt":"d27f6520c6c5bfbcc19176b71d081cdb3bccde1622bb3e420d5680e812632d53","doc/ISO-8859-5.txt":"a10ec8d6ea7a78ad15da7275f6cb1a3365118527e28f9af6d0d5830501303f3a","doc/ISO-8859-6.txt":"ccda8a2efc96115336bdd77776637b9712425e44fbcf745353b9057fbef144e7","doc/ISO-8859-7.txt":"17900fa1f27a445958f0a77d7d9056be375a6bd7ee4492aa680c7c1500bab85e","doc/ISO-8859-8-I.txt":"8357555646d54265a9b9ffa3e68b08d132312f1561c60108ff9b8b1167b6ecf2","doc/ISO-8859-8.txt":"72cd6f3afb7b4a9c16a66a362473315770b7755d72c86c870e52fc3eba86c8af","doc/KOI8-R.txt":"839cf19a38da994488004ed7814b1f6151640156a9a2af02bf2efca745fb5966","doc/KOI8-U.txt":"0cc76624ed1f024183e2298b7e019957da2c70c8ca06e0fc4e6f353f50a5054f","doc/Shift_JIS.txt":"34c49141818cb9ddbcf59cc858f78a79be8ad148d563f26415108ae1f148443f","doc/UTF-16BE.txt":"e2e280d8acbaa6d2a6b3569d60e17500a285f2baa0df3363dd85537cd5a1ef8f","doc/UTF-16LE.txt":"70bdc170e3fc5298ba68f10125fb5eeb8b077036cc96bb4416c4de396f6d76c1","doc/UTF-8.txt":"ea7bae742e613010ced002cf4b601a737d2203fad65e115611451bc4428f548a","doc/gb18030.txt":"dc71378a8f07a2d8659f69ee81fb8791fef56ba86f124b429978285237bb4a7b","doc/macintosh.txt":"57491e53866711b4672d9b9ff35380b9dac9e0d8e3d6c20bdd6140603687c023","doc/replacement.txt":"4b6c3bbd7999d9d4108a281594bd02d13607e334a95465afff8c2c08d395f0e4","doc/windows-1250.txt":"61296bb6a21cdab602300d32ecfba434cb82de5ac3bc88d58710d2f125e28d39","doc/windows-1251.txt":"7deea1c61dea1485c8ff02db2c7d578db7a9aab63ab1cfd02ec04b515864689e","doc/windows-1252.txt":"933ef3bdddfce5ee132b9f1a1aa8b47423d2587bbe475b19028d0a6d38e180b6","doc/windows-1253.txt":"1a38748b88e99071a5c7b3d5456ead4caedeabab50d50d658be105bc113714de","doc/windows-1254.txt":"f8372f86c6f8d642563cd6ddc025260553292a39423df1683a98670bd7bf2b47","doc/windows-1255.txt":"4e5852494730054e2da258a74e1b9d780abbcdd8ce22ebc218ca2efe9e90493d","doc/windows-1256.txt":"c0879c5172abedead302a406e8f60d9cd9598694a0ffa4fd288ffe4fef7b8ea1","doc/windows-1257.txt":"c28a0c9f964fcb2b46d21f537c402446501a2800670481d6abf9fd9e9018d523","doc/windows-1258.txt":"5019ae4d61805c79aacbf17c93793342dbb098d65a1837783bc3e2c6d6a23602","doc/windows-874.txt":"4ef0e4501c5feba8b17aee1818602ed44b36ca8475db771ce2fc16d392cabecc","doc/x-mac-cyrillic.txt":"58be154d8a888ca3d484b83b44f749823ef339ab27f14d90ca9a856f5050a8bd","doc/x-user-defined.txt":"f9cd07c4321bf5cfb0be4bdddd251072999b04a6cf7a6f5bc63709a84e2c1ffc","generate-encoding-data.py":"bbe134cc013a17c32bd51e868a2ee4f2c3e2205e40afb9fc60f98fa9758961b8","rustfmt.toml":"85c1a3b4382fd89e991cbb81b70fb52780472edc064c963943cdaaa56e0a2030","src/ascii.rs":"a9cf02c992229952976761655c044be23675a278de1a6186d8baaf66879c4f4a","src/big5.rs":"659ddb66f82142468774715cd9ecdc8356005fec9d91e46d54ae2280a88adb4b","src/data.rs":"18793e23f98d9aba1ed2858c43b2103c9c05a8571429b2128510c68d05ba8cb6","src/euc_jp.rs":"b178172274af1a6be38d388e5a2468e7140e9facf63d2f679f4ed77c3fc20a92","src/euc_kr.rs":"e27bd9c4e2172bb02004dd2a1fcbe02118415a1aec6a90b5632c9eac6f12ba7d","src/gb18030.rs":"752869f47ad0cad02ad3ccb6fde3eb277fa528e10e3d9432a90563db610d9dc2","src/handles.rs":"c722b818f4178f04232ef540a840f7104f087c29b1cd2724bed77df9f54fbd45","src/iso_2022_jp.rs":"d904a92d49fc5878e6cb82a21185051c801489cf2800db8f37269965ca7974cc","src/lib.rs":"f8a3ac57e172ecb35b89382ee0c340c267ec19056ada5dddc6fb48ec2119b7e0","src/macros.rs":"04b3d81f1701b746dcce466ad2c3202a0a3e9c00f1d8db336276660640a12d7b","src/mem.rs":"32f805cbc5c0f3fcc34b5536d76d5ca7f9925228cd9e5ac8c23b38799d353434","src/replacement.rs":"61e0ae52664b3856f1ed202af6266b441ffcf5e2c9b22976047540e4a3252b41","src/shift_jis.rs":"7106d0a9785425b5af2323f3046e6130bd5125effa4ab484d84b529914855d67","src/simd_funcs.rs":"6139e2c7eebd8f3f35b8ce70891c6731595e5d18fa48b11f04f88c6342335935","src/single_byte.rs":"986571bb52f2cf7779c1dc8bdd8058c3183524a512bc2fcca6637eeee394b573","src/test_data/big5_in.txt":"4c5a8691f8dc717311889c63894026d2fb62725a86c4208ca274a9cc8d42a503","src/test_data/big5_in_ref.txt":"99d399e17750cf9c7cf30bb253dbfe35b81c4fcbdead93cfa48b1429213473c7","src/test_data/big5_out.txt":"6193ca97c297aa20e09396038d18e938bb7ea331c26f0f2454097296723a0b13","src/test_data/big5_out_ref.txt":"36567691f557df144f6cc520015a87038dfa156f296fcf103b56ae9a718be1fc","src/test_data/euc_kr_in.txt":"c86a7224f3215fa0d04e685622a752fdc72763e8ae076230c7fd62de57ec4074","src/test_data/euc_kr_in_ref.txt":"1f419f4ca47d708b54c73c461545a022ae2e20498fdbf8005a483d752a204883","src/test_data/euc_kr_out.txt":"e7f32e026f70be1e1b58e0047baf7d3d2c520269c4f9b9992e158b4decb0a1a3","src/test_data/euc_kr_out_ref.txt":"c9907857980b20b8e9e3b584482ed6567a2be6185d72237b6322f0404944924e","src/test_data/gb18030_in.txt":"ab7231b2d3e9afacdbd7d7f3b9e5361a7ff9f7e1cfdb4f3bd905b9362b309e53","src/test_data/gb18030_in_ref.txt":"dc5069421adca2043c55f5012b55a76fdff651d22e6e699fd0978f8d5706815c","src/test_data/gb18030_out.txt":"f0208d527f5ca63de7d9a0323be8d5cf12d8a104b2943d92c2701f0c3364dac1","src/test_data/gb18030_out_ref.txt":"6819fe47627e4ea01027003fc514b9f21a1322e732d7f1fb92cc6c5455bc6c07","src/test_data/iso_2022_jp_in.txt":"cd24bbdcb1834e25db54646fbf4c41560a13dc7540f6be3dba4f5d97d44513af","src/test_data/iso_2022_jp_in_ref.txt":"3dc4e6a5e06471942d086b16c9440945e78415f6f3f47e43717e4bc2eac2cdf5","src/test_data/iso_2022_jp_out.txt":"9b6f015329dda6c3f9ee5ce6dbd6fa9c89acc21283e886836c78b8d833480c21","src/test_data/iso_2022_jp_out_ref.txt":"78cb260093a20116ad9a42f43b05d1848c5ab100b6b9a850749809e943884b35","src/test_data/jis0208_in.txt":"6df3030553ffb0a6615bb33dc8ea9dca6d9623a9028e2ffec754ce3c3da824cc","src/test_data/jis0208_in_ref.txt":"3dc4e6a5e06471942d086b16c9440945e78415f6f3f47e43717e4bc2eac2cdf5","src/test_data/jis0208_out.txt":"4ec24477e1675ce750733bdc3c5add1cd27b6bd4ce1f09289564646e9654e857","src/test_data/jis0208_out_ref.txt":"c3e1cef5032b2b1d93a406f31ff940c4e2dfe8859b8b17ca2761fee7a75a0e48","src/test_data/jis0212_in.txt":"c011f0dd72bd7c8cd922df9374ef8d2769a77190514c77f6c62b415852eeb9fe","src/test_data/jis0212_in_ref.txt":"7d9458b3d2f73e7092a7f505c08ce1d233dde18aa679fbcf9889256239cc9e06","src/test_data/shift_jis_in.txt":"02e389ccef0dd2122e63f503899402cb7f797912c2444cc80ab93131116c5524","src/test_data/shift_jis_in_ref.txt":"512f985950ca902e643c88682dba9708b7c38d3c5ec2925168ab00ac94ab19f9","src/test_data/shift_jis_out.txt":"5fbc44da7bf639bf6cfe0fa1fd3eba7102b88f81919c9ea991302712f69426fb","src/test_data/shift_jis_out_ref.txt":"466322c6fed8286c64582731755290c2296508efdd258826e6279686649b481f","src/test_labels_names.rs":"c962c7aeac3d9ef2aca70c9e21983b231d4cf998cb06879374b0401e5149d1da","src/testing.rs":"b299d27055f3b068de66cc10a75c024b881c48bc093627c01e0b1f8bd7d94666","src/utf_16.rs":"32b6ecc3696542fb269a93c915e7402dde545e793f9599cf7dc63688fa226e0d","src/utf_8.rs":"5d16295faf1707b2058629eb2467200b86b02acfceda27433ac1d05ac385f9f3","src/utf_8_core.rs":"fe218284dbb9111a2336ff1684d7f58b55d9c4fe7363089a29e57f52901f0707","src/variant.rs":"e2c988a645429fd7457d7fbc487b838ab7ec7bfb8943d192992f18bbac8973f0","src/x_user_defined.rs":"1d25920ec5d005cbd623f34473d68991521aa3a83e52d4eb9b645711f10cd484"},"package":"21a550ec129ca2f8593227888625c7c5331c6ad878e2cee6b7ac25e1c7d05746"}
\ No newline at end of file
+{"files":{".cargo_vcs_info.json":"ac7c1c20f66a56b656a6f614313b82b29d10eaa0f917dea423151ef697f165ee",".travis.yml":"262b9925451db996c31e4bb8bb6bbdb36c3f6009f407db5ed4b0ed25cd72d8c8","CONTRIBUTING.md":"0d4bfb1068c2b2f32b7bc7833662dfff628d86b0f31068266027c719fb727360","COPYRIGHT":"20d4fff11cca11529df3f02096fbe8ffe350219cdb07cdedea34e6a762866da5","Cargo.toml":"ce4b3b7f96ceb43116772723a0ee3c89397d14f02b2fbc7bf0a11dd7f674dcd7","Ideas.md":"7fbeddb0f8ba7b233673ee705997adc2fddb1636a17fe662532b35ef2810a51d","LICENSE-APACHE":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30","LICENSE-MIT":"74aa8b6d04c36bb640ee81187a3f24a2fa94e36d4c1d4f2ca164c3784ae87a83","README.md":"e28de59f165d0038827957ea651dd32e570479825e40fc7bfb94ec973d642f16","doc/Big5.txt":"f73a2edc5cb6c2d140ba6e07f4542e1c4a234950378acde1df93480f0ca0be0b","doc/EUC-JP.txt":"ee2818b907d0137f40a9ab9fd525fc700a44dbdddb6cf0c157a656566bae4bf1","doc/EUC-KR.txt":"71d9e2ccf3b124e8bdfb433c8cf2773fd878077038d0cec3c7237a50f4a78a30","doc/GBK.txt":"c1b522b5a799884e5001da661f42c5a8f4d0acb9ef1d74b206f22b5f65365606","doc/IBM866.txt":"a5a433e804d0f83af785015179fbc1d9b0eaf1f7960efcd04093e136b51fbd0e","doc/ISO-2022-JP.txt":"af86684f5a8f0e2868d7b2c292860140c3d2e5527530ca091f1b28198e8e2fe6","doc/ISO-8859-10.txt":"6d3949ad7c81ca176895101ed81a1db7df1060d64e262880b94bd31bb344ab4d","doc/ISO-8859-13.txt":"3951dd89cf93f7729148091683cf8511f4529388b7dc8dcd0d62eaed55be93fa","doc/ISO-8859-14.txt":"3d330784a0374fd255a38b47949675cc7168c800530534b0a01cac6edc623adc","doc/ISO-8859-15.txt":"24b1084aab5127a85aab99153f86e24694d0a3615f53b5ce23683f97cf66c47a","doc/ISO-8859-16.txt":"ce0272559b92ba76d7a7e476f6424ae4a5cc72e75b183611b08392e44add4d25","doc/ISO-8859-2.txt":"18ceff88c13d1b5ba455a3919b1e3de489045c4c3d2dd7e8527c125c75d54aad","doc/ISO-8859-3.txt":"21798404c68f4f5db59223362f24999da96968c0628427321fccce7d2849a130","doc/ISO-8859-4.txt":"d27f6520c6c5bfbcc19176b71d081cdb3bccde1622bb3e420d5680e812632d53","doc/ISO-8859-5.txt":"a10ec8d6ea7a78ad15da7275f6cb1a3365118527e28f9af6d0d5830501303f3a","doc/ISO-8859-6.txt":"ccda8a2efc96115336bdd77776637b9712425e44fbcf745353b9057fbef144e7","doc/ISO-8859-7.txt":"17900fa1f27a445958f0a77d7d9056be375a6bd7ee4492aa680c7c1500bab85e","doc/ISO-8859-8-I.txt":"8357555646d54265a9b9ffa3e68b08d132312f1561c60108ff9b8b1167b6ecf2","doc/ISO-8859-8.txt":"72cd6f3afb7b4a9c16a66a362473315770b7755d72c86c870e52fc3eba86c8af","doc/KOI8-R.txt":"839cf19a38da994488004ed7814b1f6151640156a9a2af02bf2efca745fb5966","doc/KOI8-U.txt":"0cc76624ed1f024183e2298b7e019957da2c70c8ca06e0fc4e6f353f50a5054f","doc/Shift_JIS.txt":"34c49141818cb9ddbcf59cc858f78a79be8ad148d563f26415108ae1f148443f","doc/UTF-16BE.txt":"e2e280d8acbaa6d2a6b3569d60e17500a285f2baa0df3363dd85537cd5a1ef8f","doc/UTF-16LE.txt":"70bdc170e3fc5298ba68f10125fb5eeb8b077036cc96bb4416c4de396f6d76c1","doc/UTF-8.txt":"ea7bae742e613010ced002cf4b601a737d2203fad65e115611451bc4428f548a","doc/gb18030.txt":"dc71378a8f07a2d8659f69ee81fb8791fef56ba86f124b429978285237bb4a7b","doc/macintosh.txt":"57491e53866711b4672d9b9ff35380b9dac9e0d8e3d6c20bdd6140603687c023","doc/replacement.txt":"4b6c3bbd7999d9d4108a281594bd02d13607e334a95465afff8c2c08d395f0e4","doc/windows-1250.txt":"61296bb6a21cdab602300d32ecfba434cb82de5ac3bc88d58710d2f125e28d39","doc/windows-1251.txt":"7deea1c61dea1485c8ff02db2c7d578db7a9aab63ab1cfd02ec04b515864689e","doc/windows-1252.txt":"933ef3bdddfce5ee132b9f1a1aa8b47423d2587bbe475b19028d0a6d38e180b6","doc/windows-1253.txt":"1a38748b88e99071a5c7b3d5456ead4caedeabab50d50d658be105bc113714de","doc/windows-1254.txt":"f8372f86c6f8d642563cd6ddc025260553292a39423df1683a98670bd7bf2b47","doc/windows-1255.txt":"4e5852494730054e2da258a74e1b9d780abbcdd8ce22ebc218ca2efe9e90493d","doc/windows-1256.txt":"c0879c5172abedead302a406e8f60d9cd9598694a0ffa4fd288ffe4fef7b8ea1","doc/windows-1257.txt":"c28a0c9f964fcb2b46d21f537c402446501a2800670481d6abf9fd9e9018d523","doc/windows-1258.txt":"5019ae4d61805c79aacbf17c93793342dbb098d65a1837783bc3e2c6d6a23602","doc/windows-874.txt":"4ef0e4501c5feba8b17aee1818602ed44b36ca8475db771ce2fc16d392cabecc","doc/x-mac-cyrillic.txt":"58be154d8a888ca3d484b83b44f749823ef339ab27f14d90ca9a856f5050a8bd","doc/x-user-defined.txt":"f9cd07c4321bf5cfb0be4bdddd251072999b04a6cf7a6f5bc63709a84e2c1ffc","generate-encoding-data.py":"bbe134cc013a17c32bd51e868a2ee4f2c3e2205e40afb9fc60f98fa9758961b8","rustfmt.toml":"85c1a3b4382fd89e991cbb81b70fb52780472edc064c963943cdaaa56e0a2030","src/ascii.rs":"a9cf02c992229952976761655c044be23675a278de1a6186d8baaf66879c4f4a","src/big5.rs":"659ddb66f82142468774715cd9ecdc8356005fec9d91e46d54ae2280a88adb4b","src/data.rs":"18793e23f98d9aba1ed2858c43b2103c9c05a8571429b2128510c68d05ba8cb6","src/euc_jp.rs":"b178172274af1a6be38d388e5a2468e7140e9facf63d2f679f4ed77c3fc20a92","src/euc_kr.rs":"e27bd9c4e2172bb02004dd2a1fcbe02118415a1aec6a90b5632c9eac6f12ba7d","src/gb18030.rs":"752869f47ad0cad02ad3ccb6fde3eb277fa528e10e3d9432a90563db610d9dc2","src/handles.rs":"c722b818f4178f04232ef540a840f7104f087c29b1cd2724bed77df9f54fbd45","src/iso_2022_jp.rs":"d904a92d49fc5878e6cb82a21185051c801489cf2800db8f37269965ca7974cc","src/lib.rs":"590c07228b9e5f9cbbee5430fb1c51ae73b6db8d13afaeb4c0792f7eefa45dde","src/macros.rs":"04b3d81f1701b746dcce466ad2c3202a0a3e9c00f1d8db336276660640a12d7b","src/mem.rs":"c374b8ba7265e1f304328099b8f3187797cf8e7ba9d10f84da462cd96899a9c8","src/replacement.rs":"61e0ae52664b3856f1ed202af6266b441ffcf5e2c9b22976047540e4a3252b41","src/shift_jis.rs":"7106d0a9785425b5af2323f3046e6130bd5125effa4ab484d84b529914855d67","src/simd_funcs.rs":"745283779e40e73d608636bbb0297b27689df19ee7af984d0fcfb85455789a54","src/single_byte.rs":"986571bb52f2cf7779c1dc8bdd8058c3183524a512bc2fcca6637eeee394b573","src/test_data/big5_in.txt":"4c5a8691f8dc717311889c63894026d2fb62725a86c4208ca274a9cc8d42a503","src/test_data/big5_in_ref.txt":"99d399e17750cf9c7cf30bb253dbfe35b81c4fcbdead93cfa48b1429213473c7","src/test_data/big5_out.txt":"6193ca97c297aa20e09396038d18e938bb7ea331c26f0f2454097296723a0b13","src/test_data/big5_out_ref.txt":"36567691f557df144f6cc520015a87038dfa156f296fcf103b56ae9a718be1fc","src/test_data/euc_kr_in.txt":"c86a7224f3215fa0d04e685622a752fdc72763e8ae076230c7fd62de57ec4074","src/test_data/euc_kr_in_ref.txt":"1f419f4ca47d708b54c73c461545a022ae2e20498fdbf8005a483d752a204883","src/test_data/euc_kr_out.txt":"e7f32e026f70be1e1b58e0047baf7d3d2c520269c4f9b9992e158b4decb0a1a3","src/test_data/euc_kr_out_ref.txt":"c9907857980b20b8e9e3b584482ed6567a2be6185d72237b6322f0404944924e","src/test_data/gb18030_in.txt":"ab7231b2d3e9afacdbd7d7f3b9e5361a7ff9f7e1cfdb4f3bd905b9362b309e53","src/test_data/gb18030_in_ref.txt":"dc5069421adca2043c55f5012b55a76fdff651d22e6e699fd0978f8d5706815c","src/test_data/gb18030_out.txt":"f0208d527f5ca63de7d9a0323be8d5cf12d8a104b2943d92c2701f0c3364dac1","src/test_data/gb18030_out_ref.txt":"6819fe47627e4ea01027003fc514b9f21a1322e732d7f1fb92cc6c5455bc6c07","src/test_data/iso_2022_jp_in.txt":"cd24bbdcb1834e25db54646fbf4c41560a13dc7540f6be3dba4f5d97d44513af","src/test_data/iso_2022_jp_in_ref.txt":"3dc4e6a5e06471942d086b16c9440945e78415f6f3f47e43717e4bc2eac2cdf5","src/test_data/iso_2022_jp_out.txt":"9b6f015329dda6c3f9ee5ce6dbd6fa9c89acc21283e886836c78b8d833480c21","src/test_data/iso_2022_jp_out_ref.txt":"78cb260093a20116ad9a42f43b05d1848c5ab100b6b9a850749809e943884b35","src/test_data/jis0208_in.txt":"6df3030553ffb0a6615bb33dc8ea9dca6d9623a9028e2ffec754ce3c3da824cc","src/test_data/jis0208_in_ref.txt":"3dc4e6a5e06471942d086b16c9440945e78415f6f3f47e43717e4bc2eac2cdf5","src/test_data/jis0208_out.txt":"4ec24477e1675ce750733bdc3c5add1cd27b6bd4ce1f09289564646e9654e857","src/test_data/jis0208_out_ref.txt":"c3e1cef5032b2b1d93a406f31ff940c4e2dfe8859b8b17ca2761fee7a75a0e48","src/test_data/jis0212_in.txt":"c011f0dd72bd7c8cd922df9374ef8d2769a77190514c77f6c62b415852eeb9fe","src/test_data/jis0212_in_ref.txt":"7d9458b3d2f73e7092a7f505c08ce1d233dde18aa679fbcf9889256239cc9e06","src/test_data/shift_jis_in.txt":"02e389ccef0dd2122e63f503899402cb7f797912c2444cc80ab93131116c5524","src/test_data/shift_jis_in_ref.txt":"512f985950ca902e643c88682dba9708b7c38d3c5ec2925168ab00ac94ab19f9","src/test_data/shift_jis_out.txt":"5fbc44da7bf639bf6cfe0fa1fd3eba7102b88f81919c9ea991302712f69426fb","src/test_data/shift_jis_out_ref.txt":"466322c6fed8286c64582731755290c2296508efdd258826e6279686649b481f","src/test_labels_names.rs":"c962c7aeac3d9ef2aca70c9e21983b231d4cf998cb06879374b0401e5149d1da","src/testing.rs":"b299d27055f3b068de66cc10a75c024b881c48bc093627c01e0b1f8bd7d94666","src/utf_16.rs":"288c590fb4438b6f3886ce8d37eae52069ba61bd5c037f291df36cbab9404568","src/utf_8.rs":"5d16295faf1707b2058629eb2467200b86b02acfceda27433ac1d05ac385f9f3","src/utf_8_core.rs":"fe218284dbb9111a2336ff1684d7f58b55d9c4fe7363089a29e57f52901f0707","src/variant.rs":"e2c988a645429fd7457d7fbc487b838ab7ec7bfb8943d192992f18bbac8973f0","src/x_user_defined.rs":"1d25920ec5d005cbd623f34473d68991521aa3a83e52d4eb9b645711f10cd484"},"package":"cc9945e460ad969220c1061b9574fb02ed097c6f0704ce2f3e336cb443c40c73"}
\ No newline at end of file
--- a/third_party/rust/encoding_rs/.cargo_vcs_info.json
+++ b/third_party/rust/encoding_rs/.cargo_vcs_info.json
@@ -1,5 +1,5 @@
 {
   "git": {
-    "sha1": "b67c60025bfebbf186e8b22f03edc9b6dc96df59"
+    "sha1": "ffe863483bcfeb069edf645738ac3650899d2801"
   }
 }
--- a/third_party/rust/encoding_rs/Cargo.toml
+++ b/third_party/rust/encoding_rs/Cargo.toml
@@ -7,17 +7,17 @@
 #
 # If you believe there's an error in this file please file an
 # issue against the rust-lang/cargo repository. If you're
 # editing this file be aware that the upstream Cargo.toml
 # will likely look very different (and much more reasonable)
 
 [package]
 name = "encoding_rs"
-version = "0.8.7"
+version = "0.8.8"
 authors = ["Henri Sivonen <hsivonen@hsivonen.fi>"]
 description = "A Gecko-oriented implementation of the Encoding Standard"
 homepage = "https://docs.rs/encoding_rs/"
 documentation = "https://docs.rs/encoding_rs/"
 readme = "README.md"
 keywords = ["encoding", "web", "unicode", "charset"]
 categories = ["text-processing", "encoding", "web-programming", "email"]
 license = "MIT/Apache-2.0"
--- a/third_party/rust/encoding_rs/README.md
+++ b/third_party/rust/encoding_rs/README.md
@@ -239,16 +239,24 @@ used in Firefox.
 - [x] Add SIMD acceleration for Aarch64.
 - [x] Investigate the use of NEON on 32-bit ARM.
 - [ ] Investigate Björn Höhrmann's lookup table acceleration for UTF-8 as
       adapted to Rust in rust-encoding.
 - [ ] Add actually fast CJK encode options.
 
 ## Release Notes
 
+### 0.8.8
+
+* Made the `is_foo_bidi()` not treat U+FEFF (ZERO WIDTH NO-BREAK SPACE
+  aka. BYTE ORDER MARK) as right-to-left.
+* Made the `is_foo_bidi()` functions report `true` if the input contains
+  Hebrew presentations forms (which are right-to-left but not in a
+  right-to-left-roadmapped block).
+
 ### 0.8.7
 
 * Fixed a panic in the UTF-16LE/UTF-16BE decoder when decoding to UTF-8.
 
 ### 0.8.6
 
 * Temporarily removed the debug assertion added in version 0.8.5 from
   `convert_utf16_to_latin1_lossy`.
--- a/third_party/rust/encoding_rs/src/lib.rs
+++ b/third_party/rust/encoding_rs/src/lib.rs
@@ -3,17 +3,17 @@
 //
 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
 #![cfg_attr(feature = "cargo-clippy", allow(doc_markdown, inline_always, new_ret_no_self))]
-#![doc(html_root_url = "https://docs.rs/encoding_rs/0.8.7")]
+#![doc(html_root_url = "https://docs.rs/encoding_rs/0.8.8")]
 
 //! encoding_rs is a Gecko-oriented Free Software / Open Source implementation
 //! of the [Encoding Standard](https://encoding.spec.whatwg.org/) in Rust.
 //! Gecko-oriented means that converting to and from UTF-16 is supported in
 //! addition to converting to and from UTF-8, that the performance and
 //! streamability goals are browser-oriented, and that FFI-friendliness is a
 //! goal.
 //!
--- a/third_party/rust/encoding_rs/src/mem.rs
+++ b/third_party/rust/encoding_rs/src/mem.rs
@@ -664,21 +664,24 @@ pub fn is_str_latin1(buffer: &str) -> bo
 pub fn is_utf16_latin1(buffer: &[u16]) -> bool {
     is_utf16_latin1_impl(buffer)
 }
 
 /// Checks whether a potentially-invalid UTF-8 buffer contains code points
 /// that trigger right-to-left processing.
 ///
 /// The check is done on a Unicode block basis without regard to assigned
-/// vs. unassigned code points in the block. Additionally, the four
-/// RIGHT-TO-LEFT FOO controls in General Punctuation are checked for.
-/// Control characters that are technically bidi controls but do not cause
-/// right-to-left behavior without the presence of right-to-left characters
-/// or right-to-left controls are not checked for.
+/// vs. unassigned code points in the block. Hebrew presentation forms in
+/// the Alphabetic Presentation Forms block are treated as if they formed
+/// a block on their own (i.e. it treated as right-to-left). Additionally,
+/// the four RIGHT-TO-LEFT FOO controls in General Punctuation are checked
+/// for. Control characters that are technically bidi controls but do not
+/// cause right-to-left behavior without the presence of right-to-left
+/// characters or right-to-left controls are not checked for. As a special
+/// case, U+FEFF is excluded from Arabic Presentation Forms-B.
 ///
 /// Returns `true` if the input is invalid UTF-8 or the input contains an
 /// RTL character. Returns `false` if the input is valid UTF-8 and contains
 /// no RTL characters.
 #[inline]
 pub fn is_utf8_bidi(buffer: &[u8]) -> bool {
     // As of rustc 1.25.0-nightly (73ac5d6a8 2018-01-11), this is faster
     // than UTF-8 validation followed by `is_str_bidi()` for German,
@@ -694,25 +697,25 @@ pub fn is_utf8_bidi(buffer: &[u8]) -> bo
     // U+08FF: E0 A3 BF
     // U+0900: E0 A4 80
     //
     // U+200F: E2 80 8F
     // U+202B: E2 80 AB
     // U+202E: E2 80 AE
     // U+2067: E2 81 A7
     //
-    // U+FB4F: EF AD 8F
-    // U+FB50: EF AD 90
+    // U+FB1C: EF AC 9C
+    // U+FB1D: EF AC 9D
     // U+FDFF: EF B7 BF
     // U+FE00: EF B8 80
     //
     // U+FE6F: EF B9 AF
     // U+FE70: EF B9 B0
+    // U+FEFE: EF BB BE
     // U+FEFF: EF BB BF
-    // U+FF00: EF BC 80
     //
     // U+107FF: F0 90 9F BF
     // U+10800: F0 90 A0 80
     // U+10FFF: F0 90 BF BF
     // U+11000: F0 91 80 80
     //
     // U+1E7FF: F0 9E 9F BF
     // U+1E800: F0 9E A0 80
@@ -792,29 +795,33 @@ pub fn is_utf8_bidi(buffer: &[u8]) -> bo
                             let second = bytes[read + 1];
                             let third = bytes[read + 2];
                             if ((UTF8_TRAIL_INVALID[second as usize] & UTF8_NORMAL_TRAIL)
                                 | (UTF8_TRAIL_INVALID[third as usize] & UTF8_NORMAL_TRAIL))
                                 != 0
                             {
                                 return true;
                             }
-                            if in_inclusive_range8(second, 0xAD, 0xB7) {
-                                if second == 0xAD {
-                                    if third > 0x8F {
+                            if in_inclusive_range8(second, 0xAC, 0xB7) {
+                                if second == 0xAC {
+                                    if third > 0x9C {
                                         return true;
                                     }
                                 } else {
                                     return true;
                                 }
                             } else if in_inclusive_range8(second, 0xB9, 0xBB) {
                                 if second == 0xB9 {
                                     if third > 0xAF {
                                         return true;
                                     }
+                                } else if second == 0xBB {
+                                    if third != 0xBF {
+                                        return true;
+                                    }
                                 } else {
                                     return true;
                                 }
                             }
                             read += 3;
                         }
                         0xE0 => {
                             // Three-byte special lower bound, potentially bidi
@@ -1008,29 +1015,33 @@ pub fn is_utf8_bidi(buffer: &[u8]) -> bo
                     let second = bytes[read + 1];
                     let third = bytes[read + 2];
                     if ((UTF8_TRAIL_INVALID[second as usize] & UTF8_NORMAL_TRAIL)
                         | (UTF8_TRAIL_INVALID[third as usize] & UTF8_NORMAL_TRAIL))
                         != 0
                     {
                         return true;
                     }
-                    if in_inclusive_range8(second, 0xAD, 0xB7) {
-                        if second == 0xAD {
-                            if third > 0x8F {
+                    if in_inclusive_range8(second, 0xAC, 0xB7) {
+                        if second == 0xAC {
+                            if third > 0x9C {
                                 return true;
                             }
                         } else {
                             return true;
                         }
                     } else if in_inclusive_range8(second, 0xB9, 0xBB) {
                         if second == 0xB9 {
                             if third > 0xAF {
                                 return true;
                             }
+                        } else if second == 0xBB {
+                            if third != 0xBF {
+                                return true;
+                            }
                         } else {
                             return true;
                         }
                     }
                 }
                 0xE0 => {
                     // Three-byte special lower bound, potentially bidi
                     let new_read = read + 3;
@@ -1078,42 +1089,45 @@ pub fn is_utf8_bidi(buffer: &[u8]) -> bo
         }
     }
 }
 
 /// Checks whether a valid UTF-8 buffer contains code points that trigger
 /// right-to-left processing.
 ///
 /// The check is done on a Unicode block basis without regard to assigned
-/// vs. unassigned code points in the block. Additionally, the four
-/// RIGHT-TO-LEFT FOO controls in General Punctuation are checked for.
-/// Control characters that are technically bidi controls but do not cause
-/// right-to-left behavior without the presence of right-to-left characters
-/// or right-to-left controls are not checked for.
+/// vs. unassigned code points in the block. Hebrew presentation forms in
+/// the Alphabetic Presentation Forms block are treated as if they formed
+/// a block on their own (i.e. it treated as right-to-left). Additionally,
+/// the four RIGHT-TO-LEFT FOO controls in General Punctuation are checked
+/// for. Control characters that are technically bidi controls but do not
+/// cause right-to-left behavior without the presence of right-to-left
+/// characters or right-to-left controls are not checked for. As a special
+/// case, U+FEFF is excluded from Arabic Presentation Forms-B.
 #[inline]
 pub fn is_str_bidi(buffer: &str) -> bool {
     // U+058F: D6 8F
     // U+0590: D6 90
     // U+08FF: E0 A3 BF
     // U+0900: E0 A4 80
     //
     // U+200F: E2 80 8F
     // U+202B: E2 80 AB
     // U+202E: E2 80 AE
     // U+2067: E2 81 A7
     //
-    // U+FB4F: EF AD 8F
-    // U+FB50: EF AD 90
+    // U+FB1C: EF AC 9C
+    // U+FB1D: EF AC 9D
     // U+FDFF: EF B7 BF
     // U+FE00: EF B8 80
     //
     // U+FE6F: EF B9 AF
     // U+FE70: EF B9 B0
+    // U+FEFE: EF BB BE
     // U+FEFF: EF BB BF
-    // U+FF00: EF BC 80
     //
     // U+107FF: F0 90 9F BF
     // U+10800: F0 90 A0 80
     // U+10FFF: F0 90 BF BF
     // U+11000: F0 91 80 80
     //
     // U+1E7FF: F0 9E 9F BF
     // U+1E800: F0 9E A0 80
@@ -1173,31 +1187,36 @@ pub fn is_str_bidi(buffer: &str) -> bool
                                 }
                             } else if second == 0x81 {
                                 if third == 0xA7 {
                                     return true;
                                 }
                             }
                         } else {
                             debug_assert_eq!(byte, 0xEF);
-                            if in_inclusive_range8(second, 0xAD, 0xB7) {
-                                if second == 0xAD {
+                            if in_inclusive_range8(second, 0xAC, 0xB7) {
+                                if second == 0xAC {
                                     let third = bytes[read + 2];
-                                    if third > 0x8F {
+                                    if third > 0x9C {
                                         return true;
                                     }
                                 } else {
                                     return true;
                                 }
                             } else if in_inclusive_range8(second, 0xB9, 0xBB) {
                                 if second == 0xB9 {
                                     let third = bytes[read + 2];
                                     if third > 0xAF {
                                         return true;
                                     }
+                                } else if second == 0xBB {
+                                    let third = bytes[read + 2];
+                                    if third != 0xBF {
+                                        return true;
+                                    }
                                 } else {
                                     return true;
                                 }
                             }
                         }
                     }
                     read += 3;
                 } else {
@@ -1225,66 +1244,73 @@ pub fn is_str_bidi(buffer: &str) -> bool
         }
     }
 }
 
 /// Checks whether a UTF-16 buffer contains code points that trigger
 /// right-to-left processing.
 ///
 /// The check is done on a Unicode block basis without regard to assigned
-/// vs. unassigned code points in the block. Additionally, the four
-/// RIGHT-TO-LEFT FOO controls in General Punctuation are checked for.
-/// Control characters that are technically bidi controls but do not cause
-/// right-to-left behavior without the presence of right-to-left characters
-/// or right-to-left controls are not checked for.
+/// vs. unassigned code points in the block. Hebrew presentation forms in
+/// the Alphabetic Presentation Forms block are treated as if they formed
+/// a block on their own (i.e. it treated as right-to-left). Additionally,
+/// the four RIGHT-TO-LEFT FOO controls in General Punctuation are checked
+/// for. Control characters that are technically bidi controls but do not
+/// cause right-to-left behavior without the presence of right-to-left
+/// characters or right-to-left controls are not checked for. As a special
+/// case, U+FEFF is excluded from Arabic Presentation Forms-B.
 ///
 /// Returns `true` if the input contains an RTL character or an unpaired
 /// high surrogate that could be the high half of an RTL character.
 /// Returns `false` if the input contains neither RTL characters nor
 /// unpaired high surrogates that could be higher halves of RTL characters.
 #[inline]
 pub fn is_utf16_bidi(buffer: &[u16]) -> bool {
     is_utf16_bidi_impl(buffer)
 }
 
 /// Checks whether a code point triggers right-to-left processing.
 ///
 /// The check is done on a Unicode block basis without regard to assigned
-/// vs. unassigned code points in the block. Additionally, the four
-/// RIGHT-TO-LEFT FOO controls in General Punctuation are checked for.
-/// Control characters that are technically bidi controls but do not cause
-/// right-to-left behavior without the presence of right-to-left characters
-/// or right-to-left controls are not checked for.
+/// vs. unassigned code points in the block. Hebrew presentation forms in
+/// the Alphabetic Presentation Forms block are treated as if they formed
+/// a block on their own (i.e. it treated as right-to-left). Additionally,
+/// the four RIGHT-TO-LEFT FOO controls in General Punctuation are checked
+/// for. Control characters that are technically bidi controls but do not
+/// cause right-to-left behavior without the presence of right-to-left
+/// characters or right-to-left controls are not checked for. As a special
+/// case, U+FEFF is excluded from Arabic Presentation Forms-B.
 #[inline(always)]
 pub fn is_char_bidi(c: char) -> bool {
     // Controls:
     // Every control with RIGHT-TO-LEFT in its name in
     // https://www.unicode.org/charts/PDF/U2000.pdf
     // U+200F RLM
     // U+202B RLE
     // U+202E RLO
     // U+2067 RLI
     //
     // BMP RTL:
     // https://www.unicode.org/roadmaps/bmp/
     // U+0590...U+08FF
-    // U+FB50...U+FDFF Arabic Presentation Forms A
-    // U+FE70...U+FEFF Arabic Presentation Forms B
+    // U+FB1D...U+FDFF Hebrew presentation forms and
+    //                 Arabic Presentation Forms A
+    // U+FE70...U+FEFE Arabic Presentation Forms B (excl. BOM)
     //
     // Supplementary RTL:
     // https://www.unicode.org/roadmaps/smp/
     // U+10800...U+10FFF (Lead surrogate U+D802 or U+D803)
     // U+1E800...U+1EFFF (Lead surrogate U+D83A or U+D83B)
     let code_point = c as u32;
     if code_point < 0x0590 {
         // Below Hebrew
         return false;
     }
-    if in_range32(code_point, 0x0900, 0xFB50) {
-        // Above Arabic Extended-A and below Arabic Presentation Forms
+    if in_range32(code_point, 0x0900, 0xFB1D) {
+        // Above Arabic Extended-A and below Hebrew presentation forms
         if in_inclusive_range32(code_point, 0x200F, 0x2067) {
             // In the range that contains the RTL controls
             return code_point == 0x200F
                 || code_point == 0x202B
                 || code_point == 0x202E
                 || code_point == 0x2067;
         }
         return false;
@@ -1292,36 +1318,39 @@ pub fn is_char_bidi(c: char) -> bool {
     if code_point > 0x1EFFF {
         // Above second astral RTL. (Emoji is here.)
         return false;
     }
     if in_range32(code_point, 0x11000, 0x1E800) {
         // Between astral RTL blocks
         return false;
     }
-    if in_range32(code_point, 0xFF00, 0x10800) {
-        // Above Arabic Presentations Forms B and below first
+    if in_range32(code_point, 0xFEFF, 0x10800) {
+        // Above Arabic Presentations Forms B (excl. BOM) and below first
         // astral RTL
         return false;
     }
     if in_range32(code_point, 0xFE00, 0xFE70) {
         // Between Arabic Presentations Forms
         return false;
     }
     true
 }
 
 /// Checks whether a UTF-16 code unit triggers right-to-left processing.
 ///
 /// The check is done on a Unicode block basis without regard to assigned
-/// vs. unassigned code points in the block. Additionally, the four
-/// RIGHT-TO-LEFT FOO controls in General Punctuation are checked for.
-/// Control characters that are technically bidi controls but do not cause
-/// right-to-left behavior without the presence of right-to-left characters
-/// or right-to-left controls are not checked for.
+/// vs. unassigned code points in the block. Hebrew presentation forms in
+/// the Alphabetic Presentation Forms block are treated as if they formed
+/// a block on their own (i.e. it treated as right-to-left). Additionally,
+/// the four RIGHT-TO-LEFT FOO controls in General Punctuation are checked
+/// for. Control characters that are technically bidi controls but do not
+/// cause right-to-left behavior without the presence of right-to-left
+/// characters or right-to-left controls are not checked for. As a special
+/// case, U+FEFF is excluded from Arabic Presentation Forms-B.
 ///
 /// Since supplementary-plane right-to-left blocks are identifiable from the
 /// high surrogate without examining the low surrogate, this function returns
 /// `true` for such high surrogates making the function suitable for handling
 /// supplementary-plane text without decoding surrogate pairs to scalar
 /// values. Obviously, such high surrogates are then reported as right-to-left
 /// even if actually unpaired.
 #[inline(always)]
@@ -1333,27 +1362,27 @@ pub fn is_utf16_code_unit_bidi(u: u16) -
     if in_range16(u, 0x0900, 0xD802) {
         // Above Arabic Extended-A and below first RTL surrogate
         if in_inclusive_range16(u, 0x200F, 0x2067) {
             // In the range that contains the RTL controls
             return u == 0x200F || u == 0x202B || u == 0x202E || u == 0x2067;
         }
         return false;
     }
-    if in_range16(u, 0xD83C, 0xFB50) {
-        // Between astral RTL high surrogates and Arabic Presentation Forms
+    if in_range16(u, 0xD83C, 0xFB1D) {
+        // Between astral RTL high surrogates and Hebrew presentation forms
         // (Emoji is here)
         return false;
     }
     if in_range16(u, 0xD804, 0xD83A) {
         // Between RTL high surragates
         return false;
     }
-    if u > 0xFEFF {
-        // Above Arabic Presentation Forms
+    if u > 0xFEFE {
+        // Above Arabic Presentation Forms (excl. BOM)
         return false;
     }
     if in_range16(u, 0xFE00, 0xFE70) {
         // Between Arabic Presentations Forms
         return false;
     }
     true
 }
@@ -2365,23 +2394,24 @@ mod tests {
     #[test]
     fn test_is_char_bidi() {
         assert!(!is_char_bidi('a'));
         assert!(!is_char_bidi('\u{03B1}'));
         assert!(!is_char_bidi('\u{3041}'));
         assert!(!is_char_bidi('\u{1F4A9}'));
         assert!(!is_char_bidi('\u{FE00}'));
         assert!(!is_char_bidi('\u{202C}'));
+        assert!(!is_char_bidi('\u{FEFF}'));
         assert!(is_char_bidi('\u{0590}'));
         assert!(is_char_bidi('\u{08FF}'));
         assert!(is_char_bidi('\u{061C}'));
         assert!(is_char_bidi('\u{FB50}'));
         assert!(is_char_bidi('\u{FDFF}'));
         assert!(is_char_bidi('\u{FE70}'));
-        assert!(is_char_bidi('\u{FEFF}'));
+        assert!(is_char_bidi('\u{FEFE}'));
         assert!(is_char_bidi('\u{200F}'));
         assert!(is_char_bidi('\u{202B}'));
         assert!(is_char_bidi('\u{202E}'));
         assert!(is_char_bidi('\u{2067}'));
         assert!(is_char_bidi('\u{10800}'));
         assert!(is_char_bidi('\u{10FFF}'));
         assert!(is_char_bidi('\u{1E800}'));
         assert!(is_char_bidi('\u{1EFFF}'));
@@ -2390,23 +2420,25 @@ mod tests {
     #[test]
     fn test_is_utf16_code_unit_bidi() {
         assert!(!is_utf16_code_unit_bidi(0x0062));
         assert!(!is_utf16_code_unit_bidi(0x03B1));
         assert!(!is_utf16_code_unit_bidi(0x3041));
         assert!(!is_utf16_code_unit_bidi(0xD801));
         assert!(!is_utf16_code_unit_bidi(0xFE00));
         assert!(!is_utf16_code_unit_bidi(0x202C));
+        assert!(!is_utf16_code_unit_bidi(0xFEFF));
         assert!(is_utf16_code_unit_bidi(0x0590));
         assert!(is_utf16_code_unit_bidi(0x08FF));
         assert!(is_utf16_code_unit_bidi(0x061C));
+        assert!(is_utf16_code_unit_bidi(0xFB1D));
         assert!(is_utf16_code_unit_bidi(0xFB50));
         assert!(is_utf16_code_unit_bidi(0xFDFF));
         assert!(is_utf16_code_unit_bidi(0xFE70));
-        assert!(is_utf16_code_unit_bidi(0xFEFF));
+        assert!(is_utf16_code_unit_bidi(0xFEFE));
         assert!(is_utf16_code_unit_bidi(0x200F));
         assert!(is_utf16_code_unit_bidi(0x202B));
         assert!(is_utf16_code_unit_bidi(0x202E));
         assert!(is_utf16_code_unit_bidi(0x2067));
         assert!(is_utf16_code_unit_bidi(0xD802));
         assert!(is_utf16_code_unit_bidi(0xD803));
         assert!(is_utf16_code_unit_bidi(0xD83A));
         assert!(is_utf16_code_unit_bidi(0xD83B));
@@ -2415,23 +2447,24 @@ mod tests {
     #[test]
     fn test_is_str_bidi() {
         assert!(!is_str_bidi("abcdefghijklmnopaabcdefghijklmnop"));
         assert!(!is_str_bidi("abcdefghijklmnop\u{03B1}abcdefghijklmnop"));
         assert!(!is_str_bidi("abcdefghijklmnop\u{3041}abcdefghijklmnop"));
         assert!(!is_str_bidi("abcdefghijklmnop\u{1F4A9}abcdefghijklmnop"));
         assert!(!is_str_bidi("abcdefghijklmnop\u{FE00}abcdefghijklmnop"));
         assert!(!is_str_bidi("abcdefghijklmnop\u{202C}abcdefghijklmnop"));
+        assert!(!is_str_bidi("abcdefghijklmnop\u{FEFF}abcdefghijklmnop"));
         assert!(is_str_bidi("abcdefghijklmnop\u{0590}abcdefghijklmnop"));
         assert!(is_str_bidi("abcdefghijklmnop\u{08FF}abcdefghijklmnop"));
         assert!(is_str_bidi("abcdefghijklmnop\u{061C}abcdefghijklmnop"));
         assert!(is_str_bidi("abcdefghijklmnop\u{FB50}abcdefghijklmnop"));
         assert!(is_str_bidi("abcdefghijklmnop\u{FDFF}abcdefghijklmnop"));
         assert!(is_str_bidi("abcdefghijklmnop\u{FE70}abcdefghijklmnop"));
-        assert!(is_str_bidi("abcdefghijklmnop\u{FEFF}abcdefghijklmnop"));
+        assert!(is_str_bidi("abcdefghijklmnop\u{FEFE}abcdefghijklmnop"));
         assert!(is_str_bidi("abcdefghijklmnop\u{200F}abcdefghijklmnop"));
         assert!(is_str_bidi("abcdefghijklmnop\u{202B}abcdefghijklmnop"));
         assert!(is_str_bidi("abcdefghijklmnop\u{202E}abcdefghijklmnop"));
         assert!(is_str_bidi("abcdefghijklmnop\u{2067}abcdefghijklmnop"));
         assert!(is_str_bidi("abcdefghijklmnop\u{10800}abcdefghijklmnop"));
         assert!(is_str_bidi("abcdefghijklmnop\u{10FFF}abcdefghijklmnop"));
         assert!(is_str_bidi("abcdefghijklmnop\u{1E800}abcdefghijklmnop"));
         assert!(is_str_bidi("abcdefghijklmnop\u{1EFFF}abcdefghijklmnop"));
@@ -2452,16 +2485,19 @@ mod tests {
             "abcdefghijklmnop\u{1F4A9}abcdefghijklmnop".as_bytes()
         ));
         assert!(!is_utf8_bidi(
             "abcdefghijklmnop\u{FE00}abcdefghijklmnop".as_bytes()
         ));
         assert!(!is_utf8_bidi(
             "abcdefghijklmnop\u{202C}abcdefghijklmnop".as_bytes()
         ));
+        assert!(!is_utf8_bidi(
+            "abcdefghijklmnop\u{FEFF}abcdefghijklmnop".as_bytes()
+        ));
         assert!(is_utf8_bidi(
             "abcdefghijklmnop\u{0590}abcdefghijklmnop".as_bytes()
         ));
         assert!(is_utf8_bidi(
             "abcdefghijklmnop\u{08FF}abcdefghijklmnop".as_bytes()
         ));
         assert!(is_utf8_bidi(
             "abcdefghijklmnop\u{061C}abcdefghijklmnop".as_bytes()
@@ -2471,17 +2507,17 @@ mod tests {
         ));
         assert!(is_utf8_bidi(
             "abcdefghijklmnop\u{FDFF}abcdefghijklmnop".as_bytes()
         ));
         assert!(is_utf8_bidi(
             "abcdefghijklmnop\u{FE70}abcdefghijklmnop".as_bytes()
         ));
         assert!(is_utf8_bidi(
-            "abcdefghijklmnop\u{FEFF}abcdefghijklmnop".as_bytes()
+            "abcdefghijklmnop\u{FEFE}abcdefghijklmnop".as_bytes()
         ));
         assert!(is_utf8_bidi(
             "abcdefghijklmnop\u{200F}abcdefghijklmnop".as_bytes()
         ));
         assert!(is_utf8_bidi(
             "abcdefghijklmnop\u{202B}abcdefghijklmnop".as_bytes()
         ));
         assert!(is_utf8_bidi(
@@ -2525,42 +2561,50 @@ mod tests {
         assert!(!is_utf16_bidi(&[
             0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xFE00, 0x62, 0x63, 0x64, 0x65, 0x66,
             0x67, 0x68, 0x69,
         ]));
         assert!(!is_utf16_bidi(&[
             0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x202C, 0x62, 0x63, 0x64, 0x65, 0x66,
             0x67, 0x68, 0x69,
         ]));
+        assert!(!is_utf16_bidi(&[
+            0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xFEFF, 0x62, 0x63, 0x64, 0x65, 0x66,
+            0x67, 0x68, 0x69,
+        ]));
         assert!(is_utf16_bidi(&[
             0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x0590, 0x62, 0x63, 0x64, 0x65, 0x66,
             0x67, 0x68, 0x69,
         ]));
         assert!(is_utf16_bidi(&[
             0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x08FF, 0x62, 0x63, 0x64, 0x65, 0x66,
             0x67, 0x68, 0x69,
         ]));
         assert!(is_utf16_bidi(&[
             0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x061C, 0x62, 0x63, 0x64, 0x65, 0x66,
             0x67, 0x68, 0x69,
         ]));
         assert!(is_utf16_bidi(&[
+            0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xFB1D, 0x62, 0x63, 0x64, 0x65, 0x66,
+            0x67, 0x68, 0x69,
+        ]));
+        assert!(is_utf16_bidi(&[
             0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xFB50, 0x62, 0x63, 0x64, 0x65, 0x66,
             0x67, 0x68, 0x69,
         ]));
         assert!(is_utf16_bidi(&[
             0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xFDFF, 0x62, 0x63, 0x64, 0x65, 0x66,
             0x67, 0x68, 0x69,
         ]));
         assert!(is_utf16_bidi(&[
             0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xFE70, 0x62, 0x63, 0x64, 0x65, 0x66,
             0x67, 0x68, 0x69,
         ]));
         assert!(is_utf16_bidi(&[
-            0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xFEFF, 0x62, 0x63, 0x64, 0x65, 0x66,
+            0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xFEFE, 0x62, 0x63, 0x64, 0x65, 0x66,
             0x67, 0x68, 0x69,
         ]));
         assert!(is_utf16_bidi(&[
             0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x200F, 0x62, 0x63, 0x64, 0x65, 0x66,
             0x67, 0x68, 0x69,
         ]));
         assert!(is_utf16_bidi(&[
             0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x202B, 0x62, 0x63, 0x64, 0x65, 0x66,
@@ -2618,16 +2662,20 @@ mod tests {
         assert_ne!(
             check_str_for_latin1_and_bidi("abcdefghijklmnop\u{FE00}abcdefghijklmnop"),
             Latin1Bidi::Bidi
         );
         assert_ne!(
             check_str_for_latin1_and_bidi("abcdefghijklmnop\u{202C}abcdefghijklmnop"),
             Latin1Bidi::Bidi
         );
+        assert_ne!(
+            check_str_for_latin1_and_bidi("abcdefghijklmnop\u{FEFF}abcdefghijklmnop"),
+            Latin1Bidi::Bidi
+        );
         assert_eq!(
             check_str_for_latin1_and_bidi("abcdefghijklmnop\u{0590}abcdefghijklmnop"),
             Latin1Bidi::Bidi
         );
         assert_eq!(
             check_str_for_latin1_and_bidi("abcdefghijklmnop\u{08FF}abcdefghijklmnop"),
             Latin1Bidi::Bidi
         );
@@ -2643,17 +2691,17 @@ mod tests {
             check_str_for_latin1_and_bidi("abcdefghijklmnop\u{FDFF}abcdefghijklmnop"),
             Latin1Bidi::Bidi
         );
         assert_eq!(
             check_str_for_latin1_and_bidi("abcdefghijklmnop\u{FE70}abcdefghijklmnop"),
             Latin1Bidi::Bidi
         );
         assert_eq!(
-            check_str_for_latin1_and_bidi("abcdefghijklmnop\u{FEFF}abcdefghijklmnop"),
+            check_str_for_latin1_and_bidi("abcdefghijklmnop\u{FEFE}abcdefghijklmnop"),
             Latin1Bidi::Bidi
         );
         assert_eq!(
             check_str_for_latin1_and_bidi("abcdefghijklmnop\u{200F}abcdefghijklmnop"),
             Latin1Bidi::Bidi
         );
         assert_eq!(
             check_str_for_latin1_and_bidi("abcdefghijklmnop\u{202B}abcdefghijklmnop"),
@@ -2706,16 +2754,20 @@ mod tests {
         assert_ne!(
             check_utf8_for_latin1_and_bidi("abcdefghijklmnop\u{FE00}abcdefghijklmnop".as_bytes()),
             Latin1Bidi::Bidi
         );
         assert_ne!(
             check_utf8_for_latin1_and_bidi("abcdefghijklmnop\u{202C}abcdefghijklmnop".as_bytes()),
             Latin1Bidi::Bidi
         );
+        assert_ne!(
+            check_utf8_for_latin1_and_bidi("abcdefghijklmnop\u{FEFF}abcdefghijklmnop".as_bytes()),
+            Latin1Bidi::Bidi
+        );
         assert_eq!(
             check_utf8_for_latin1_and_bidi("abcdefghijklmnop\u{0590}abcdefghijklmnop".as_bytes()),
             Latin1Bidi::Bidi
         );
         assert_eq!(
             check_utf8_for_latin1_and_bidi("abcdefghijklmnop\u{08FF}abcdefghijklmnop".as_bytes()),
             Latin1Bidi::Bidi
         );
@@ -2731,17 +2783,17 @@ mod tests {
             check_utf8_for_latin1_and_bidi("abcdefghijklmnop\u{FDFF}abcdefghijklmnop".as_bytes()),
             Latin1Bidi::Bidi
         );
         assert_eq!(
             check_utf8_for_latin1_and_bidi("abcdefghijklmnop\u{FE70}abcdefghijklmnop".as_bytes()),
             Latin1Bidi::Bidi
         );
         assert_eq!(
-            check_utf8_for_latin1_and_bidi("abcdefghijklmnop\u{FEFF}abcdefghijklmnop".as_bytes()),
+            check_utf8_for_latin1_and_bidi("abcdefghijklmnop\u{FEFE}abcdefghijklmnop".as_bytes()),
             Latin1Bidi::Bidi
         );
         assert_eq!(
             check_utf8_for_latin1_and_bidi("abcdefghijklmnop\u{200F}abcdefghijklmnop".as_bytes()),
             Latin1Bidi::Bidi
         );
         assert_eq!(
             check_utf8_for_latin1_and_bidi("abcdefghijklmnop\u{202B}abcdefghijklmnop".as_bytes()),
@@ -2812,16 +2864,23 @@ mod tests {
         );
         assert_ne!(
             check_utf16_for_latin1_and_bidi(&[
                 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x202C, 0x62, 0x63, 0x64, 0x65,
                 0x66, 0x67, 0x68, 0x69,
             ]),
             Latin1Bidi::Bidi
         );
+        assert_ne!(
+            check_utf16_for_latin1_and_bidi(&[
+                0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xFEFF, 0x62, 0x63, 0x64, 0x65,
+                0x66, 0x67, 0x68, 0x69,
+            ]),
+            Latin1Bidi::Bidi
+        );
         assert_eq!(
             check_utf16_for_latin1_and_bidi(&[
                 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x0590, 0x62, 0x63, 0x64, 0x65,
                 0x66, 0x67, 0x68, 0x69,
             ]),
             Latin1Bidi::Bidi
         );
         assert_eq!(
@@ -2835,16 +2894,23 @@ mod tests {
             check_utf16_for_latin1_and_bidi(&[
                 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x061C, 0x62, 0x63, 0x64, 0x65,
                 0x66, 0x67, 0x68, 0x69,
             ]),
             Latin1Bidi::Bidi
         );
         assert_eq!(
             check_utf16_for_latin1_and_bidi(&[
+                0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xFB1D, 0x62, 0x63, 0x64, 0x65,
+                0x66, 0x67, 0x68, 0x69,
+            ]),
+            Latin1Bidi::Bidi
+        );
+        assert_eq!(
+            check_utf16_for_latin1_and_bidi(&[
                 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xFB50, 0x62, 0x63, 0x64, 0x65,
                 0x66, 0x67, 0x68, 0x69,
             ]),
             Latin1Bidi::Bidi
         );
         assert_eq!(
             check_utf16_for_latin1_and_bidi(&[
                 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xFDFF, 0x62, 0x63, 0x64, 0x65,
@@ -2856,17 +2922,17 @@ mod tests {
             check_utf16_for_latin1_and_bidi(&[
                 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xFE70, 0x62, 0x63, 0x64, 0x65,
                 0x66, 0x67, 0x68, 0x69,
             ]),
             Latin1Bidi::Bidi
         );
         assert_eq!(
             check_utf16_for_latin1_and_bidi(&[
-                0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xFEFF, 0x62, 0x63, 0x64, 0x65,
+                0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xFEFE, 0x62, 0x63, 0x64, 0x65,
                 0x66, 0x67, 0x68, 0x69,
             ]),
             Latin1Bidi::Bidi
         );
         assert_eq!(
             check_utf16_for_latin1_and_bidi(&[
                 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x200F, 0x62, 0x63, 0x64, 0x65,
                 0x66, 0x67, 0x68, 0x69,
@@ -2931,34 +2997,34 @@ mod tests {
             Latin1Bidi::Bidi
         );
     }
 
     #[inline(always)]
     pub fn reference_is_char_bidi(c: char) -> bool {
         match c {
             '\u{0590}'...'\u{08FF}'
-            | '\u{FB50}'...'\u{FDFF}'
-            | '\u{FE70}'...'\u{FEFF}'
+            | '\u{FB1D}'...'\u{FDFF}'
+            | '\u{FE70}'...'\u{FEFE}'
             | '\u{10800}'...'\u{10FFF}'
             | '\u{1E800}'...'\u{1EFFF}'
             | '\u{200F}'
             | '\u{202B}'
             | '\u{202E}'
             | '\u{2067}' => true,
             _ => false,
         }
     }
 
     #[inline(always)]
     pub fn reference_is_utf16_code_unit_bidi(u: u16) -> bool {
         match u {
             0x0590...0x08FF
-            | 0xFB50...0xFDFF
-            | 0xFE70...0xFEFF
+            | 0xFB1D...0xFDFF
+            | 0xFE70...0xFEFE
             | 0xD802
             | 0xD803
             | 0xD83A
             | 0xD83B
             | 0x200F
             | 0x202B
             | 0x202E
             | 0x2067 => true,
@@ -3045,16 +3111,29 @@ mod tests {
                     }
                 }
             }
             assert_eq!(is_utf8_bidi(&buf[..]), expect);
         }
     }
 
     #[test]
+    fn test_is_utf16_bidi_thoroughly() {
+        let mut buf = [0; 32];
+        for i in 0..0x10000u32 {
+            let u = i as u16;
+            buf[15] = u;
+            assert_eq!(
+                is_utf16_bidi(&buf[..]),
+                reference_is_utf16_code_unit_bidi(u)
+            );
+        }
+    }
+
+    #[test]
     fn test_is_utf8_bidi_edge_cases() {
         assert!(!is_utf8_bidi(b"\xD5\xBF\x61"));
         assert!(!is_utf8_bidi(b"\xD6\x80\x61"));
         assert!(!is_utf8_bidi(b"abc"));
         assert!(is_utf8_bidi(b"\xD5\xBF\xC2"));
         assert!(is_utf8_bidi(b"\xD6\x80\xC2"));
         assert!(is_utf8_bidi(b"ab\xC2"));
     }
--- a/third_party/rust/encoding_rs/src/simd_funcs.rs
+++ b/third_party/rust/encoding_rs/src/simd_funcs.rs
@@ -273,18 +273,18 @@ pub fn is_u16x8_bidi(s: u16x8) -> bool {
 
     if (below_hebrew | in_range16x8!(s, 0x0900, 0x200F) | in_range16x8!(s, 0x2068, 0xD802)).all() {
         return false;
     }
 
     // Quick refutation failed. Let's do the full check.
 
     (in_range16x8!(s, 0x0590, 0x0900)
-        | in_range16x8!(s, 0xFB50, 0xFE00)
-        | in_range16x8!(s, 0xFE70, 0xFF00)
+        | in_range16x8!(s, 0xFB1D, 0xFE00)
+        | in_range16x8!(s, 0xFE70, 0xFEFF)
         | in_range16x8!(s, 0xD802, 0xD804)
         | in_range16x8!(s, 0xD83A, 0xD83C)
         | s.eq(u16x8::splat(0x200F))
         | s.eq(u16x8::splat(0x202B))
         | s.eq(u16x8::splat(0x202E))
         | s.eq(u16x8::splat(0x2067)))
         .any()
 }
--- a/third_party/rust/encoding_rs/src/utf_16.rs
+++ b/third_party/rust/encoding_rs/src/utf_16.rs
@@ -396,9 +396,63 @@ mod tests {
                 decoder.decode_to_utf16(b"\xFE", &mut output[..needed], true);
             assert_eq!(result, CoderResult::InputEmpty);
             assert_eq!(read, 1);
             assert_eq!(written, 1);
             assert!(had_errors);
             assert_eq!(output[0], 0xFFFD);
         }
     }
+
+    #[test]
+    fn test_utf_16le_decode_near_end() {
+        let mut output = [0u8; 4];
+        let mut decoder = UTF_16LE.new_decoder();
+        {
+            let (result, read, written, had_errors) =
+                decoder.decode_to_utf8(&[0x03], &mut output[..], false);
+            assert_eq!(result, CoderResult::InputEmpty);
+            assert_eq!(read, 1);
+            assert_eq!(written, 0);
+            assert!(!had_errors);
+            assert_eq!(output[0], 0x0);
+        }
+        {
+            let (result, read, written, had_errors) =
+                decoder.decode_to_utf8(&[0x26, 0x03, 0x26], &mut output[..], false);
+            assert_eq!(result, CoderResult::OutputFull);
+            assert_eq!(read, 1);
+            assert_eq!(written, 3);
+            assert!(!had_errors);
+            assert_eq!(output[0], 0xE2);
+            assert_eq!(output[1], 0x98);
+            assert_eq!(output[2], 0x83);
+            assert_eq!(output[3], 0x00);
+        }
+    }
+
+    #[test]
+    fn test_utf_16be_decode_near_end() {
+        let mut output = [0u8; 4];
+        let mut decoder = UTF_16BE.new_decoder();
+        {
+            let (result, read, written, had_errors) =
+                decoder.decode_to_utf8(&[0x26], &mut output[..], false);
+            assert_eq!(result, CoderResult::InputEmpty);
+            assert_eq!(read, 1);
+            assert_eq!(written, 0);
+            assert!(!had_errors);
+            assert_eq!(output[0], 0x0);
+        }
+        {
+            let (result, read, written, had_errors) =
+                decoder.decode_to_utf8(&[0x03, 0x26, 0x03], &mut output[..], false);
+            assert_eq!(result, CoderResult::OutputFull);
+            assert_eq!(read, 1);
+            assert_eq!(written, 3);
+            assert!(!had_errors);
+            assert_eq!(output[0], 0xE2);
+            assert_eq!(output[1], 0x98);
+            assert_eq!(output[2], 0x83);
+            assert_eq!(output[3], 0x00);
+        }
+    }
 }
--- a/toolkit/components/find/nsWebBrowserFind.cpp
+++ b/toolkit/components/find/nsWebBrowserFind.cpp
@@ -134,21 +134,17 @@ nsWebBrowserFind::FindNext(bool* aResult
   rv = rootDocShell->GetDocShellEnumerator(nsIDocShellTreeItem::typeAll,
                                            enumDirection,
                                            getter_AddRefs(docShellEnumerator));
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   // remember where we started
-  nsCOMPtr<nsIDocShellTreeItem> startingItem =
-    do_QueryInterface(searchFrame->GetDocShell(), &rv);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
+  nsCOMPtr<nsIDocShellTreeItem> startingItem = searchFrame->GetDocShell();
 
   nsCOMPtr<nsIDocShellTreeItem> curItem;
 
   // XXX We should avoid searching in frameset documents here.
   // We also need to honour mSearchSubFrames and mSearchParentFrames.
   bool hasMore, doFind = false;
   while (NS_SUCCEEDED(docShellEnumerator->HasMoreElements(&hasMore)) &&
          hasMore) {
--- a/toolkit/components/passwordmgr/LoginManagerContent.jsm
+++ b/toolkit/components/passwordmgr/LoginManagerContent.jsm
@@ -965,26 +965,30 @@ var LoginManagerContent = {
                          null;
     let mockPassword = { name: newPasswordField.name,
                          value: newPasswordField.value };
     let mockOldPassword = oldPasswordField ?
                             { name: oldPasswordField.name,
                               value: oldPasswordField.value } :
                             null;
 
-    // Make sure to pass the opener's top in case it was in a frame.
-    let openerTopWindow = win.opener ? win.opener.top : null;
+    // Make sure to pass the opener's top ID in case it was in a frame.
+    let openerTopWindowID = null;
+    if (win.opener) {
+      openerTopWindowID = win.opener.top.windowUtils.outerWindowID;
+    }
 
     messageManager.sendAsyncMessage("RemoteLogins:onFormSubmit",
                                     { hostname,
                                       formSubmitURL,
                                       usernameField: mockUsername,
                                       newPasswordField: mockPassword,
-                                      oldPasswordField: mockOldPassword },
-                                    { openerTopWindow });
+                                      oldPasswordField: mockOldPassword,
+                                      openerTopWindowID,
+                                    });
   },
 
   /**
    * Attempt to find the username and password fields in a form, and fill them
    * in using the provided logins and recipes.
    *
    * @param {LoginForm} form
    * @param {nsILoginInfo[]} foundLogins an array of nsILoginInfo that could be
--- a/toolkit/components/passwordmgr/LoginManagerParent.jsm
+++ b/toolkit/components/passwordmgr/LoginManagerParent.jsm
@@ -87,17 +87,17 @@ var LoginManagerParent = {
 
       case "RemoteLogins:onFormSubmit": {
         // TODO Verify msg.target's principals against the formOrigin?
         this.onFormSubmit(data.hostname,
                           data.formSubmitURL,
                           data.usernameField,
                           data.newPasswordField,
                           data.oldPasswordField,
-                          msg.objects.openerTopWindow,
+                          data.openerTopWindowID,
                           msg.target);
         break;
       }
 
       case "RemoteLogins:insecureLoginFormPresent": {
         this.setHasInsecureLoginForms(msg.target, data.hasInsecureLoginForms);
         break;
       }
@@ -283,24 +283,39 @@ var LoginManagerParent = {
     target.messageManager.sendAsyncMessage("RemoteLogins:loginsAutoCompleted", {
       requestId,
       logins: jsLogins,
     });
   },
 
   onFormSubmit(hostname, formSubmitURL,
                          usernameField, newPasswordField,
-                         oldPasswordField, openerTopWindow,
+                         oldPasswordField, openerTopWindowID,
                          target) {
     function getPrompter() {
       var prompterSvc = Cc["@mozilla.org/login-manager/prompter;1"].
                         createInstance(Ci.nsILoginManagerPrompter);
       prompterSvc.init(target.ownerGlobal);
       prompterSvc.browser = target;
-      prompterSvc.opener = openerTopWindow;
+
+      for (let win of Services.wm.getEnumerator(null)) {
+        if (!win.gBrowser && !win.getBrowser) {
+          continue;
+        }
+
+        let tabbrowser = win.gBrowser || win.getBrowser();
+        if (tabbrowser) {
+          let browser = tabbrowser.getBrowserForOuterWindowID(openerTopWindowID);
+          if (browser) {
+            prompterSvc.openerBrowser = browser;
+            break;
+          }
+        }
+      }
+
       return prompterSvc;
     }
 
     function recordLoginUse(login) {
       // Update the lastUsed timestamp and increment the use count.
       let propBag = Cc["@mozilla.org/hash-property-bag;1"].
                     createInstance(Ci.nsIWritablePropertyBag);
       propBag.setProperty("timeLastUsed", Date.now());
--- a/toolkit/components/passwordmgr/nsILoginManagerPrompter.idl
+++ b/toolkit/components/passwordmgr/nsILoginManagerPrompter.idl
@@ -33,22 +33,21 @@ interface nsILoginManagerPrompter : nsIS
 
   /**
    * The browser this prompter is being created for.
    * This is required if the init function received a chrome window as argument.
    */
   attribute Element browser;
 
   /**
-   * The opener that was used to open the window passed to init.
+   * The opener browser that was used to open the window passed to init.
    * The opener can be used to determine in which window the prompt
-   * should be shown. Must be a content window that is not a frame window,
-   * make sure to pass the top window using e.g. window.top.
+   * should be shown.
    */
-  attribute nsIDOMWindow opener;
+  attribute Element openerBrowser;
 
   /**
    * Ask the user if they want to save a login (Yes, Never, Not Now)
    *
    * @param aLogin
    *        The login to be saved.
    */
   void promptToSavePassword(in nsILoginInfo aLogin);
--- a/toolkit/components/passwordmgr/nsLoginManagerPrompter.js
+++ b/toolkit/components/passwordmgr/nsLoginManagerPrompter.js
@@ -242,17 +242,17 @@ LoginManagerPrompter.prototype = {
   classID: Components.ID("{8aa66d77-1bbb-45a6-991e-b8f47751c291}"),
   QueryInterface: ChromeUtils.generateQI([Ci.nsIAuthPrompt,
                                            Ci.nsIAuthPrompt2,
                                            Ci.nsILoginManagerPrompter]),
 
   _factory: null,
   _chromeWindow: null,
   _browser: null,
-  _opener: null,
+  _openerBrowser: null,
 
   __strBundle: null, // String bundle for L10N
   get _strBundle() {
     if (!this.__strBundle) {
       this.__strBundle = Services.strings.createBundle(
                   "chrome://passwordmgr/locale/passwordmgr.properties");
       if (!this.__strBundle)
         throw new Error("String bundle for Login Manager not present!");
@@ -718,37 +718,38 @@ LoginManagerPrompter.prototype = {
       this._chromeWindow = aWindow;
       // needs to be set explicitly using setBrowser
       this._browser = null;
     } else {
       let {win, browser} = this._getChromeWindow(aWindow);
       this._chromeWindow = win;
       this._browser = browser;
     }
-    this._opener = null;
+    this._openerBrowser = null;
     this._factory = aFactory || null;
 
     this.log("===== initialized =====");
   },
 
   set browser(aBrowser) {
     this._browser = aBrowser;
   },
 
-  set opener(aOpener) {
-    this._opener = aOpener;
+  set openerBrowser(aOpenerBrowser) {
+    this._openerBrowser = aOpenerBrowser;
   },
 
   promptToSavePassword(aLogin) {
     this.log("promptToSavePassword");
     var notifyObj = this._getPopupNote() || this._getNotifyBox();
-    if (notifyObj)
+    if (notifyObj) {
       this._showSaveLoginNotification(notifyObj, aLogin);
-    else
+    } else {
       this._showSaveLoginDialog(aLogin);
+    }
   },
 
   /**
    * Displays a notification bar.
    */
   _showLoginNotification(aNotifyBox, aName, aText, aButtons) {
     var oldBar = aNotifyBox.getNotificationWithValue(aName);
     const priority = aNotifyBox.PRIORITY_INFO_MEDIUM;
@@ -1392,62 +1393,53 @@ LoginManagerPrompter.prototype = {
   },
 
   /**
    * Given a content DOM window, returns the chrome window and browser it's in.
    */
   _getChromeWindow(aWindow) {
     // Handle non-e10s toolkit consumers.
     if (!Cu.isCrossProcessWrapper(aWindow)) {
-      let chromeWin = aWindow.docShell.chromeEventHandler.ownerGlobal;
+      let browser = aWindow.docShell.chromeEventHandler;
+      if (!browser) {
+        return null;
+      }
+
+      let chromeWin = browser.ownerGlobal;
       if (!chromeWin) {
         return null;
       }
 
-      // gBrowser only exists on some apps, like Firefox.
-      let tabbrowser = chromeWin.gBrowser ||
-        (typeof chromeWin.getBrowser == "function" ? chromeWin.getBrowser() : null);
-      // At least serve the chrome window if getBrowser()
-      // or getBrowserForContentWindow() are not supported.
-      if (!tabbrowser || typeof tabbrowser.getBrowserForContentWindow != "function") {
-        return { win: chromeWin };
-      }
-
-      let browser = tabbrowser.getBrowserForContentWindow(aWindow);
       return { win: chromeWin, browser };
     }
 
-    for (let win of Services.wm.getEnumerator(null)) {
-      let tabbrowser = win.gBrowser || win.getBrowser();
-      let browser = tabbrowser.getBrowserForContentWindow(aWindow);
-      if (browser) {
-        return { win, browser };
-      }
-    }
     return null;
   },
 
   _getNotifyWindow() {
     // Some sites pop up a temporary login window, which disappears
     // upon submission of credentials. We want to put the notification
     // bar in the opener window if this seems to be happening.
-    if (this._opener) {
+    if (this._openerBrowser) {
       let chromeDoc = this._chromeWindow.document.documentElement;
 
       // Check to see if the current window was opened with chrome
       // disabled, and if so use the opener window. But if the window
       // has been used to visit other pages (ie, has a history),
       // assume it'll stick around and *don't* use the opener.
       if (chromeDoc.getAttribute("chromehidden") && !this._browser.canGoBack) {
         this.log("Using opener window for notification bar.");
-        return this._getChromeWindow(this._opener);
+        return { win: this._openerBrowser.ownerGlobal, browser: this._openerBrowser };
       }
     }
 
-    return { win: this._chromeWindow, browser: this._browser };
+    return {
+      win: this._chromeWindow,
+      browser: this._browser,
+    };
   },
 
   /**
    * Returns the popup notification to this prompter,
    * or null if there isn't one available.
    */
   _getPopupNote() {
     let popupNote = null;
--- a/toolkit/components/prompts/src/nsPrompter.js
+++ b/toolkit/components/prompts/src/nsPrompter.js
@@ -286,18 +286,25 @@ var PromptUtilsTemp = {
             // Get the topmost window, in case we're in a frame.
             var promptWin = domWin.top;
 
             // Get the chrome window for the content window we're using.
             // (Unwrap because we need a non-IDL property below.)
             var chromeWin =
                 promptWin.docShell.chromeEventHandler.ownerGlobal.wrappedJSObject;
 
-            if (chromeWin.getTabModalPromptBox)
-                promptBox = chromeWin.getTabModalPromptBox(promptWin);
+            if (chromeWin) {
+                if (chromeWin.gBrowser &&
+                    chromeWin.gBrowser.getTabModalPromptBox) {
+                    let browser = promptWin.docShell.chromeEventHandler;
+                    promptBox = chromeWin.gBrowser.getTabModalPromptBox(browser);
+                } else if (chromeWin.getTabModalPromptBox) {
+                    chromeWin.getTabModalPromptBox(promptWin);
+                }
+            }
         } catch (e) {
             // If any errors happen, just assume no tabmodal prompter.
         }
 
         return promptBox;
     },
 };
 
--- a/toolkit/components/reader/test/browser_readerMode_with_anchor.js
+++ b/toolkit/components/reader/test/browser_readerMode_with_anchor.js
@@ -24,17 +24,20 @@ add_task(async function test_loading_wit
 });
 
 add_task(async function test_loading_withoutHash() {
   await BrowserTestUtils.withNewTab(TEST_PATH + "readerModeArticle.html", async function(browser) {
     let pageShownPromise = BrowserTestUtils.waitForContentEvent(browser, "AboutReaderContentReady");
     let readerButton = document.getElementById("reader-mode-button");
     readerButton.click();
     await pageShownPromise;
-    is(gBrowser.contentDocumentAsCPOW.documentElement.scrollTop, 0, "scrollTop should be 0");
+    await ContentTask.spawn(gBrowser.selectedBrowser, null, async () => {
+      Assert.equal(content.document.documentElement.scrollTop, 0,
+                   "scrollTop should be 0");
+    });
     await BrowserTestUtils.synthesizeMouseAtCenter("#foo-anchor", {}, browser);
     await ContentTask.spawn(browser, null, async function() {
       let foo = content.document.getElementById("foo");
       ok(foo, "foo element should be in document");
       let {scrollTop} = content.document.documentElement;
       let {offsetTop} = foo;
       Assert.lessOrEqual(Math.abs(scrollTop - offsetTop), 1,
         `scrollTop (${scrollTop}) should be within 1 CSS pixel of offsetTop (${offsetTop})`);
--- a/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp
+++ b/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp
@@ -557,18 +557,17 @@ nsTypeAheadFind::FindItNow(nsIPresShell 
          * the element only if focus is in our content window, i.e.
          * |if (focusedWindow.top == ourWindow.top)| */
         bool shouldFocusEditableElement = false;
         if (fm) {
           nsCOMPtr<mozIDOMWindowProxy> focusedWindow;
           nsresult rv = fm->GetFocusedWindow(getter_AddRefs(focusedWindow));
           if (NS_SUCCEEDED(rv) && focusedWindow) {
             auto* fwPI = nsPIDOMWindowOuter::From(focusedWindow);
-            nsCOMPtr<nsIDocShellTreeItem> fwTreeItem
-              (do_QueryInterface(fwPI->GetDocShell(), &rv));
+            nsCOMPtr<nsIDocShellTreeItem> fwTreeItem(fwPI->GetDocShell());
             if (NS_SUCCEEDED(rv)) {
               nsCOMPtr<nsIDocShellTreeItem> fwRootTreeItem;
               rv = fwTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(fwRootTreeItem));
               if (NS_SUCCEEDED(rv) && fwRootTreeItem == rootContentTreeItem)
                 shouldFocusEditableElement = true;
             }
           }
         }
--- a/toolkit/content/tests/browser/browser_datetime_datepicker.js
+++ b/toolkit/content/tests/browser/browser_datetime_datepicker.js
@@ -162,17 +162,20 @@ add_task(async function test_datepicker_
   await helper.openPicker(`data:text/html, <input type="date" value="${inputValue}">`);
   // Click the first item (top-left corner) of the calendar
   helper.click(helper.getElement(DAYS_VIEW).children[0]);
   await ContentTask.spawn(helper.tab.linkedBrowser, {}, async function() {
     let inputEl = content.document.querySelector("input");
     await ContentTaskUtils.waitForEvent(inputEl, "input");
   });
 
-  Assert.equal(gBrowser.contentDocumentAsCPOW.querySelector("input").value, firstDayOnCalendar);
+  let value = await ContentTask.spawn(gBrowser.selectedBrowser, null, async () => {
+    return content.document.querySelector("input").value;
+  });
+  Assert.equal(value, firstDayOnCalendar);
 
   await helper.tearDown();
 });
 
 /**
  * Make sure picker is in correct state when it is reopened.
  */
 add_task(async function test_datepicker_reopen_state() {
--- a/toolkit/content/tests/browser/browser_saveImageURL.js
+++ b/toolkit/content/tests/browser/browser_saveImageURL.js
@@ -33,25 +33,28 @@ add_task(async function preferred_API() 
   }, async function(browser) {
     let url = await ContentTask.spawn(browser, null, async function() {
       let image = content.document.getElementById("image");
       return image.href;
     });
 
     saveImageURL(url, "image.jpg", null, true, false, null, null, null, null,
       false, gBrowser.contentPrincipal);
-    let channel = gBrowser.contentWindowAsCPOW.docShell.currentDocumentChannel;
-    if (channel) {
-      ok(true, channel.QueryInterface(Ci.nsIHttpChannelInternal)
-                      .channelIsForDownload);
+    await ContentTask.spawn(gBrowser.selectedBrowser, null, async () => {
+      let channel = docShell.currentDocumentChannel;
+      if (channel) {
+        todo(channel.QueryInterface(Ci.nsIHttpChannelInternal)
+                    .channelIsForDownload);
 
-      // Throttleable is the only class flag assigned to downloads.
-      ok(channel.QueryInterface(Ci.nsIClassOfService).classFlags,
-         Ci.nsIClassOfService.Throttleable);
-    }
+        // Throttleable is the only class flag assigned to downloads.
+        todo(channel.QueryInterface(Ci.nsIClassOfService).classFlags ==
+             Ci.nsIClassOfService.Throttleable);
+      }
+    });
+
     await waitForFilePicker();
   });
 });
 
 /**
  * Test that saveImageURL will still work when passed a document instead
  * of the aIsContentWindowPrivate argument. This is the deprecated API, and
  * will not work in apps using remote browsers having PREF_UNSAFE_FORBIDDEN
@@ -69,21 +72,24 @@ add_task(async function deprecated_API()
       return image.href;
     });
 
     // Now get the document directly from content. If we run this test with
     // e10s-enabled, this will be a CPOW, which is forbidden. We'll just
     // pass the XUL document instead to test this interface.
     let doc = document;
 
-    let channel = gBrowser.contentWindowAsCPOW.docShell.currentDocumentChannel;
-    if (channel) {
-      ok(true, channel.QueryInterface(Ci.nsIHttpChannelInternal)
-                      .channelIsForDownload);
 
-      // Throttleable is the only class flag assigned to downloads.
-      ok(channel.QueryInterface(Ci.nsIClassOfService).classFlags,
-         Ci.nsIClassOfService.Throttleable);
-    }
+    await ContentTask.spawn(gBrowser.selectedBrowser, null, async () => {
+      let channel = docShell.currentDocumentChannel;
+      if (channel) {
+        todo(channel.QueryInterface(Ci.nsIHttpChannelInternal)
+                    .channelIsForDownload);
+
+        // Throttleable is the only class flag assigned to downloads.
+        todo(channel.QueryInterface(Ci.nsIClassOfService).classFlags ==
+             Ci.nsIClassOfService.Throttleable);
+      }
+    });
     saveImageURL(url, "image.jpg", null, true, false, null, doc, null, null);
     await waitForFilePicker();
   });
 });
--- a/toolkit/content/widgets/browser.xml
+++ b/toolkit/content/widgets/browser.xml
@@ -494,34 +494,24 @@
                 readonly="true"
                 onget="return this.isRemoteBrowser ? this._remoteWebNavigation : this.docShell.QueryInterface(Components.interfaces.nsIWebNavigation);"/>
 
       <field name="_remoteWebProgress">null</field>
       <property name="webProgress"
                 readonly="true"
                 onget="return this.isRemoteBrowser ? this._remoteWebProgress : this.docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIWebProgress);"/>
 
-      <field name="_contentWindow">null</field>
-      <property name="contentWindowAsCPOW"
-                readonly="true"
-                onget="return this.isRemoteBrowser ? this._contentWindow : this.contentWindow;"/>
-
       <property name="sessionHistory"
                 onget="return this.webNavigation.sessionHistory;"
                 readonly="true"/>
 
       <property name="markupDocumentViewer"
                 onget="return this.docShell.contentViewer;"
                 readonly="true"/>
 
-      <field name="_contentDocument">null</field>
-      <property name="contentDocumentAsCPOW"
-                onget="return this.isRemoteBrowser ? this._contentDocument : this.contentDocument;"
-                readonly="true"/>
-
       <field name="_contentTitle">""</field>
       <property name="contentTitle"
                 onget="return this.isRemoteBrowser ? this._contentTitle : this.contentDocument.title;"
                 readonly="true"/>
 
       <field name="_characterSet">""</field>
       <property name="characterSet"
                 onget="return this.isRemoteBrowser ? this._characterSet : this.docShell.charset;">
--- a/toolkit/content/widgets/videocontrols.js
+++ b/toolkit/content/widgets/videocontrols.js
@@ -1108,36 +1108,41 @@ this.VideoControlsImplPageWidget = class
         clickToPlay: {
           keyframes: [
             { transform: "scale(3)", opacity: 0 },
             { transform: "scale(1)", opacity: 0.55 },
           ],
           options: {
             easing: "ease",
             duration: 400,
+             // The fill mode here and below is a workaround to avoid flicker
+             // due to bug 1495350.
+             fill: "both",
           },
         },
         controlBar: {
           keyframes: [
             { opacity: 0 },
             { opacity: 1 },
           ],
           options: {
             easing: "ease",
             duration: 200,
+            fill: "both",
           },
         },
         statusOverlay: {
           keyframes: [
             { opacity: 0 },
             { opacity: 0, offset: .72 }, // ~750ms into animation
             { opacity: 1 },
           ],
           options: {
             duration: 1050,
+            fill: "both",
           },
         },
       },
 
       startFade(element, fadeIn, immediate = false) {
         let animationProp =
           this.animationProps[element.id];
         if (!animationProp) {
@@ -1198,17 +1203,17 @@ this.VideoControlsImplPageWidget = class
             animation.cancel();
             finishedPromise = Promise.resolve();
           } else {
             switch (animation.playState) {
               case "idle":
               case "finished":
                 // There is no animation currently playing.
                 // Schedule a new animation with the desired playback direction.
-                animation.updatePlaybackRate(fadeIn ? 1 : -1);
+                animation.playbackRate = fadeIn ? 1 : -1;
                 animation.play();
                 break;
               case "running":
                 // Allow the animation to play from its current position in
                 // reverse to finish.
                 animation.reverse();
                 break;
               case "pause":
@@ -1217,24 +1222,31 @@ this.VideoControlsImplPageWidget = class
                 throw new Error("Unknown Animation playState: " + animation.playState);
             }
             finishedPromise = animation.finished;
           }
         } else { // immediate
           animation.cancel();
           finishedPromise = Promise.resolve();
         }
-        finishedPromise.then(() => {
+        finishedPromise.then(animation => {
           if (element == this.controlBar) {
             this.onControlBarAnimationFinished();
           }
           element.classList.remove(fadeIn ? "fadein" : "fadeout");
           if (!fadeIn) {
             element.hidden = true;
           }
+          if (animation) {
+            // Explicitly clear the animation effect so that filling animations
+            // stop overwriting stylesheet styles. Remove when bug 1495350 is
+            // fixed and animations are no longer filling animations.
+            // This also stops them from accumulating (See bug 1253476).
+            animation.cancel();
+          }
         }, () => { /* Do nothing on rejection */ });
       },
 
       _triggeredByControls: false,
 
       startPlay() {
         this._triggeredByControls = true;
         this.hideClickToPlay();
--- a/toolkit/content/widgets/videocontrols.xml
+++ b/toolkit/content/widgets/videocontrols.xml
@@ -1099,36 +1099,41 @@
         clickToPlay: {
           keyframes: [
             { transform: "scale(3)", opacity: 0 },
             { transform: "scale(1)", opacity: 0.55 },
           ],
           options: {
             easing: "ease",
             duration: 400,
+            // The fill mode here and below is a workaround to avoid flicker
+            // due to bug 1495350.
+            fill: "both",
           },
         },
         controlBar: {
           keyframes: [
             { opacity: 0 },
             { opacity: 1 },
           ],
           options: {
             easing: "ease",
             duration: 200,
+            fill: "both",
           },
         },
         statusOverlay: {
           keyframes: [
             { opacity: 0 },
             { opacity: 0, offset: .72 }, // ~750ms into animation
             { opacity: 1 },
           ],
           options: {
             duration: 1050,
+            fill: "both",
           },
         },
       },
 
       startFade(element, fadeIn, immediate = false) {
         // Bug 493523, the scrubber doesn't call valueChanged while hidden,
         // so our dependent state (eg, timestamp in the thumb) will be stale.
         // As a workaround, update it manually when it first becomes unhidden.
@@ -1193,17 +1198,17 @@
             animation.cancel();
             finishedPromise = Promise.resolve();
           } else {
             switch (animation.playState) {
               case "idle":
               case "finished":
                 // There is no animation currently playing.
                 // Schedule a new animation with the desired playback direction.
-                animation.updatePlaybackRate(fadeIn ? 1 : -1);
+                animation.playbackRate = fadeIn ? 1 : -1;
                 animation.play();
                 break;
               case "running":
                 // Allow the animation to play from its current position in
                 // reverse to finish.
                 animation.reverse();
                 break;
               case "pause":
@@ -1211,24 +1216,31 @@
                 throw new Error("Unknown Animation playState: " + animation.playState);
             }
             finishedPromise = animation.finished;
           }
         } else { // immediate
           animation.cancel();
           finishedPromise = Promise.resolve();
         }
-        finishedPromise.then(() => {
+        finishedPromise.then(animation => {
           if (element == this.controlBar) {
             this.onControlBarAnimationFinished();
           }
           element.classList.remove(fadeIn ? "fadein" : "fadeout");
           if (!fadeIn) {
             element.hidden = true;
           }
+          if (animation) {
+            // Explicitly clear the animation effect so that filling animations
+            // stop overwriting stylesheet styles. Remove when bug 1495350 is
+            // fixed and animations are no longer filling animations.
+            // This also stops them from accumulating (See bug 1253476).
+            animation.cancel();
+          }
         }, () => { /* Do nothing on rejection */ });
       },
 
       _triggeredByControls: false,
 
       startPlay() {
         this._triggeredByControls = true;
         this.hideClickToPlay();
--- a/toolkit/modules/RemoteWebProgress.jsm
+++ b/toolkit/modules/RemoteWebProgress.jsm
@@ -2,22 +2,21 @@
 // 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 = ["RemoteWebProgressManager"];
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
-function RemoteWebProgressRequest(spec, originalSpec, matchedList, requestCPOW) {
+function RemoteWebProgressRequest(spec, originalSpec, matchedList) {
   this.wrappedJSObject = this;
 
   this._uri = Services.io.newURI(spec);
   this._originalURI = Services.io.newURI(originalSpec);
-  this._requestCPOW = requestCPOW;
   this._matchedList = matchedList;
 }
 
 RemoteWebProgressRequest.prototype = {
   QueryInterface: ChromeUtils.generateQI([Ci.nsIChannel, Ci.nsIClassifiedChannel]),
 
   get URI() { return this._uri; },
   get originalURI() { return this._originalURI; },
@@ -25,17 +24,16 @@ RemoteWebProgressRequest.prototype = {
 };
 
 function RemoteWebProgress(aManager, aIsTopLevel) {
   this.wrappedJSObject = this;
 
   this._manager = aManager;
 
   this._isLoadingDocument = false;
-  this._DOMWindow = null;
   this._DOMWindowID = 0;
   this._isTopLevel = aIsTopLevel;
   this._loadType = 0;
 }
 
 RemoteWebProgress.prototype = {
   NOTIFY_STATE_REQUEST:  0x00000001,
   NOTIFY_STATE_DOCUMENT: 0x00000002,
@@ -45,17 +43,19 @@ RemoteWebProgress.prototype = {
   NOTIFY_PROGRESS:       0x00000010,
   NOTIFY_STATUS:         0x00000020,
   NOTIFY_SECURITY:       0x00000040,
   NOTIFY_LOCATION:       0x00000080,
   NOTIFY_REFRESH:        0x00000100,
   NOTIFY_ALL:            0x000001ff,
 
   get isLoadingDocument() { return this._isLoadingDocument; },
-  get DOMWindow() { return this._DOMWindow; },
+  get DOMWindow() {
+    throw Cr.NS_ERROR_NOT_AVAILABLE;
+  },
   get DOMWindowID() { return this._DOMWindowID; },
   get isTopLevel() { return this._isTopLevel; },
   get loadType() { return this._loadType; },
 
   addProgressListener(aListener, aNotifyMask) {
     this._manager.addProgressListener(aListener, aNotifyMask);
   },
 
@@ -145,17 +145,16 @@ RemoteWebProgressManager.prototype = {
           Cu.reportError("RemoteWebProgress failed to call " + methodName + ": " + ex + "\n");
         }
       }
     }
   },
 
   receiveMessage(aMessage) {
     let json = aMessage.json;
-    let objects = aMessage.objects;
     // This message is a custom one we send as a result of a loadURI call.
     // It shouldn't go through the same processing as all the forwarded
     // webprogresslistener messages.
     if (aMessage.name == "Content:LoadURIResult") {
       this._browser.inLoadURI = false;
       return;
     }
 
@@ -165,34 +164,29 @@ RemoteWebProgressManager.prototype = {
     // really have a concept of subframes/content we always create a new object
     // for those.
     if (json.webProgress) {
       webProgress = isTopLevel ? this._topLevelWebProgress
                                : new RemoteWebProgress(this, false);
 
       // Update the actual WebProgress fields.
       webProgress._isLoadingDocument = json.webProgress.isLoadingDocument;
-      webProgress._DOMWindow = objects.DOMWindow;
       webProgress._DOMWindowID = json.webProgress.DOMWindowID;
       webProgress._loadType = json.webProgress.loadType;
-      webProgress._webProgressCPOW = objects.webProgress;
     }
 
     // The WebProgressRequest object however is always dynamic.
     let request = null;
     if (json.requestURI) {
       request = new RemoteWebProgressRequest(json.requestURI,
                                              json.originalRequestURI,
-                                             json.matchedList,
-                                             objects.request);
+                                             json.matchedList);
     }
 
     if (isTopLevel) {
-      this._browser._contentWindow = objects.contentWindow;
-      this._browser._contentDocument = objects.contentDocument;
       // Setting a content-type back to `null` is quite nonsensical for the
       // frontend, especially since we're not expecting it.
       if (json.documentContentType !== null) {
         this._browser._documentContentType = json.documentContentType;
       }
       if (typeof json.inLoadURI != "undefined") {
         this._browser.inLoadURI = json.inLoadURI;
       }
--- a/toolkit/modules/WebProgressChild.jsm
+++ b/toolkit/modules/WebProgressChild.jsm
@@ -82,84 +82,60 @@ class WebProgressChild {
       webProgress: aWebProgress || null,
       requestURI: this._requestSpec(aRequest, "URI"),
       originalRequestURI: this._requestSpec(aRequest, "originalURI"),
       documentContentType: contentDocument ? contentDocument.contentType : null,
       innerWindowID,
     };
   }
 
-  _setupObjects(aWebProgress, aRequest) {
-    let domWindow;
-    try {
-      domWindow = aWebProgress && aWebProgress.DOMWindow;
-    } catch (e) {
-      // If nsDocShell::Destroy has already been called, then we'll
-      // get NS_NOINTERFACE when trying to get the DOM window. Ignore
-      // that here.
-      domWindow = null;
-    }
-
-    return {
-      contentWindow: this.mm.content,
-      contentDocument: this.mm.content.document,
-      // DOMWindow is not necessarily the this.mm.content-window with subframes.
-      DOMWindow: domWindow,
-      webProgress: aWebProgress,
-      request: aRequest,
-    };
-  }
-
-  _send(name, data, objects) {
-    this.mm.sendAsyncMessage(name, data, objects);
+  _send(name, data) {
+    this.mm.sendAsyncMessage(name, data);
   }
 
   onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
     let json = this._setupJSON(aWebProgress, aRequest, aStateFlags);
-    let objects = this._setupObjects(aWebProgress, aRequest);
 
     json.stateFlags = aStateFlags;
     json.status = aStatus;
 
     // It's possible that this state change was triggered by
     // loading an internal error page, for which the parent
     // will want to know some details, so we'll update it with
     // the documentURI.
     if (aWebProgress && aWebProgress.isTopLevel) {
       json.documentURI = this.mm.content.document.documentURIObject.spec;
       json.charset = this.mm.content.document.characterSet;
       json.mayEnableCharacterEncodingMenu = this.mm.docShell.mayEnableCharacterEncodingMenu;
       json.inLoadURI = this.inLoadURI;
     }
 
-    this._send("Content:StateChange", json, objects);
+    this._send("Content:StateChange", json);
   }
 
   // Note: Because the nsBrowserStatusFilter timeout runnable is
   // SystemGroup-labeled, this method should not modify this.mm.content DOM or
   // run this.mm.content JS.
   onProgressChange(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal) {
     let json = this._setupJSON(aWebProgress, aRequest);
-    let objects = this._setupObjects(aWebProgress, aRequest);
 
     json.curSelf = aCurSelf;
     json.maxSelf = aMaxSelf;
     json.curTotal = aCurTotal;
     json.maxTotal = aMaxTotal;
 
-    this._send("Content:ProgressChange", json, objects);
+    this._send("Content:ProgressChange", json);
   }
 
   onProgressChange64(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal) {
     this.onProgressChange(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal);
   }
 
   onLocationChange(aWebProgress, aRequest, aLocationURI, aFlags) {
     let json = this._setupJSON(aWebProgress, aRequest);
-    let objects = this._setupObjects(aWebProgress, aRequest);
 
     json.location = aLocationURI ? aLocationURI.spec : "";
     json.flags = aFlags;
 
     // These properties can change even for a sub-frame navigation.
     let webNav = this.mm.docShell.QueryInterface(Ci.nsIWebNavigation);
     json.canGoBack = webNav.canGoBack;
     json.canGoForward = webNav.canGoForward;
@@ -183,57 +159,55 @@ class WebProgressChild {
           uri = uri.mutate()
                    .setUserPass("")
                    .finalize();
         } catch (ex) { /* Ignore failures on about: URIs. */ }
         CrashReporter.annotateCrashReport("URL", uri.spec);
       }
     }
 
-    this._send("Content:LocationChange", json, objects);
+    this._send("Content:LocationChange", json);
   }
 
   // Note: Because the nsBrowserStatusFilter timeout runnable is
   // SystemGroup-labeled, this method should not modify this.mm.content DOM or
   // run this.mm.content JS.
   onStatusChange(aWebProgress, aRequest, aStatus, aMessage) {
     let json = this._setupJSON(aWebProgress, aRequest);
-    let objects = this._setupObjects(aWebProgress, aRequest);
 
     json.status = aStatus;
     json.message = aMessage;
 
-    this._send("Content:StatusChange", json, objects);
+    this._send("Content:StatusChange", json);
   }
 
   getSecInfoAsString() {
     let secInfo = this.mm.docShell.securityUI.secInfo;
     if (secInfo) {
       return serializationHelper.serializeToString(secInfo);
     }
 
     return null;
   }
 
   onSecurityChange(aWebProgress, aRequest, aOldState, aState,
                    aContentBlockingLogJSON) {
     let json = this._setupJSON(aWebProgress, aRequest);
-    let objects = this._setupObjects(aWebProgress, aRequest);
 
     json.oldState = aOldState;
     json.state = aState;
     json.secInfo = this.getSecInfoAsString();
     json.contentBlockingLogJSON = aContentBlockingLogJSON;
 
     json.matchedList = null;
     if (aRequest && aRequest instanceof Ci.nsIClassifiedChannel) {
       json.matchedList = aRequest.matchedList;
     }
 
-    this._send("Content:SecurityChange", json, objects);
+    this._send("Content:SecurityChange", json);
   }
 
   onRefreshAttempted(aWebProgress, aURI, aDelay, aSameURI) {
     return true;
   }
 
   sendLoadCallResult() {
     this.mm.sendAsyncMessage("Content:LoadURIResult");
--- a/toolkit/themes/shared/extensions/extensions.inc.css
+++ b/toolkit/themes/shared/extensions/extensions.inc.css
@@ -148,22 +148,16 @@
 
 button.warning {
   list-style-image: url("chrome://mozapps/skin/extensions/alerticon-warning.svg");
 }
 
 
 /*** category selector ***/
 
-#categories {
-  /* With photon this should be 70px but there are some hidden forward/back
-     buttons that are 39px tall above this. */
-  padding-top: 31px;
-}
-
 #categories > .category[disabled] {
   overflow: hidden;
   height: 0;
   min-height: 0;
   opacity: 0;
   transition-property: min-height, opacity;
   transition-duration: 1s, 0.8s;
 }
--- a/toolkit/xre/nsUpdateDriver.cpp
+++ b/toolkit/xre/nsUpdateDriver.cpp
@@ -170,18 +170,18 @@ GetFile(nsIFile* dir, const nsACString& 
   rv = dir->Clone(getter_AddRefs(file));
   if (NS_FAILED(rv))
     return false;
 
   rv = file->AppendNative(name);
   if (NS_FAILED(rv))
     return false;
 
-  result = do_QueryInterface(file, &rv);
-  return NS_SUCCEEDED(rv);
+  result = file;
+  return true;
 }
 
 static bool
 GetStatusFile(nsIFile *dir, nsCOMPtr<nsIFile> &result)
 {
   return GetFile(dir, NS_LITERAL_CSTRING("update.status"), result);
 }
 
--- a/widget/nsBaseFilePicker.cpp
+++ b/widget/nsBaseFilePicker.cpp
@@ -300,18 +300,18 @@ NS_IMETHODIMP nsBaseFilePicker::SetDispl
   if (!aDirectory) {
     mDisplayDirectory = nullptr;
     return NS_OK;
   }
   nsCOMPtr<nsIFile> directory;
   nsresult rv = aDirectory->Clone(getter_AddRefs(directory));
   if (NS_FAILED(rv))
     return rv;
-  mDisplayDirectory = do_QueryInterface(directory, &rv);
-  return rv;
+  mDisplayDirectory = directory;
+  return NS_OK;
 }
 
 // Get the display directory
 NS_IMETHODIMP nsBaseFilePicker::GetDisplayDirectory(nsIFile **aDirectory)
 {
   *aDirectory = nullptr;
 
   // if displaySpecialDirectory has been previously called, let's abort this