Merge mozilla-central to mozilla-inbound.
authorCosmin Sabou <csabou@mozilla.com>
Sat, 02 Mar 2019 23:50:58 +0200
changeset 520062 3030e03a5c84a6ae83c45a48caf3847601fe3d76
parent 520061 ffda293d015cea2843c45d7a0225ab0d46e49614 (current diff)
parent 520041 997e98493dbcd55c95b4a6eb4a44f563cd6174e9 (diff)
child 520063 ef7dc51a587bbc631af99eef4219ed90ad587204
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone67.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.
browser/base/content/test/performance/browser_startup_hiddenwindow.js
build/build-clang/D57636.diff
devtools/client/debugger/new/src/client/firefox/commands.js
devtools/client/debugger/new/test/mochitest/browser_dbg-windowless-workers-early-breakpoint.js
devtools/client/debugger/new/test/mochitest/browser_dbg-windowless-workers.js
gfx/layers/apz/src/TouchCounter.cpp
gfx/layers/apz/src/TouchCounter.h
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -235,20 +235,16 @@ pref("browser.startup.firstrunSkipsHomep
 // Held to nightly on Linux due to bug 1450626.
 // Disabled on Mac because the bouncing dock icon already provides feedback.
 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) && defined(NIGHTLY_BUILD)
 pref("browser.startup.blankWindow", true);
 #else
 pref("browser.startup.blankWindow", false);
 #endif
 
-// Don't create the hidden window during startup on
-// platforms that don't always need it (Win/Linux).
-pref("toolkit.lazyHiddenWindow", true);
-
 pref("browser.slowStartup.notificationDisabled", false);
 pref("browser.slowStartup.timeThreshold", 20000);
 pref("browser.slowStartup.maxSamples", 5);
 
 // This url, if changed, MUST continue to point to an https url. Pulling arbitrary content to inject into
 // this page over http opens us up to a man-in-the-middle attack that we'd rather not face. If you are a downstream
 // repackager of this code using an alternate snippet url, please keep your users safe
 pref("browser.aboutHomeSnippets.updateUrl", "https://snippets.cdn.mozilla.net/%STARTPAGE_VERSION%/%NAME%/%VERSION%/%APPBUILDID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/");
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -567,22 +567,22 @@ HistoryMenu.prototype = {
   _elements: {
     undoTabMenu: "historyUndoMenu",
     hiddenTabsMenu: "hiddenTabsMenu",
     undoWindowMenu: "historyUndoWindowMenu",
     syncTabsMenuitem: "sync-tabs-menuitem",
   },
 
   _getClosedTabCount() {
-    try {
-      return SessionStore.getClosedTabCount(window);
-    } catch (ex) {
-      // SessionStore doesn't track the hidden window, so just return zero then.
+    // SessionStore doesn't track the hidden window, so just return zero then.
+    if (window == Services.appShell.hiddenDOMWindow) {
       return 0;
     }
+
+    return SessionStore.getClosedTabCount(window);
   },
 
   toggleHiddenTabs() {
     if (window.gBrowser &&
         gBrowser.visibleTabs.length < gBrowser.tabs.length) {
       this.hiddenTabsMenu.removeAttribute("hidden");
     } else {
       this.hiddenTabsMenu.setAttribute("hidden", "true");
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1511,20 +1511,16 @@ var gBrowserInit = {
 
   _delayedStartup() {
     let { TelemetryTimestamps } =
       ChromeUtils.import("resource://gre/modules/TelemetryTimestamps.jsm");
     TelemetryTimestamps.add("delayedStartupStarted");
 
     this._cancelDelayedStartup();
 
-    // Bug 1531854 - The hidden window is force-created here
-    // until all of its dependencies are handled.
-    Services.appShell.hiddenDOMWindow;
-
     // We need to set the OfflineApps message listeners up before we
     // load homepages, which might need them.
     OfflineApps.init();
 
     gBrowser.addEventListener("InsecureLoginFormsStateChange", function() {
       gIdentityHandler.refreshForInsecureLoginForms();
     }, true);
 
--- a/browser/base/content/newInstall.js
+++ b/browser/base/content/newInstall.js
@@ -12,26 +12,25 @@ function init() {
   if (navigator.platform == "MacIntel") {
     hideMenus();
     window.addEventListener("unload", showMenus);
   }
 }
 
 let gHidden = [];
 let gCollapsed = [];
+let hiddenDoc = Services.appShell.hiddenDOMWindow.document;
 
 function hideItem(id) {
-  let hiddenDoc = Services.appShell.hiddenDOMWindow.document;
   let element = hiddenDoc.getElementById(id);
   element.hidden = true;
   gHidden.push(element);
 }
 
 function collapseItem(id) {
-  let hiddenDoc = Services.appShell.hiddenDOMWindow.document;
   let element = hiddenDoc.getElementById(id);
   element.collapsed = true;
   gCollapsed.push(element);
 }
 
 function hideMenus() {
   hideItem("macDockMenuNewWindow");
   hideItem("macDockMenuNewPrivateWindow");
--- a/browser/base/content/test/performance/browser.ini
+++ b/browser/base/content/test/performance/browser.ini
@@ -18,18 +18,16 @@ support-files =
   head.js
 [browser_appmenu.js]
 skip-if = asan || debug || (os == 'win' && bits == 32) # Bug 1382809, bug 1369959, Win32 because of intermittent OOM failures
 [browser_preferences_usage.js]
 skip-if = !debug
 [browser_startup.js]
 [browser_startup_content.js]
 skip-if = !e10s
-[browser_startup_hiddenwindow.js]
-skip-if = os == 'mac'
 [browser_startup_flicker.js]
 run-if = debug || devedition || nightly_build # Requires startupRecorder.js, which isn't shipped everywhere by default
 [browser_tabclose_grow.js]
 [browser_tabclose.js]
 skip-if = (os == 'win' && bits == 32) || (os == 'mac') # Bug 1488537, Bug 1531417
 [browser_tabdetach.js]
 [browser_tabopen.js]
 skip-if = (verify && (os == 'mac'))
--- a/browser/base/content/test/performance/browser_startup_content.js
+++ b/browser/base/content/test/performance/browser_startup_content.js
@@ -81,33 +81,37 @@ const whitelist = {
     "resource://gre/modules/addons/Content.js",
   ]),
   processScripts: new Set([
     "chrome://global/content/process-content.js",
     "resource:///modules/ContentObservers.js",
     "data:,ChromeUtils.import('resource://gre/modules/ExtensionProcessScript.jsm')",
     "resource://devtools/client/jsonview/converter-observer.js",
     "resource://gre/modules/WebRequestContent.js",
-    "resource://webcompat/aboutPageProcessScript.js",
   ]),
 };
 
 // Items on this list are allowed to be loaded but not
 // required, as opposed to items in the main whitelist,
 // which are all required.
 const intermittently_loaded_whitelist = {
   modules: new Set([
     "resource://gre/modules/nsAsyncShutdown.jsm",
     "resource://gre/modules/sessionstore/Utils.jsm",
 
-    // Webcompat about:config front-end
+    // Webcompat about:config front-end. This is presently nightly-only and
+    // part of a system add-on which may not load early enough for the test.
     "resource://webcompat/AboutCompat.jsm",
   ]),
   frameScripts: new Set([]),
-  processScripts: new Set([]),
+  processScripts: new Set([
+    // Webcompat about:config front-end. This is presently nightly-only and
+    // part of a system add-on which may not load early enough for the test.
+    "resource://webcompat/aboutPageProcessScript.js",
+  ]),
 };
 
 const blacklist = {
   services: new Set([
     "@mozilla.org/base/telemetry-startup;1",
     "@mozilla.org/embedcomp/default-tooltiptextprovider;1",
     "@mozilla.org/push/Service;1",
   ]),
deleted file mode 100644
--- a/browser/base/content/test/performance/browser_startup_hiddenwindow.js
+++ /dev/null
@@ -1,38 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-add_task(async function() {
-  if (!AppConstants.NIGHTLY_BUILD && !AppConstants.MOZ_DEV_EDITION && !AppConstants.DEBUG) {
-    ok(!("@mozilla.org/test/startuprecorder;1" in Cc),
-       "the startup recorder component shouldn't exist in this non-nightly/non-devedition/" +
-       "non-debug build.");
-    return;
-  }
-
-  let startupRecorder = Cc["@mozilla.org/test/startuprecorder;1"].getService().wrappedJSObject;
-  await startupRecorder.done;
-
-  let extras = Cu.cloneInto(startupRecorder.data.extras, {});
-
-  let phasesExpectations = {
-    "before profile selection": false,
-    "before opening first browser window": false,
-    "before first paint": !Services.prefs.getBoolPref("toolkit.lazyHiddenWindow"),
-
-    // Bug 1531854
-    "before handling user events": true,
-    "before becoming idle": true,
-  };
-
-  for (let phase in extras) {
-    if (!(phase in phasesExpectations)) {
-      ok(false, `Startup phase '${phase}' should be specified.`);
-      continue;
-    }
-
-    is(extras[phase].hiddenWindowLoaded, phasesExpectations[phase],
-       `Hidden window loaded at '${phase}': ${phasesExpectations[phase]}`);
-  }
-});
--- a/browser/components/BrowserGlue.jsm
+++ b/browser/components/BrowserGlue.jsm
@@ -1438,21 +1438,19 @@ BrowserGlue.prototype = {
     }
 
     Services.telemetry.scalarSet("contentblocking.fingerprinting_blocking_enabled", fpEnabled);
     Services.telemetry.scalarSet("contentblocking.cryptomining_blocking_enabled", cmEnabled);
     Services.telemetry.scalarSet("contentblocking.category", categoryPref);
   },
 
   _sendMediaTelemetry() {
-    let win = Services.wm.getMostRecentWindow("navigator:browser");
-    if (win) {
-      let v = win.document.createElementNS("http://www.w3.org/1999/xhtml", "video");
-      v.reportCanPlayTelemetry();
-    }
+    let win = Services.appShell.hiddenDOMWindow;
+    let v = win.document.createElementNS("http://www.w3.org/1999/xhtml", "video");
+    v.reportCanPlayTelemetry();
   },
 
   /**
    * Application shutdown handler.
    */
   _onQuitApplicationGranted() {
     // This pref must be set here because SessionStore will use its value
     // on quit-application.
@@ -2802,25 +2800,23 @@ BrowserGlue.prototype = {
     prefValue = prefValue || "general:5,suggestion:Infinity";
     Services.prefs.setCharPref(prefName, prefValue);
   },
 
   /**
    * Open preferences even if there are no open windows.
    */
   _openPreferences(...args) {
-    let chromeWindow = BrowserWindowTracker.getTopWindow();
-    if (chromeWindow) {
-      chromeWindow.openPreferences(...args);
+    if (Services.appShell.hiddenDOMWindow.openPreferences) {
+      Services.appShell.hiddenDOMWindow.openPreferences(...args);
       return;
     }
 
-    if (Services.appShell.hiddenDOMWindow.openPreferences) {
-      Services.appShell.hiddenDOMWindow.openPreferences(...args);
-    }
+    let chromeWindow = BrowserWindowTracker.getTopWindow();
+    chromeWindow.openPreferences(...args);
   },
 
   _openURLInNewWindow(url) {
     let urlString = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
     urlString.data = url;
     return new Promise(resolve => {
       let win = Services.ww.openWindow(null, AppConstants.BROWSER_CHROME_URL,
                                        "_blank", "chrome,all,dialog=no", urlString);
--- a/browser/components/downloads/DownloadsCommon.jsm
+++ b/browser/components/downloads/DownloadsCommon.jsm
@@ -180,30 +180,29 @@ var DownloadsCommon = {
     return PrefObserver.animateNotifications;
   },
 
   /**
    * Get access to one of the DownloadsData, PrivateDownloadsData, or
    * HistoryDownloadsData objects, depending on the privacy status of the
    * specified window and on whether history downloads should be included.
    *
-   * @param [optional] window
+   * @param window
    *        The browser window which owns the download button.
-   *        If not given, the privacy status will be assumed as non-private.
    * @param [optional] history
    *        True to include history downloads when the window is public.
    * @param [optional] privateAll
    *        Whether to force the public downloads data to be returned together
    *        with the private downloads data for a private window.
    * @param [optional] limited
    *        True to limit the amount of downloads returned to
    *        `kMaxHistoryResultsForLimitedView`.
    */
   getData(window, history = false, privateAll = false, limited = false) {
-    let isPrivate = window && PrivateBrowsingUtils.isContentWindowPrivate(window);
+    let isPrivate = PrivateBrowsingUtils.isContentWindowPrivate(window);
     if (isPrivate && !privateAll) {
       return PrivateDownloadsData;
     }
     if (history) {
       if (isPrivate && privateAll)
         return LimitedPrivateHistoryDownloadData;
       return limited ? LimitedHistoryDownloadsData : HistoryDownloadsData;
     }
--- a/browser/components/newtab/lib/DownloadsManager.jsm
+++ b/browser/components/newtab/lib/DownloadsManager.jsm
@@ -1,16 +1,15 @@
 const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
 const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
 
 const {actionTypes: at} = ChromeUtils.import("resource://activity-stream/common/Actions.jsm");
 
 XPCOMUtils.defineLazyModuleGetters(this, {
-  BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
   DownloadsCommon: "resource:///modules/DownloadsCommon.jsm",
   DownloadsViewUI: "resource:///modules/DownloadsViewUI.jsm",
   FileUtils: "resource://gre/modules/FileUtils.jsm",
 });
 
 const DOWNLOAD_CHANGED_DELAY_TIME = 1000; // time in ms to delay timer for downloads changed events
 
 this.DownloadsManager = class DownloadsManager {
@@ -37,18 +36,18 @@ this.DownloadsManager = class DownloadsM
                    DownloadsCommon.strings.sizeUnknown,
       referrer: download.source.referrer,
       date_added: download.endTime,
     };
   }
 
   init(store) {
     this._store = store;
-    this._downloadData = DownloadsCommon.getData(null /* null for non-private downloads */, 
-                                                 true, false, true);
+    this._browser = Services.appShell.hiddenDOMWindow;
+    this._downloadData = DownloadsCommon.getData(this._browser.ownerGlobal, true, false, true);
     this._downloadData.addView(this);
   }
 
   onDownloadAdded(download) {
     if (!this._downloadItems.has(download.source.url)) {
       this._downloadItems.set(download.source.url, download);
 
       // On startup, all existing downloads fire this notification, so debounce them
@@ -137,17 +136,17 @@ this.DownloadsManager = class DownloadsM
           DownloadsCommon.showDownloadedFile(
             new FileUtils.File(download.target.path));
         });
         break;
       case at.OPEN_DOWNLOAD_FILE:
         doDownloadAction(download => {
           DownloadsCommon.openDownloadedFile(
             new FileUtils.File(download.target.path), null,
-            BrowserWindowTracker.getTopWindow());
+            this._browser.ownerGlobal);
         });
         break;
       case at.UNINIT:
         this.uninit();
         break;
     }
   }
 };
--- a/browser/components/sessionstore/SessionSaver.jsm
+++ b/browser/components/sessionstore/SessionSaver.jsm
@@ -171,30 +171,32 @@ var SessionSaverInternal = {
 
     // Interval until the next disk operation is allowed.
     let interval = this._isIdle ? this._intervalWhileIdle : this._intervalWhileActive;
     delay = Math.max(this._lastSaveTime + interval - Date.now(), delay, 0);
 
     // Schedule a state save.
     this._wasIdle = this._isIdle;
     this._timeoutID = setTimeout(() => {
+      let hiddenDOMWindow = Services.appShell.hiddenDOMWindow;
+
       // Execute _saveStateAsync when we have enough idle time. Otherwise,
       // another idle request is made to schedule _saveStateAsync again.
       let saveStateAsyncWhenIdle = (deadline) => {
         // When looking at the telemetry data, the time it takes to execute
         // _saveStateAsync is around 5.9ms (median). Therefore,
         // we'll not execute the function when the idle time is less than 5ms.
         if (deadline.timeRemaining() < 5) {
-          this._idleCallbackID = requestIdleCallback(saveStateAsyncWhenIdle);
+          this._idleCallbackID = hiddenDOMWindow.requestIdleCallback(saveStateAsyncWhenIdle);
           return;
         }
         this._saveStateAsync();
       };
 
-      this._idleCallbackID = requestIdleCallback(saveStateAsyncWhenIdle);
+      this._idleCallbackID = hiddenDOMWindow.requestIdleCallback(saveStateAsyncWhenIdle);
     }, delay);
   },
 
   /**
    * Sets the last save time to the current time. This will cause us to wait for
    * at least the configured interval when runDelayed() is called next.
    */
   updateLastSaveTime() {
@@ -202,17 +204,17 @@ var SessionSaverInternal = {
   },
 
   /**
    * Cancels all pending session saves.
    */
   cancel() {
     clearTimeout(this._timeoutID);
     this._timeoutID = null;
-    cancelIdleCallback(this._idleCallbackID);
+    Services.appShell.hiddenDOMWindow.cancelIdleCallback(this._idleCallbackID);
     this._idleCallbackID = null;
   },
 
   /**
    * Observe idle/ active notifications.
    */
   observe(subject, topic, data) {
     switch (topic) {
--- a/browser/components/tests/startupRecorder.js
+++ b/browser/components/tests/startupRecorder.js
@@ -44,17 +44,16 @@ let afterPaintListener = () => {
 function startupRecorder() {
   this.wrappedJSObject = this;
   this.data = {
     images: {
       "image-drawing": new Set(),
       "image-loading": new Set(),
     },
     code: {},
-    extras: {},
     prefStats: {},
   };
   this.done = new Promise(resolve => { this._resolve = resolve; });
 }
 startupRecorder.prototype = {
   classID: Components.ID("{11c095b2-e42e-4bdf-9dd0-aed87595f6a4}"),
 
   QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),
@@ -69,19 +68,16 @@ startupRecorder.prototype = {
       services: Object.keys(Cc).filter(c => {
         try {
           return Cm.isServiceInstantiatedByContractID(c, Ci.nsISupports);
         } catch (e) {
           return false;
         }
       }),
     };
-    this.data.extras[name] = {
-      hiddenWindowLoaded: Services.appShell.hasHiddenWindow,
-    };
   },
 
   observe(subject, topic, data) {
     if (topic == "app-startup") {
       // We can't ensure our observer will be called first or last, so the list of
       // topics we observe here should avoid the topics used to trigger things
       // during startup (eg. the topics observed by BrowserGlue.jsm).
       let topics = [
--- a/browser/modules/Sanitizer.jsm
+++ b/browser/modules/Sanitizer.jsm
@@ -465,54 +465,51 @@ var Sanitizer = {
         }
       },
       async clear(range, privateStateForNewWindow = "non-private") {
         // NB: this closes all *browser* windows, not other windows like the library, about window,
         // browser console, etc.
 
         // Keep track of the time in case we get stuck in la-la-land because of onbeforeunload
         // dialogs
-        let startDate = Date.now();
+        let existingWindow = Services.appShell.hiddenDOMWindow;
+        let startDate = existingWindow.performance.now();
 
         // First check if all these windows are OK with being closed:
         let windowList = [];
         for (let someWin of Services.wm.getEnumerator("navigator:browser")) {
           windowList.push(someWin);
           // If someone says "no" to a beforeunload prompt, we abort here:
           if (!this._canCloseWindow(someWin)) {
             this._resetAllWindowClosures(windowList);
             throw new Error("Sanitize could not close windows: cancelled by user");
           }
 
           // ...however, beforeunload prompts spin the event loop, and so the code here won't get
           // hit until the prompt has been dismissed. If more than 1 minute has elapsed since we
           // started prompting, stop, because the user might not even remember initiating the
           // 'forget', and the timespans will be all wrong by now anyway:
-          if (Date.now() > (startDate + 60 * 1000)) {
+          if (existingWindow.performance.now() > (startDate + 60 * 1000)) {
             this._resetAllWindowClosures(windowList);
             throw new Error("Sanitize could not close windows: timeout");
           }
         }
 
-        if (windowList.length == 0) {
-          return;
-        }
-
         // If/once we get here, we should actually be able to close all windows.
 
         let refObj = {};
         TelemetryStopwatch.start("FX_SANITIZE_OPENWINDOWS", refObj);
 
         // First create a new window. We do this first so that on non-mac, we don't
         // accidentally close the app by closing all the windows.
         let handler = Cc["@mozilla.org/browser/clh;1"].getService(Ci.nsIBrowserHandler);
         let defaultArgs = handler.defaultArgs;
         let features = "chrome,all,dialog=no," + privateStateForNewWindow;
-        let newWindow = windowList[0].openDialog(AppConstants.BROWSER_CHROME_URL, "_blank",
-                                                 features, defaultArgs);
+        let newWindow = existingWindow.openDialog(AppConstants.BROWSER_CHROME_URL, "_blank",
+                                                  features, defaultArgs);
 
         let onFullScreen = null;
         if (AppConstants.platform == "macosx") {
           onFullScreen = function(e) {
             newWindow.removeEventListener("fullscreen", onFullScreen);
             let docEl = newWindow.document.documentElement;
             let sizemode = docEl.getAttribute("sizemode");
             if (!newWindow.fullScreen && sizemode == "fullscreen") {
deleted file mode 100644
--- a/build/build-clang/D57636.diff
+++ /dev/null
@@ -1,193 +0,0 @@
-Index: include/clang/Basic/BuiltinsAArch64.def
-===================================================================
---- a/clang/include/clang/Basic/BuiltinsAArch64.def
-+++ b/clang/include/clang/Basic/BuiltinsAArch64.def
-@@ -203,8 +203,8 @@
- 
- TARGET_HEADER_BUILTIN(_ReadWriteBarrier, "v", "nh", "intrin.h", ALL_MS_LANGUAGES, "")
- TARGET_HEADER_BUILTIN(__getReg, "ULLii", "nh", "intrin.h", ALL_MS_LANGUAGES, "")
--TARGET_HEADER_BUILTIN(_ReadStatusReg,  "ii",  "nh", "intrin.h", ALL_MS_LANGUAGES, "")
--TARGET_HEADER_BUILTIN(_WriteStatusReg, "vii", "nh", "intrin.h", ALL_MS_LANGUAGES, "")
-+TARGET_HEADER_BUILTIN(_ReadStatusReg,  "LLii",  "nh", "intrin.h", ALL_MS_LANGUAGES, "")
-+TARGET_HEADER_BUILTIN(_WriteStatusReg, "viLLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "")
- TARGET_HEADER_BUILTIN(_AddressOfReturnAddress, "v*", "nh", "intrin.h", ALL_MS_LANGUAGES, "")
- 
- #undef BUILTIN
-Index: lib/CodeGen/CGBuiltin.cpp
-===================================================================
---- a/clang/lib/CodeGen/CGBuiltin.cpp
-+++ b/clang/lib/CodeGen/CGBuiltin.cpp
-@@ -7062,19 +7062,16 @@
-     llvm::Value *Metadata = llvm::MetadataAsValue::get(Context, RegName);
- 
-     llvm::Type *RegisterType = Int64Ty;
--    llvm::Type *ValueType = Int32Ty;
-     llvm::Type *Types[] = { RegisterType };
- 
-     if (BuiltinID == AArch64::BI_ReadStatusReg) {
-       llvm::Value *F = CGM.getIntrinsic(llvm::Intrinsic::read_register, Types);
--      llvm::Value *Call = Builder.CreateCall(F, Metadata);
- 
--      return Builder.CreateTrunc(Call, ValueType);
-+      return Builder.CreateCall(F, Metadata);
-     }
- 
-     llvm::Value *F = CGM.getIntrinsic(llvm::Intrinsic::write_register, Types);
-     llvm::Value *ArgValue = EmitScalarExpr(E->getArg(1));
--    ArgValue = Builder.CreateZExt(ArgValue, RegisterType);
- 
-     return Builder.CreateCall(F, { Metadata, ArgValue });
-   }
-Index: lib/Headers/intrin.h
-===================================================================
---- a/clang/lib/Headers/intrin.h
-+++ b/clang/lib/Headers/intrin.h
-@@ -554,8 +554,8 @@
- #if defined(__aarch64__)
- unsigned __int64 __getReg(int);
- long _InterlockedAdd(long volatile *Addend, long Value);
--int _ReadStatusReg(int);
--void _WriteStatusReg(int, int);
-+__int64 _ReadStatusReg(int);
-+void _WriteStatusReg(int, __int64);
- 
- static inline unsigned short _byteswap_ushort (unsigned short val) {
-   return __builtin_bswap16(val);
-Index: test/CodeGen/arm64-microsoft-status-reg.cpp
-===================================================================
---- a/clang/test/CodeGen/arm64-microsoft-status-reg.cpp
-+++ b/clang/test/CodeGen/arm64-microsoft-status-reg.cpp
-@@ -23,87 +23,101 @@
- #define ARM64_TPIDRRO_EL0       ARM64_SYSREG(3,3,13, 0,3)  // Thread ID Register, User Read Only [CP15_TPIDRURO]
- #define ARM64_TPIDR_EL1         ARM64_SYSREG(3,0,13, 0,4)  // Thread ID Register, Privileged Only [CP15_TPIDRPRW]
- 
--void check_ReadWriteStatusReg(int v) {
--  int ret;
-+// From intrin.h
-+__int64 _ReadStatusReg(int);
-+void _WriteStatusReg(int, __int64);
-+
-+void check_ReadWriteStatusReg(__int64 v) {
-+  __int64 ret;
-   ret = _ReadStatusReg(ARM64_CNTVCT);
--// CHECK-ASM: mrs     x8, CNTVCT_EL0
--// CHECK-IR: call i64 @llvm.read_register.i64(metadata ![[MD2:.*]])
-+// CHECK-ASM: mrs     x0, CNTVCT_EL0
-+// CHECK-IR: %[[VAR:.*]] = call i64 @llvm.read_register.i64(metadata ![[MD2:.*]])
-+// CHECK-IR-NEXT: store i64 %[[VAR]]
- 
-   ret = _ReadStatusReg(ARM64_PMCCNTR_EL0);
--// CHECK-ASM: mrs     x8, PMCCNTR_EL0
--// CHECK-IR: call i64 @llvm.read_register.i64(metadata ![[MD3:.*]])
-+// CHECK-ASM: mrs     x0, PMCCNTR_EL0
-+// CHECK-IR: %[[VAR:.*]] = call i64 @llvm.read_register.i64(metadata ![[MD3:.*]])
-+// CHECK-IR-NEXT: store i64 %[[VAR]]
- 
-   ret = _ReadStatusReg(ARM64_PMSELR_EL0);
--// CHECK-ASM: mrs     x8, PMSELR_EL0
--// CHECK-IR: call i64 @llvm.read_register.i64(metadata ![[MD4:.*]])
-+// CHECK-ASM: mrs     x0, PMSELR_EL0
-+// CHECK-IR: %[[VAR:.*]] = call i64 @llvm.read_register.i64(metadata ![[MD4:.*]])
-+// CHECK-IR-NEXT: store i64 %[[VAR]]
- 
-   ret = _ReadStatusReg(ARM64_PMXEVCNTR_EL0);
--// CHECK-ASM: mrs     x8, PMXEVCNTR_EL0
--// CHECK-IR: call i64 @llvm.read_register.i64(metadata ![[MD5:.*]])
-+// CHECK-ASM: mrs     x0, PMXEVCNTR_EL0
-+// CHECK-IR: %[[VAR:.*]] = call i64 @llvm.read_register.i64(metadata ![[MD5:.*]])
-+// CHECK-IR-NEXT: store i64 %[[VAR]]
- 
-   ret = _ReadStatusReg(ARM64_PMXEVCNTRn_EL0(0));
--// CHECK-ASM: mrs     x8, PMEVCNTR0_EL0
--// CHECK-IR: call i64 @llvm.read_register.i64(metadata ![[MD6:.*]])
-+// CHECK-ASM: mrs     x0, PMEVCNTR0_EL0
-+// CHECK-IR: %[[VAR:.*]] = call i64 @llvm.read_register.i64(metadata ![[MD6:.*]])
-+// CHECK-IR-NEXT: store i64 %[[VAR]]
- 
-   ret = _ReadStatusReg(ARM64_PMXEVCNTRn_EL0(1));
--// CHECK-ASM: mrs     x8, PMEVCNTR1_EL0
--// CHECK-IR: call i64 @llvm.read_register.i64(metadata ![[MD7:.*]])
-+// CHECK-ASM: mrs     x0, PMEVCNTR1_EL0
-+// CHECK-IR: %[[VAR:.*]] = call i64 @llvm.read_register.i64(metadata ![[MD7:.*]])
-+// CHECK-IR-NEXT: store i64 %[[VAR]]
- 
-   ret = _ReadStatusReg(ARM64_PMXEVCNTRn_EL0(30));
--// CHECK-ASM: mrs     x8, PMEVCNTR30_EL0
--// CHECK-IR: call i64 @llvm.read_register.i64(metadata ![[MD8:.*]])
-+// CHECK-ASM: mrs     x0, PMEVCNTR30_EL0
-+// CHECK-IR: %[[VAR:.*]] = call i64 @llvm.read_register.i64(metadata ![[MD8:.*]])
-+// CHECK-IR-NEXT: store i64 %[[VAR]]
- 
-   ret = _ReadStatusReg(ARM64_TPIDR_EL0);
--// CHECK-ASM: mrs     x8, TPIDR_EL0
--// CHECK-IR: call i64 @llvm.read_register.i64(metadata ![[MD9:.*]])
-+// CHECK-ASM: mrs     x0, TPIDR_EL0
-+// CHECK-IR: %[[VAR:.*]] = call i64 @llvm.read_register.i64(metadata ![[MD9:.*]])
-+// CHECK-IR-NEXT: store i64 %[[VAR]]
- 
-   ret = _ReadStatusReg(ARM64_TPIDRRO_EL0);
--// CHECK-ASM: mrs     x8, TPIDRRO_EL0
--// CHECK-IR: call i64 @llvm.read_register.i64(metadata ![[MD10:.*]])
-+// CHECK-ASM: mrs     x0, TPIDRRO_EL0
-+// CHECK-IR: %[[VAR:.*]] = call i64 @llvm.read_register.i64(metadata ![[MD10:.*]])
-+// CHECK-IR-NEXT: store i64 %[[VAR]]
- 
-   ret = _ReadStatusReg(ARM64_TPIDR_EL1);
--// CHECK-ASM: mrs     x8, TPIDR_EL1
--// CHECK-IR: call i64 @llvm.read_register.i64(metadata ![[MD11:.*]])
-+// CHECK-ASM: mrs     x0, TPIDR_EL1
-+// CHECK-IR: %[[VAR:.*]] = call i64 @llvm.read_register.i64(metadata ![[MD11:.*]])
-+// CHECK-IR-NEXT: store i64 %[[VAR]]
- 
- 
-   _WriteStatusReg(ARM64_CNTVCT, v);
--// CHECK-ASM: msr     S3_3_C14_C0_2, x8
-+// CHECK-ASM: msr     S3_3_C14_C0_2, x0
- // CHECK-IR: call void @llvm.write_register.i64(metadata ![[MD2:.*]], i64 {{%.*}})
- 
-   _WriteStatusReg(ARM64_PMCCNTR_EL0, v);
--// CHECK-ASM: msr     PMCCNTR_EL0, x8
-+// CHECK-ASM: msr     PMCCNTR_EL0, x0
- // CHECK-IR: call void @llvm.write_register.i64(metadata ![[MD3:.*]], i64 {{%.*}})
- 
-   _WriteStatusReg(ARM64_PMSELR_EL0, v);
--// CHECK-ASM: msr     PMSELR_EL0, x8
-+// CHECK-ASM: msr     PMSELR_EL0, x0
- // CHECK-IR: call void @llvm.write_register.i64(metadata ![[MD4:.*]], i64 {{%.*}})
- 
-   _WriteStatusReg(ARM64_PMXEVCNTR_EL0, v);
--// CHECK-ASM: msr     PMXEVCNTR_EL0, x8
-+// CHECK-ASM: msr     PMXEVCNTR_EL0, x0
- // CHECK-IR: call void @llvm.write_register.i64(metadata ![[MD5:.*]], i64 {{%.*}})
- 
-   _WriteStatusReg(ARM64_PMXEVCNTRn_EL0(0), v);
--// CHECK-ASM: msr     PMEVCNTR0_EL0, x8
-+// CHECK-ASM: msr     PMEVCNTR0_EL0, x0
- // CHECK-IR: call void @llvm.write_register.i64(metadata ![[MD6:.*]], i64 {{%.*}})
- 
-   _WriteStatusReg(ARM64_PMXEVCNTRn_EL0(1), v);
--// CHECK-ASM: msr     PMEVCNTR1_EL0, x8
-+// CHECK-ASM: msr     PMEVCNTR1_EL0, x0
- // CHECK-IR: call void @llvm.write_register.i64(metadata ![[MD7:.*]], i64 {{%.*}})
- 
-   _WriteStatusReg(ARM64_PMXEVCNTRn_EL0(30), v);
--// CHECK-ASM: msr     PMEVCNTR30_EL0, x8
-+// CHECK-ASM: msr     PMEVCNTR30_EL0, x0
- // CHECK-IR: call void @llvm.write_register.i64(metadata ![[MD8:.*]], i64 {{%.*}})
- 
-   _WriteStatusReg(ARM64_TPIDR_EL0, v);
--// CHECK-ASM: msr     TPIDR_EL0, x8
-+// CHECK-ASM: msr     TPIDR_EL0, x0
- // CHECK-IR: call void @llvm.write_register.i64(metadata ![[MD9:.*]], i64 {{%.*}})
- 
-   _WriteStatusReg(ARM64_TPIDRRO_EL0, v);
--// CHECK-ASM: msr     TPIDRRO_EL0, x8
-+// CHECK-ASM: msr     TPIDRRO_EL0, x0
- // CHECK-IR: call void @llvm.write_register.i64(metadata ![[MD10:.*]], i64 {{%.*}})
- 
-   _WriteStatusReg(ARM64_TPIDR_EL1, v);
--// CHECK-ASM: msr     TPIDR_EL1, x8
-+// CHECK-ASM: msr     TPIDR_EL1, x0
- // CHECK-IR: call void @llvm.write_register.i64(metadata ![[MD11:.*]], i64 {{%.*}})
- }
- 
--- a/build/build-clang/clang-win64.json
+++ b/build/build-clang/clang-win64.json
@@ -1,23 +1,22 @@
 {
-    "llvm_revision": "353414",
+    "llvm_revision": "355016",
     "stages": "3",
     "build_libcxx": false,
     "build_type": "Release",
     "assertions": false,
-    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_800/rc2",
-    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_800/rc2",
-    "lld_repo": "https://llvm.org/svn/llvm-project/lld/tags/RELEASE_800/rc2",
-    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_800/rc2",
-    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_800/rc2",
+    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_800/rc3",
+    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_800/rc3",
+    "lld_repo": "https://llvm.org/svn/llvm-project/lld/tags/RELEASE_800/rc3",
+    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_800/rc3",
+    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_800/rc3",
     "python_path": "c:/mozilla-build/python/python.exe",
     "cc": "cl.exe",
     "cxx": "cl.exe",
     "ml": "ml64.exe",
     "patches": [
       "workaround-issue38586.patch",
       "unpoison-thread-stacks.patch",
       "downgrade-mangling-error.patch",
-      "loosen-msvc-detection.patch",
-      "D57636.diff"
+      "loosen-msvc-detection.patch"
     ]
 }
--- a/devtools/server/actors/root.js
+++ b/devtools/server/actors/root.js
@@ -1,17 +1,17 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const { Cc, Ci, Cu } = require("chrome");
+const { Cu } = require("chrome");
 const Services = require("Services");
 const { Pool } = require("devtools/shared/protocol");
 const { LazyPool, createExtraActors } = require("devtools/shared/protocol/lazy-pool");
 const { DebuggerServer } = require("devtools/server/main");
 
 loader.lazyRequireGetter(this, "ChromeWindowTargetActor",
   "devtools/server/actors/targets/chrome-window", true);
 loader.lazyRequireGetter(this, "ContentProcessTargetActor",
@@ -537,23 +537,26 @@ RootActor.prototype = {
     }
     if (("id" in request) && typeof (request.id) != "number") {
       return { error: "wrongParameter",
                message: "getProcess requires a valid `id` attribute." };
     }
     // If the request doesn't contains id parameter or id is 0
     // (id == 0, based on onListProcesses implementation)
     if ((!("id" in request)) || request.id === 0) {
-      // Check if we are running on xpcshell.
+      // Check if we are running on xpcshell. hiddenDOMWindow is going to throw on it.
       // When running on xpcshell, there is no valid browsing context to attach to
       // and so ParentProcessTargetActor doesn't make sense as it inherits from
       // BrowsingContextTargetActor. So instead use ContentProcessTargetActor, which
       // matches xpcshell needs.
-      const env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
-      const isXpcshell = env.exists("XPCSHELL_TEST_PROFILE_DIR");
+      let isXpcshell = true;
+      try {
+        isXpcshell = !Services.wm.getMostRecentWindow(null) &&
+                     !Services.appShell.hiddenDOMWindow;
+      } catch (e) {}
 
       if (!isXpcshell && this._parentProcessTargetActor &&
           (!this._parentProcessTargetActor.docShell ||
             this._parentProcessTargetActor.docShell.isBeingDestroyed)) {
         this._parentProcessTargetActor.destroy();
         this._parentProcessTargetActor = null;
       }
       if (!this._parentProcessTargetActor) {
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -434,33 +434,22 @@ nsDocShell::~nsDocShell() {
 
   if (--gDocShellCount == 0) {
     NS_IF_RELEASE(sURIFixup);
   }
 
   MOZ_LOG(gDocShellLeakLog, LogLevel::Debug, ("DOCSHELL %p destroyed\n", this));
 
 #ifdef DEBUG
-  nsAutoCString url;
-  if (mLastOpenedURI) {
-    url = mLastOpenedURI->GetSpecOrDefault();
-
-    // Data URLs can be very long, so truncate to avoid flooding the log.
-    const uint32_t maxURLLength = 1000;
-    if (url.Length() > maxURLLength) {
-      url.Truncate(maxURLLength);
-    }
-  }
   // We're counting the number of |nsDocShells| to help find leaks
   --gNumberOfDocShells;
   if (!PR_GetEnv("MOZ_QUIET")) {
-    printf_stderr("--DOCSHELL %p == %ld [pid = %d] [id = %s] [url = %s]\n", (void*)this,
+    printf_stderr("--DOCSHELL %p == %ld [pid = %d] [id = %s]\n", (void*)this,
                   gNumberOfDocShells, getpid(),
-                  nsIDToCString(mHistoryID).get(),
-                  url.get());
+                  nsIDToCString(mHistoryID).get());
   }
 #endif
 }
 
 /* static */
 already_AddRefed<nsDocShell> nsDocShell::Create(
     BrowsingContext* aBrowsingContext) {
   MOZ_ASSERT(aBrowsingContext, "DocShell without a BrowsingContext!");
@@ -1176,20 +1165,16 @@ bool nsDocShell::SetCurrentURI(nsIURI* a
   bool uriIsEqual = false;
   if (!mCurrentURI || !aURI ||
       NS_FAILED(mCurrentURI->Equals(aURI, &uriIsEqual)) || !uriIsEqual) {
     mTitleValidForCurrentURI = false;
   }
 
   mCurrentURI = aURI;
 
-#ifdef DEBUG
-  mLastOpenedURI = aURI;
-#endif
-
   if (!NS_IsAboutBlank(mCurrentURI)) {
     mHasLoadedNonBlankURI = true;
   }
 
   bool isRoot = false;      // Is this the root docshell
   bool isSubFrame = false;  // Is this a subframe navigation?
 
   nsCOMPtr<nsIDocShellTreeItem> root;
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -971,20 +971,16 @@ class nsDocShell final : public nsDocLoa
 
   // An observed docshell wrapper is created when recording markers is enabled.
   mozilla::UniquePtr<mozilla::ObservedDocShell> mObserved;
 
   // mCurrentURI should be marked immutable on set if possible.
   nsCOMPtr<nsIURI> mCurrentURI;
   nsCOMPtr<nsIReferrerInfo> mReferrerInfo;
 
-#ifdef DEBUG
-  nsCOMPtr<nsIURI> mLastOpenedURI;
-#endif
-
   // Reference to the SHEntry for this docshell until the page is destroyed.
   // Somebody give me better name
   nsCOMPtr<nsISHEntry> mOSHE;
 
   // Reference to the SHEntry for this docshell until the page is loaded
   // Somebody give me better name.
   // If mLSHE is non-null, non-pushState subframe loads don't create separate
   // root history entries. That is, frames loaded during the parent page
--- a/dom/base/nsCCUncollectableMarker.cpp
+++ b/dom/base/nsCCUncollectableMarker.cpp
@@ -344,29 +344,26 @@ nsresult nsCCUncollectableMarker::Observ
     NS_ENSURE_SUCCESS(rv, rv);
 
     MarkWindowList(windowList, cleanupJS);
   }
 
   nsCOMPtr<nsIAppShellService> appShell =
       do_GetService(NS_APPSHELLSERVICE_CONTRACTID);
   if (appShell) {
-    bool hasHiddenWindow = false;
-    appShell->GetHasHiddenWindow(&hasHiddenWindow);
-    if (hasHiddenWindow) {
-      nsCOMPtr<nsIXULWindow> hw;
-      appShell->GetHiddenWindow(getter_AddRefs(hw));
+    nsCOMPtr<nsIXULWindow> hw;
+    appShell->GetHiddenWindow(getter_AddRefs(hw));
+    if (hw) {
       nsCOMPtr<nsIDocShell> shell;
       hw->GetDocShell(getter_AddRefs(shell));
       MarkDocShell(shell, cleanupJS);
     }
     bool hasHiddenPrivateWindow = false;
     appShell->GetHasHiddenPrivateWindow(&hasHiddenPrivateWindow);
     if (hasHiddenPrivateWindow) {
-      nsCOMPtr<nsIXULWindow> hw;
       appShell->GetHiddenPrivateWindow(getter_AddRefs(hw));
       if (hw) {
         nsCOMPtr<nsIDocShell> shell;
         hw->GetDocShell(getter_AddRefs(shell));
         MarkDocShell(shell, cleanupJS);
       }
     }
   }
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -7495,38 +7495,37 @@ void nsContentUtils::TransferableToIPCTr
         nsAutoString dataAsString;
         text->GetData(dataAsString);
         IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
         item->flavor() = flavorStr;
         item->data() = dataAsString;
       } else if (nsCOMPtr<nsISupportsCString> ctext = do_QueryInterface(data)) {
         nsAutoCString dataAsString;
         ctext->GetData(dataAsString);
-        IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
-        item->flavor() = flavorStr;
 
         Shmem dataAsShmem = ConvertToShmem(aChild, aParent, dataAsString);
         if (!dataAsShmem.IsReadable() || !dataAsShmem.Size<char>()) {
           continue;
         }
 
+        IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
+        item->flavor() = flavorStr;
         item->data() = dataAsShmem;
       } else if (nsCOMPtr<nsIInputStream> stream = do_QueryInterface(data)) {
         // Images to be pasted on the clipboard are nsIInputStreams
-        IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
-        item->flavor() = flavorStr;
-
         nsCString imageData;
         NS_ConsumeStream(stream, UINT32_MAX, imageData);
 
         Shmem imageDataShmem = ConvertToShmem(aChild, aParent, imageData);
         if (!imageDataShmem.IsReadable() || !imageDataShmem.Size<char>()) {
           continue;
         }
 
+        IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
+        item->flavor() = flavorStr;
         item->data() = imageDataShmem;
       } else if (nsCOMPtr<imgIContainer> image = do_QueryInterface(data)) {
         // Images to be placed on the clipboard are imgIContainers.
         RefPtr<mozilla::gfx::SourceSurface> surface = image->GetFrame(
             imgIContainer::FRAME_CURRENT, imgIContainer::FLAG_SYNC_DECODE);
         if (!surface) {
           continue;
         }
@@ -7565,23 +7564,27 @@ void nsContentUtils::TransferableToIPCTr
           // If we can send this over as a blob, do so. Otherwise, we're
           // responding to a sync message and the child can't process the blob
           // constructor before processing our response, which would crash. In
           // that case, hope that the caller is nsClipboardProxy::GetData,
           // called from editor and send over images as raw data.
           if (aInSyncMessage) {
             nsAutoCString type;
             if (IsFileImage(file, type)) {
-              IPCDataTransferItem* item =
-                  aIPCDataTransfer->items().AppendElement();
-              item->flavor() = type;
               nsAutoCString data;
               SlurpFileToString(file, data);
 
               Shmem dataAsShmem = ConvertToShmem(aChild, aParent, data);
+              if (!dataAsShmem.IsReadable() || !dataAsShmem.Size<char>()) {
+                continue;
+              }
+
+              IPCDataTransferItem* item =
+                  aIPCDataTransfer->items().AppendElement();
+              item->flavor() = type;
               item->data() = dataAsShmem;
             }
 
             continue;
           }
 
           if (aParent) {
             bool isDir = false;
--- a/dom/media/mp4/Index.cpp
+++ b/dom/media/mp4/Index.cpp
@@ -404,20 +404,16 @@ Index::Index(const IndiceWrapper& aIndic
         return;
       }
       if (indice.sync || mIsAudio) {
         haveSync = true;
       }
       if (!haveSync) {
         continue;
       }
-      if (indice.start_composition == indice.end_composition) {
-        // Ignore this sample as it doesn't account for the buffered range.
-        continue;
-      }
       Sample sample;
       sample.mByteRange =
           MediaByteRange(indice.start_offset, indice.end_offset);
       sample.mCompositionRange = MP4Interval<Microseconds>(
           indice.start_composition, indice.end_composition);
       sample.mDecodeTime = indice.start_decode;
       sample.mSync = indice.sync || mIsAudio;
       // FIXME: Make this infallible after bug 968520 is done.
@@ -556,19 +552,21 @@ TimeIntervals Index::ConvertByteRangesTo
           if (range.ContainsStrict(mIndex[i].mByteRange)) {
             timeRanges += TimeInterval(
                 TimeUnit::FromMicroseconds(mIndex[i].mCompositionRange.start),
                 TimeUnit::FromMicroseconds(mIndex[i].mCompositionRange.end));
           }
         }
       }
       if (end > start) {
-        timeRanges += TimeInterval(
-            TimeUnit::FromMicroseconds(mDataOffset[start].mTime.start),
-            TimeUnit::FromMicroseconds(mDataOffset[end - 1].mTime.end));
+        for (uint32_t i = start; i < end; i++) {
+          timeRanges += TimeInterval(
+              TimeUnit::FromMicroseconds(mDataOffset[i].mTime.start),
+              TimeUnit::FromMicroseconds(mDataOffset[i].mTime.end));
+        }
       }
       if (end < mDataOffset.Length()) {
         // Find samples in partial block contained in the byte range.
         for (size_t i = mDataOffset[end].mIndex;
              i < mIndex.Length() && range.ContainsStrict(mIndex[i].mByteRange);
              i++) {
           timeRanges += TimeInterval(
               TimeUnit::FromMicroseconds(mIndex[i].mCompositionRange.start),
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -15,22 +15,22 @@
 #include "mozilla/gfx/CompositorHitTestInfo.h"
 #include "mozilla/gfx/Logging.h"              // for gfx::TreeLog
 #include "mozilla/gfx/Matrix.h"               // for Matrix4x4
 #include "mozilla/layers/APZInputBridge.h"    // for APZInputBridge
 #include "mozilla/layers/APZTestData.h"       // for APZTestData
 #include "mozilla/layers/IAPZCTreeManager.h"  // for IAPZCTreeManager
 #include "mozilla/layers/LayersTypes.h"
 #include "mozilla/layers/KeyboardMap.h"  // for KeyboardMap
+#include "mozilla/layers/TouchCounter.h" // for TouchCounter
 #include "mozilla/RecursiveMutex.h"      // for RecursiveMutex
 #include "mozilla/RefPtr.h"              // for RefPtr
 #include "mozilla/TimeStamp.h"           // for mozilla::TimeStamp
 #include "mozilla/UniquePtr.h"           // for UniquePtr
 #include "nsCOMPtr.h"                    // for already_AddRefed
-#include "TouchCounter.h"                // for TouchCounter
 
 #if defined(MOZ_WIDGET_ANDROID)
 #  include "mozilla/layers/AndroidDynamicToolbarAnimator.h"
 #endif  // defined(MOZ_WIDGET_ANDROID)
 
 namespace mozilla {
 class MultiTouchInput;
 
--- a/gfx/layers/apz/src/InputBlockState.h
+++ b/gfx/layers/apz/src/InputBlockState.h
@@ -9,19 +9,19 @@
 
 #include "InputData.h"           // for MultiTouchInput
 #include "mozilla/RefCounted.h"  // for RefCounted
 #include "mozilla/RefPtr.h"      // for RefPtr
 #include "mozilla/gfx/Matrix.h"  // for Matrix4x4
 #include "mozilla/layers/APZUtils.h"
 #include "mozilla/layers/LayersTypes.h"  // for TouchBehaviorFlags
 #include "mozilla/layers/AsyncDragMetrics.h"
+#include "mozilla/layers/TouchCounter.h"
 #include "mozilla/TimeStamp.h"  // for TimeStamp
 #include "nsTArray.h"           // for nsTArray
-#include "TouchCounter.h"
 
 namespace mozilla {
 namespace layers {
 
 class AsyncPanZoomController;
 class OverscrollHandoffChain;
 class CancelableBlockState;
 class TouchBlockState;
--- a/gfx/layers/apz/src/InputQueue.h
+++ b/gfx/layers/apz/src/InputQueue.h
@@ -6,20 +6,20 @@
 
 #ifndef mozilla_layers_InputQueue_h
 #define mozilla_layers_InputQueue_h
 
 #include "APZUtils.h"
 #include "DragTracker.h"
 #include "InputData.h"
 #include "mozilla/EventForwards.h"
+#include "mozilla/layers/TouchCounter.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/UniquePtr.h"
 #include "nsTArray.h"
-#include "TouchCounter.h"
 
 namespace mozilla {
 
 class InputData;
 class MultiTouchInput;
 class ScrollWheelInput;
 
 namespace layers {
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_zoom_prevented.html
@@ -0,0 +1,81 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <meta name="viewport" content="width=device-width">
+  <title>Checking prevent-default for zooming</title>
+  <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+  <script type="application/javascript" src="apz_test_utils.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+  <script type="application/javascript">
+
+function* testPreventDefault(testDriver, aTouchStartToCancel) {
+  var initial_resolution = getResolution();
+  ok(initial_resolution > 0,
+      "The initial_resolution is " + initial_resolution + ", which is some sane value");
+
+  // preventDefault exactly one touchstart based on the value of aTouchStartToCancel
+  var touchStartCount = 0;
+  var canceller = function(e) {
+    dump("touchstart listener hit, count: " + touchStartCount + "\n");
+    touchStartCount++;
+    if (touchStartCount == aTouchStartToCancel) {
+      dump("calling preventDefault on touchstart\n");
+      e.preventDefault();
+      document.documentElement.removeEventListener("touchstart", canceller, {passive: false});
+    }
+  };
+  document.documentElement.addEventListener("touchstart", canceller, {passive: false});
+
+  var waitForTouchEnd = function(e) {
+    dump("touchend listener hit\n");
+    setTimeout(testDriver, 0);
+  };
+  document.documentElement.addEventListener("touchend", waitForTouchEnd, {passive: true, once: true});
+
+  // Ensure that APZ gets updated hit-test info
+  yield waitForAllPaints(testDriver);
+
+  var zoom_in = [
+      [ { x: 125, y: 250 }, { x: 175, y: 350 } ],
+      [ { x: 120, y: 220 }, { x: 180, y: 380 } ],
+      [ { x: 115, y: 190 }, { x: 185, y: 410 } ],
+      [ { x: 110, y: 160 }, { x: 190, y: 440 } ],
+      [ { x: 105, y: 130 }, { x: 195, y: 470 } ],
+      [ { x: 100, y: 100 }, { x: 200, y: 500 } ],
+  ];
+
+  var touchIds = [0, 1];
+  yield* synthesizeNativeTouchSequences(document.body, zoom_in, null, touchIds);
+
+  yield; // wait for the touchend listener to fire
+
+  // Flush state and get the resolution we're at now
+  yield waitForApzFlushedRepaints(testDriver);
+  let final_resolution = getResolution();
+  is(final_resolution, initial_resolution, "The final resolution (" + final_resolution + ") matches the initial resolution");
+}
+
+function* test(testDriver) {
+  // Register a listener that fails the test if the APZ:TransformEnd event fires,
+  // because this test shouldn't actually be triggering any transforms
+  SpecialPowers.Services.obs.addObserver(function() {
+    ok(false, "The test fired an unexpected APZ:TransformEnd");
+  }, "APZ:TransformEnd");
+
+  yield* testPreventDefault(testDriver, 1);
+  yield* testPreventDefault(testDriver, 2);
+}
+
+waitUntilApzStable()
+.then(runContinuation(test))
+.then(subtestDone);
+
+  </script>
+</head>
+<body>
+  Here is some text to stare at as the test runs. It serves no functional
+  purpose, but gives you an idea of the zoom level. It's harder to tell what
+  the zoom level is when the page is just solid white.
+</body>
+</html>
--- a/gfx/layers/apz/test/mochitest/test_group_zoom.html
+++ b/gfx/layers/apz/test/mochitest/test_group_zoom.html
@@ -27,29 +27,33 @@ var prefs = [
   // offset values.
   ["browser.chrome.dynamictoolbar", false],
   // Explicitly enable pinch-zooming, so this test can run on desktop
   // even though zooming isn't enabled by default on desktop yet.
   ["apz.allow_zooming", true],
   // Pinch-zooming currently requires meta viewport support (this requirement
   // will eventually be removed).
   ["dom.meta-viewport.enabled", true],
+  // Increase the content response timeout because some tests do preventDefault
+  // and we want to make sure APZ actually waits for them.
+  ["apz.content_response_timeout", 60000],
 ];
 
 // Increase the tap timeouts so the double-tap is still detected in case of
 // random delays during testing.
 var doubletap_prefs = [
   ...prefs,
   ["ui.click_hold_context_menus.delay", 10000],
   ["apz.max_tap_time", 10000],
 ];
 
 var subtests = [
   {"file": "helper_bug1280013.html", "prefs": prefs},
   {"file": "helper_basic_zoom.html", "prefs": prefs},
+  {"file": "helper_zoom_prevented.html", "prefs": prefs},
   {"file": "helper_zoomed_pan.html", "prefs": prefs},
   {"file": "helper_fixed_position_scroll_hittest.html", "prefs": prefs},
   {"file": "helper_basic_doubletap_zoom.html", "prefs": doubletap_prefs},
 ];
 
 if (isApzEnabled()) {
   // This has a lot of subtests, and Android emulators are slow.
   SimpleTest.requestLongerTimeout(2);
--- a/gfx/layers/apz/util/APZEventState.cpp
+++ b/gfx/layers/apz/util/APZEventState.cpp
@@ -99,16 +99,17 @@ APZEventState::APZEventState(nsIWidget* 
                              ContentReceivedInputBlockCallback&& aCallback)
     : mWidget(nullptr)  // initialized in constructor body
       ,
       mActiveElementManager(new ActiveElementManager()),
       mContentReceivedInputBlockCallback(std::move(aCallback)),
       mPendingTouchPreventedResponse(false),
       mPendingTouchPreventedBlockId(0),
       mEndTouchIsClick(false),
+      mFirstTouchCancelled(false),
       mTouchEndCancelled(false),
       mLastTouchIdentifier(0) {
   nsresult rv;
   mWidget = do_GetWeakReference(aWidget, &rv);
   MOZ_ASSERT(NS_SUCCEEDED(rv),
              "APZEventState constructed with a widget that"
              " does not support weak references. APZ will NOT work!");
 
@@ -332,16 +333,32 @@ void APZEventState::ProcessTouchEvent(co
       sentContentResponse = SendPendingTouchPreventedResponse(false);
       // sentContentResponse can be true here if we get two TOUCH_STARTs in a
       // row and just responded to the first one.
 
       // We're about to send a response back to APZ, but we should only do it
       // for events that went through APZ (which should be all of them).
       MOZ_ASSERT(aEvent.mFlags.mHandledByAPZ);
 
+      // If the first touchstart event was preventDefaulted, ensure that any
+      // subsequent additional touchstart events also get preventDefaulted. This
+      // ensures that e.g. pinch zooming is prevented even if just the first
+      // touchstart was prevented by content.
+      if (mTouchCounter.GetActiveTouchCount() == 0) {
+        mFirstTouchCancelled = isTouchPrevented;
+      } else {
+        if (mFirstTouchCancelled && !isTouchPrevented) {
+          APZES_LOG(
+              "Propagating prevent-default from first-touch for block %" PRIu64
+              "\n",
+              aInputBlockId);
+        }
+        isTouchPrevented |= mFirstTouchCancelled;
+      }
+
       if (isTouchPrevented) {
         mContentReceivedInputBlockCallback(aGuid, aInputBlockId,
                                            isTouchPrevented);
         sentContentResponse = true;
       } else {
         APZES_LOG("Event not prevented; pending response for %" PRIu64 " %s\n",
                   aInputBlockId, Stringify(aGuid).c_str());
         mPendingTouchPreventedResponse = true;
@@ -368,16 +385,21 @@ void APZEventState::ProcessTouchEvent(co
       break;
     }
 
     default:
       MOZ_ASSERT_UNREACHABLE("Unknown touch event type");
       break;
   }
 
+  mTouchCounter.Update(aEvent);
+  if (mTouchCounter.GetActiveTouchCount() == 0) {
+    mFirstTouchCancelled = false;
+  }
+
   if (sentContentResponse && !isTouchPrevented &&
       aApzResponse == nsEventStatus_eConsumeDoDefault &&
       gfxPrefs::PointerEventsEnabled()) {
     WidgetTouchEvent cancelEvent(aEvent);
     cancelEvent.mMessage = eTouchPointerCancel;
     cancelEvent.mFlags.mCancelable = false;  // mMessage != eTouchCancel;
     for (uint32_t i = 0; i < cancelEvent.mTouches.Length(); ++i) {
       if (mozilla::dom::Touch* touch = cancelEvent.mTouches[i]) {
--- a/gfx/layers/apz/util/APZEventState.h
+++ b/gfx/layers/apz/util/APZEventState.h
@@ -8,16 +8,17 @@
 #define mozilla_layers_APZEventState_h
 
 #include <stdint.h>
 
 #include "Units.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/layers/GeckoContentController.h"  // for APZStateChange
 #include "mozilla/layers/ScrollableLayerGuid.h"     // for ScrollableLayerGuid
+#include "mozilla/layers/TouchCounter.h"            // for TouchCounter
 #include "mozilla/RefPtr.h"
 #include "nsCOMPtr.h"
 #include "nsISupportsImpl.h"        // for NS_INLINE_DECL_REFCOUNTING
 #include "nsIWeakReferenceUtils.h"  // for nsWeakPtr
 
 #include <functional>
 
 template <class>
@@ -89,20 +90,22 @@ class APZEventState {
                              const nsCOMPtr<nsIWidget>& aWidget);
   already_AddRefed<nsIWidget> GetWidget() const;
   already_AddRefed<nsIContent> GetTouchRollup() const;
 
  private:
   nsWeakPtr mWidget;
   RefPtr<ActiveElementManager> mActiveElementManager;
   ContentReceivedInputBlockCallback mContentReceivedInputBlockCallback;
+  TouchCounter mTouchCounter;
   bool mPendingTouchPreventedResponse;
   ScrollableLayerGuid mPendingTouchPreventedGuid;
   uint64_t mPendingTouchPreventedBlockId;
   bool mEndTouchIsClick;
+  bool mFirstTouchCancelled;
   bool mTouchEndCancelled;
   int32_t mLastTouchIdentifier;
 
   // Because touch-triggered mouse events (e.g. mouse events from a tap
   // gesture) happen asynchronously from the touch events themselves, we
   // need to stash and replicate some of the state from the touch events
   // to the mouse events. One piece of state is the rollup content, which
   // is the content for which a popup window was recently closed. If we
rename from gfx/layers/apz/src/TouchCounter.cpp
rename to gfx/layers/apz/util/TouchCounter.cpp
--- a/gfx/layers/apz/src/TouchCounter.cpp
+++ b/gfx/layers/apz/util/TouchCounter.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "TouchCounter.h"
 
 #include "InputData.h"
+#include "mozilla/TouchEvents.h"
 
 namespace mozilla {
 namespace layers {
 
 TouchCounter::TouchCounter() : mActiveTouchCount(0) {}
 
 void TouchCounter::Update(const MultiTouchInput& aInput) {
   switch (aInput.mType) {
@@ -19,24 +20,55 @@ void TouchCounter::Update(const MultiTou
       // touch-start event contains all active touches of the current session
       mActiveTouchCount = aInput.mTouches.Length();
       break;
     case MultiTouchInput::MULTITOUCH_END:
       if (mActiveTouchCount >= aInput.mTouches.Length()) {
         // touch-end event contains only released touches
         mActiveTouchCount -= aInput.mTouches.Length();
       } else {
-        NS_WARNING("Got an unexpected touchend/touchcancel");
+        NS_WARNING("Got an unexpected touchend");
         mActiveTouchCount = 0;
       }
       break;
     case MultiTouchInput::MULTITOUCH_CANCEL:
       mActiveTouchCount = 0;
       break;
     case MultiTouchInput::MULTITOUCH_MOVE:
       break;
   }
 }
 
+void TouchCounter::Update(const WidgetTouchEvent& aEvent) {
+  switch (aEvent.mMessage) {
+    case eTouchStart:
+      // touch-start event contains all active touches of the current session
+      mActiveTouchCount = aEvent.mTouches.Length();
+      break;
+    case eTouchEnd: {
+      // touch-end contains all touches, but ones being lifted are marked as
+      // changed
+      uint32_t liftedTouches = 0;
+      for (const auto& touch : aEvent.mTouches) {
+        if (touch->mChanged) {
+          liftedTouches++;
+        }
+      }
+      if (mActiveTouchCount >= liftedTouches) {
+        mActiveTouchCount -= liftedTouches;
+      } else {
+        NS_WARNING("Got an unexpected touchend");
+        mActiveTouchCount = 0;
+      }
+      break;
+    }
+    case eTouchCancel:
+      mActiveTouchCount = 0;
+      break;
+    default:
+      break;
+  }
+}
+
 uint32_t TouchCounter::GetActiveTouchCount() const { return mActiveTouchCount; }
 
 }  // namespace layers
 }  // namespace mozilla
rename from gfx/layers/apz/src/TouchCounter.h
rename to gfx/layers/apz/util/TouchCounter.h
--- a/gfx/layers/apz/src/TouchCounter.h
+++ b/gfx/layers/apz/util/TouchCounter.h
@@ -11,21 +11,24 @@
 
 namespace mozilla {
 
 class MultiTouchInput;
 
 namespace layers {
 
 // TouchCounter simply tracks the number of active touch points. Feed it
-// your input events to update the internal state.
+// your input events to update the internal state. Generally you should
+// only be calling one of the Update functions, depending on which type
+// of touch inputs you have access to.
 class TouchCounter {
  public:
   TouchCounter();
   void Update(const MultiTouchInput& aInput);
+  void Update(const WidgetTouchEvent& aEvent);
   uint32_t GetActiveTouchCount() const;
 
  private:
   uint32_t mActiveTouchCount;
 };
 
 }  // namespace layers
 }  // namespace mozilla
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -113,16 +113,17 @@ EXPORTS.mozilla.layers += [
     'apz/util/APZEventState.h',
     'apz/util/APZThreadUtils.h',
     'apz/util/ChromeProcessController.h',
     'apz/util/ContentProcessController.h',
     'apz/util/DoubleTapToZoom.h',
     'apz/util/InputAPZContext.h',
     'apz/util/ScrollLinkedEffectDetector.h',
     'apz/util/TouchActionHelper.h',
+    'apz/util/TouchCounter.h',
     'AsyncCanvasRenderer.h',
     'AtomicRefCountedWithFinalize.h',
     'AxisPhysicsModel.h',
     'AxisPhysicsMSDModel.h',
     'basic/BasicCompositor.h',
     'basic/MacIOSurfaceTextureHostBasic.h',
     'basic/TextureHostBasic.h',
     'BSPTree.h',
@@ -331,30 +332,30 @@ UNIFIED_SOURCES += [
     'apz/src/InputQueue.cpp',
     'apz/src/KeyboardMap.cpp',
     'apz/src/KeyboardScrollAction.cpp',
     'apz/src/KeyboardScrollAnimation.cpp',
     'apz/src/OverscrollHandoffState.cpp',
     'apz/src/PotentialCheckerboardDurationTracker.cpp',
     'apz/src/QueuedInput.cpp',
     'apz/src/SimpleVelocityTracker.cpp',
-    'apz/src/TouchCounter.cpp',
     'apz/src/WheelScrollAnimation.cpp',
     'apz/testutil/APZTestData.cpp',
     'apz/util/ActiveElementManager.cpp',
     'apz/util/APZCCallbackHelper.cpp',
     'apz/util/APZEventState.cpp',
     'apz/util/APZThreadUtils.cpp',
     'apz/util/CheckerboardReportService.cpp',
     'apz/util/ChromeProcessController.cpp',
     'apz/util/ContentProcessController.cpp',
     'apz/util/DoubleTapToZoom.cpp',
     'apz/util/InputAPZContext.cpp',
     'apz/util/ScrollLinkedEffectDetector.cpp',
     'apz/util/TouchActionHelper.cpp',
+    'apz/util/TouchCounter.cpp',
     'AsyncCanvasRenderer.cpp',
     'AxisPhysicsModel.cpp',
     'AxisPhysicsMSDModel.cpp',
     'basic/BasicCanvasLayer.cpp',
     'basic/BasicColorLayer.cpp',
     'basic/BasicCompositor.cpp',
     'basic/BasicContainerLayer.cpp',
     'basic/BasicImages.cpp',
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1204,16 +1204,34 @@ static bool StartGC(JSContext* cx, unsig
 
   JSGCInvocationKind gckind = shrinking ? GC_SHRINK : GC_NORMAL;
   rt->gc.startDebugGC(gckind, budget);
 
   args.rval().setUndefined();
   return true;
 }
 
+static bool FinishGC(JSContext* cx, unsigned argc, Value* vp) {
+  CallArgs args = CallArgsFromVp(argc, vp);
+
+  if (args.length() > 0) {
+    RootedObject callee(cx, &args.callee());
+    ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
+    return false;
+  }
+
+  JSRuntime* rt = cx->runtime();
+  if (rt->gc.isIncrementalGCInProgress()) {
+    rt->gc.finishGC(JS::GCReason::DEBUG_GC);
+  }
+
+  args.rval().setUndefined();
+  return true;
+}
+
 static bool GCSlice(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   if (args.length() > 1) {
     RootedObject callee(cx, &args.callee());
     ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
     return false;
   }
@@ -5848,16 +5866,20 @@ gc::ZealModeHelpText),
 #endif
 
     JS_FN_HELP("startgc", StartGC, 1, 0,
 "startgc([n [, 'shrinking']])",
 "  Start an incremental GC and run a slice that processes about n objects.\n"
 "  If 'shrinking' is passesd as the optional second argument, perform a\n"
 "  shrinking GC rather than a normal GC."),
 
+    JS_FN_HELP("finishgc", FinishGC, 0, 0,
+"finishgc()",
+"   Finish an in-progress incremental GC, if none is running then do nothing."),
+
     JS_FN_HELP("gcslice", GCSlice, 1, 0,
 "gcslice([n])",
 "  Start or continue an an incremental GC, running a slice that processes about n objects."),
 
     JS_FN_HELP("abortgc", AbortGC, 1, 0,
 "abortgc()",
 "  Abort the current incremental GC."),
 
--- a/js/src/jit-test/tests/gc/weak-marking-01.js
+++ b/js/src/jit-test/tests/gc/weak-marking-01.js
@@ -8,16 +8,17 @@ gczeal(0);
 // All reachable keys should be found, and the rest should be swept.
 function basicSweeping() {
   var wm1 = new WeakMap();
   wm1.set({'name': 'obj1'}, {'name': 'val1'});
   var hold = {'name': 'obj2'};
   wm1.set(hold, {'name': 'val2'});
   wm1.set({'name': 'obj3'}, {'name': 'val3'});
 
+  finishgc();
   startgc(100000, 'shrinking');
   gcslice();
 
   assertEq(wm1.get(hold).name, 'val2');
   assertEq(nondeterministicGetWeakMapKeys(wm1).length, 1);
 }
 
 basicSweeping();
@@ -31,16 +32,17 @@ function weakGraph() {
   var obj4 = {'name': 'obj4'};
   var clear = {'name': ''}; // Make the interpreter forget about the last obj created
 
   wm1.set(obj2, obj3);
   wm1.set(obj3, obj1);
   wm1.set(obj4, obj1); // This edge will be cleared
   obj1 = obj3 = obj4 = undefined;
 
+  finishgc();
   startgc(100000, 'shrinking');
   gcslice();
 
   assertEq(obj2.name, "obj2");
   assertEq(wm1.get(obj2).name, "obj3");
   assertEq(wm1.get(wm1.get(obj2)).name, "obj1");
   print(nondeterministicGetWeakMapKeys(wm1).map(o => o.name).join(","));
   assertEq(nondeterministicGetWeakMapKeys(wm1).length, 2);
@@ -59,16 +61,17 @@ function deadWeakMap() {
 
   wm1.set(obj2, obj3);
   wm1.set(obj3, obj1);
   wm1.set(obj4, obj1); // This edge will be cleared
   var initialCount = finalizeCount();
   obj1 = obj3 = obj4 = undefined;
   wm1 = undefined;
 
+  finishgc();
   startgc(100000, 'shrinking');
   gcslice();
 
   assertEq(obj2.name, "obj2");
   assertEq(finalizeCount(), initialCount + 1);
 }
 
 deadWeakMap();
@@ -86,16 +89,17 @@ function deadKeys() {
   var obj3 = makeFinalizeObserver();
   var clear = {}; // Make the interpreter forget about the last obj created
 
   wm1.set(obj1, obj1);
   wm1.set(obj3, obj2);
   obj1 = obj3 = undefined;
   var initialCount = finalizeCount();
 
+  finishgc();
   startgc(100000, 'shrinking');
   gcslice();
 
   assertEq(finalizeCount(), initialCount + 2);
   assertEq(nondeterministicGetWeakMapKeys(wm1).length, 0);
 }
 
 deadKeys();
@@ -119,16 +123,17 @@ function weakKeysRealloc() {
     wm3.set(Object.create(null), wm2);
   }
   wm3.set(Object.create(null), makeFinalizeObserver());
   wm2 = undefined;
   wm3 = undefined;
   obj2 = undefined;
 
   var initialCount = finalizeCount();
+  finishgc();
   startgc(100000, 'shrinking');
   gcslice();
   assertEq(finalizeCount(), initialCount + 1);
 }
 
 weakKeysRealloc();
 
 // The weakKeys table is populated during regular marking. When a key is later
@@ -137,16 +142,17 @@ weakKeysRealloc();
 // traversals will include non-keys, etc.
 function deletedKeys() {
   var wm = new WeakMap;
   var g = newGlobal();
 
   for (var i = 0; i < 1000; i++)
     wm.set(g.Object.create(null), i);
 
+  finishgc();
   startgc(100, 'shrinking');
   for (var key of nondeterministicGetWeakMapKeys(wm)) {
     if (wm.get(key) % 2)
       wm.delete(key);
   }
 
   gc();
 }
@@ -166,16 +172,17 @@ function incrementalAdds() {
   wm2.set(obj2, wm3);
   for (var i = 0; i < 10000; i++) {
     wm3.set(Object.create(null), wm2);
   }
   wm3.set(Object.create(null), makeFinalizeObserver());
   obj2 = undefined;
 
   var obj3 = [];
+  finishgc();
   startgc(100, 'shrinking');
   var M = 10;
   var N = 800;
   for (var j = 0; j < M; j++) {
     for (var i = 0; i < N; i++)
       wm3.set(Object.create(null), makeFinalizeObserver()); // Should be swept
     for (var i = 0; i < N; i++) {
       obj3.push({'name': 'obj3'});
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -2086,38 +2086,33 @@ JSObject* BaselineCompilerHandler::maybe
   if (realm->creationOptions().cloneSingletons()) {
     return nullptr;
   }
 
   realm->behaviors().setSingletonsAsValues();
   return script()->getObject(pc());
 }
 
-typedef JSObject* (*SingletonObjectLiteralFn)(JSContext*, HandleScript,
-                                              jsbytecode*);
-static const VMFunction SingletonObjectLiteralInfo =
-    FunctionInfo<SingletonObjectLiteralFn>(SingletonObjectLiteralOperation,
-                                           "SingletonObjectLiteralOperation");
-
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_OBJECT() {
   // If we know we don't have to clone the object literal, just push it
   // directly. Note that the interpreter always does the VM call; that's fine
   // because this op is only used in run-once code.
   if (JSObject* obj = handler.maybeNoCloneSingletonObject()) {
     frame.push(ObjectValue(*obj));
     return true;
   }
 
   prepareVMCall();
 
   pushBytecodePCArg();
   pushScriptArg(R2.scratchReg());
 
-  if (!callVM(SingletonObjectLiteralInfo)) {
+  using Fn = JSObject* (*)(JSContext*, HandleScript, jsbytecode*);
+  if (!callVM<Fn, SingletonObjectLiteralOperation>()) {
     return false;
   }
 
   // Box and push return value.
   masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
   frame.push(R0);
   return true;
 }
@@ -2129,126 +2124,111 @@ bool BaselineCompilerCodeGen::emit_JSOP_
   if (!cso) {
     return false;
   }
 
   frame.push(ObjectValue(*cso));
   return true;
 }
 
-typedef ArrayObject* (*ProcessCallSiteObjFn)(JSContext*, HandleScript,
-                                             jsbytecode*);
-static const VMFunction ProcessCallSiteObjInfo =
-    FunctionInfo<ProcessCallSiteObjFn>(ProcessCallSiteObjOperation,
-                                       "ProcessCallSiteObjOperation");
-
 template <>
 bool BaselineInterpreterCodeGen::emit_JSOP_CALLSITEOBJ() {
   prepareVMCall();
 
   pushBytecodePCArg();
   pushScriptArg(R2.scratchReg());
 
-  if (!callVM(ProcessCallSiteObjInfo)) {
+  using Fn = ArrayObject* (*)(JSContext*, HandleScript, jsbytecode*);
+  if (!callVM<Fn, ProcessCallSiteObjOperation>()) {
     return false;
   }
 
   // Box and push return value.
   masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
   frame.push(R0);
   return true;
 }
 
-typedef JSObject* (*CloneRegExpObjectFn)(JSContext*, Handle<RegExpObject*>);
-static const VMFunction CloneRegExpObjectInfo =
-    FunctionInfo<CloneRegExpObjectFn>(CloneRegExpObject, "CloneRegExpObject");
-
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_REGEXP() {
   prepareVMCall();
   pushScriptObjectArg(ScriptObjectType::RegExp);
-  if (!callVM(CloneRegExpObjectInfo)) {
+
+  using Fn = JSObject* (*)(JSContext*, Handle<RegExpObject*>);
+  if (!callVM<Fn, CloneRegExpObject>()) {
     return false;
   }
 
   // Box and push return value.
   masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
   frame.push(R0);
   return true;
 }
 
-typedef JSObject* (*LambdaFn)(JSContext*, HandleFunction, HandleObject);
-static const VMFunction LambdaInfo =
-    FunctionInfo<LambdaFn>(js::Lambda, "Lambda");
-
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_LAMBDA() {
   prepareVMCall();
   masm.loadPtr(frame.addressOfEnvironmentChain(), R0.scratchReg());
 
   pushArg(R0.scratchReg());
   pushScriptObjectArg(ScriptObjectType::Function);
 
-  if (!callVM(LambdaInfo)) {
+  using Fn = JSObject* (*)(JSContext*, HandleFunction, HandleObject);
+  if (!callVM<Fn, js::Lambda>()) {
     return false;
   }
 
   // Box and push return value.
   masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
   frame.push(R0);
   return true;
 }
 
-typedef JSObject* (*LambdaArrowFn)(JSContext*, HandleFunction, HandleObject,
-                                   HandleValue);
-static const VMFunction LambdaArrowInfo =
-    FunctionInfo<LambdaArrowFn>(js::LambdaArrow, "LambdaArrow");
-
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_LAMBDA_ARROW() {
   // Keep pushed newTarget in R0.
   frame.popRegsAndSync(1);
 
   prepareVMCall();
   masm.loadPtr(frame.addressOfEnvironmentChain(), R2.scratchReg());
 
   pushArg(R0);
   pushArg(R2.scratchReg());
   pushScriptObjectArg(ScriptObjectType::Function);
 
-  if (!callVM(LambdaArrowInfo)) {
+  using Fn =
+      JSObject* (*)(JSContext*, HandleFunction, HandleObject, HandleValue);
+  if (!callVM<Fn, js::LambdaArrow>()) {
     return false;
   }
 
   // Box and push return value.
   masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
   frame.push(R0);
   return true;
 }
 
-typedef bool (*SetFunNameFn)(JSContext*, HandleFunction, HandleValue,
-                             FunctionPrefixKind);
-static const VMFunction SetFunNameInfo =
-    FunctionInfo<SetFunNameFn>(js::SetFunctionName, "SetFunName");
-
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_SETFUNNAME() {
   frame.popRegsAndSync(2);
 
   frame.push(R0);
   frame.syncStack(0);
 
   masm.unboxObject(R0, R0.scratchReg());
 
   prepareVMCall();
 
   pushUint8BytecodeOperandArg();
   pushArg(R1);
   pushArg(R0.scratchReg());
-  return callVM(SetFunNameInfo);
+
+  using Fn =
+      bool (*)(JSContext*, HandleFunction, HandleValue, FunctionPrefixKind);
+  return callVM<Fn, SetFunctionName>();
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_BITOR() {
   return emitBinaryArith();
 }
 
 template <typename Handler>
@@ -2464,21 +2444,16 @@ bool BaselineCompilerCodeGen::emit_JSOP_
   return true;
 }
 
 template <>
 bool BaselineInterpreterCodeGen::emit_JSOP_NEWARRAY() {
   MOZ_CRASH("NYI: interpreter JSOP_NEWARRAY");
 }
 
-typedef ArrayObject* (*NewArrayCopyOnWriteFn)(JSContext*, HandleArrayObject,
-                                              gc::InitialHeap);
-const VMFunction NewArrayCopyOnWriteInfo = FunctionInfo<NewArrayCopyOnWriteFn>(
-    js::NewDenseCopyOnWriteArray, "NewDenseCopyOnWriteArray");
-
 template <>
 bool BaselineCompilerCodeGen::emit_JSOP_NEWARRAY_COPYONWRITE() {
   // This is like the interpreter implementation, but we can call
   // getOrFixupCopyOnWriteObject at compile-time.
 
   RootedScript scriptRoot(cx, handler.script());
   JSObject* obj =
       ObjectGroup::getOrFixupCopyOnWriteObject(cx, scriptRoot, handler.pc());
@@ -2486,40 +2461,36 @@ bool BaselineCompilerCodeGen::emit_JSOP_
     return false;
   }
 
   prepareVMCall();
 
   pushArg(Imm32(gc::DefaultHeap));
   pushArg(ImmGCPtr(obj));
 
-  if (!callVM(NewArrayCopyOnWriteInfo)) {
+  using Fn = ArrayObject* (*)(JSContext*, HandleArrayObject, gc::InitialHeap);
+  if (!callVM<Fn, js::NewDenseCopyOnWriteArray>()) {
     return false;
   }
 
   // Box and push return value.
   masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
   frame.push(R0);
   return true;
 }
 
-typedef ArrayObject* (*NewArrayCopyOnWriteOperationFn)(JSContext*, HandleScript,
-                                                       jsbytecode*);
-const VMFunction NewArrayCopyOnWriteOperationInfo =
-    FunctionInfo<NewArrayCopyOnWriteOperationFn>(
-        NewArrayCopyOnWriteOperation, "NewArrayCopyOnWriteOperation");
-
 template <>
 bool BaselineInterpreterCodeGen::emit_JSOP_NEWARRAY_COPYONWRITE() {
   prepareVMCall();
 
   pushBytecodePCArg();
   pushScriptArg(R2.scratchReg());
 
-  if (!callVM(NewArrayCopyOnWriteOperationInfo)) {
+  using Fn = ArrayObject* (*)(JSContext*, HandleScript, jsbytecode*);
+  if (!callVM<Fn, NewArrayCopyOnWriteOperation>()) {
     return false;
   }
 
   // Box and push return value.
   masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
   frame.push(R0);
   return true;
 }
@@ -2603,35 +2574,31 @@ bool BaselineCodeGen<Handler>::emit_JSOP
   return true;
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_INITHIDDENELEM() {
   return emit_JSOP_INITELEM();
 }
 
-typedef bool (*MutateProtoFn)(JSContext* cx, HandlePlainObject obj,
-                              HandleValue newProto);
-static const VMFunction MutateProtoInfo =
-    FunctionInfo<MutateProtoFn>(MutatePrototype, "MutatePrototype");
-
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_MUTATEPROTO() {
   // Keep values on the stack for the decompiler.
   frame.syncStack(0);
 
   masm.unboxObject(frame.addressOfStackValue(-2), R0.scratchReg());
   masm.loadValue(frame.addressOfStackValue(-1), R1);
 
   prepareVMCall();
 
   pushArg(R1);
   pushArg(R0.scratchReg());
 
-  if (!callVM(MutateProtoInfo)) {
+  using Fn = bool (*)(JSContext*, HandlePlainObject, HandleValue);
+  if (!callVM<Fn, MutatePrototype>()) {
     return false;
   }
 
   frame.pop();
   return true;
 }
 
 template <typename Handler>
@@ -2760,36 +2727,37 @@ bool BaselineCodeGen<Handler>::emit_JSOP
   return emitSetElemSuper(/* strict = */ false);
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_STRICTSETELEM_SUPER() {
   return emitSetElemSuper(/* strict = */ true);
 }
 
-typedef bool (*DeleteElementFn)(JSContext*, HandleValue, HandleValue, bool*);
-static const VMFunction DeleteElementStrictInfo = FunctionInfo<DeleteElementFn>(
-    DeleteElementJit<true>, "DeleteElementStrict");
-static const VMFunction DeleteElementNonStrictInfo =
-    FunctionInfo<DeleteElementFn>(DeleteElementJit<false>,
-                                  "DeleteElementNonStrict");
-
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emitDelElem(bool strict) {
   // Keep values on the stack for the decompiler.
   frame.syncStack(0);
   masm.loadValue(frame.addressOfStackValue(-2), R0);
   masm.loadValue(frame.addressOfStackValue(-1), R1);
 
   prepareVMCall();
 
   pushArg(R1);
   pushArg(R0);
-  if (!callVM(strict ? DeleteElementStrictInfo : DeleteElementNonStrictInfo)) {
-    return false;
+
+  using Fn = bool (*)(JSContext*, HandleValue, HandleValue, bool*);
+  if (strict) {
+    if (!callVM<Fn, DeleteElementJit<true>>()) {
+      return false;
+    }
+  } else {
+    if (!callVM<Fn, DeleteElementJit<false>>()) {
+      return false;
+    }
   }
 
   masm.boxNonDouble(JSVAL_TYPE_BOOLEAN, ReturnReg, R1);
   frame.popn(2);
   frame.push(R1);
   return true;
 }
 
@@ -2925,29 +2893,26 @@ bool BaselineInterpreterCodeGen::tryOpti
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_BINDGNAME() {
   if (tryOptimizeBindGlobalName()) {
     return true;
   }
   return emitBindName(JSOP_BINDGNAME);
 }
 
-typedef JSObject* (*BindVarFn)(JSContext*, JSObject*);
-static const VMFunction BindVarInfo =
-    FunctionInfo<BindVarFn>(BindVarOperation, "BindVarOperation");
-
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_BINDVAR() {
   frame.syncStack(0);
   masm.loadPtr(frame.addressOfEnvironmentChain(), R0.scratchReg());
 
   prepareVMCall();
   pushArg(R0.scratchReg());
 
-  if (!callVM(BindVarInfo)) {
+  using Fn = JSObject* (*)(JSContext*, JSObject*);
+  if (!callVM<Fn, BindVarOperation>()) {
     return false;
   }
 
   masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
   frame.push(R0);
   return true;
 }
 
@@ -2988,21 +2953,16 @@ bool BaselineCodeGen<Handler>::emit_JSOP
   return emit_JSOP_SETPROP();
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_STRICTSETGNAME() {
   return emit_JSOP_SETPROP();
 }
 
-typedef bool (*SetPropertySuperFn)(JSContext*, HandleObject, HandleValue,
-                                   HandlePropertyName, HandleValue, bool);
-static const VMFunction SetPropertySuperInfo =
-    FunctionInfo<SetPropertySuperFn>(js::SetPropertySuper, "SetPropertySuper");
-
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emitSetPropSuper(bool strict) {
   // Incoming stack is |receiver, obj, rval|. We need to shuffle stack to
   // leave rval when operation is complete.
 
   // Pop rval into R0, then load receiver into R1 and replace with rval.
   frame.popRegsAndSync(1);
   masm.loadValue(frame.addressOfStackValue(-2), R1);
@@ -3012,17 +2972,19 @@ bool BaselineCodeGen<Handler>::emitSetPr
 
   pushArg(Imm32(strict));
   pushArg(R0);  // rval
   pushScriptNameArg();
   pushArg(R1);  // receiver
   masm.unboxObject(frame.addressOfStackValue(-1), R0.scratchReg());
   pushArg(R0.scratchReg());  // obj
 
-  if (!callVM(SetPropertySuperInfo)) {
+  using Fn = bool (*)(JSContext*, HandleObject, HandleValue, HandlePropertyName,
+                      HandleValue, bool);
+  if (!callVM<Fn, js::SetPropertySuper>()) {
     return false;
   }
 
   frame.pop();
   return true;
 }
 
 template <typename Handler>
@@ -3075,39 +3037,36 @@ bool BaselineCodeGen<Handler>::emit_JSOP
   if (!emitNextIC()) {
     return false;
   }
 
   frame.push(R0);
   return true;
 }
 
-typedef bool (*DeletePropertyFn)(JSContext*, HandleValue, HandlePropertyName,
-                                 bool*);
-static const VMFunction DeletePropertyStrictInfo =
-    FunctionInfo<DeletePropertyFn>(DeletePropertyJit<true>,
-                                   "DeletePropertyStrict");
-static const VMFunction DeletePropertyNonStrictInfo =
-    FunctionInfo<DeletePropertyFn>(DeletePropertyJit<false>,
-                                   "DeletePropertyNonStrict");
-
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emitDelProp(bool strict) {
   // Keep value on the stack for the decompiler.
   frame.syncStack(0);
   masm.loadValue(frame.addressOfStackValue(-1), R0);
 
   prepareVMCall();
 
   pushScriptNameArg();
   pushArg(R0);
 
-  if (!callVM(strict ? DeletePropertyStrictInfo
-                     : DeletePropertyNonStrictInfo)) {
-    return false;
+  using Fn = bool (*)(JSContext*, HandleValue, HandlePropertyName, bool*);
+  if (strict) {
+    if (!callVM<Fn, DeletePropertyJit<true>>()) {
+      return false;
+    }
+  } else {
+    if (!callVM<Fn, DeletePropertyJit<false>>()) {
+      return false;
+    }
   }
 
   masm.boxNonDouble(JSVAL_TYPE_BOOLEAN, ReturnReg, R1);
   frame.pop();
   frame.push(R1);
   return true;
 }
 
@@ -3268,32 +3227,29 @@ bool BaselineCodeGen<Handler>::emitBindN
   return true;
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_BINDNAME() {
   return emitBindName(JSOP_BINDNAME);
 }
 
-typedef bool (*DeleteNameFn)(JSContext*, HandlePropertyName, HandleObject,
-                             MutableHandleValue);
-static const VMFunction DeleteNameInfo =
-    FunctionInfo<DeleteNameFn>(DeleteNameOperation, "DeleteNameOperation");
-
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_DELNAME() {
   frame.syncStack(0);
   masm.loadPtr(frame.addressOfEnvironmentChain(), R0.scratchReg());
 
   prepareVMCall();
 
   pushArg(R0.scratchReg());
   pushScriptNameArg();
 
-  if (!callVM(DeleteNameInfo)) {
+  using Fn = bool (*)(JSContext*, HandlePropertyName, HandleObject,
+                      MutableHandleValue);
+  if (!callVM<Fn, js::DeleteNameOperation>()) {
     return false;
   }
 
   frame.push(R0);
   return true;
 }
 
 template <>
@@ -3338,35 +3294,31 @@ bool BaselineCompilerCodeGen::emit_JSOP_
       return false;
     }
   }
 
   frame.push(R0);
   return true;
 }
 
-typedef bool (*GetImportOperationFn)(JSContext*, HandleObject, HandleScript,
-                                     jsbytecode*, MutableHandleValue);
-static const VMFunction GetImportOperationInfo =
-    FunctionInfo<GetImportOperationFn>(GetImportOperation,
-                                       "GetImportOperation");
-
 template <>
 bool BaselineInterpreterCodeGen::emit_JSOP_GETIMPORT() {
   frame.syncStack(0);
 
   masm.loadPtr(frame.addressOfEnvironmentChain(), R0.scratchReg());
 
   prepareVMCall();
 
   pushBytecodePCArg();
   pushScriptArg(R2.scratchReg());
   pushArg(R0.scratchReg());
 
-  if (!callVM(GetImportOperationInfo)) {
+  using Fn = bool (*)(JSContext*, HandleObject, HandleScript, jsbytecode*,
+                      MutableHandleValue);
+  if (!callVM<Fn, GetImportOperation>()) {
     return false;
   }
 
   // Enter the type monitor IC.
   if (!emitNextIC()) {
     return false;
   }
 
@@ -3381,127 +3333,109 @@ bool BaselineCodeGen<Handler>::emit_JSOP
   if (!emitNextIC()) {
     return false;
   }
 
   frame.push(R0);
   return true;
 }
 
-typedef bool (*SetIntrinsicFn)(JSContext*, JSScript*, jsbytecode*, HandleValue);
-static const VMFunction SetIntrinsicInfo = FunctionInfo<SetIntrinsicFn>(
-    SetIntrinsicOperation, "SetIntrinsicOperation");
-
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_SETINTRINSIC() {
   frame.syncStack(0);
   masm.loadValue(frame.addressOfStackValue(-1), R0);
 
   prepareVMCall();
 
   pushArg(R0);
   pushBytecodePCArg();
   pushScriptArg(R2.scratchReg());
 
-  return callVM(SetIntrinsicInfo);
-}
-
-typedef bool (*DefVarFn)(JSContext*, HandleObject, HandleScript, jsbytecode*);
-static const VMFunction DefVarInfo =
-    FunctionInfo<DefVarFn>(DefVarOperation, "DefVarOperation");
+  using Fn = bool (*)(JSContext*, JSScript*, jsbytecode*, HandleValue);
+  return callVM<Fn, SetIntrinsicOperation>();
+}
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_DEFVAR() {
   frame.syncStack(0);
 
   masm.loadPtr(frame.addressOfEnvironmentChain(), R0.scratchReg());
 
   prepareVMCall();
 
   pushBytecodePCArg();
   pushScriptArg(R2.scratchReg());
   pushArg(R0.scratchReg());
 
-  return callVM(DefVarInfo);
-}
-
-typedef bool (*DefLexicalFn)(JSContext*, HandleObject, HandleScript,
-                             jsbytecode*);
-static const VMFunction DefLexicalInfo =
-    FunctionInfo<DefLexicalFn>(DefLexicalOperation, "DefLexicalOperation");
+  using Fn = bool (*)(JSContext*, HandleObject, HandleScript, jsbytecode*);
+  return callVM<Fn, DefVarOperation>();
+}
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emitDefLexical(JSOp op) {
   MOZ_ASSERT(op == JSOP_DEFCONST || op == JSOP_DEFLET);
 
   frame.syncStack(0);
 
   masm.loadPtr(frame.addressOfEnvironmentChain(), R0.scratchReg());
 
   prepareVMCall();
 
   pushBytecodePCArg();
   pushScriptArg(R2.scratchReg());
   pushArg(R0.scratchReg());
 
-  return callVM(DefLexicalInfo);
+  using Fn = bool (*)(JSContext*, HandleObject, HandleScript, jsbytecode*);
+  return callVM<Fn, DefLexicalOperation>();
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_DEFCONST() {
   return emitDefLexical(JSOP_DEFCONST);
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_DEFLET() {
   return emitDefLexical(JSOP_DEFLET);
 }
 
-typedef bool (*DefFunOperationFn)(JSContext*, HandleScript, HandleObject,
-                                  HandleFunction);
-static const VMFunction DefFunOperationInfo =
-    FunctionInfo<DefFunOperationFn>(DefFunOperation, "DefFunOperation");
-
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_DEFFUN() {
   frame.popRegsAndSync(1);
   masm.unboxObject(R0, R0.scratchReg());
   masm.loadPtr(frame.addressOfEnvironmentChain(), R1.scratchReg());
 
   prepareVMCall();
 
   pushArg(R0.scratchReg());
   pushArg(R1.scratchReg());
   pushScriptArg(R2.scratchReg());
 
-  return callVM(DefFunOperationInfo);
-}
-
-typedef bool (*InitPropGetterSetterFn)(JSContext*, jsbytecode*, HandleObject,
-                                       HandlePropertyName, HandleObject);
-static const VMFunction InitPropGetterSetterInfo =
-    FunctionInfo<InitPropGetterSetterFn>(InitGetterSetterOperation,
-                                         "InitPropGetterSetterOperation");
+  using Fn = bool (*)(JSContext*, HandleScript, HandleObject, HandleFunction);
+  return callVM<Fn, DefFunOperation>();
+}
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emitInitPropGetterSetter() {
   // Keep values on the stack for the decompiler.
   frame.syncStack(0);
 
   prepareVMCall();
 
   masm.unboxObject(frame.addressOfStackValue(-1), R0.scratchReg());
   masm.unboxObject(frame.addressOfStackValue(-2), R1.scratchReg());
 
   pushArg(R0.scratchReg());
   pushScriptNameArg();
   pushArg(R1.scratchReg());
   pushBytecodePCArg();
 
-  if (!callVM(InitPropGetterSetterInfo)) {
+  using Fn = bool (*)(JSContext*, jsbytecode*, HandleObject, HandlePropertyName,
+                      HandleObject);
+  if (!callVM<Fn, InitPropGetterSetterOperation>()) {
     return false;
   }
 
   frame.pop();
   return true;
 }
 
 template <typename Handler>
@@ -3519,39 +3453,35 @@ bool BaselineCodeGen<Handler>::emit_JSOP
   return emitInitPropGetterSetter();
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_INITHIDDENPROP_SETTER() {
   return emitInitPropGetterSetter();
 }
 
-typedef bool (*InitElemGetterSetterFn)(JSContext*, jsbytecode*, HandleObject,
-                                       HandleValue, HandleObject);
-static const VMFunction InitElemGetterSetterInfo =
-    FunctionInfo<InitElemGetterSetterFn>(InitGetterSetterOperation,
-                                         "InitElemGetterSetterOperation");
-
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emitInitElemGetterSetter() {
   // Load index and value in R0 and R1, but keep values on the stack for the
   // decompiler.
   frame.syncStack(0);
   masm.loadValue(frame.addressOfStackValue(-2), R0);
   masm.unboxObject(frame.addressOfStackValue(-1), R1.scratchReg());
 
   prepareVMCall();
 
   pushArg(R1.scratchReg());
   pushArg(R0);
   masm.unboxObject(frame.addressOfStackValue(-3), R0.scratchReg());
   pushArg(R0.scratchReg());
   pushBytecodePCArg();
 
-  if (!callVM(InitElemGetterSetterInfo)) {
+  using Fn = bool (*)(JSContext*, jsbytecode*, HandleObject, HandleValue,
+                      HandleObject);
+  if (!callVM<Fn, InitElemGetterSetterOperation>()) {
     return false;
   }
 
   frame.popn(2);
   return true;
 }
 
 template <typename Handler>
@@ -3795,26 +3725,23 @@ bool BaselineCompilerCodeGen::emit_JSOP_
   return true;
 }
 
 template <>
 bool BaselineInterpreterCodeGen::emit_JSOP_NEWTARGET() {
   MOZ_CRASH("NYI: interpreter JSOP_NEWTARGET");
 }
 
-typedef bool (*ThrowRuntimeLexicalErrorFn)(JSContext* cx, unsigned);
-static const VMFunction ThrowRuntimeLexicalErrorInfo =
-    FunctionInfo<ThrowRuntimeLexicalErrorFn>(jit::ThrowRuntimeLexicalError,
-                                             "ThrowRuntimeLexicalError");
-
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emitThrowConstAssignment() {
   prepareVMCall();
   pushArg(Imm32(JSMSG_BAD_CONST_ASSIGN));
-  return callVM(ThrowRuntimeLexicalErrorInfo);
+
+  using Fn = bool (*)(JSContext*, unsigned);
+  return callVM<Fn, jit::ThrowRuntimeLexicalError>();
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_THROWSETCONST() {
   return emitThrowConstAssignment();
 }
 
 template <typename Handler>
@@ -3831,17 +3758,19 @@ template <typename Handler>
 bool BaselineCodeGen<Handler>::emitUninitializedLexicalCheck(
     const ValueOperand& val) {
   Label done;
   masm.branchTestMagicValue(Assembler::NotEqual, val, JS_UNINITIALIZED_LEXICAL,
                             &done);
 
   prepareVMCall();
   pushArg(Imm32(JSMSG_UNINITIALIZED_LEXICAL));
-  if (!callVM(ThrowRuntimeLexicalErrorInfo)) {
+
+  using Fn = bool (*)(JSContext*, unsigned);
+  if (!callVM<Fn, jit::ThrowRuntimeLexicalError>()) {
     return false;
   }
 
   masm.bind(&done);
   return true;
 }
 
 template <>
@@ -3997,30 +3926,26 @@ bool BaselineCodeGen<Handler>::emit_JSOP
   return emitSpreadCall(JSOP_SPREADEVAL);
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_STRICTSPREADEVAL() {
   return emitSpreadCall(JSOP_STRICTSPREADEVAL);
 }
 
-typedef bool (*OptimizeSpreadCallFn)(JSContext*, HandleValue, bool*);
-static const VMFunction OptimizeSpreadCallInfo =
-    FunctionInfo<OptimizeSpreadCallFn>(OptimizeSpreadCall,
-                                       "OptimizeSpreadCall");
-
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_OPTIMIZE_SPREADCALL() {
   frame.syncStack(0);
   masm.loadValue(frame.addressOfStackValue(-1), R0);
 
   prepareVMCall();
   pushArg(R0);
 
-  if (!callVM(OptimizeSpreadCallInfo)) {
+  using Fn = bool (*)(JSContext*, HandleValue, bool*);
+  if (!callVM<Fn, OptimizeSpreadCall>()) {
     return false;
   }
 
   masm.boxNonDouble(JSVAL_TYPE_BOOLEAN, ReturnReg, R0);
   frame.push(R0);
   return true;
 }
 
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -662,15 +662,14 @@ class BaselineInterpreterHandler {
 
 using BaselineInterpreterCodeGen = BaselineCodeGen<BaselineInterpreterHandler>;
 
 class BaselineInterpreterGenerator final : private BaselineInterpreterCodeGen {
  public:
   explicit BaselineInterpreterGenerator(JSContext* cx);
 };
 
-extern const VMFunction NewArrayCopyOnWriteInfo;
 extern const VMFunction ImplicitThisInfo;
 
 }  // namespace jit
 }  // namespace js
 
 #endif /* jit_BaselineCompiler_h */
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -6207,16 +6207,22 @@ void CodeGenerator::visitNewArray(LNewAr
   masm.bind(ool->rejoin());
 }
 
 void CodeGenerator::visitOutOfLineNewArray(OutOfLineNewArray* ool) {
   visitNewArrayCallVM(ool->lir());
   masm.jump(ool->rejoin());
 }
 
+typedef ArrayObject* (*NewArrayCopyOnWriteFn)(JSContext*, HandleArrayObject,
+                                              gc::InitialHeap);
+static const VMFunction NewArrayCopyOnWriteInfo =
+    FunctionInfo<NewArrayCopyOnWriteFn>(js::NewDenseCopyOnWriteArray,
+                                        "NewDenseCopyOnWriteArray");
+
 void CodeGenerator::visitNewArrayCopyOnWrite(LNewArrayCopyOnWrite* lir) {
   Register objReg = ToRegister(lir->output());
   Register tempReg = ToRegister(lir->temp());
   ArrayObject* templateObject = lir->mir()->templateObject();
   gc::InitialHeap initialHeap = lir->mir()->initialHeap();
 
   // If we have a template object, we can inline call object creation.
   OutOfLineCode* ool =
@@ -6720,17 +6726,17 @@ void CodeGenerator::visitInitElem(LInitE
   pushArg(ImmPtr(lir->mir()->resumePoint()->pc()));
 
   callVM(InitElemInfo, lir);
 }
 
 typedef bool (*InitElemGetterSetterFn)(JSContext*, jsbytecode*, HandleObject,
                                        HandleValue, HandleObject);
 static const VMFunction InitElemGetterSetterInfo =
-    FunctionInfo<InitElemGetterSetterFn>(InitGetterSetterOperation,
+    FunctionInfo<InitElemGetterSetterFn>(InitElemGetterSetterOperation,
                                          "InitElemGetterSetterOperation");
 
 void CodeGenerator::visitInitElemGetterSetter(LInitElemGetterSetter* lir) {
   Register obj = ToRegister(lir->object());
   Register value = ToRegister(lir->value());
 
   pushArg(value);
   pushArg(ToValue(lir, LInitElemGetterSetter::IdIndex));
@@ -6752,17 +6758,17 @@ void CodeGenerator::visitMutateProto(LMu
   pushArg(objReg);
 
   callVM(MutatePrototypeInfo, lir);
 }
 
 typedef bool (*InitPropGetterSetterFn)(JSContext*, jsbytecode*, HandleObject,
                                        HandlePropertyName, HandleObject);
 static const VMFunction InitPropGetterSetterInfo =
-    FunctionInfo<InitPropGetterSetterFn>(InitGetterSetterOperation,
+    FunctionInfo<InitPropGetterSetterFn>(InitPropGetterSetterOperation,
                                          "InitPropGetterSetterOperation");
 
 void CodeGenerator::visitInitPropGetterSetter(LInitPropGetterSetter* lir) {
   Register obj = ToRegister(lir->object());
   Register value = ToRegister(lir->value());
 
   pushArg(value);
   pushArg(ImmGCPtr(lir->mir()->name()));
--- a/js/src/jit/VMFunctionList-inl.h
+++ b/js/src/jit/VMFunctionList-inl.h
@@ -18,23 +18,48 @@ namespace jit {
 // List of all VM functions to be used with callVM. Each entry stores the name
 // (must be unique, used for the VMFunctionId enum and profiling) and the C++
 // function to be called. This list must be sorted on the name field.
 #define VMFUNCTION_LIST(_)                                                   \
   _(BaselineDebugPrologue, js::jit::DebugPrologue)                           \
   _(BaselineGetFunctionThis, js::jit::BaselineGetFunctionThis)               \
   _(BaselineThrowInitializedThis, js::jit::BaselineThrowInitializedThis)     \
   _(BaselineThrowUninitializedThis, js::jit::BaselineThrowUninitializedThis) \
+  _(BindVarOperation, js::BindVarOperation)                                  \
   _(CheckIsCallable, js::jit::CheckIsCallable)                               \
   _(CheckOverRecursedBaseline, js::jit::CheckOverRecursedBaseline)           \
+  _(CloneRegExpObject, js::CloneRegExpObject)                                \
+  _(DefFunOperation, js::DefFunOperation)                                    \
+  _(DefLexicalOperation, js::DefLexicalOperation)                            \
+  _(DefVarOperation, js::DefVarOperation)                                    \
+  _(DeleteElementNonStrict, js::DeleteElementJit<false>)                     \
+  _(DeleteElementStrict, js::DeleteElementJit<true>)                         \
+  _(DeleteNameOperation, js::DeleteNameOperation)                            \
+  _(DeletePropertyNonStrict, js::DeletePropertyJit<false>)                   \
+  _(DeletePropertyStrict, js::DeletePropertyJit<true>)                       \
+  _(GetImportOperation, js::GetImportOperation)                              \
   _(GetNonSyntacticGlobalThis, js::GetNonSyntacticGlobalThis)                \
+  _(InitElemGetterSetterOperation, js::InitElemGetterSetterOperation)        \
+  _(InitPropGetterSetterOperation, js::InitPropGetterSetterOperation)        \
   _(InterruptCheck, js::jit::InterruptCheck)                                 \
   _(IonCompileScriptForBaseline, js::jit::IonCompileScriptForBaseline)       \
+  _(Lambda, js::Lambda)                                                      \
+  _(LambdaArrow, js::LambdaArrow)                                            \
+  _(MutatePrototype, js::jit::MutatePrototype)                               \
+  _(NewArrayCopyOnWriteOperation, js::NewArrayCopyOnWriteOperation)          \
+  _(NewDenseCopyOnWriteArray, js::NewDenseCopyOnWriteArray)                  \
+  _(OptimizeSpreadCall, js::OptimizeSpreadCall)                              \
+  _(ProcessCallSiteObjOperation, js::ProcessCallSiteObjOperation)            \
+  _(SetFunctionName, js::SetFunctionName)                                    \
+  _(SetIntrinsicOperation, js::SetIntrinsicOperation)                        \
+  _(SetPropertySuper, js::SetPropertySuper)                                  \
+  _(SingletonObjectLiteralOperation, js::SingletonObjectLiteralOperation)    \
   _(ThrowBadDerivedReturn, js::jit::ThrowBadDerivedReturn)                   \
-  _(ThrowCheckIsObject, js::ThrowCheckIsObject)
+  _(ThrowCheckIsObject, js::ThrowCheckIsObject)                              \
+  _(ThrowRuntimeLexicalError, js::jit::ThrowRuntimeLexicalError)
 
 enum class VMFunctionId {
 #define DEF_ID(name, fp) name,
   VMFUNCTION_LIST(DEF_ID)
 #undef DEF_ID
       Count
 };
 
--- a/js/src/jit/arm64/vixl/Disasm-vixl.cpp
+++ b/js/src/jit/arm64/vixl/Disasm-vixl.cpp
@@ -21,16 +21,17 @@
 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "jit/arm64/vixl/Disasm-vixl.h"
 
+#include "mozilla/Sprintf.h"
 #include <cstdlib>
 
 namespace vixl {
 
 Disassembler::Disassembler() {
   buffer_size_ = 256;
   buffer_ = reinterpret_cast<char*>(malloc(buffer_size_));
   buffer_pos_ = 0;
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -3673,17 +3673,17 @@ static MOZ_NEVER_INLINE JS_HAZ_JSNATIVE_
     CASE(JSOP_INITPROP_SETTER)
     CASE(JSOP_INITHIDDENPROP_SETTER) {
       MOZ_ASSERT(REGS.stackDepth() >= 2);
 
       ReservedRooted<JSObject*> obj(&rootObject0, &REGS.sp[-2].toObject());
       ReservedRooted<PropertyName*> name(&rootName0, script->getName(REGS.pc));
       ReservedRooted<JSObject*> val(&rootObject1, &REGS.sp[-1].toObject());
 
-      if (!InitGetterSetterOperation(cx, REGS.pc, obj, name, val)) {
+      if (!InitPropGetterSetterOperation(cx, REGS.pc, obj, name, val)) {
         goto error;
       }
 
       REGS.sp--;
     }
     END_CASE(JSOP_INITPROP_GETTER)
 
     CASE(JSOP_INITELEM_GETTER)
@@ -3691,17 +3691,17 @@ static MOZ_NEVER_INLINE JS_HAZ_JSNATIVE_
     CASE(JSOP_INITELEM_SETTER)
     CASE(JSOP_INITHIDDENELEM_SETTER) {
       MOZ_ASSERT(REGS.stackDepth() >= 3);
 
       ReservedRooted<JSObject*> obj(&rootObject0, &REGS.sp[-3].toObject());
       ReservedRooted<Value> idval(&rootValue0, REGS.sp[-2]);
       ReservedRooted<JSObject*> val(&rootObject1, &REGS.sp[-1].toObject());
 
-      if (!InitGetterSetterOperation(cx, REGS.pc, obj, idval, val)) {
+      if (!InitElemGetterSetterOperation(cx, REGS.pc, obj, idval, val)) {
         goto error;
       }
 
       REGS.sp -= 2;
     }
     END_CASE(JSOP_INITELEM_GETTER)
 
     CASE(JSOP_HOLE) { PUSH_MAGIC(JS_ELEMENTS_HOLE); }
@@ -4927,19 +4927,19 @@ unsigned js::GetInitDataPropAttrs(JSOp o
     case JSOP_INITHIDDENELEM:
       // Non-enumerable, but writable and configurable
       return 0;
     default:;
   }
   MOZ_CRASH("Unknown data initprop");
 }
 
-bool js::InitGetterSetterOperation(JSContext* cx, jsbytecode* pc,
-                                   HandleObject obj, HandleId id,
-                                   HandleObject val) {
+static bool InitGetterSetterOperation(JSContext* cx, jsbytecode* pc,
+                                      HandleObject obj, HandleId id,
+                                      HandleObject val) {
   MOZ_ASSERT(val->isCallable());
 
   JSOp op = JSOp(*pc);
 
   unsigned attrs = 0;
   if (!IsHiddenInitOp(op)) {
     attrs |= JSPROP_ENUMERATE;
   }
@@ -4952,26 +4952,27 @@ bool js::InitGetterSetterOperation(JSCon
 
   MOZ_ASSERT(op == JSOP_INITPROP_SETTER || op == JSOP_INITELEM_SETTER ||
              op == JSOP_INITHIDDENPROP_SETTER ||
              op == JSOP_INITHIDDENELEM_SETTER);
   attrs |= JSPROP_SETTER;
   return DefineAccessorProperty(cx, obj, id, nullptr, val, attrs);
 }
 
-bool js::InitGetterSetterOperation(JSContext* cx, jsbytecode* pc,
-                                   HandleObject obj, HandlePropertyName name,
-                                   HandleObject val) {
+bool js::InitPropGetterSetterOperation(JSContext* cx, jsbytecode* pc,
+                                       HandleObject obj,
+                                       HandlePropertyName name,
+                                       HandleObject val) {
   RootedId id(cx, NameToId(name));
   return InitGetterSetterOperation(cx, pc, obj, id, val);
 }
 
-bool js::InitGetterSetterOperation(JSContext* cx, jsbytecode* pc,
-                                   HandleObject obj, HandleValue idval,
-                                   HandleObject val) {
+bool js::InitElemGetterSetterOperation(JSContext* cx, jsbytecode* pc,
+                                       HandleObject obj, HandleValue idval,
+                                       HandleObject val) {
   RootedId id(cx);
   if (!ToPropertyKey(cx, idval, &id)) {
     return false;
   }
 
   return InitGetterSetterOperation(cx, pc, obj, id, val);
 }
 
--- a/js/src/vm/Interpreter.h
+++ b/js/src/vm/Interpreter.h
@@ -510,29 +510,28 @@ bool ThrowMsgOperation(JSContext* cx, co
 bool GetAndClearException(JSContext* cx, MutableHandleValue res);
 
 bool DeleteNameOperation(JSContext* cx, HandlePropertyName name,
                          HandleObject scopeObj, MutableHandleValue res);
 
 bool ImplicitThisOperation(JSContext* cx, HandleObject scopeObj,
                            HandlePropertyName name, MutableHandleValue res);
 
-bool InitGetterSetterOperation(JSContext* cx, jsbytecode* pc, HandleObject obj,
-                               HandleId id, HandleObject val);
-
-bool InitGetterSetterOperation(JSContext* cx, jsbytecode* pc, HandleObject obj,
-                               HandlePropertyName name, HandleObject val);
+bool InitPropGetterSetterOperation(JSContext* cx, jsbytecode* pc,
+                                   HandleObject obj, HandlePropertyName name,
+                                   HandleObject val);
 
 unsigned GetInitDataPropAttrs(JSOp op);
 
 bool EnterWithOperation(JSContext* cx, AbstractFramePtr frame, HandleValue val,
                         Handle<WithScope*> scope);
 
-bool InitGetterSetterOperation(JSContext* cx, jsbytecode* pc, HandleObject obj,
-                               HandleValue idval, HandleObject val);
+bool InitElemGetterSetterOperation(JSContext* cx, jsbytecode* pc,
+                                   HandleObject obj, HandleValue idval,
+                                   HandleObject val);
 
 bool SpreadCallOperation(JSContext* cx, HandleScript script, jsbytecode* pc,
                          HandleValue thisv, HandleValue callee, HandleValue arr,
                          HandleValue newTarget, MutableHandleValue res);
 
 bool OptimizeSpreadCall(JSContext* cx, HandleValue arg, bool* optimized);
 
 JSObject* NewObjectOperation(JSContext* cx, HandleScript script, jsbytecode* pc,
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -6654,32 +6654,20 @@ nsresult PresShell::EventHandler::Handle
     // automatically updated.
     if (!DispatchPrecedingPointerEvent(
             aFrame, aGUIEvent, pointerCapturingContent, aDontRetargetEvents,
             &eventTargetData, aEventStatus)) {
       return NS_OK;
     }
 
     // frame could be null after dispatching pointer events.
-    if (aGUIEvent->mClass == eTouchEventClass) {
-      if (aGUIEvent->mMessage == eTouchStart) {
-        WidgetTouchEvent* touchEvent = aGUIEvent->AsTouchEvent();
-        if (nsIFrame* newFrame =
-                TouchManager::SuppressInvalidPointsAndGetTargetedFrame(
-                    touchEvent)) {
-          eventTargetData.SetFrameAndComputePresShellAndContent(newFrame,
-                                                                aGUIEvent);
-        }
-      } else if (PresShell* newShell =
-                     PresShell::GetShellForTouchEvent(aGUIEvent)) {
-        // Touch events (except touchstart) are dispatching to the captured
-        // element. Get correct shell from it.
-        eventTargetData.mPresShell = newShell;
-      }
-    }
+    // XXX Despite of this comment, we update the event target data outside
+    //     DispatchPrecedingPointerEvent().  Can we make it call
+    //     UpdateTouchEventTarget()?
+    eventTargetData.UpdateTouchEventTarget(aGUIEvent);
 
     // Handle the event in the correct shell.
     // We pass the subshell's root frame as the frame to start from. This is
     // the only correct alternative; if the event was captured then it
     // must have been captured by us or some ancestor shell and we
     // now ask the subshell to dispatch it normally.
     eventTargetData.mPresShell->PushCurrentEventInfo(eventTargetData.mFrame,
                                                      eventTargetData.mContent);
@@ -10860,8 +10848,37 @@ bool PresShell::EventHandler::EventTarge
   while (content && !content->IsElement()) {
     content = content->GetFlattenedTreeParent();
   }
   mContent = content;
 
   // If we found an element, target it.  Otherwise, target *nothing*.
   return !!mContent;
 }
+
+void PresShell::EventHandler::EventTargetData::UpdateTouchEventTarget(
+    WidgetGUIEvent* aGUIEvent) {
+  MOZ_ASSERT(aGUIEvent);
+
+  if (aGUIEvent->mClass != eTouchEventClass) {
+    return;
+  }
+
+  if (aGUIEvent->mMessage == eTouchStart) {
+    WidgetTouchEvent* touchEvent = aGUIEvent->AsTouchEvent();
+    nsIFrame* newFrame =
+        TouchManager::SuppressInvalidPointsAndGetTargetedFrame(touchEvent);
+    if (!newFrame) {
+      return;  // XXX Why don't we stop handling the event in this case?
+    }
+    SetFrameAndComputePresShellAndContent(newFrame, aGUIEvent);
+    return;
+  }
+
+  PresShell* newPresShell = PresShell::GetShellForTouchEvent(aGUIEvent);
+  if (!newPresShell) {
+    return;  // XXX Why don't we stop handling the event in this case?
+  }
+
+  // Touch events (except touchstart) are dispatching to the captured
+  // element. Get correct shell from it.
+  mPresShell = newPresShell;
+}
--- a/layout/base/PresShell.h
+++ b/layout/base/PresShell.h
@@ -634,16 +634,25 @@ class PresShell final : public nsIPresSh
        * @param aGUIEvent       The handling event.
        * @return                true if caller can keep handling the event.
        *                        Otherwise, false.
        *                        Note that even if this returns true, mContent
        *                        may be nullptr.
        */
       bool ComputeElementFromFrame(WidgetGUIEvent* aGUIEvent);
 
+      /**
+       * UpdateTouchEventTarget() updates mFrame, mPresShell and mContent if
+       * aGUIEvent is a touch event and there is new proper target.
+       *
+       * @param aGUIEvent       The handled event.  If it's not a touch event,
+       *                        this method does nothing.
+       */
+      void UpdateTouchEventTarget(WidgetGUIEvent* aGUIEvent);
+
       RefPtr<PresShell> mPresShell;
       nsIFrame* mFrame;
       nsCOMPtr<nsIContent> mContent;
       nsCOMPtr<nsIContent> mOverrideClickTarget;
     };
 
     /**
      * MaybeFlushPendingNotifications() maybe flush pending notifications if
--- a/mobile/android/geckoview/api.txt
+++ b/mobile/android/geckoview/api.txt
@@ -30,26 +30,21 @@ package org.mozilla.geckoview {
     field protected final android.graphics.RectF mTempRect;
     field protected final boolean mUseFloatingToolbar;
   }
 
   @android.support.annotation.UiThread public final class CompositorController {
     method public void addDrawCallback(@android.support.annotation.NonNull java.lang.Runnable);
     method public int getClearColor();
     method @android.support.annotation.Nullable public java.lang.Runnable getFirstPaintCallback();
-    method public void getPixels(@android.support.annotation.NonNull org.mozilla.geckoview.CompositorController.GetPixelsCallback);
     method public void removeDrawCallback(@android.support.annotation.NonNull java.lang.Runnable);
     method public void setClearColor(int);
     method public void setFirstPaintCallback(@android.support.annotation.Nullable java.lang.Runnable);
   }
 
-  public static interface CompositorController.GetPixelsCallback {
-    method @android.support.annotation.UiThread public void onPixelsResult(int, int, @android.support.annotation.Nullable java.nio.IntBuffer);
-  }
-
   @android.support.annotation.AnyThread public class ContentBlocking {
     ctor public ContentBlocking();
     field public static final int AT_AD = 2;
     field public static final int AT_ALL = 62;
     field public static final int AT_ANALYTIC = 4;
     field public static final int AT_CONTENT = 16;
     field public static final int AT_SOCIAL = 8;
     field public static final int AT_TEST = 32;
@@ -131,16 +126,17 @@ package org.mozilla.geckoview {
   public static interface DynamicToolbarAnimator.ToolbarChromeProxy {
     method @android.support.annotation.UiThread @android.support.annotation.Nullable public android.graphics.Bitmap getBitmapOfToolbarChrome();
     method @android.support.annotation.UiThread public boolean isToolbarChromeVisible();
     method @android.support.annotation.UiThread public void toggleToolbarChrome(boolean);
   }
 
   public class GeckoDisplay {
     ctor protected GeckoDisplay(org.mozilla.geckoview.GeckoSession);
+    method @android.support.annotation.UiThread @android.support.annotation.NonNull public org.mozilla.geckoview.GeckoResult<android.graphics.Bitmap> capturePixels();
     method @android.support.annotation.UiThread public void screenOriginChanged(int, int);
     method @android.support.annotation.UiThread public boolean shouldPinOnScreen();
     method @android.support.annotation.UiThread public void surfaceChanged(@android.support.annotation.NonNull android.view.Surface, int, int);
     method @android.support.annotation.UiThread public void surfaceChanged(@android.support.annotation.NonNull android.view.Surface, int, int, int, int);
     method @android.support.annotation.UiThread public void surfaceDestroyed();
   }
 
   public interface GeckoResponse<T> {
@@ -730,16 +726,17 @@ package org.mozilla.geckoview {
 
   public class GeckoVRManager {
     method @android.support.annotation.AnyThread public static synchronized void setExternalContext(long);
   }
 
   @android.support.annotation.UiThread public class GeckoView extends android.widget.FrameLayout {
     ctor public GeckoView(android.content.Context);
     ctor public GeckoView(android.content.Context, android.util.AttributeSet);
+    method @android.support.annotation.UiThread @android.support.annotation.NonNull public org.mozilla.geckoview.GeckoResult<android.graphics.Bitmap> capturePixels();
     method public void coverUntilFirstPaint(int);
     method @android.support.annotation.NonNull public org.mozilla.geckoview.DynamicToolbarAnimator getDynamicToolbarAnimator();
     method @android.support.annotation.AnyThread @android.support.annotation.NonNull public org.mozilla.gecko.EventDispatcher getEventDispatcher();
     method @android.support.annotation.NonNull public org.mozilla.geckoview.PanZoomController getPanZoomController();
     method @android.support.annotation.AnyThread @android.support.annotation.Nullable public org.mozilla.geckoview.GeckoSession getSession();
     method @android.support.annotation.UiThread @android.support.annotation.Nullable public org.mozilla.geckoview.GeckoSession releaseSession();
     method @android.support.annotation.UiThread public void setSession(@android.support.annotation.NonNull org.mozilla.geckoview.GeckoSession);
     method @android.support.annotation.UiThread public void setSession(@android.support.annotation.NonNull org.mozilla.geckoview.GeckoSession, @android.support.annotation.Nullable org.mozilla.geckoview.GeckoRuntime);
--- a/mobile/android/geckoview/proguard-rules.txt
+++ b/mobile/android/geckoview/proguard-rules.txt
@@ -81,16 +81,19 @@
 # through introspection.
 
 -keepclassmembers class **.R$* {
   public static <fields>;
 }
 
 # GeckoView specific rules.
 
+# Keep everthing in org.mozilla.geckoview
+-keep class org.mozilla.geckoview.** { *; }
+
 -keep class org.mozilla.gecko.SysInfo {
     *;
 }
 
 # Keep the annotation.
 -keep @interface org.mozilla.gecko.annotation.JNITarget
 
 # Keep classes tagged with the annotation.
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/www/colors.html
@@ -0,0 +1,6 @@
+<html>
+    <head><title>Colours</title></head>
+    <body style="background-color:green;">
+        <div id="fullscreen" style="width:100%;height:100%;position:fixed;top:0;left:0;z-index:100;"></div>
+    </body>
+</html>
\ No newline at end of file
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/BaseSessionTest.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/BaseSessionTest.kt
@@ -48,16 +48,17 @@ open class BaseSessionTest(noErrorCollec
         const val VIDEO_BAD_PATH = "/assets/www/badVideoPath.html"
         const val UNKNOWN_HOST_URI = "http://www.test.invalid/"
         const val FULLSCREEN_PATH = "/assets/www/fullscreen.html"
         const val VIEWPORT_PATH = "/assets/www/viewport.html"
         const val IFRAME_REDIRECT_LOCAL = "/assets/www/iframe_redirect_local.html"
         const val IFRAME_REDIRECT_AUTOMATION = "/assets/www/iframe_redirect_automation.html"
         const val AUTOPLAY_PATH = "/assets/www/autoplay.html"
         const val SCROLL_TEST_PATH = "/assets/www/scroll.html"
+        const val COLORS_HTML_PATH = "/assets/www/colors.html"
     }
 
     @get:Rule val sessionRule = GeckoSessionTestRule()
 
     @get:Rule val errors = ErrorCollector()
 
     val mainSession get() = sessionRule.session
 
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ScreenshotTest.kt
@@ -0,0 +1,106 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+package org.mozilla.geckoview.test
+
+
+import android.graphics.*
+import android.support.test.filters.MediumTest
+import android.support.test.runner.AndroidJUnit4
+import android.view.Surface
+import org.hamcrest.Matchers.notNullValue
+import org.hamcrest.Matchers.nullValue
+import org.junit.Assert.fail
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.ExpectedException
+import org.junit.runner.RunWith
+import org.mozilla.geckoview.GeckoResult
+import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.ReuseSession
+import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.WithDisplay
+
+const val SCREEN_HEIGHT = 100
+const val SCREEN_WIDTH = 100
+
+@RunWith(AndroidJUnit4::class)
+@MediumTest
+@ReuseSession(false)
+class ScreenshotTest : BaseSessionTest() {
+
+    @get:Rule
+    val expectedEx: ExpectedException = ExpectedException.none()
+
+    private fun getComparisonScreenshot(width: Int, height: Int): Bitmap {
+        val screenshotFile = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
+        val canvas = Canvas(screenshotFile)
+        val paint = Paint()
+        paint.color = Color.rgb(0, 128, 0)
+        canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), paint)
+        return screenshotFile
+    }
+
+    private fun assertScreenshotResult(result: GeckoResult<Bitmap>, comparisonImage: Bitmap) {
+        sessionRule.waitForResult(result).let {
+            assertThat("Screenshot is not null",
+                    it, notNullValue())
+            assert(it.sameAs(comparisonImage)) {  "Screenshots are the same" }
+        }
+    }
+
+    @WithDisplay(height = SCREEN_HEIGHT, width = SCREEN_WIDTH)
+    @Test
+    fun capturePixelsSucceeds() {
+        val screenshotFile = getComparisonScreenshot(SCREEN_WIDTH, SCREEN_HEIGHT)
+
+        sessionRule.session.loadTestPath(COLORS_HTML_PATH)
+        sessionRule.waitForPageStop()
+
+        sessionRule.display?.let {
+            assertScreenshotResult(it.capturePixels(), screenshotFile)
+        }
+    }
+
+    @WithDisplay(height = SCREEN_HEIGHT, width = SCREEN_WIDTH)
+    @Test
+    fun capturePixelsCanBeCalledMultipleTimes() {
+        val screenshotFile = getComparisonScreenshot(SCREEN_WIDTH, SCREEN_HEIGHT)
+
+        sessionRule.session.loadTestPath(COLORS_HTML_PATH)
+        sessionRule.waitForPageStop()
+
+        sessionRule.display?.let {
+            val call1 = it.capturePixels()
+            val call2 = it.capturePixels()
+            val call3 = it.capturePixels()
+            assertScreenshotResult(call1, screenshotFile)
+            assertScreenshotResult(call2, screenshotFile)
+            assertScreenshotResult(call3, screenshotFile)
+        }
+    }
+
+    @WithDisplay(height = SCREEN_HEIGHT, width = SCREEN_WIDTH)
+    @Test
+    fun capturePixelsCompletesCompositorPausedRestarted() {
+        sessionRule.display?.let {
+            it.surfaceDestroyed()
+            val result = it.capturePixels()
+            val texture = SurfaceTexture(0)
+            texture.setDefaultBufferSize(SCREEN_WIDTH, SCREEN_HEIGHT)
+            val surface = Surface(texture)
+            it.surfaceChanged(surface, SCREEN_WIDTH, SCREEN_HEIGHT)
+            sessionRule.waitForResult(result)
+        }
+    }
+
+    @Test
+    fun capturePixelsThrowsCompositorNotReady() {
+        expectedEx.expect(IllegalStateException::class.java)
+        expectedEx.expectMessage("Compositor must be ready before pixels can be captured")
+        val session = sessionRule.createClosedSession()
+        val display = session.acquireDisplay()
+
+        sessionRule.waitForResult(display.capturePixels())
+        fail("IllegalStateException expected to be thrown")
+    }
+}
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/rule/GeckoSessionTestRule.java
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/rule/GeckoSessionTestRule.java
@@ -31,16 +31,17 @@ import org.hamcrest.Matcher;
 import org.json.JSONObject;
 
 import org.junit.rules.ErrorCollector;
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
 
 import android.app.Instrumentation;
+import android.graphics.Color;
 import android.graphics.Point;
 import android.graphics.SurfaceTexture;
 import android.net.LocalSocketAddress;
 import android.os.Looper;
 import android.os.SystemClock;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.test.InstrumentationRegistry;
@@ -953,16 +954,20 @@ public class GeckoSessionTestRule implem
      * Get the runtime set up for the current test.
      *
      * @return GeckoRuntime object.
      */
     public @NonNull GeckoRuntime getRuntime() {
         return RuntimeCreator.getRuntime();
     }
 
+    public @Nullable GeckoDisplay getDisplay() {
+        return mDisplay;
+    }
+
     protected static Object setDelegate(final @NonNull Class<?> cls,
                                         final @NonNull GeckoSession session,
                                         final @Nullable Object delegate)
             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
         if (cls == GeckoSession.TextInputDelegate.class) {
             return SessionTextInput.class.getMethod("setDelegate", cls)
                    .invoke(session.getTextInput(), delegate);
         }
@@ -1206,19 +1211,20 @@ public class GeckoSessionTestRule implem
         if (useDefaultSession && mReuseSession && sCachedSession != null) {
             mMainSession = sCachedSession;
         } else {
             mMainSession = new GeckoSession(settings);
         }
         prepareSession(mMainSession);
 
         if (mDisplaySize != null) {
+            mDisplay = mMainSession.acquireDisplay();
             mDisplayTexture = new SurfaceTexture(0);
+            mDisplayTexture.setDefaultBufferSize(mDisplaySize.x, mDisplaySize.y);
             mDisplaySurface = new Surface(mDisplayTexture);
-            mDisplay = mMainSession.acquireDisplay();
             mDisplay.surfaceChanged(mDisplaySurface, mDisplaySize.x, mDisplaySize.y);
         }
 
         if (useDefaultSession && mReuseSession) {
             if (sCachedSession == null) {
                 // We are creating a cached session.
                 final boolean withDevTools = mWithDevTools;
                 mWithDevTools = true; // Always get an RDP tab for cached session.
@@ -1283,17 +1289,16 @@ public class GeckoSessionTestRule implem
         // For the cached session, we may get multiple initial loads. We should specifically look
         // for an about:blank load, and wait until that has stopped.
         final boolean lookForAboutBlank = session.equals(sCachedSession);
 
         try {
             // We cannot detect initial page load without progress delegate.
             assertThat("ProgressDelegate cannot be null-delegate when opening session",
                        GeckoSession.ProgressDelegate.class, not(isIn(mNullDelegates)));
-
             mCallRecordHandler = new CallRecordHandler() {
                 private boolean mIsAboutBlank = !lookForAboutBlank;
 
                 @Override
                 public boolean handleCall(final Method method, final Object[] args) {
                     final boolean matching = DEFAULT_DELEGATES.contains(
                             method.getDeclaringClass()) && session.equals(args[0]);
                     if (matching && sOnPageStart.equals(method)) {
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/CompositorController.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/CompositorController.java
@@ -3,36 +3,32 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.geckoview;
 
 import org.mozilla.gecko.annotation.RobocopTarget;
 import org.mozilla.gecko.util.ThreadUtils;
 
+import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.annotation.UiThread;
 
+import java.nio.ByteBuffer;
 import java.nio.IntBuffer;
 import java.util.ArrayList;
 import java.util.List;
 
 @UiThread
 public final class CompositorController {
     private final GeckoSession.Compositor mCompositor;
 
-    public interface GetPixelsCallback {
-        @UiThread
-        void onPixelsResult(int width, int height, @Nullable IntBuffer pixels);
-    }
-
     private List<Runnable> mDrawCallbacks;
-    private GetPixelsCallback mGetPixelsCallback;
     private int mDefaultClearColor = Color.WHITE;
     private Runnable mFirstPaintCallback;
 
     /* package */ CompositorController(final GeckoSession session) {
         mCompositor = session.mCompositor;
     }
 
     /* package */ void onCompositorReady() {
@@ -88,42 +84,16 @@ public final class CompositorController 
         }
 
         if (mDrawCallbacks.remove(callback) && mDrawCallbacks.isEmpty() &&
                 mCompositor.isReady()) {
             mCompositor.enableLayerUpdateNotifications(false);
         }
     }
 
-    /* package */ void recvScreenPixels(final int width, final int height,
-                                        final int[] pixels) {
-        if (mGetPixelsCallback != null) {
-            mGetPixelsCallback.onPixelsResult(width, height, IntBuffer.wrap(pixels));
-            mGetPixelsCallback = null;
-        }
-    }
-
-    /**
-     * Request current pixel values from the compositor. May be called on any thread. Must
-     * not be called again until the callback is invoked.
-     *
-     * @param callback Callback for getting pixels.
-     */
-    @RobocopTarget
-    public void getPixels(final @NonNull GetPixelsCallback callback) {
-        ThreadUtils.assertOnUiThread();
-
-        if (mCompositor.isReady()) {
-            mGetPixelsCallback = callback;
-            mCompositor.requestScreenPixels();
-        } else {
-            callback.onPixelsResult(0, 0, null);
-        }
-    }
-
     /**
      * Get the current clear color when drawing.
      *
      * @return Curent clear color.
      */
     public int getClearColor() {
         ThreadUtils.assertOnUiThread();
         return mDefaultClearColor;
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoDisplay.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoDisplay.java
@@ -1,16 +1,17 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * vim: ts=4 sw=4 expandtab:
  * 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/. */
 
 package org.mozilla.geckoview;
 
+import android.graphics.Bitmap;
 import android.support.annotation.NonNull;
 import android.support.annotation.UiThread;
 import android.view.Surface;
 
 import org.mozilla.gecko.util.ThreadUtils;
 
 /**
  * Applications use a GeckoDisplay instance to provide {@link GeckoSession} with a {@link Surface} for
@@ -113,9 +114,36 @@ public class GeckoDisplay {
      *
      * @return True if display should be pinned on the screen.
      */
     @UiThread
     public boolean shouldPinOnScreen() {
         ThreadUtils.assertOnUiThread();
         return session.getDisplay() == this && session.shouldPinOnScreen();
     }
+
+    /**
+     * Request a {@link Bitmap} of the visible portion of the web page currently being
+     * rendered.
+     *
+     * Returned {@link Bitmap} will have the same dimensions as the {@link Surface} the
+     * {@link GeckoDisplay} is currently using.
+     *
+     * If the {@link GeckoSession#isCompositorReady} is false the {@link GeckoResult} will complete
+     * with an {@link IllegalStateException}.
+     *
+     * This function must be called on the UI thread.
+     *
+     * @return A {@link GeckoResult} that completes with a {@link Bitmap} containing
+     * the pixels and size information of the currently visible rendered web page.
+     */
+    @UiThread
+    public @NonNull GeckoResult<Bitmap> capturePixels() {
+        ThreadUtils.assertOnUiThread();
+        if (!session.isCompositorReady()) {
+            return GeckoResult.fromException(
+                    new IllegalStateException("Compositor must be ready before pixels can be captured"));
+        }
+        GeckoResult<Bitmap> result = new GeckoResult<>();
+        session.mCompositor.requestScreenPixels(result);
+        return result;
+    }
 }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoResult.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoResult.java
@@ -1,10 +1,11 @@
 package org.mozilla.geckoview;
 
+import org.mozilla.gecko.annotation.RobocopTarget;
 import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.os.Handler;
 import android.os.Looper;
 import android.os.SystemClock;
 import android.support.annotation.AnyThread;
 import android.support.annotation.NonNull;
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
@@ -4,19 +4,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.geckoview;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
-import java.net.URLConnection;
-import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.UUID;
 
 import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.EventDispatcher;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.GeckoThread;
 import org.mozilla.gecko.IGeckoEditableParent;
 import org.mozilla.gecko.mozglue.JNIObject;
@@ -26,16 +23,17 @@ import org.mozilla.gecko.util.EventCallb
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.IntentUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.Resources;
 import android.database.Cursor;
+import android.graphics.Bitmap;
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.IInterface;
 import android.os.Parcel;
@@ -211,22 +209,17 @@ public class GeckoSession implements Par
         private void recvToolbarAnimatorMessage(int message) {
             GeckoSession.this.handleCompositorMessage(message);
         }
 
         @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
         public native void setDefaultClearColor(int color);
 
         @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
-        public native void requestScreenPixels();
-
-        @WrapForJNI(calledFrom = "ui")
-        private void recvScreenPixels(int width, int height, int[] pixels) {
-            GeckoSession.this.recvScreenPixels(width, height, pixels);
-        }
+        /* package */ native void requestScreenPixels(final GeckoResult<Bitmap> result);
 
         @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
         public native void enableLayerUpdateNotifications(boolean enable);
 
         @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
         public native void sendToolbarPixelsToCompositor(final int width, final int height,
                                                          final int[] pixels);
 
@@ -4359,22 +4352,16 @@ public class GeckoSession implements Par
                 } else {
                     Log.w(LOGTAG, "Unexpected message: " + message);
                 }
                 break;
             }
         }
     }
 
-    /* package */ void recvScreenPixels(int width, int height, int[] pixels) {
-        if (mController != null) {
-            mController.recvScreenPixels(width, height, pixels);
-        }
-    }
-
     /* package */ boolean isCompositorReady() {
         return mCompositorReady;
     }
 
     /* package */ void onCompositorReady() {
         if (DEBUG) {
             ThreadUtils.assertOnUiThread();
         }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoView.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoView.java
@@ -4,24 +4,26 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.geckoview;
 
 import org.mozilla.gecko.AndroidGamepadManager;
 import org.mozilla.gecko.EventDispatcher;
 import org.mozilla.gecko.InputMethods;
+import org.mozilla.gecko.annotation.RobocopTarget;
 import org.mozilla.gecko.util.ActivityUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.annotation.SuppressLint;
 import android.annotation.TargetApi;
 import android.app.Activity;
 import android.content.Context;
 import android.content.res.Configuration;
+import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.Build;
 import android.os.Handler;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -161,16 +163,32 @@ public class GeckoView extends FrameLayo
                 GeckoView.this.mSurfaceView.getLocationOnScreen(mOrigin);
                 mDisplay.screenOriginChanged(mOrigin[0], mOrigin[1]);
             }
         }
 
         public boolean shouldPinOnScreen() {
             return mDisplay != null ? mDisplay.shouldPinOnScreen() : false;
         }
+
+        /**
+         * Request a {@link Bitmap} of the visible portion of the web page currently being
+         * rendered.
+         *
+         * @return A {@link GeckoResult} that completes with a {@link Bitmap} containing
+         * the pixels and size information of the currently visible rendered web page.
+         */
+        @UiThread
+        @NonNull GeckoResult<Bitmap> capturePixels() {
+            if (mDisplay == null) {
+                return GeckoResult.fromException(new IllegalStateException("Display must be created before pixels can be captured"));
+            }
+
+            return mDisplay.capturePixels();
+        }
     }
 
     public GeckoView(final Context context) {
         super(context);
         init();
     }
 
     public GeckoView(final Context context, final AttributeSet attrs) {
@@ -704,9 +722,23 @@ public class GeckoView extends FrameLayo
             final AutofillValue value = values.valueAt(i);
             if (value.isText()) {
                 // Only text is currently supported.
                 strValues.put(values.keyAt(i), value.getTextValue());
             }
         }
         mSession.getTextInput().autofill(strValues);
     }
+
+    /**
+     * Request a {@link Bitmap} of the visible portion of the web page currently being
+     * rendered.
+     *
+     * See {@link GeckoDisplay#capturePixels} for more details.
+     *
+     * @return A {@link GeckoResult} that completes with a {@link Bitmap} containing
+     * the pixels and size information of the currently visible rendered web page.
+     */
+    @UiThread
+    public @NonNull GeckoResult<Bitmap> capturePixels() {
+        return mDisplay.capturePixels();
+    }
 }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md
@@ -68,16 +68,28 @@ exclude: true
 [67.1]: ../GeckoSession.html#getDefaultUserAgent--
 [67.2]: ../GeckoVRManager.html
 
 - Initial WebExtension support. [`GeckoRuntime#registerWebExtension`][67.15]
   allows embedders to register a local web extension.
 
 [67.15]: ../GeckoRuntime.html#registerWebExtension-org.mozilla.geckoview.WebExtension-
 
+- Added API to [`GeckoView`][65.5] to take screenshot of the visible page. Calling [`capturePixels`][67.16] returns a ['GeckoResult'][65.25] that completes to a [`Bitmap`][67.17] of the current [`Surface`][67.18] contents, or an [`IllegalStateException`][67.19] if the [`GeckoSession`][65.9] is not ready to render content.
+
+[67.16]: ../GeckoView.html#capturePixels
+[67.17]: https://developer.android.com/reference/android/graphics/Bitmap
+[67.18]: https://developer.android.com/reference/android/view/Surface
+[67.19]: https://developer.android.com/reference/java/lang/IllegalStateException
+
+- Added API to capture a screenshot to [`GeckoDisplay`][67.20]. [`capturePixels`][67.21] returns a ['GeckoResult'][65.25] that completes to a [`Bitmap`][67.16] of the current [`Surface`][67.17] contents, or an [`IllegalStateException`][67.18] if the [`GeckoSession`][65.9] is not ready to render content.
+
+[67.20]: ../GeckoDisplay.html
+[67.21]: ../GeckoDisplay.html#capturePixels
+
 ## v66
 - Removed redundant field `trackingMode` from [`SecurityInformation`][66.6].
   Use `TrackingProtectionDelegate.onTrackerBlocked` for notification of blocked
   elements during page load.
 
 [66.6]: ../GeckoSession.ProgressDelegate.SecurityInformation.html
 
 - Added [`@NonNull`][66.1] or [`@Nullable`][66.2] to all APIs.
@@ -187,9 +199,9 @@ exclude: true
 [65.23]: ../GeckoSession.FinderResult.html
 
 - Update [`CrashReporter#sendCrashReport`][65.24] to return the crash ID as a
   [`GeckoResult<String>`][65.25].
 
 [65.24]: ../CrashReporter.html#sendCrashReport-android.content.Context-android.os.Bundle-java.lang.String-
 [65.25]: ../GeckoResult.html
 
-[api-version]: df89fa914cfd095b37e696f3e767b55fc03a4835
+[api-version]: 0bcb9f0f763b746bb6f27f5d275c351818ab971b
--- a/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java
+++ b/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java
@@ -38,17 +38,16 @@ import android.view.MenuItem;
 import android.view.View;
 import android.view.WindowManager;
 import android.widget.ProgressBar;
 
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
-import java.util.Arrays;
 import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.Locale;
 
 public class GeckoViewActivity extends AppCompatActivity {
     private static final String LOGTAG = "GeckoViewActivity";
     private static final String DEFAULT_URL = "about:blank";
     private static final String USE_MULTIPROCESS_EXTRA = "use_multiprocess";
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/FennecNativeDriver.java
+++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/FennecNativeDriver.java
@@ -8,38 +8,39 @@ import java.io.BufferedOutputStream;
 import java.io.BufferedReader;
 import java.io.DataOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.nio.ByteBuffer;
 import java.nio.IntBuffer;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.lang.StringBuffer;
 import java.lang.Math;
 
-import org.mozilla.geckoview.CompositorController;
 import org.mozilla.gecko.gfx.PanningPerfAPI;
 import org.mozilla.gecko.util.BundleEventListener;
 import org.mozilla.gecko.util.EventCallback;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.StrictModeContext;
 import org.mozilla.geckoview.GeckoView;
 
 import android.app.Activity;
+import android.graphics.Bitmap;
 import android.util.Log;
 import android.view.View;
 
 import com.robotium.solo.Solo;
 
-public class FennecNativeDriver implements Driver, CompositorController.GetPixelsCallback {
+public class FennecNativeDriver implements Driver {
     private static final int FRAME_TIME_THRESHOLD = 25;     // allow 25ms per frame (40fps)
 
     private final Activity mActivity;
     private final Solo mSolo;
     private final String mRootPath;
 
     private static String mLogFile;
     private static LogLevel mLogLevel = LogLevel.INFO;
@@ -186,25 +187,20 @@ public class FennecNativeDriver implemen
             for (final View v : mSolo.getViews()) {
                 log(LogLevel.WARN, "  View: " + v);
             }
         }
         return geckoView;
     }
 
     private volatile boolean mGotPixelsResult;
-    private int mPixelsWidth;
-    private int mPixelsHeight;
-    private IntBuffer mPixelsResult;
+    private Bitmap mPixelsResult;
 
-    @Override
-    public synchronized void onPixelsResult(int aWidth, int aHeight, IntBuffer aPixels) {
-        mPixelsWidth = aWidth;
-        mPixelsHeight = aHeight;
-        mPixelsResult = aPixels;
+    public synchronized void onPixelsResult(final Bitmap aResult) {
+        mPixelsResult = aResult;
         mGotPixelsResult = true;
         notifyAll();
     }
 
     private static final int COLOR_DEVIATION = 3;
 
     // Due to anti-aliasing, border pixels can be blended. This should filter them out.
     private static boolean differentColor(final int c1, final int c2) {
@@ -254,68 +250,62 @@ public class FennecNativeDriver implemen
         final GeckoView view = getSurfaceView();
         if (view == null) {
             return null;
         }
 
         view.post(new Runnable() {
             @Override
             public void run() {
-                view.getSession().getCompositorController().getPixels(FennecNativeDriver.this);
+                view.capturePixels().then( value -> {
+                    FennecNativeDriver.this.onPixelsResult(value);
+                    return null;
+                });
             }
         });
 
         synchronized (this) {
             while (!mGotPixelsResult) {
                 try {
                     wait();
                 } catch (InterruptedException ie) {
                 }
             }
         }
 
-        final IntBuffer pixelBuffer = mPixelsResult;
-        int w = mPixelsWidth;
-        int h = mPixelsHeight;
+        final ByteBuffer pixelBuffer = ByteBuffer.allocate(mPixelsResult.getByteCount());
+        mPixelsResult.copyPixelsToBuffer(pixelBuffer);
+        int w = mPixelsResult.getWidth();
+        int h = mPixelsResult.getHeight();
 
         mGotPixelsResult = false;
-        mPixelsWidth = 0;
-        mPixelsHeight = 0;
         mPixelsResult = null;
 
 
         if ((pixelBuffer == null) || (w == 0) || (h == 0)) {
             return null;
         }
 
         // The page used in robocop tests is a grid of different colored squares.
         // The function will log the color of each square found in the screen capture.
         // This allows the screen capture to be examined in the log output in a human
         // readable format.
         // logPixels(pixelBuffer, w, h);
-
-        // now we need to (1) flip the image, because GL likes to do things up-side-down,
-        // and (2) rearrange the bits from AGBR-8888 to ARGB-8888.
+        
         pixelBuffer.position(0);
         String mapFile = mRootPath + "/pixels.map";
 
         FileOutputStream fos = null;
         BufferedOutputStream bos = null;
         DataOutputStream dos = null;
         try {
             fos = new FileOutputStream(mapFile);
             bos = new BufferedOutputStream(fos);
             dos = new DataOutputStream(bos);
-
-            for (int y = h - 1; y >= 0; y--) {
-                for (int x = 0; x < w; x++) {
-                    int agbr = pixelBuffer.get();
-                    dos.writeInt((agbr & 0xFF00FF00) | ((agbr >> 16) & 0x000000FF) | ((agbr << 16) & 0x00FF0000));
-                }
-            }
+            dos.write(pixelBuffer.array());
         } catch (IOException e) {
             throw new RoboCopException("exception with pixel writer on file: " + mapFile);
         } finally {
             try {
                 if (dos != null) {
                     dos.flush();
                     dos.close();
                 }
--- a/security/manager/ssl/nsCryptoHash.cpp
+++ b/security/manager/ssl/nsCryptoHash.cpp
@@ -33,19 +33,16 @@ static const uint64_t STREAM_BUFFER_SIZE
 nsCryptoHash::nsCryptoHash() : mHashContext(nullptr), mInitialized(false) {}
 
 NS_IMPL_ISUPPORTS(nsCryptoHash, nsICryptoHash)
 
 NS_IMETHODIMP
 nsCryptoHash::Init(uint32_t algorithm) {
   HASH_HashType hashType;
   switch (algorithm) {
-    case nsICryptoHash::MD2:
-      hashType = HASH_AlgMD2;
-      break;
     case nsICryptoHash::MD5:
       hashType = HASH_AlgMD5;
       break;
     case nsICryptoHash::SHA1:
       hashType = HASH_AlgSHA1;
       break;
     case nsICryptoHash::SHA256:
       hashType = HASH_AlgSHA256;
@@ -80,18 +77,16 @@ nsCryptoHash::Init(uint32_t algorithm) {
 
   HASH_Begin(mHashContext.get());
   mInitialized = true;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCryptoHash::InitWithString(const nsACString &aAlgorithm) {
-  if (aAlgorithm.LowerCaseEqualsLiteral("md2")) return Init(nsICryptoHash::MD2);
-
   if (aAlgorithm.LowerCaseEqualsLiteral("md5")) return Init(nsICryptoHash::MD5);
 
   if (aAlgorithm.LowerCaseEqualsLiteral("sha1"))
     return Init(nsICryptoHash::SHA1);
 
   if (aAlgorithm.LowerCaseEqualsLiteral("sha256"))
     return Init(nsICryptoHash::SHA256);
 
--- a/security/manager/ssl/nsICryptoHash.idl
+++ b/security/manager/ssl/nsICryptoHash.idl
@@ -11,20 +11,21 @@ interface nsIInputStream;
  */
 
 [builtinclass, scriptable, uuid(1e5b7c43-4688-45ce-92e1-77ed931e3bbe)]
 interface nsICryptoHash : nsISupports
 {
     /**
      * Hashing Algorithms.  These values are to be used by the
      * |init| method to indicate which hashing function to
-     * use.  These values map directly onto the values defined
-     * in mozilla/security/nss/lib/cryptohi/hasht.h.
+     * use.  These values must be identical to the values defined
+     * in security/nss/lib/util/hasht.h in type HASH_HashType.
+     * This allows us to use NSS mapping functions like
+     * HASH_GetHashOidTagByHashType with these values.
      */
-    const short MD2    = 1;  /* String value: "md2"    */
     const short MD5    = 2;  /* String value: "md5"    */
     const short SHA1   = 3;  /* String value: "sha1"   */
     const short SHA256 = 4;  /* String value: "sha256" */
     const short SHA384 = 5;  /* String value: "sha384" */
     const short SHA512 = 6;  /* String value: "sha512" */
 
     /**
      * Initialize the hashing object. This method may be
--- a/security/manager/ssl/tests/unit/test_hash_algorithms.js
+++ b/security/manager/ssl/tests/unit/test_hash_algorithms.js
@@ -4,28 +4,16 @@
 // of the supported algorithms.
 
 const messages = [
   "The quick brown fox jumps over the lazy dog",
   "",
 ];
 const ALGORITHMS = [
   {
-    initString: "md2",
-    initConstant: Ci.nsICryptoHash.MD2,
-    hexHashes: [
-      "03d85a0d629d2c442e987525319fc471",
-      "8350e5a3e24c153df2275c9f80692773",
-    ],
-    b64Hashes: [
-      "A9haDWKdLEQumHUlMZ/EcQ==",
-      "g1Dlo+JMFT3yJ1yfgGkncw==",
-    ],
-  },
-  {
     initString: "md5",
     initConstant: Ci.nsICryptoHash.MD5,
     hexHashes: [
       "9e107d9d372bb6826bd81d3542a419d6",
       "d41d8cd98f00b204e9800998ecf8427e",
     ],
     b64Hashes: [
       "nhB9nTcrtoJr2B01QqQZ1g==",
--- a/testing/geckodriver/CHANGES.md
+++ b/testing/geckodriver/CHANGES.md
@@ -4,16 +4,25 @@ Change log
 All notable changes to this program is documented in this file.
 
 
 Unreleased
 ----------
 
 ### Removed
 
+- Dropped support for legacy Selenium web element references
+
+  The legacy way of serialising web elements, using `{"ELEMENT": <UUID>}`,
+  has been removed in this release.  This may break older Selenium
+  clients and clients which are otherwise not compatible with the
+  WebDriver standard.
+
+  Thanks to Shivam Singhal for this patch.
+
 - Removed `--webdriver-port` command-line option
 
   `--webdriver-port <PORT>` was an undocumented alias for `--port`,
   initially used for backwards compatibility with clients
   prior to Selenium 3.0.0.
 
 ### Changed
 
--- a/testing/geckodriver/src/command.rs
+++ b/testing/geckodriver/src/command.rs
@@ -9,17 +9,16 @@ use std::fs::File;
 use std::io::prelude::*;
 use uuid::Uuid;
 use webdriver::command::{WebDriverCommand, WebDriverExtensionCommand};
 use webdriver::common::WebElement;
 use webdriver::error::{ErrorStatus, WebDriverError, WebDriverResult};
 use webdriver::httpapi::WebDriverExtensionRoute;
 
 pub const CHROME_ELEMENT_KEY: &'static str = "chromeelement-9fc5-4b51-a3c8-01716eedeb04";
-pub const LEGACY_ELEMENT_KEY: &'static str = "ELEMENT";
 
 pub fn extension_routes() -> Vec<(Method, &'static str, GeckoExtensionRoute)> {
     return vec![
         (
             Method::GET,
             "/session/{sessionId}/moz/context",
             GeckoExtensionRoute::GetContext,
         ),
--- a/testing/geckodriver/src/marionette.rs
+++ b/testing/geckodriver/src/marionette.rs
@@ -1,12 +1,11 @@
 use crate::command::{
     AddonInstallParameters, AddonUninstallParameters, GeckoContextParameters,
     GeckoExtensionCommand, GeckoExtensionRoute, XblLocatorParameters, CHROME_ELEMENT_KEY,
-    LEGACY_ELEMENT_KEY,
 };
 use mozprofile::preferences::Pref;
 use mozprofile::profile::Profile;
 use mozrunner::runner::{FirefoxProcess, FirefoxRunner, Runner, RunnerProcess};
 use serde::de::{self, Deserialize, Deserializer};
 use serde::ser::{Serialize, Serializer};
 use serde_json::{self, Map, Value};
 use std::error::Error;
@@ -388,22 +387,20 @@ impl MarionetteSession {
             json_data.as_object(),
             ErrorStatus::UnknownError,
             "Failed to convert data to an object"
         );
 
         let chrome_element = data.get(CHROME_ELEMENT_KEY);
         let element = data.get(ELEMENT_KEY);
         let frame = data.get(FRAME_KEY);
-        let legacy_element = data.get(LEGACY_ELEMENT_KEY);
         let window = data.get(WINDOW_KEY);
 
         let value = try_opt!(
             element
-                .or(legacy_element)
                 .or(chrome_element)
                 .or(frame)
                 .or(window),
             ErrorStatus::UnknownError,
             "Failed to extract web element from Marionette response"
         );
         let id = try_opt!(
             value.as_str(),
--- a/testing/mochitest/leaks.py
+++ b/testing/mochitest/leaks.py
@@ -16,19 +16,17 @@ class ShutdownLeaks(object):
     DOM windows (that are still around after test suite shutdown, despite running
     the GC) to the tests that created them and prints leak statistics.
     """
 
     def __init__(self, logger):
         self.logger = logger
         self.tests = []
         self.leakedWindows = {}
-        self.hiddenWindowsCount = 0
         self.leakedDocShells = set()
-        self.hiddenDocShellsCount = 0
         self.currentTest = None
         self.seenShutdown = set()
 
     def log(self, message):
         action = message['action']
 
         # Remove 'log' when clipboard is gone and/or structured.
         if action in ('log', 'process_output'):
@@ -76,29 +74,16 @@ class ShutdownLeaks(object):
                                   "shutdown" %
                                   (test["fileName"], len(test["leakedDocShells"])))
                 failures += 1
                 self.logger.info("TEST-INFO | %s | docShell(s) leaked: %s" %
                                  (test["fileName"], ', '.join(["[pid = %s] [id = %s]" %
                                                                x for x in test["leakedDocShells"]]
                                                               )))
 
-            if test["hiddenWindowsCount"] > 0:
-                # Note: to figure out how many hidden windows were created, we divide
-                # this number by 2, because 1 hidden window creation implies in
-                # 1 outer window + 1 inner window.
-                self.logger.info(
-                    "TEST-INFO | %s | This test created %d hidden window(s)"
-                    % (test["fileName"], test["hiddenWindowsCount"] / 2))
-
-            if test["hiddenDocShellsCount"] > 0:
-                self.logger.info(
-                    "TEST-INFO | %s | This test created %d hidden docshell(s)"
-                    % (test["fileName"], test["hiddenDocShellsCount"]))
-
         return failures
 
     def _logWindow(self, line):
         created = line[:2] == "++"
         pid = self._parseValue(line, "pid")
         serial = self._parseValue(line, "serial")
 
         # log line has invalid format
@@ -111,21 +96,17 @@ class ShutdownLeaks(object):
 
         if self.currentTest:
             windows = self.currentTest["windows"]
             if created:
                 windows.add(key)
             else:
                 windows.discard(key)
         elif int(pid) in self.seenShutdown and not created:
-            url = self._parseValue(line, "url")
-            if not self._isHiddenWindowURL(url):
-                self.leakedWindows[key] = url
-            else:
-                self.hiddenWindowsCount += 1
+            self.leakedWindows[key] = self._parseValue(line, "url")
 
     def _logDocShell(self, line):
         created = line[:2] == "++"
         pid = self._parseValue(line, "pid")
         id = self._parseValue(line, "id")
 
         # log line has invalid format
         if not pid or not id:
@@ -137,65 +118,55 @@ class ShutdownLeaks(object):
 
         if self.currentTest:
             docShells = self.currentTest["docShells"]
             if created:
                 docShells.add(key)
             else:
                 docShells.discard(key)
         elif int(pid) in self.seenShutdown and not created:
-            url = self._parseValue(line, "url")
-            if not self._isHiddenWindowURL(url):
-                self.leakedDocShells.add(key)
-            else:
-                self.hiddenDocShellsCount += 1
+            self.leakedDocShells.add(key)
 
     def _parseValue(self, line, name):
         match = re.search("\[%s = (.+?)\]" % name, line)
         if match:
             return match.group(1)
         return None
 
     def _parseLeakingTests(self):
         leakingTests = []
 
         for test in self.tests:
             leakedWindows = [
                 id for id in test["windows"] if id in self.leakedWindows]
             test["leakedWindows"] = [self.leakedWindows[id]
                                      for id in leakedWindows]
-            test["hiddenWindowsCount"] = self.hiddenWindowsCount
             test["leakedWindowsString"] = ', '.join(
                 ["[pid = %s] [serial = %s]" % x for x in leakedWindows])
             test["leakedDocShells"] = [
                 id for id in test["docShells"] if id in self.leakedDocShells]
-            test["hiddenDocShellsCount"] = self.hiddenDocShellsCount
             test["leakCount"] = len(
                 test["leakedWindows"]) + len(test["leakedDocShells"])
 
-            if test["leakCount"] or test["hiddenWindowsCount"] or test["hiddenDocShellsCount"]:
+            if test["leakCount"]:
                 leakingTests.append(test)
 
         return sorted(leakingTests, key=itemgetter("leakCount"), reverse=True)
 
     def _zipLeakedWindows(self, leakedWindows):
         counts = []
         counted = set()
 
         for url in leakedWindows:
             if url not in counted:
                 counts.append((url, leakedWindows.count(url)))
                 counted.add(url)
 
         return sorted(counts, key=itemgetter(1), reverse=True)
 
-    def _isHiddenWindowURL(self, url):
-        return (url == "resource://gre-resources/hiddenWindow.html" or  # Win / Linux
-                url == "chrome://browser/content/hiddenWindow.xul")     # Mac
-
 
 class LSANLeaks(object):
 
     """
     Parses the log when running an LSAN build, looking for interesting stack frames
     in allocation stacks, and prints out reports.
     """
 
--- a/testing/webdriver/src/actions.rs
+++ b/testing/webdriver/src/actions.rs
@@ -924,17 +924,16 @@ mod test {
     }
 
     #[test]
     fn test_json_pointer_action_move_with_origin_webelement_and_legacy_element() {
         let json = r#"{
             "type":"pointerMove",
             "duration":100,
             "origin":{
-                "ELEMENT":"elem",
                 "element-6066-11e4-a52e-4f735466cecf":"elem"
             },
             "x":5,
             "y":10
         }"#;
         let data = PointerAction::Move(PointerMoveAction {
             duration: Some(100),
             origin: PointerOrigin::Element(WebElement { id: "elem".into() }),
@@ -946,17 +945,17 @@ mod test {
     }
 
     #[test]
     fn test_json_pointer_action_move_with_origin_only_legacy_element() {
         let json = r#"{
             "type":"pointerMove",
             "duration":100,
             "origin":{
-                "ELEMENT":"elem"
+                "element-6066-11e4-a52e-4f735466cecf":"elem"
             },
             "x":5,
             "y":10
         }"#;
 
         assert!(serde_json::from_str::<PointerOrigin>(&json).is_err());
     }
 
--- a/toolkit/modules/Timer.jsm
+++ b/toolkit/modules/Timer.jsm
@@ -4,23 +4,22 @@
 
 "use strict";
 
 /**
  * JS module implementation of setTimeout and clearTimeout.
  */
 
 var EXPORTED_SYMBOLS = ["setTimeout", "setTimeoutWithTarget", "clearTimeout",
-                        "setInterval", "setIntervalWithTarget", "clearInterval",
-                        "requestIdleCallback", "cancelIdleCallback"];
+                        "setInterval", "setIntervalWithTarget", "clearInterval"];
 
 // This gives us >=2^30 unique timer IDs, enough for 1 per ms for 12.4 days.
 var gNextId = 1; // setTimeout and setInterval must return a positive integer
 
-var gTimerTable = new Map(); // int -> nsITimer or idleCallback
+var gTimerTable = new Map(); // int -> nsITimer
 
 // Don't generate this for every timer.
 var setTimeout_timerCallbackQI = ChromeUtils.generateQI([Ci.nsITimerCallback, Ci.nsINamed]);
 
 function _setTimeoutOrIsInterval(aCallback, aMilliseconds, aIsInterval,
                                  aTarget, aArgs) {
   if (typeof aCallback !== "function") {
     throw new Error(`callback is not a function in ${aIsInterval ? "setInterval" : "setTimeout"}`);
@@ -82,32 +81,8 @@ function setIntervalWithTarget(aCallback
 }
 
 var clearInterval = this.clearTimeout = function clearTimeout(aId) {
   if (gTimerTable.has(aId)) {
     gTimerTable.get(aId).cancel();
     gTimerTable.delete(aId);
   }
 };
-
-function requestIdleCallback(aCallback, aOptions) {
-  if (typeof aCallback !== "function") {
-    throw new Error("callback is not a function in requestIdleCallback");
-  }
-  let id = gNextId++;
-
-  let callback = (...aArgs) => {
-    if (gTimerTable.has(id)) {
-      gTimerTable.delete(id);
-      aCallback(...aArgs);
-    }
-  };
-
-  ChromeUtils.idleDispatch(callback, aOptions);
-  gTimerTable.set(id, callback);
-  return id;
-}
-
-function cancelIdleCallback(aId) {
-  if (gTimerTable.has(aId)) {
-    gTimerTable.delete(aId);
-  }
-}
--- a/toolkit/modules/tests/xpcshell/test_timer.js
+++ b/toolkit/modules/tests/xpcshell/test_timer.js
@@ -58,22 +58,21 @@ add_task(async function test_setInterval
   Assert.ok(interval1 > 0, "setTimeout returns a positive number");
 
   imported.clearInterval(interval1);
 
   const EXPECTED_CALLS = 5;
   let calls = 0;
 
   await new Promise((resolve) => {
-    let interval2 = imported.setInterval((param1, param2) => {
+    imported.setInterval((param1, param2) => {
       Assert.ok(true, "Should be called");
       Assert.equal(param1, 15, "first parameter is correct");
       Assert.equal(param2, "hola", "second parameter is correct");
       if (calls >= EXPECTED_CALLS) {
-        imported.clearInterval(interval2);
         resolve();
       }
       calls++;
     }, 100, 15, "hola");
   });
 });
 
 add_task(async function test_setIntervalWithTarget() {
@@ -84,22 +83,21 @@ add_task(async function test_setInterval
   Assert.ok(interval1 > 0, "setTimeout returns a positive number");
 
   imported.clearInterval(interval1);
 
   const EXPECTED_CALLS = 5;
   let calls = 0;
 
   await new Promise((resolve) => {
-    let interval2 = imported.setIntervalWithTarget((param1, param2) => {
+    imported.setIntervalWithTarget((param1, param2) => {
       Assert.ok(true, "Should be called");
       Assert.equal(param1, 15, "first parameter is correct");
       Assert.equal(param2, "hola", "second parameter is correct");
       if (calls >= EXPECTED_CALLS) {
-        imported.clearInterval(interval2);
         resolve();
       }
       calls++;
     }, 100, target, 15, "hola");
   });
 });
 
 add_task(async function test_setTimeoutNonFunction() {
@@ -116,29 +114,8 @@ add_task(async function test_setTimeoutW
   Assert.throws(() => { imported.setTimeoutWithTarget({}, 0); },
                 /callback is not a function in setTimeout/);
 });
 
 add_task(async function test_setIntervalWithTargetNonFunction() {
   Assert.throws(() => { imported.setIntervalWithTarget({}, 0); },
                 /callback is not a function in setInterval/);
 });
-
-add_task(async function test_requestIdleCallback() {
-  let request1 = imported.requestIdleCallback(() => do_throw("Should not be called"));
-  Assert.equal(typeof request1, "number", "requestIdleCallback returns a number");
-  Assert.ok(request1 > 0, "setTimeout returns a positive number");
-
-  imported.cancelIdleCallback(request1);
-
-  await new Promise((resolve) => {
-    let request2 = imported.requestIdleCallback((deadline) => {
-      Assert.ok(true, "Should be called");
-      Assert.equal(typeof deadline.didTimeout, "boolean", "deadline parameter has .didTimeout property");
-      Assert.equal(typeof deadline.timeRemaining(), "number", "deadline parameter has .timeRemaining() function");
-      resolve();
-    }, {timeout: 100});
-
-    Assert.equal(typeof request2, "number", "requestIdleCallback returns a number");
-    Assert.ok(request2 > 0, "requestIdleCallback returns a positive number");
-    Assert.notEqual(request1, request2, "Calling requestIdleCallback again returns a different value");
-  });
-});
--- a/toolkit/themes/shared/extensions/extensions.inc.css
+++ b/toolkit/themes/shared/extensions/extensions.inc.css
@@ -469,16 +469,20 @@ button.warning {
 .warning,
 .pending,
 .error {
   margin-inline-start: 48px;
   font-weight: bold;
   -moz-box-align: center;
 }
 
+.addon-view[type="theme"] .icon:-moz-locale-dir(rtl) {
+  transform: scaleX(-1);
+}
+
 .content-container {
   -moz-box-align: center;
 }
 
 #updates-list > .addon > .content-container,
 .basicinfo-container {
   -moz-box-align: start;
 }
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -4605,26 +4605,18 @@ nsresult XREMain::XRE_mainRun() {
   SaveToEnv("XRE_PROFILE_PATH=");
   SaveToEnv("XRE_PROFILE_LOCAL_PATH=");
   SaveToEnv("XRE_START_OFFLINE=");
   SaveToEnv("XUL_APP_FILE=");
   SaveToEnv("XRE_BINARY_PATH=");
   SaveToEnv("XRE_RESTARTED_BY_PROFILE_MANAGER=");
 
   if (!mShuttingDown) {
-#ifdef XP_MACOSX
-    bool lazyHiddenWindow = false;
-#else
-    bool lazyHiddenWindow = Preferences::GetBool("toolkit.lazyHiddenWindow", false);
-#endif
-
-    if (!lazyHiddenWindow) {
-      rv = appStartup->CreateHiddenWindow();
-      NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
-    }
+    rv = appStartup->CreateHiddenWindow();
+    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
 
 #ifdef XP_WIN
     Preferences::RegisterCallbackAndCall(RegisterApplicationRestartChanged,
                                          PREF_WIN_REGISTER_APPLICATION_RESTART);
 #  if defined(MOZ_LAUNCHER_PROCESS)
     SetupLauncherProcessPref();
 #  endif  // defined(MOZ_LAUNCHER_PROCESS)
 #endif
--- a/tools/lint/eslint/modules.json
+++ b/tools/lint/eslint/modules.json
@@ -220,17 +220,17 @@
   "SyncedBookmarksMirror.jsm": ["SyncedBookmarksMirror"],
   "tabs.js": ["TabEngine", "TabSetRecord"],
   "tabs.jsm": ["BrowserTabs"],
   "tcpsocket_test.jsm": ["createSocket", "createServer", "enablePrefsAndPermissions", "socketCompartmentInstanceOfArrayBuffer"],
   "telemetry.js": ["SyncTelemetry"],
   "test.jsm": ["Foo"],
   "test2.jsm": ["Bar"],
   "test_bug883784.jsm": ["Test"],
-  "Timer.jsm": ["setTimeout", "setTimeoutWithTarget", "clearTimeout", "setInterval", "setIntervalWithTarget", "clearInterval", "requestIdleCallback", "cancelIdleCallback"],
+  "Timer.jsm": ["setTimeout", "setTimeoutWithTarget", "clearTimeout", "setInterval", "setIntervalWithTarget", "clearInterval"],
   "TippyTopProvider.jsm": ["TippyTopProvider", "getDomain"],
   "Tokenize.jsm": ["tokenize", "toksToTfIdfVector"],
   "tokenserverclient.js": ["TokenServerClient", "TokenServerClientError", "TokenServerClientNetworkError", "TokenServerClientServerError"],
   "ToolboxProcess.jsm": ["BrowserToolboxProcess"],
   "tps.jsm": ["ACTIONS", "Addons", "Addresses", "Bookmarks", "CreditCards", "Formdata", "History", "Passwords", "Prefs", "Tabs", "TPS", "Windows"],
   "Translation.jsm": ["Translation", "TranslationTelemetry"],
   "Traversal.jsm": ["TraversalRules", "TraversalHelper"],
   "UpdateTelemetry.jsm": ["AUSTLMY"],
new file mode 100644
--- /dev/null
+++ b/widget/android/bindings/AndroidGraphics-classes.txt
@@ -0,0 +1,10 @@
+[android.graphics.Bitmap = skip:true]
+copyPixelsFromBuffer(Ljava/nio/Buffer;)V =
+createBitmap(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap; =
+
+[android.graphics.Bitmap$Config = skip:true]
+valueOf(Ljava/lang/String;)Landroid/graphics/Bitmap$Config; =
+ALPHA_8 =
+ARGB_8888 =
+RGBA_F16 =
+RGB_565 =
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/widget/android/bindings/JavaExceptions-classes.txt
@@ -0,0 +1,2 @@
+[java.lang.IllegalStateException = skip:true]
+<init>(Ljava/lang/String;)V =
\ No newline at end of file
--- a/widget/android/bindings/moz.build
+++ b/widget/android/bindings/moz.build
@@ -7,20 +7,22 @@
 with Files("**"):
     BUG_COMPONENT = ("Firefox for Android", "Graphics, Panning and Zooming")
 
 # List of stems to generate .cpp and .h files for.  To add a stem, add it to
 # this list and ensure that $(stem)-classes.txt exists in this directory.
 generated = [
     'AccessibilityEvent',
     'AndroidBuild',
+    'AndroidGraphics',
     'AndroidInputType',
     'AndroidRect',
     'InetAddress',
     'JavaBuiltins',
+    'JavaExceptions',
     'KeyEvent',
     'MediaCodec',
     'MotionEvent',
     'SurfaceTexture',
     'ViewConfiguration'
 ]
 
 SOURCES += ['!%s.cpp' % stem for stem in generated]
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -3,40 +3,46 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <android/log.h>
 #include <android/native_window.h>
 #include <android/native_window_jni.h>
 #include <math.h>
+#include <queue>
 #include <unistd.h>
 
 #include "mozilla/MiscEvents.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/TouchEvents.h"
 #include "mozilla/TypeTraits.h"
 #include "mozilla/WeakPtr.h"
 #include "mozilla/WheelHandlingHelper.h"  // for WheelDeltaAdjustmentStrategy
 
 #include "mozilla/a11y/SessionAccessibility.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/MouseEventBinding.h"
 #include "mozilla/Unused.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/layers/RenderTrace.h"
+#include "mozilla/gfx/DataSurfaceHelpers.h"
+#include "mozilla/gfx/2D.h"
 #include <algorithm>
 
 using mozilla::Unused;
 using mozilla::dom::ContentChild;
 using mozilla::dom::ContentParent;
 
 #include "nsWindow.h"
 
+#include "AndroidGraphics.h"
+#include "JavaExceptions.h"
+
 #include "nsIBaseWindow.h"
 #include "nsIBrowserDOMWindow.h"
 #include "nsIDOMChromeWindow.h"
 #include "nsIObserverService.h"
 #include "nsISupportsPrimitives.h"
 #include "nsIWidgetListener.h"
 #include "nsIWindowWatcher.h"
 #include "nsIXULWindow.h"
@@ -792,16 +798,17 @@ nsresult nsWindow::AndroidView::GetInitD
 class nsWindow::LayerViewSupport final
     : public GeckoSession::Compositor::Natives<LayerViewSupport> {
   using LockedWindowPtr = WindowPtr<LayerViewSupport>::Locked;
 
   WindowPtr<LayerViewSupport> mWindow;
   GeckoSession::Compositor::WeakRef mCompositor;
   Atomic<bool, ReleaseAcquire> mCompositorPaused;
   jni::Object::GlobalRef mSurface;
+  std::queue<java::GeckoResult::GlobalRef> mCapturePixelsResults;
 
   // In order to use Event::HasSameTypeAs in PostTo(), we cannot make
   // LayerViewEvent a template because each template instantiation is
   // a different type. So implement LayerViewEvent as a ProxyEvent.
   class LayerViewEvent final : public nsAppShell::ProxyEvent {
     using Event = nsAppShell::Event;
 
    public:
@@ -852,17 +859,21 @@ class nsWindow::LayerViewSupport final
     if (RefPtr<nsThread> uiThread = GetAndroidUiThread()) {
       GeckoSession::Compositor::GlobalRef compositor(mCompositor);
       if (!compositor) {
         return;
       }
 
       uiThread->Dispatch(NS_NewRunnableFunction(
           "LayerViewSupport::OnDetach",
-          [compositor, disposer = RefPtr<Runnable>(aDisposer)] {
+          [compositor, disposer = RefPtr<Runnable>(aDisposer), result = &mCapturePixelsResults] {
+            while (!result->empty()) {
+              result->front()->CompleteExceptionally(java::sdk::IllegalStateException::New("The compositor has detached from the session").Cast<jni::Throwable>());
+              result->pop();
+            }
             compositor->OnCompositorDetached();
             disposer->Run();
           }));
     }
   }
 
   const GeckoSession::Compositor::Ref& GetJavaCompositor() const {
     return mCompositor;
@@ -877,16 +888,37 @@ class nsWindow::LayerViewSupport final
   GetUiCompositorControllerChild() {
     RefPtr<UiCompositorControllerChild> child;
     if (LockedWindowPtr window{mWindow}) {
       child = window->GetUiCompositorControllerChild();
     }
     return child.forget();
   }
 
+  int8_t* FlipScreenPixels(Shmem& aMem, const ScreenIntSize& aSize) {
+    const IntSize size(aSize.width, aSize.height);
+    RefPtr<DataSourceSurface> image = gfx::CreateDataSourceSurfaceFromData(
+      size, 
+      SurfaceFormat::B8G8R8A8, 
+      aMem.get<uint8_t>(), 
+      StrideForFormatAndWidth(SurfaceFormat::B8G8R8A8, aSize.width));
+    RefPtr<DrawTarget> drawTarget = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
+      size, SurfaceFormat::B8G8R8A8);
+    drawTarget->SetTransform(Matrix::Scaling(1.0, -1.0) * Matrix::Translation(0, aSize.height));
+      
+    gfx::Rect drawRect(0, 0, aSize.width, aSize.height);
+    drawTarget->DrawSurface(image, drawRect, drawRect);
+
+    RefPtr<SourceSurface> snapshot = drawTarget->Snapshot();
+    RefPtr<DataSourceSurface> data = snapshot->GetDataSurface();
+    DataSourceSurface::ScopedMap* smap =
+      new DataSourceSurface::ScopedMap(data, DataSourceSurface::READ);
+    return reinterpret_cast<int8_t*>(smap->GetData());
+  }
+
   /**
    * Compositor methods
    */
  public:
   void AttachNPZC(jni::Object::Param aNPZC) {
     MOZ_ASSERT(NS_IsMainThread());
     if (!mWindow) {
       return;  // Already shut down.
@@ -1073,38 +1105,47 @@ class nsWindow::LayerViewSupport final
   void SetDefaultClearColor(int32_t aColor) {
     MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
     if (RefPtr<UiCompositorControllerChild> child =
             GetUiCompositorControllerChild()) {
       child->SetDefaultClearColor((uint32_t)aColor);
     }
   }
 
-  void RequestScreenPixels() {
+  void RequestScreenPixels(jni::Object::Param aResult) {
     MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
-    if (RefPtr<UiCompositorControllerChild> child =
-            GetUiCompositorControllerChild()) {
-      child->RequestScreenPixels();
+    mCapturePixelsResults.push(java::GeckoResult::GlobalRef(java::GeckoResult::LocalRef(aResult)));
+    if (mCapturePixelsResults.size() == 1) {
+      if (RefPtr<UiCompositorControllerChild> child =
+              GetUiCompositorControllerChild()) {
+        child->RequestScreenPixels();
+      }
     }
-  }
+  } 
 
   void RecvScreenPixels(Shmem&& aMem, const ScreenIntSize& aSize) {
     MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
-
-    auto pixels =
-        mozilla::jni::IntArray::New(aMem.get<int>(), aMem.Size<int>());
-    auto compositor = GeckoSession::Compositor::LocalRef(mCompositor);
-    if (compositor) {
-      compositor->RecvScreenPixels(aSize.width, aSize.height, pixels);
+    auto aResult = java::GeckoResult::LocalRef(mCapturePixelsResults.front());
+    if (aResult) {
+      auto pixels =
+          mozilla::jni::ByteBuffer::New(FlipScreenPixels(aMem, aSize), aMem.Size<int8_t>());
+      auto bitmap = java::sdk::Bitmap::CreateBitmap(aSize.width, aSize.height, java::sdk::Config::ARGB_8888());
+      bitmap->CopyPixelsFromBuffer(pixels);
+      aResult->Complete(bitmap);
+      mCapturePixelsResults.pop();
     }
 
     // Pixels have been copied, so Dealloc Shmem
     if (RefPtr<UiCompositorControllerChild> child =
             GetUiCompositorControllerChild()) {
       child->DeallocPixelBuffer(aMem);
+
+      if (!mCapturePixelsResults.empty()) {
+        child->RequestScreenPixels();
+      }
     }
   }
 
   void EnableLayerUpdateNotifications(bool aEnable) {
     MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
     if (RefPtr<UiCompositorControllerChild> child =
             GetUiCompositorControllerChild()) {
       child->EnableLayerUpdateNotifications(aEnable);
--- a/widget/windows/WindowsUIUtils.cpp
+++ b/widget/windows/WindowsUIUtils.cpp
@@ -13,22 +13,19 @@
 
 #include "nsIObserverService.h"
 #include "nsIBaseWindow.h"
 #include "nsIDocShell.h"
 #include "nsIAppShellService.h"
 #include "nsAppShellCID.h"
 #include "nsIXULWindow.h"
 #include "mozilla/Services.h"
-#include "mozilla/WidgetUtils.h"
 #include "mozilla/WindowsVersion.h"
 #include "nsString.h"
 #include "nsIWidget.h"
-#include "nsIWindowMediator.h"
-#include "nsPIDOMWindow.h"
 
 /* mingw currently doesn't support windows.ui.viewmanagement.h, so we disable it
  * until it's fixed. */
 #ifndef __MINGW32__
 
 #  include <windows.ui.viewmanagement.h>
 
 #  pragma comment(lib, "runtimeobject.lib")
@@ -134,41 +131,37 @@ WindowsUIUtils::GetInTabletMode(bool* aR
 
 NS_IMETHODIMP
 WindowsUIUtils::UpdateTabletModeState() {
 #ifndef __MINGW32__
   if (!IsWin10OrLater()) {
     return NS_OK;
   }
 
-  nsresult rv;
-  nsCOMPtr<nsIWindowMediator> winMediator(
-      do_GetService(NS_WINDOWMEDIATOR_CONTRACTID, &rv));
+  nsCOMPtr<nsIAppShellService> appShell(
+      do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
+  nsCOMPtr<nsIXULWindow> hiddenWindow;
+
+  nsresult rv = appShell->GetHiddenWindow(getter_AddRefs(hiddenWindow));
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  nsCOMPtr<nsIWidget> widget;
-  nsCOMPtr<mozIDOMWindowProxy> navWin;
-
-  rv = winMediator->GetMostRecentWindow(u"navigator:browser",
-                                        getter_AddRefs(navWin));
-  if (NS_FAILED(rv) || !navWin) {
-    // Fall back to the hidden window
-    nsCOMPtr<nsIAppShellService> appShell(
-      do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
-
-    rv = appShell->GetHiddenDOMWindow(getter_AddRefs(navWin));
-    if (NS_FAILED(rv) || !navWin) {
-      return rv;
-    }
+  nsCOMPtr<nsIDocShell> docShell;
+  rv = hiddenWindow->GetDocShell(getter_AddRefs(docShell));
+  if (NS_FAILED(rv) || !docShell) {
+    return rv;
   }
 
-  nsPIDOMWindowOuter* win = nsPIDOMWindowOuter::From(navWin);
-  widget = widget::WidgetUtils::DOMWindowToWidget(win);
+  nsCOMPtr<nsIBaseWindow> baseWindow(do_QueryInterface(docShell));
+
+  if (!baseWindow) return NS_ERROR_FAILURE;
+
+  nsCOMPtr<nsIWidget> widget;
+  baseWindow->GetMainWidget(getter_AddRefs(widget));
 
   if (!widget) return NS_ERROR_FAILURE;
 
   HWND winPtr = (HWND)widget->GetNativeData(NS_NATIVE_WINDOW);
   ComPtr<IUIViewSettingsInterop> uiViewSettingsInterop;
 
   HRESULT hr = GetActivationFactory(
       HStringReference(RuntimeClass_Windows_UI_ViewManagement_UIViewSettings)
--- a/xpfe/appshell/nsAppShellService.cpp
+++ b/xpfe/appshell/nsAppShellService.cpp
@@ -86,25 +86,19 @@ nsAppShellService::CreateHiddenWindow() 
 }
 
 NS_IMETHODIMP
 nsAppShellService::SetScreenId(uint32_t aScreenId) {
   mScreenId = aScreenId;
   return NS_OK;
 }
 
-void nsAppShellService::EnsureHiddenWindow() {
-  if (!mHiddenWindow) {
-    CreateHiddenWindowHelper(/* aIsPrivate = */ false);
-  }
-}
-
 void nsAppShellService::EnsurePrivateHiddenWindow() {
   if (!mHiddenPrivateWindow) {
-    CreateHiddenWindowHelper(/* aIsPrivate = */ true);
+    CreateHiddenWindowHelper(true);
   }
 }
 
 nsresult nsAppShellService::CreateHiddenWindowHelper(bool aIsPrivate) {
   nsresult rv;
   int32_t initialHeight = 100, initialWidth = 100;
 
 #ifdef XP_MACOSX
@@ -720,29 +714,23 @@ nsresult nsAppShellService::JustCreateTo
 
   return rv;
 }
 
 NS_IMETHODIMP
 nsAppShellService::GetHiddenWindow(nsIXULWindow** aWindow) {
   NS_ENSURE_ARG_POINTER(aWindow);
 
-  EnsureHiddenWindow();
-
   *aWindow = mHiddenWindow;
   NS_IF_ADDREF(*aWindow);
   return *aWindow ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsAppShellService::GetHiddenDOMWindow(mozIDOMWindowProxy** aWindow) {
-  NS_ENSURE_ARG_POINTER(aWindow);
-
-  EnsureHiddenWindow();
-
   nsresult rv;
   nsCOMPtr<nsIDocShell> docShell;
   NS_ENSURE_TRUE(mHiddenWindow, NS_ERROR_FAILURE);
 
   rv = mHiddenWindow->GetDocShell(getter_AddRefs(docShell));
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
 
@@ -759,42 +747,32 @@ nsAppShellService::GetHiddenPrivateWindo
 
   *aWindow = mHiddenPrivateWindow;
   NS_IF_ADDREF(*aWindow);
   return *aWindow ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsAppShellService::GetHiddenPrivateDOMWindow(mozIDOMWindowProxy** aWindow) {
-  NS_ENSURE_ARG_POINTER(aWindow);
-
   EnsurePrivateHiddenWindow();
 
   nsresult rv;
   nsCOMPtr<nsIDocShell> docShell;
   NS_ENSURE_TRUE(mHiddenPrivateWindow, NS_ERROR_FAILURE);
 
   rv = mHiddenPrivateWindow->GetDocShell(getter_AddRefs(docShell));
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
 
   nsCOMPtr<nsPIDOMWindowOuter> hiddenPrivateDOMWindow(docShell->GetWindow());
   hiddenPrivateDOMWindow.forget(aWindow);
   return *aWindow ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
-nsAppShellService::GetHasHiddenWindow(bool* aHasHiddenWindow) {
-  NS_ENSURE_ARG_POINTER(aHasHiddenWindow);
-
-  *aHasHiddenWindow = !!mHiddenWindow;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsAppShellService::GetHasHiddenPrivateWindow(bool* aHasPrivateWindow) {
   NS_ENSURE_ARG_POINTER(aHasPrivateWindow);
 
   *aHasPrivateWindow = !!mHiddenPrivateWindow;
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/xpfe/appshell/nsAppShellService.h
+++ b/xpfe/appshell/nsAppShellService.h
@@ -31,17 +31,16 @@ class nsAppShellService final : public n
   NS_DECL_NSIOBSERVER
 
   nsAppShellService();
 
  protected:
   ~nsAppShellService();
 
   nsresult CreateHiddenWindowHelper(bool aIsPrivate);
-  void EnsureHiddenWindow();
   void EnsurePrivateHiddenWindow();
 
   nsresult JustCreateTopWindow(nsIXULWindow *aParent, nsIURI *aUrl,
                                uint32_t aChromeMask, int32_t aInitialWidth,
                                int32_t aInitialHeight, bool aIsHiddenWindow,
                                nsITabParent *aOpeningTab,
                                mozIDOMWindowProxy *aOpenerWindow,
                                nsWebShellWindow **aResult);
--- a/xpfe/appshell/nsIAppShellService.idl
+++ b/xpfe/appshell/nsIAppShellService.idl
@@ -128,21 +128,16 @@ interface nsIAppShellService : nsISuppor
    * Remove a window from the application's window registry. Note that
    * this method won't automatically attempt to quit the app when
    * the last window is unregistered. For that, see Quit().
    * @param aWindow you see the pattern
    */
   void unregisterTopLevelWindow(in nsIXULWindow aWindow);
 
   /**
-   * Whether the hidden window has been lazily created.
-   */
-  readonly attribute boolean hasHiddenWindow;
-
-  /**
    * Whether the hidden private window has been lazily created.
    */
   [noscript]
   readonly attribute boolean hasHiddenPrivateWindow;
 
   /**
    * Start/stop tracking lags in the event loop.
    * If the event loop gets unresponsive, a "event-loop-lag" notification
--- a/xpfe/appshell/nsXULWindow.cpp
+++ b/xpfe/appshell/nsXULWindow.cpp
@@ -457,38 +457,29 @@ NS_IMETHODIMP nsXULWindow::Destroy() {
 
 #if defined(XP_WIN)
   // We need to explicitly set the focus on Windows, but
   // only if the parent is visible.
   nsCOMPtr<nsIBaseWindow> parent(do_QueryReferent(mParentWindow));
   if (parent) {
     nsCOMPtr<nsIWidget> parentWidget;
     parent->GetMainWidget(getter_AddRefs(parentWidget));
-
-    if (parentWidget && parentWidget->IsVisible()) {
-      bool isParentHiddenWindow = false;
-
+    if (!parentWidget || parentWidget->IsVisible()) {
+      nsCOMPtr<nsIBaseWindow> baseHiddenWindow;
       if (appShell) {
-        bool hasHiddenWindow = false;
-        appShell->GetHasHiddenWindow(&hasHiddenWindow);
-        if (hasHiddenWindow) {
-          nsCOMPtr<nsIBaseWindow> baseHiddenWindow;
-          nsCOMPtr<nsIXULWindow> hiddenWindow;
-          appShell->GetHiddenWindow(getter_AddRefs(hiddenWindow));
-          if (hiddenWindow) {
-            baseHiddenWindow = do_GetInterface(hiddenWindow);
-            isParentHiddenWindow = (baseHiddenWindow == parent);
-          }
-        }
+        nsCOMPtr<nsIXULWindow> hiddenWindow;
+        appShell->GetHiddenWindow(getter_AddRefs(hiddenWindow));
+        if (hiddenWindow) baseHiddenWindow = do_GetInterface(hiddenWindow);
       }
-
       // somebody screwed up somewhere. hiddenwindow shouldn't be anybody's
       // parent. still, when it happens, skip activating it.
-      if (!isParentHiddenWindow) {
-        parentWidget->PlaceBehind(eZPlacementTop, 0, true);
+      if (baseHiddenWindow != parent) {
+        nsCOMPtr<nsIWidget> parentWidget;
+        parent->GetMainWidget(getter_AddRefs(parentWidget));
+        if (parentWidget) parentWidget->PlaceBehind(eZPlacementTop, 0, true);
       }
     }
   }
 #endif
 
   RemoveTooltipSupport();
 
   mDOMWindow = nullptr;