Merge inbound to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Wed, 17 Aug 2016 16:38:41 -0700
changeset 309839 a70835fe9f55c040d4d044b5c55f658d2e399dae
parent 309736 b25d09b7fab57ddb82f14916a443eb34fda31137 (current diff)
parent 309838 73a588493cbeef5fac53a63420ad82c5fac52185 (diff)
child 309864 97a52326b06a07930216ebefa5af333271578904
push id30570
push userkwierso@gmail.com
push dateWed, 17 Aug 2016 23:38:48 +0000
treeherdermozilla-central@a70835fe9f55 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone51.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 inbound to central, a=merge
dom/base/StructuredCloneHolder.cpp
dom/base/StructuredCloneHolder.h
dom/broadcastchannel/BroadcastChannel.cpp
dom/indexedDB/ActorsParent.cpp
dom/indexedDB/IDBObjectStore.cpp
dom/indexedDB/IndexedDatabaseInlines.h
dom/ipc/StructuredCloneData.h
dom/messagechannel/SharedMessagePortMessage.h
dom/presentation/PresentationSessionInfo.cpp
js/public/StructuredClone.h
js/src/builtin/TestingFunctions.cpp
js/src/vm/StructuredClone.cpp
layout/style/nsRuleNode.cpp
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
mobile/android/app/mobile.js
mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
mobile/android/base/moz.build
mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoEvent.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/JavaPanZoomController.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/Layer.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/PluginLayer.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/ScrollbarLayer.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/TextureGenerator.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/TextureReaper.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/TouchEventHandler.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/VirtualLayer.java
mobile/android/themes/core/scrollbar-nonapz.css
modules/libpref/init/all.js
old-configure.in
testing/web-platform/meta/html/semantics/embedded-content/the-img-element/invalid-src.html.ini
widget/android/GeneratedJNIWrappers.cpp
widget/android/GeneratedJNIWrappers.h
--- a/accessible/base/nsAccessibilityService.cpp
+++ b/accessible/base/nsAccessibilityService.cpp
@@ -1285,16 +1285,18 @@ nsAccessibilityService::Init()
 #endif
 
   gIsShutdown = false;
 
   // Now its safe to start platform accessibility.
   if (XRE_IsParentProcess())
     PlatformInit();
 
+  statistics::A11yInitialized();
+
   return true;
 }
 
 void
 nsAccessibilityService::Shutdown()
 {
   // Application is going to be closed, shutdown accessibility and mark
   // accessibility service as shutdown to prevent calls of its methods.
--- a/b2g/confvars.sh
+++ b/b2g/confvars.sh
@@ -14,29 +14,20 @@ MOZ_B2G_VERSION=2.6.0.0-prerelease
 MOZ_B2G_OS_NAME=Boot2Gecko
 
 MOZ_BRANDING_DIRECTORY=b2g/branding/unofficial
 MOZ_OFFICIAL_BRANDING_DIRECTORY=b2g/branding/official
 # MOZ_APP_DISPLAYNAME is set by branding/configure.sh
 
 MOZ_SAFE_BROWSING=1
 
-MOZ_WEBSMS_BACKEND=1
 MOZ_NO_SMART_CARDS=1
 MOZ_APP_STATIC_INI=1
-NSS_DISABLE_DBM=1
 MOZ_NO_EV_CERTS=1
 
-MOZ_WEBSPEECH=1
-if test -n "$NIGHTLY_BUILD"; then
-MOZ_WEBSPEECH_MODELS=1
-MOZ_WEBSPEECH_POCKETSPHINX=1
-fi
-MOZ_WEBSPEECH_TEST_BACKEND=1
-
 if test "$OS_TARGET" = "Android"; then
 MOZ_CAPTURE=1
 MOZ_RAW=1
 MOZ_AUDIO_CHANNEL_MANAGER=1
 fi
 
 # use custom widget for html:select
 MOZ_USE_NATIVE_POPUP_WINDOWS=1
--- a/b2g/graphene/confvars.sh
+++ b/b2g/graphene/confvars.sh
@@ -22,21 +22,19 @@ MOZ_B2G_OS_NAME=Boot2Gecko
 
 MOZ_BRANDING_DIRECTORY=b2g/branding/unofficial
 MOZ_OFFICIAL_BRANDING_DIRECTORY=b2g/branding/official
 # MOZ_APP_DISPLAYNAME is set by branding/configure.sh
 
 MOZ_SAFE_BROWSING=1
 MOZ_CAPTIVEDETECT=1
 
-MOZ_WEBSMS_BACKEND=1
 MOZ_NO_SMART_CARDS=1
 MOZ_APP_STATIC_INI=1
 NSS_NO_LIBPKIX=1
-NSS_DISABLE_DBM=1
 
 if test "$OS_TARGET" = "Android"; then
 MOZ_CAPTURE=1
 MOZ_RAW=1
 MOZ_AUDIO_CHANNEL_MANAGER=1
 fi
 
 MOZ_APP_ID={d1bfe7d9-c01e-4237-998b-7b5f960a4314}
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -880,16 +880,27 @@ addEventListener("DOMContentLoaded", fun
         .QueryInterface(Ci.nsIInterfaceRequestor)
         .getInterface(Ci.nsIXULWindow)
         .XULBrowserWindow = window.XULBrowserWindow;
   window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow =
     new nsBrowserAccess();
 
   let initBrowser =
     document.getAnonymousElementByAttribute(gBrowser, "anonid", "initialBrowser");
+
+  // The window's first argument is a tab if and only if we are swapping tabs.
+  // We must set the browser's usercontextid before updateBrowserRemoteness(),
+  // so that the newly created remote tab child has the correct usercontextid.
+  if (window.arguments) {
+    let tabToOpen = window.arguments[0];
+    if (tabToOpen instanceof XULElement && tabToOpen.hasAttribute("usercontextid")) {
+      initBrowser.setAttribute("usercontextid", tabToOpen.getAttribute("usercontextid"));
+    }
+  }
+
   gBrowser.updateBrowserRemoteness(initBrowser, gMultiProcessBrowser);
 });
 
 var gBrowserInit = {
   delayedStartupFinished: false,
 
   onLoad: function() {
     gBrowser.addEventListener("DOMUpdatePageReport", gPopupBlockerObserver, false);
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -4212,17 +4212,16 @@ var SessionStoreInternal = {
    *
    * @param aTab
    *        The tab that will be "reset"
    */
   _resetLocalTabRestoringState: function (aTab) {
     NS_ASSERT(aTab.linkedBrowser.__SS_restoreState,
               "given tab is not restoring");
 
-    let window = aTab.ownerGlobal;
     let browser = aTab.linkedBrowser;
 
     // Keep the tab's previous state for later in this method
     let previousState = browser.__SS_restoreState;
 
     // The browser is no longer in any sort of restoring state.
     delete browser.__SS_restoreState;
 
--- a/browser/themes/shared/identity-block/identity-block.inc.css
+++ b/browser/themes/shared/identity-block/identity-block.inc.css
@@ -19,21 +19,23 @@
   font-size: .9em;
   padding: 3px 5px;
   overflow: hidden;
   /* The padding-left and padding-right transitions handle the delayed hiding of
      the forward button when hovered. */
   transition: padding-left, padding-right;
 }
 
-#urlbar[pageproxystate="valid"] > #identity-box.verifiedIdentity {
+#urlbar[pageproxystate="valid"] > #identity-box.verifiedIdentity > #connection-icon,
+#urlbar[pageproxystate="valid"] > #identity-box.verifiedIdentity > #identity-icon-labels {
   color: var(--identity-box-verified-color);
 }
 
-#urlbar[pageproxystate="valid"] > #identity-box.chromeUI {
+#urlbar[pageproxystate="valid"] > #identity-box.chromeUI > #connection-icon,
+#urlbar[pageproxystate="valid"] > #identity-box.chromeUI > #identity-icon-labels {
   color: var(--identity-box-chrome-color);
 }
 
 #identity-icon-labels:-moz-locale-dir(ltr) {
   padding-left: 2px;
 }
 
 #identity-icon-labels:-moz-locale-dir(rtl) {
@@ -43,17 +45,17 @@
 @conditionalForwardWithUrlbar@ > #forward-button[disabled] + #urlbar > #identity-box {
   padding-inline-start: calc(var(--backbutton-urlbar-overlap) + 5px);
 }
 
 @conditionalForwardWithUrlbar@:hover:not([switchingtabs]) > #forward-button[disabled] + #urlbar > #identity-box {
   /* Forward button hiding is delayed when hovered, so we should use the same
      delay for the identity box. We handle both horizontal paddings (for LTR and
      RTL), the latter two delays here are for padding-left and padding-right. */
-  transition-delay: 0s, 100s, 100s;
+  transition-delay: 100s, 100s;
 }
 
 @conditionalForwardWithUrlbar@:not(:hover) > #forward-button[disabled] + #urlbar > #identity-box {
   /* when not hovered anymore, trigger a new non-delayed transition to react to the forward button hiding */
   padding-inline-start: calc(var(--backbutton-urlbar-overlap) + 5.01px);
 }
 
 /* MAIN IDENTITY ICON */
--- a/build/moz.configure/old.configure
+++ b/build/moz.configure/old.configure
@@ -165,17 +165,16 @@ def old_configure_options(*options):
     '--enable-b2g-ril',
     '--enable-bundled-fonts',
     '--enable-clang-plugin',
     '--enable-content-sandbox',
     '--enable-cookies',
     '--enable-cpp-rtti',
     '--enable-crashreporter',
     '--enable-ctypes',
-    '--enable-dbm',
     '--enable-dbus',
     '--enable-debug-js-modules',
     '--enable-directshow',
     '--enable-dtrace',
     '--enable-dump-painting',
     '--enable-elf-hack',
     '--enable-extensions',
     '--enable-faststripe',
@@ -186,17 +185,16 @@ def old_configure_options(*options):
     '--enable-gio',
     '--enable-gnomeui',
     '--enable-gold',
     '--enable-hardware-aec-ns',
     '--enable-icf',
     '--enable-install-strip',
     '--enable-ion',
     '--enable-ios-target',
-    '--enable-ipdl-tests',
     '--enable-jitspew',
     '--enable-libjpeg-turbo',
     '--enable-libproxy',
     '--enable-llvm-hacks',
     '--enable-logrefcnt',
     '--enable-maintenance-service',
     '--enable-memory-sanitizer',
     '--enable-mobile-optimize',
@@ -211,17 +209,16 @@ def old_configure_options(*options):
     '--enable-oom-breakpoint',
     '--enable-optimize',
     '--enable-parental-controls',
     '--enable-permissions',
     '--enable-pie',
     '--enable-png-arm-neon-support',
     '--enable-posix-nspr-emulation',
     '--enable-pref-extensions',
-    '--enable-printing',
     '--enable-pulseaudio',
     '--enable-raw',
     '--enable-readline',
     '--enable-reflow-perf',
     '--enable-release',
     '--enable-require-all-d3dc-versions',
     '--enable-safe-browsing',
     '--enable-sandbox',
@@ -230,36 +227,32 @@ def old_configure_options(*options):
     '--enable-skia',
     '--enable-skia-gpu',
     '--enable-small-chunk-size',
     '--enable-startup-notification',
     '--enable-startupcache',
     '--enable-stdcxx-compat',
     '--enable-strip',
     '--enable-synth-pico',
-    '--enable-synth-speechd',
     '--enable-system-cairo',
     '--enable-system-extension-dirs',
     '--enable-system-ffi',
     '--enable-system-pixman',
     '--enable-system-sqlite',
     '--enable-tasktracer',
     '--enable-thread-sanitizer',
     '--enable-trace-logging',
     '--enable-tree-freetype',
     '--enable-ui-locale',
     '--enable-universalchardet',
     '--enable-updater',
     '--enable-url-classifier',
     '--enable-valgrind',
     '--enable-verify-mar',
     '--enable-webrtc',
-    '--enable-websms-backend',
-    '--enable-webspeech',
-    '--enable-webspeechtestbackend',
     '--enable-xul',
     '--enable-zipwriter',
     '--includedir',
     '--libdir',
     '--no-create',
     '--prefix',
     '--with-android-cxx-stl',
     '--with-android-distribution-directory',
@@ -313,18 +306,16 @@ def old_configure_options(*options):
     '--x-includes',
     '--x-libraries',
 
     # Below are the configure flags used by comm-central.
     '--enable-ldap',
     '--enable-mapi',
     '--enable-calendar',
     '--enable-incomplete-external-linkage',
-
-    '--enable-ipc-fuzzer',
 )
 @imports(_from='__builtin__', _import='compile')
 @imports(_from='__builtin__', _import='open')
 @imports('logging')
 @imports('os')
 @imports('subprocess')
 @imports('sys')
 @imports(_from='mozbuild.shellutil', _import='quote')
--- a/devtools/client/responsive.html/manager.js
+++ b/devtools/client/responsive.html/manager.js
@@ -1,25 +1,31 @@
 /* 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 { Ci } = require("chrome");
 const promise = require("promise");
 const { Task } = require("devtools/shared/task");
 const EventEmitter = require("devtools/shared/event-emitter");
-const { TouchEventSimulator } = require("devtools/shared/touch/simulator");
 const { getOwnerWindow } = require("sdk/tabs/utils");
 const { startup } = require("sdk/window/helpers");
 const message = require("./utils/message");
 const { swapToInnerBrowser } = require("./browser/swap");
+const { EmulationFront } = require("devtools/shared/fronts/emulation");
 
 const TOOL_URL = "chrome://devtools/content/responsive.html/index.xhtml";
 
+loader.lazyRequireGetter(this, "DebuggerClient",
+                         "devtools/shared/client/main", true);
+loader.lazyRequireGetter(this, "DebuggerServer",
+                         "devtools/server/main", true);
+
 /**
  * ResponsiveUIManager is the external API for the browser UI, etc. to use when
  * opening and closing the responsive UI.
  *
  * While the HTML UI is in an experimental stage, the older ResponsiveUIManager
  * from devtools/client/responsivedesign/responsivedesign.jsm delegates to this
  * object when the pref "devtools.responsive.html.enabled" is true.
  */
@@ -244,21 +250,16 @@ ResponsiveUI.prototype = {
    * design tool.  It is safe to reference this window directly even with e10s,
    * as the tool UI is always loaded in the parent process.  The web content
    * contained *within* the tool UI on the other hand is loaded in the child
    * process.
    */
   toolWindow: null,
 
   /**
-   * Touch event simulator.
-   */
-  touchEventSimulator: null,
-
-  /**
    * Open RDM while preserving the state of the page.  We use `swapFrameLoaders`
    * to ensure all in-page state is preserved, just like when you move a tab to
    * a new window.
    *
    * For more details, see /devtools/docs/responsive-design-mode.md.
    */
   init: Task.async(function* () {
     let ui = this;
@@ -279,17 +280,18 @@ ResponsiveUI.prototype = {
         return ui.getViewportBrowser();
       })
     });
     yield this.swap.start();
 
     // Notify the inner browser to start the frame script
     yield message.request(this.toolWindow, "start-frame-script");
 
-    this.touchEventSimulator = new TouchEventSimulator(this.getViewportBrowser());
+    // Get the protocol ready to speak with emulation actor
+    yield this.connectToServer();
   }),
 
   /**
    * Close RDM and restore page content back into a regular tab.
    *
    * @param object
    *        Destroy options, which currently includes a `reason` string.
    * @return boolean
@@ -310,43 +312,60 @@ ResponsiveUI.prototype = {
     // Ensure init has finished before starting destroy
     if (!isTabClosing) {
       yield this.inited;
     }
 
     this.tab.removeEventListener("TabClose", this);
     this.toolWindow.removeEventListener("message", this);
 
-    // Stop the touch event simulator if it was running
     if (!isTabClosing) {
-      yield this.touchEventSimulator.stop();
-    }
+      // Stop the touch event simulator if it was running
+      yield this.emulationFront.clearTouchEventsOverride();
 
-    // Notify the inner browser to stop the frame script
-    if (!isTabClosing) {
+      // Notify the inner browser to stop the frame script
       yield message.request(this.toolWindow, "stop-frame-script");
     }
 
     // Destroy local state
     let swap = this.swap;
     this.browserWindow = null;
     this.tab = null;
     this.inited = null;
     this.toolWindow = null;
-    this.touchEventSimulator = null;
     this.swap = null;
 
+    // Close the debugger client used to speak with emulation actor
+    let clientClosed = new Promise((resolve, reject) => {
+      this.client.close(resolve);
+      this.client = this.emulationFront = null;
+    });
+    if (!isTabClosing) {
+      yield clientClosed;
+    }
+
     // Undo the swap and return the content back to a normal tab
     swap.stop();
 
     this.destroyed = true;
 
     return true;
   }),
 
+  connectToServer: Task.async(function* () {
+    if (!DebuggerServer.initialized) {
+      DebuggerServer.init();
+      DebuggerServer.addBrowserActors();
+    }
+    this.client = new DebuggerClient(DebuggerServer.connectPipe());
+    yield this.client.connect();
+    let { tab } = yield this.client.getTab();
+    this.emulationFront = EmulationFront(this.client, tab);
+  }),
+
   handleEvent(event) {
     let { browserWindow, tab } = this;
 
     switch (event.type) {
       case "message":
         this.handleMessage(event);
         break;
       case "TabClose":
@@ -379,22 +398,24 @@ ResponsiveUI.prototype = {
         let { enabled } = event.data;
         this.updateTouchSimulation(enabled);
         break;
     }
   },
 
   updateTouchSimulation: Task.async(function* (enabled) {
     if (enabled) {
-      let reloadNeeded = yield this.touchEventSimulator.start();
+      let reloadNeeded = yield this.emulationFront.setTouchEventsOverride(
+        Ci.nsIDocShell.TOUCHEVENTS_OVERRIDE_ENABLED
+      );
       if (reloadNeeded) {
         this.getViewportBrowser().reload();
       }
     } else {
-      this.touchEventSimulator.stop();
+      this.emulationFront.clearTouchEventsOverride();
     }
   }),
 
   /**
    * Helper for tests. Assumes a single viewport for now.
    */
   getViewportSize() {
     return this.toolWindow.getViewportSize();
--- a/devtools/client/responsive.html/test/browser/browser_shutdown_close_sync.js
+++ b/devtools/client/responsive.html/test/browser/browser_shutdown_close_sync.js
@@ -2,37 +2,49 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Verify RDM closes synchronously when tabs are closed.
 
 const TEST_URL = "http://example.com/";
 
+function waitForClientClose(ui) {
+  return new Promise(resolve => {
+    info("RDM's debugger client is now closed");
+    ui.client.addOneTimeListener("closed", resolve);
+  });
+}
+
 add_task(function* () {
   let tab = yield addTab(TEST_URL);
 
   let { ui } = yield openRDM(tab);
+  let clientClosed = waitForClientClose(ui);
 
   closeRDM(tab, {
     reason: "TabClose",
   });
 
   // This flag is set at the end of `ResponsiveUI.destroy`.  If it is true
   // without yielding on `closeRDM` above, then we must have closed
   // synchronously.
   is(ui.destroyed, true, "RDM closed synchronously");
 
+  yield clientClosed;
   yield removeTab(tab);
 });
 
 add_task(function* () {
   let tab = yield addTab(TEST_URL);
 
   let { ui } = yield openRDM(tab);
+  let clientClosed = waitForClientClose(ui);
 
   yield removeTab(tab);
 
   // This flag is set at the end of `ResponsiveUI.destroy`.  If it is true without
   // yielding on `closeRDM` itself and only removing the tab, then we must have closed
   // synchronously in response to tab closing.
   is(ui.destroyed, true, "RDM closed synchronously");
+
+  yield clientClosed;
 });
new file mode 100644
--- /dev/null
+++ b/devtools/server/actors/emulation.js
@@ -0,0 +1,62 @@
+/* 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 { Ci } = require("chrome");
+const protocol = require("devtools/shared/protocol");
+const { emulationSpec } = require("devtools/shared/specs/emulation");
+const { SimulatorCore } = require("devtools/shared/touch/simulator-core");
+
+let EmulationActor = protocol.ActorClass(emulationSpec, {
+  initialize(conn, tabActor) {
+    protocol.Actor.prototype.initialize.call(this, conn);
+    this.docShell = tabActor.docShell;
+    this.simulatorCore = new SimulatorCore(tabActor.chromeEventHandler);
+  },
+
+  _previousTouchEventsOverride: null,
+
+  setTouchEventsOverride(flag) {
+    if (this.docShell.touchEventsOverride == flag) {
+      return false;
+    }
+    if (this._previousTouchEventsOverride === null) {
+      this._previousTouchEventsOverride = this.docShell.touchEventsOverride;
+    }
+
+    // Start or stop the touch simulator depending on the override flag
+    if (flag == Ci.nsIDocShell.TOUCHEVENTS_OVERRIDE_ENABLED) {
+      this.simulatorCore.start();
+    } else {
+      this.simulatorCore.stop();
+    }
+
+    this.docShell.touchEventsOverride = flag;
+    return true;
+  },
+
+  getTouchEventsOverride() {
+    return this.docShell.touchEventsOverride;
+  },
+
+  clearTouchEventsOverride() {
+    if (this._previousTouchEventsOverride !== null) {
+      this.setTouchEventsOverride(this._previousTouchEventsOverride);
+    }
+  },
+
+  disconnect() {
+    this.destroy();
+  },
+
+  destroy() {
+    this.clearTouchEventsOverride();
+    this.docShell = null;
+    this.simulatorCore = null;
+    protocol.Actor.prototype.destroy.call(this);
+  },
+});
+
+exports.EmulationActor = EmulationActor;
--- a/devtools/server/actors/moz.build
+++ b/devtools/server/actors/moz.build
@@ -21,16 +21,17 @@ DevToolsModules(
     'childtab.js',
     'chrome.js',
     'common.js',
     'css-properties.js',
     'csscoverage.js',
     'device.js',
     'director-manager.js',
     'director-registry.js',
+    'emulation.js',
     'environment.js',
     'errordocs.js',
     'eventlooplag.js',
     'frame.js',
     'framerate.js',
     'gcli.js',
     'heap-snapshot-file.js',
     'highlighters.css',
--- a/devtools/server/main.js
+++ b/devtools/server/main.js
@@ -564,16 +564,21 @@ var DebuggerServer = {
       constructor: "PromisesActor",
       type: { tab: true }
     });
     this.registerModule("devtools/server/actors/performance-entries", {
       prefix: "performanceEntries",
       constructor: "PerformanceEntriesActor",
       type: { tab: true }
     });
+    this.registerModule("devtools/server/actors/emulation", {
+      prefix: "emulation",
+      constructor: "EmulationActor",
+      type: { tab: true }
+    });
   },
 
   /**
    * Passes a set of options to the BrowserAddonActors for the given ID.
    *
    * @param id string
    *        The ID of the add-on to pass the options to
    * @param options object
new file mode 100644
--- /dev/null
+++ b/devtools/shared/fronts/emulation.js
@@ -0,0 +1,24 @@
+/* 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 { Front, FrontClass } = require("devtools/shared/protocol");
+const { emulationSpec } = require("devtools/shared/specs/emulation");
+
+/**
+ * The corresponding Front object for the EmulationActor.
+ */
+const EmulationFront = FrontClass(emulationSpec, {
+  initialize: function (client, form) {
+    Front.prototype.initialize.call(this, client);
+    this.actorID = form.emulationActor;
+    this.manage(this);
+  },
+
+  destroy: function () {
+    Front.prototype.destroy.call(this);
+  },
+});
+
+exports.EmulationFront = EmulationFront;
--- a/devtools/shared/fronts/moz.build
+++ b/devtools/shared/fronts/moz.build
@@ -10,16 +10,17 @@ DevToolsModules(
     'animation.js',
     'call-watcher.js',
     'canvas.js',
     'css-properties.js',
     'csscoverage.js',
     'device.js',
     'director-manager.js',
     'director-registry.js',
+    'emulation.js',
     'eventlooplag.js',
     'framerate.js',
     'gcli.js',
     'highlighters.js',
     'inspector.js',
     'layout.js',
     'memory.js',
     'performance-entries.js',
new file mode 100644
--- /dev/null
+++ b/devtools/shared/specs/emulation.js
@@ -0,0 +1,35 @@
+/* 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 { Arg, RetVal, generateActorSpec } = require("devtools/shared/protocol");
+
+const emulationSpec = generateActorSpec({
+  typeName: "emulation",
+
+  methods: {
+    setTouchEventsOverride: {
+      request: {
+        flag: Arg(0, "number")
+      },
+      response: {
+        reload: RetVal("boolean")
+      }
+    },
+
+    getTouchEventsOverride: {
+      request: {},
+      response: {
+        flag: RetVal("number")
+      }
+    },
+
+    clearTouchEventsOverride: {
+      request: {},
+      response: {}
+    },
+  }
+});
+
+exports.emulationSpec = emulationSpec;
--- a/devtools/shared/specs/moz.build
+++ b/devtools/shared/specs/moz.build
@@ -11,16 +11,17 @@ DevToolsModules(
     'breakpoint.js',
     'call-watcher.js',
     'canvas.js',
     'css-properties.js',
     'csscoverage.js',
     'device.js',
     'director-manager.js',
     'director-registry.js',
+    'emulation.js',
     'environment.js',
     'eventlooplag.js',
     'frame.js',
     'framerate.js',
     'gcli.js',
     'heap-snapshot-file.js',
     'highlighters.js',
     'inspector.js',
--- a/devtools/shared/touch/moz.build
+++ b/devtools/shared/touch/moz.build
@@ -1,10 +1,11 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DevToolsModules(
     'simulator-content.js',
+    'simulator-core.js',
     'simulator.js',
 )
--- a/devtools/shared/touch/simulator-content.js
+++ b/devtools/shared/touch/simulator-content.js
@@ -1,371 +1,43 @@
 /* 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/. */
-/* globals addMessageListener, sendAsyncMessage, addEventListener,
-   removeEventListener */
+ /* globals addMessageListener, sendAsyncMessage */
 "use strict";
 
-const { interfaces: Ci, utils: Cu } = Components;
-const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
-
-var systemAppOrigin = (function () {
-  let systemOrigin = "_";
-  try {
-    systemOrigin = Services.io.newURI(
-      Services.prefs.getCharPref("b2g.system_manifest_url"), null, null)
-      .prePath;
-  } catch (e) {
-    // Fall back to default value
-  }
-  return systemOrigin;
-})();
-
-var threshold = 25;
-try {
-  threshold = Services.prefs.getIntPref("ui.dragThresholdX");
-} catch (e) {
-  // Fall back to default value
-}
-
-var delay = 500;
-try {
-  delay = Services.prefs.getIntPref("ui.click_hold_context_menus.delay");
-} catch (e) {
-  // Fall back to default value
-}
+const { utils: Cu } = Components;
+const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
+const { SimulatorCore } = require("devtools/shared/touch/simulator-core");
 
 /**
- * Simulate touch events for platforms where they aren't generally available.
+ * Launches SimulatorCore in the content window to simulate touch events
  * This frame script is managed by `simulator.js`.
  */
+
 var simulator = {
-  events: [
-    "mousedown",
-    "mousemove",
-    "mouseup",
-    "touchstart",
-    "touchend",
-    "mouseenter",
-    "mouseover",
-    "mouseout",
-    "mouseleave"
-  ],
-
   messages: [
     "TouchEventSimulator:Start",
     "TouchEventSimulator:Stop",
   ],
 
-  contextMenuTimeout: null,
-
   init() {
+    this.simulatorCore = new SimulatorCore(docShell.chromeEventHandler);
     this.messages.forEach(msgName => {
       addMessageListener(msgName, this);
     });
   },
 
   receiveMessage(msg) {
     switch (msg.name) {
       case "TouchEventSimulator:Start":
-        this.start();
+        this.simulatorCore.start();
+        sendAsyncMessage("TouchEventSimulator:Started");
         break;
       case "TouchEventSimulator:Stop":
-        this.stop();
+        this.simulatorCore.stop();
+        sendAsyncMessage("TouchEventSimulator:Stopped");
         break;
     }
   },
-
-  start() {
-    this.events.forEach(evt => {
-      // Only listen trusted events to prevent messing with
-      // event dispatched manually within content documents
-      addEventListener(evt, this, true, false);
-    });
-    sendAsyncMessage("TouchEventSimulator:Started");
-  },
-
-  stop() {
-    this.events.forEach(evt => {
-      removeEventListener(evt, this, true);
-    });
-    sendAsyncMessage("TouchEventSimulator:Stopped");
-  },
-
-  handleEvent(evt) {
-    // The gaia system window use an hybrid system even on the device which is
-    // a mix of mouse/touch events. So let's not cancel *all* mouse events
-    // if it is the current target.
-    let content = this.getContent(evt.target);
-    if (!content) {
-      return;
-    }
-    let isSystemWindow = content.location.toString()
-                                .startsWith(systemAppOrigin);
-
-    // App touchstart & touchend should also be dispatched on the system app
-    // to match on-device behavior.
-    if (evt.type.startsWith("touch") && !isSystemWindow) {
-      let sysFrame = content.realFrameElement;
-      if (!sysFrame) {
-        return;
-      }
-      let sysDocument = sysFrame.ownerDocument;
-      let sysWindow = sysDocument.defaultView;
-
-      let touchEvent = sysDocument.createEvent("touchevent");
-      let touch = evt.touches[0] || evt.changedTouches[0];
-      let point = sysDocument.createTouch(sysWindow, sysFrame, 0,
-                                          touch.pageX, touch.pageY,
-                                          touch.screenX, touch.screenY,
-                                          touch.clientX, touch.clientY,
-                                          1, 1, 0, 0);
-
-      let touches = sysDocument.createTouchList(point);
-      let targetTouches = touches;
-      let changedTouches = touches;
-      touchEvent.initTouchEvent(evt.type, true, true, sysWindow, 0,
-                                false, false, false, false,
-                                touches, targetTouches, changedTouches);
-      sysFrame.dispatchEvent(touchEvent);
-      return;
-    }
-
-    // Ignore all but real mouse event coming from physical mouse
-    // (especially ignore mouse event being dispatched from a touch event)
-    if (evt.button ||
-        evt.mozInputSource != Ci.nsIDOMMouseEvent.MOZ_SOURCE_MOUSE ||
-        evt.isSynthesized) {
-      return;
-    }
-
-    let eventTarget = this.target;
-    let type = "";
-    switch (evt.type) {
-      case "mouseenter":
-      case "mouseover":
-      case "mouseout":
-      case "mouseleave":
-        // Don't propagate events which are not related to touch events
-        evt.stopPropagation();
-        break;
-
-      case "mousedown":
-        this.target = evt.target;
-
-        this.contextMenuTimeout =
-          this.sendContextMenu(evt.target, evt.pageX, evt.pageY);
-
-        this.cancelClick = false;
-        this.startX = evt.pageX;
-        this.startY = evt.pageY;
-
-        // Capture events so if a different window show up the events
-        // won't be dispatched to something else.
-        evt.target.setCapture(false);
-
-        type = "touchstart";
-        break;
-
-      case "mousemove":
-        if (!eventTarget) {
-          // Don't propagate mousemove event when touchstart event isn't fired
-          evt.stopPropagation();
-          return;
-        }
-
-        if (!this.cancelClick) {
-          if (Math.abs(this.startX - evt.pageX) > threshold ||
-              Math.abs(this.startY - evt.pageY) > threshold) {
-            this.cancelClick = true;
-            content.clearTimeout(this.contextMenuTimeout);
-          }
-        }
-
-        type = "touchmove";
-        break;
-
-      case "mouseup":
-        if (!eventTarget) {
-          return;
-        }
-        this.target = null;
-
-        content.clearTimeout(this.contextMenuTimeout);
-        type = "touchend";
-
-        // Only register click listener after mouseup to ensure
-        // catching only real user click. (Especially ignore click
-        // being dispatched on form submit)
-        if (evt.detail == 1) {
-          addEventListener("click", this, true, false);
-        }
-        break;
-
-      case "click":
-        // Mouse events has been cancelled so dispatch a sequence
-        // of events to where touchend has been fired
-        evt.preventDefault();
-        evt.stopImmediatePropagation();
-
-        removeEventListener("click", this, true, false);
-
-        if (this.cancelClick) {
-          return;
-        }
-
-        content.setTimeout(function dispatchMouseEvents(self) {
-          try {
-            self.fireMouseEvent("mousedown", evt);
-            self.fireMouseEvent("mousemove", evt);
-            self.fireMouseEvent("mouseup", evt);
-          } catch (e) {
-            console.error("Exception in touch event helper: " + e);
-          }
-        }, this.getDelayBeforeMouseEvent(evt), this);
-        return;
-    }
-
-    let target = eventTarget || this.target;
-    if (target && type) {
-      this.sendTouchEvent(evt, target, type);
-    }
-
-    if (!isSystemWindow) {
-      evt.preventDefault();
-      evt.stopImmediatePropagation();
-    }
-  },
-
-  fireMouseEvent(type, evt) {
-    let content = this.getContent(evt.target);
-    let utils = content.QueryInterface(Ci.nsIInterfaceRequestor)
-                       .getInterface(Ci.nsIDOMWindowUtils);
-    utils.sendMouseEvent(type, evt.clientX, evt.clientY, 0, 1, 0, true, 0,
-                         Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH);
-  },
-
-  sendContextMenu(target, x, y) {
-    let doc = target.ownerDocument;
-    let evt = doc.createEvent("MouseEvent");
-    evt.initMouseEvent("contextmenu", true, true, doc.defaultView,
-                       0, x, y, x, y, false, false, false, false,
-                       0, null);
-
-    let content = this.getContent(target);
-    let timeout = content.setTimeout((function contextMenu() {
-      target.dispatchEvent(evt);
-      this.cancelClick = true;
-    }).bind(this), delay);
-
-    return timeout;
-  },
-
-  sendTouchEvent(evt, target, name) {
-    function clone(obj) {
-      return Cu.cloneInto(obj, target);
-    }
-    // When running OOP b2g desktop, we need to send the touch events
-    // using the mozbrowser api on the unwrapped frame.
-    if (target.localName == "iframe" && target.mozbrowser === true) {
-      if (name == "touchstart") {
-        this.touchstartTime = Date.now();
-      } else if (name == "touchend") {
-        // If we have a "fast" tap, don't send a click as both will be turned
-        // into a click and that breaks eg. checkboxes.
-        if (Date.now() - this.touchstartTime < delay) {
-          this.cancelClick = true;
-        }
-      }
-      let unwrapped = XPCNativeWrapper.unwrap(target);
-      unwrapped.sendTouchEvent(name, clone([0]),       // event type, id
-                               clone([evt.clientX]),   // x
-                               clone([evt.clientY]),   // y
-                               clone([1]), clone([1]), // rx, ry
-                               clone([0]), clone([0]), // rotation, force
-                               1);                     // count
-      return;
-    }
-    let document = target.ownerDocument;
-    let content = this.getContent(target);
-    if (!content) {
-      return;
-    }
-
-    let touchEvent = document.createEvent("touchevent");
-    let point = document.createTouch(content, target, 0,
-                                     evt.pageX, evt.pageY,
-                                     evt.screenX, evt.screenY,
-                                     evt.clientX, evt.clientY,
-                                     1, 1, 0, 0);
-
-    let touches = document.createTouchList(point);
-    let targetTouches = touches;
-    let changedTouches = touches;
-    if (name === "touchend" || name === "touchcancel") {
-      // "touchend" and "touchcancel" events should not have the removed touch
-      // neither in touches nor in targetTouches
-      touches = targetTouches = document.createTouchList();
-    }
-
-    touchEvent.initTouchEvent(name, true, true, content, 0,
-                              false, false, false, false,
-                              touches, targetTouches, changedTouches);
-    target.dispatchEvent(touchEvent);
-  },
-
-  getContent(target) {
-    let win = (target && target.ownerDocument)
-      ? target.ownerDocument.defaultView
-      : null;
-    return win;
-  },
-
-  getDelayBeforeMouseEvent(evt) {
-    // On mobile platforms, Firefox inserts a 300ms delay between
-    // touch events and accompanying mouse events, except if the
-    // content window is not zoomable and the content window is
-    // auto-zoomed to device-width.
-
-    // If the preference dom.meta-viewport.enabled is set to false,
-    // we couldn't read viewport's information from getViewportInfo().
-    // So we always simulate 300ms delay when the
-    // dom.meta-viewport.enabled is false.
-    let savedMetaViewportEnabled =
-      Services.prefs.getBoolPref("dom.meta-viewport.enabled");
-    if (!savedMetaViewportEnabled) {
-      return 300;
-    }
-
-    let content = this.getContent(evt.target);
-    if (!content) {
-      return 0;
-    }
-
-    let utils = content.QueryInterface(Ci.nsIInterfaceRequestor)
-                       .getInterface(Ci.nsIDOMWindowUtils);
-
-    let allowZoom = {},
-        minZoom = {},
-        maxZoom = {},
-        autoSize = {};
-
-    utils.getViewportInfo(content.innerWidth, content.innerHeight, {},
-                          allowZoom, minZoom, maxZoom, {}, {}, autoSize);
-
-    // FIXME: On Safari and Chrome mobile platform, if the css property
-    // touch-action set to none or manipulation would also suppress 300ms
-    // delay. But Firefox didn't support this property now, we can't get
-    // this value from utils.getVisitedDependentComputedStyle() to check
-    // if we should suppress 300ms delay.
-    if (!allowZoom.value ||                   // user-scalable = no
-        minZoom.value === maxZoom.value ||    // minimum-scale = maximum-scale
-        autoSize.value                        // width = device-width
-    ) {
-      return 0;
-    } else {
-      return 300;
-    }
-  }
 };
 
 simulator.init();
new file mode 100644
--- /dev/null
+++ b/devtools/shared/touch/simulator-core.js
@@ -0,0 +1,362 @@
+/* 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 { Ci, Cu } = require("chrome");
+const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
+
+var systemAppOrigin = (function () {
+  let systemOrigin = "_";
+  try {
+    systemOrigin = Services.io.newURI(
+      Services.prefs.getCharPref("b2g.system_manifest_url"), null, null)
+      .prePath;
+  } catch (e) {
+    // Fall back to default value
+  }
+  return systemOrigin;
+})();
+
+var threshold = 25;
+try {
+  threshold = Services.prefs.getIntPref("ui.dragThresholdX");
+} catch (e) {
+  // Fall back to default value
+}
+
+var delay = 500;
+try {
+  delay = Services.prefs.getIntPref("ui.click_hold_context_menus.delay");
+} catch (e) {
+  // Fall back to default value
+}
+
+function SimulatorCore(simulatorTarget) {
+  this.simulatorTarget = simulatorTarget;
+}
+
+/**
+ * Simulate touch events for platforms where they aren't generally available.
+ */
+SimulatorCore.prototype = {
+  events: [
+    "mousedown",
+    "mousemove",
+    "mouseup",
+    "touchstart",
+    "touchend",
+    "mouseenter",
+    "mouseover",
+    "mouseout",
+    "mouseleave"
+  ],
+
+  contextMenuTimeout: null,
+
+  simulatorTarget: null,
+
+  enabled: false,
+
+  start() {
+    if (this.enabled) {
+      // Simulator is already started
+      return;
+    }
+    this.events.forEach(evt => {
+      // Only listen trusted events to prevent messing with
+      // event dispatched manually within content documents
+      this.simulatorTarget.addEventListener(evt, this, true, false);
+    });
+    this.enabled = true;
+  },
+
+  stop() {
+    if (!this.enabled) {
+      // Simulator isn't running
+      return;
+    }
+    this.events.forEach(evt => {
+      this.simulatorTarget.removeEventListener(evt, this, true);
+    });
+    this.enabled = false;
+  },
+
+  handleEvent(evt) {
+    // The gaia system window use an hybrid system even on the device which is
+    // a mix of mouse/touch events. So let's not cancel *all* mouse events
+    // if it is the current target.
+    let content = this.getContent(evt.target);
+    if (!content) {
+      return;
+    }
+    let isSystemWindow = content.location.toString()
+                                .startsWith(systemAppOrigin);
+
+    // App touchstart & touchend should also be dispatched on the system app
+    // to match on-device behavior.
+    if (evt.type.startsWith("touch") && !isSystemWindow) {
+      let sysFrame = content.realFrameElement;
+      if (!sysFrame) {
+        return;
+      }
+      let sysDocument = sysFrame.ownerDocument;
+      let sysWindow = sysDocument.defaultView;
+
+      let touchEvent = sysDocument.createEvent("touchevent");
+      let touch = evt.touches[0] || evt.changedTouches[0];
+      let point = sysDocument.createTouch(sysWindow, sysFrame, 0,
+                                          touch.pageX, touch.pageY,
+                                          touch.screenX, touch.screenY,
+                                          touch.clientX, touch.clientY,
+                                          1, 1, 0, 0);
+
+      let touches = sysDocument.createTouchList(point);
+      let targetTouches = touches;
+      let changedTouches = touches;
+      touchEvent.initTouchEvent(evt.type, true, true, sysWindow, 0,
+                                false, false, false, false,
+                                touches, targetTouches, changedTouches);
+      sysFrame.dispatchEvent(touchEvent);
+      return;
+    }
+
+    // Ignore all but real mouse event coming from physical mouse
+    // (especially ignore mouse event being dispatched from a touch event)
+    if (evt.button ||
+        evt.mozInputSource != Ci.nsIDOMMouseEvent.MOZ_SOURCE_MOUSE ||
+        evt.isSynthesized) {
+      return;
+    }
+
+    let eventTarget = this.target;
+    let type = "";
+    switch (evt.type) {
+      case "mouseenter":
+      case "mouseover":
+      case "mouseout":
+      case "mouseleave":
+        // Don't propagate events which are not related to touch events
+        evt.stopPropagation();
+        break;
+
+      case "mousedown":
+        this.target = evt.target;
+
+        this.contextMenuTimeout =
+          this.sendContextMenu(evt.target, evt.pageX, evt.pageY);
+
+        this.cancelClick = false;
+        this.startX = evt.pageX;
+        this.startY = evt.pageY;
+
+        // Capture events so if a different window show up the events
+        // won't be dispatched to something else.
+        evt.target.setCapture(false);
+
+        type = "touchstart";
+        break;
+
+      case "mousemove":
+        if (!eventTarget) {
+          // Don't propagate mousemove event when touchstart event isn't fired
+          evt.stopPropagation();
+          return;
+        }
+
+        if (!this.cancelClick) {
+          if (Math.abs(this.startX - evt.pageX) > threshold ||
+              Math.abs(this.startY - evt.pageY) > threshold) {
+            this.cancelClick = true;
+            content.clearTimeout(this.contextMenuTimeout);
+          }
+        }
+
+        type = "touchmove";
+        break;
+
+      case "mouseup":
+        if (!eventTarget) {
+          return;
+        }
+        this.target = null;
+
+        content.clearTimeout(this.contextMenuTimeout);
+        type = "touchend";
+
+        // Only register click listener after mouseup to ensure
+        // catching only real user click. (Especially ignore click
+        // being dispatched on form submit)
+        if (evt.detail == 1) {
+          this.simulatorTarget.addEventListener("click", this, true, false);
+        }
+        break;
+
+      case "click":
+        // Mouse events has been cancelled so dispatch a sequence
+        // of events to where touchend has been fired
+        evt.preventDefault();
+        evt.stopImmediatePropagation();
+
+        this.simulatorTarget.removeEventListener("click", this, true, false);
+
+        if (this.cancelClick) {
+          return;
+        }
+
+        content.setTimeout(function dispatchMouseEvents(self) {
+          try {
+            self.fireMouseEvent("mousedown", evt);
+            self.fireMouseEvent("mousemove", evt);
+            self.fireMouseEvent("mouseup", evt);
+          } catch (e) {
+            console.error("Exception in touch event helper: " + e);
+          }
+        }, this.getDelayBeforeMouseEvent(evt), this);
+        return;
+    }
+
+    let target = eventTarget || this.target;
+    if (target && type) {
+      this.sendTouchEvent(evt, target, type);
+    }
+
+    if (!isSystemWindow) {
+      evt.preventDefault();
+      evt.stopImmediatePropagation();
+    }
+  },
+
+  fireMouseEvent(type, evt) {
+    let content = this.getContent(evt.target);
+    let utils = content.QueryInterface(Ci.nsIInterfaceRequestor)
+                       .getInterface(Ci.nsIDOMWindowUtils);
+    utils.sendMouseEvent(type, evt.clientX, evt.clientY, 0, 1, 0, true, 0,
+                         Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH);
+  },
+
+  sendContextMenu(target, x, y) {
+    let doc = target.ownerDocument;
+    let evt = doc.createEvent("MouseEvent");
+    evt.initMouseEvent("contextmenu", true, true, doc.defaultView,
+                       0, x, y, x, y, false, false, false, false,
+                       0, null);
+
+    let content = this.getContent(target);
+    let timeout = content.setTimeout((function contextMenu() {
+      target.dispatchEvent(evt);
+      this.cancelClick = true;
+    }).bind(this), delay);
+
+    return timeout;
+  },
+
+  sendTouchEvent(evt, target, name) {
+    function clone(obj) {
+      return Cu.cloneInto(obj, target);
+    }
+    // When running OOP b2g desktop, we need to send the touch events
+    // using the mozbrowser api on the unwrapped frame.
+    if (target.localName == "iframe" && target.mozbrowser === true) {
+      if (name == "touchstart") {
+        this.touchstartTime = Date.now();
+      } else if (name == "touchend") {
+        // If we have a "fast" tap, don't send a click as both will be turned
+        // into a click and that breaks eg. checkboxes.
+        if (Date.now() - this.touchstartTime < delay) {
+          this.cancelClick = true;
+        }
+      }
+      let unwrapped = XPCNativeWrapper.unwrap(target);
+      unwrapped.sendTouchEvent(name, clone([0]),       // event type, id
+                               clone([evt.clientX]),   // x
+                               clone([evt.clientY]),   // y
+                               clone([1]), clone([1]), // rx, ry
+                               clone([0]), clone([0]), // rotation, force
+                               1);                     // count
+      return;
+    }
+    let document = target.ownerDocument;
+    let content = this.getContent(target);
+    if (!content) {
+      return;
+    }
+
+    let touchEvent = document.createEvent("touchevent");
+    let point = document.createTouch(content, target, 0,
+                                     evt.pageX, evt.pageY,
+                                     evt.screenX, evt.screenY,
+                                     evt.clientX, evt.clientY,
+                                     1, 1, 0, 0);
+
+    let touches = document.createTouchList(point);
+    let targetTouches = touches;
+    let changedTouches = touches;
+    if (name === "touchend" || name === "touchcancel") {
+      // "touchend" and "touchcancel" events should not have the removed touch
+      // neither in touches nor in targetTouches
+      touches = targetTouches = document.createTouchList();
+    }
+
+    touchEvent.initTouchEvent(name, true, true, content, 0,
+                              false, false, false, false,
+                              touches, targetTouches, changedTouches);
+    target.dispatchEvent(touchEvent);
+  },
+
+  getContent(target) {
+    let win = (target && target.ownerDocument)
+      ? target.ownerDocument.defaultView
+      : null;
+    return win;
+  },
+
+  getDelayBeforeMouseEvent(evt) {
+    // On mobile platforms, Firefox inserts a 300ms delay between
+    // touch events and accompanying mouse events, except if the
+    // content window is not zoomable and the content window is
+    // auto-zoomed to device-width.
+
+    // If the preference dom.meta-viewport.enabled is set to false,
+    // we couldn't read viewport's information from getViewportInfo().
+    // So we always simulate 300ms delay when the
+    // dom.meta-viewport.enabled is false.
+    let savedMetaViewportEnabled =
+      Services.prefs.getBoolPref("dom.meta-viewport.enabled");
+    if (!savedMetaViewportEnabled) {
+      return 300;
+    }
+
+    let content = this.getContent(evt.target);
+    if (!content) {
+      return 0;
+    }
+
+    let utils = content.QueryInterface(Ci.nsIInterfaceRequestor)
+                       .getInterface(Ci.nsIDOMWindowUtils);
+
+    let allowZoom = {},
+        minZoom = {},
+        maxZoom = {},
+        autoSize = {};
+
+    utils.getViewportInfo(content.innerWidth, content.innerHeight, {},
+                          allowZoom, minZoom, maxZoom, {}, {}, autoSize);
+
+    // FIXME: On Safari and Chrome mobile platform, if the css property
+    // touch-action set to none or manipulation would also suppress 300ms
+    // delay. But Firefox didn't support this property now, we can't get
+    // this value from utils.getVisitedDependentComputedStyle() to check
+    // if we should suppress 300ms delay.
+    if (!allowZoom.value ||                   // user-scalable = no
+        minZoom.value === maxZoom.value ||    // minimum-scale = maximum-scale
+        autoSize.value                        // width = device-width
+    ) {
+      return 0;
+    } else {
+      return 300;
+    }
+  }
+};
+
+exports.SimulatorCore = SimulatorCore;
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -10662,27 +10662,16 @@ nsDocShell::DoURILoad(nsIURI* aURI,
   }
 
   nsresult rv;
   nsCOMPtr<nsIURILoader> uriLoader = do_GetService(NS_URI_LOADER_CONTRACTID, &rv);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  nsLoadFlags loadFlags = mDefaultLoadFlags;
-  if (aFirstParty) {
-    // tag first party URL loads
-    loadFlags |= nsIChannel::LOAD_INITIAL_DOCUMENT_URI;
-  }
-
-  if (mLoadType == LOAD_ERROR_PAGE) {
-    // Error pages are LOAD_BACKGROUND
-    loadFlags |= nsIChannel::LOAD_BACKGROUND;
-  }
-
   if (IsFrame()) {
 
     MOZ_ASSERT(aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
                aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_FRAME,
                "DoURILoad thinks this is a frame and InternalLoad does not");
 
     // Only allow view-source scheme in top-level docshells. view-source is
     // the only scheme to which this applies at the moment due to potential
@@ -10789,17 +10778,30 @@ nsDocShell::DoURILoad(nsIURI* aURI,
   } else if (!triggeringPrincipal && aReferrerURI) {
     rv = CreatePrincipalFromReferrer(aReferrerURI,
                                      getter_AddRefs(triggeringPrincipal));
     NS_ENSURE_SUCCESS(rv, rv);
   } else {
     triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
   }
 
+  nsLoadFlags loadFlags = mDefaultLoadFlags;
   nsSecurityFlags securityFlags = nsILoadInfo::SEC_NORMAL;
+
+  if (aFirstParty) {
+    // tag first party URL loads
+    loadFlags |= nsIChannel::LOAD_INITIAL_DOCUMENT_URI;
+  }
+
+  if (mLoadType == LOAD_ERROR_PAGE) {
+    // Error pages are LOAD_BACKGROUND
+    loadFlags |= nsIChannel::LOAD_BACKGROUND;
+    securityFlags |= nsILoadInfo::SEC_LOAD_ERROR_PAGE;
+  }
+
   if (inherit) {
     securityFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
   }
   if (isSandBoxed) {
     securityFlags |= nsILoadInfo::SEC_SANDBOXED;
   }
 
   if (UsePrivateBrowsing()) {
--- a/dom/animation/DocumentTimeline.cpp
+++ b/dom/animation/DocumentTimeline.cpp
@@ -12,18 +12,26 @@
 #include "nsDOMNavigationTiming.h"
 #include "nsIPresShell.h"
 #include "nsPresContext.h"
 #include "nsRefreshDriver.h"
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED(DocumentTimeline, AnimationTimeline,
-                                   mDocument)
+NS_IMPL_CYCLE_COLLECTION_CLASS(DocumentTimeline)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DocumentTimeline,
+                                                AnimationTimeline)
+  tmp->UnregisterFromRefreshDriver();
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DocumentTimeline,
+                                                  AnimationTimeline)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(DocumentTimeline,
                                                AnimationTimeline)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DocumentTimeline)
 NS_INTERFACE_MAP_END_INHERITING(AnimationTimeline)
 
@@ -174,18 +182,17 @@ DocumentTimeline::WillRefresh(mozilla::T
   if (!needsTicks) {
     // We already assert that GetRefreshDriver() is non-null at the beginning
     // of this function but we check it again here to be sure that ticking
     // animations does not have any side effects that cause us to lose the
     // connection with the refresh driver, such as triggering the destruction
     // of mDocument's PresShell.
     MOZ_ASSERT(GetRefreshDriver(),
                "Refresh driver should still be valid at end of WillRefresh");
-    GetRefreshDriver()->RemoveRefreshObserver(this, Flush_Style);
-    mIsObservingRefreshDriver = false;
+    UnregisterFromRefreshDriver();
   }
 }
 
 void
 DocumentTimeline::NotifyRefreshDriverCreated(nsRefreshDriver* aDriver)
 {
   MOZ_ASSERT(!mIsObservingRefreshDriver,
              "Timeline should not be observing the refresh driver before"
@@ -209,21 +216,17 @@ DocumentTimeline::NotifyRefreshDriverDes
 }
 
 void
 DocumentTimeline::RemoveAnimation(Animation* aAnimation)
 {
   AnimationTimeline::RemoveAnimation(aAnimation);
 
   if (mIsObservingRefreshDriver && mAnimations.IsEmpty()) {
-    MOZ_ASSERT(GetRefreshDriver(),
-               "Refresh driver should still be valid when "
-               "mIsObservingRefreshDriver is true");
-    GetRefreshDriver()->RemoveRefreshObserver(this, Flush_Style);
-    mIsObservingRefreshDriver = false;
+    UnregisterFromRefreshDriver();
   }
 }
 
 TimeStamp
 DocumentTimeline::ToTimeStamp(const TimeDuration& aTimeDuration) const
 {
   TimeStamp result;
   RefPtr<nsDOMNavigationTiming> timing = mDocument->GetNavigationTiming();
@@ -247,10 +250,26 @@ DocumentTimeline::GetRefreshDriver() con
   nsPresContext* presContext = presShell->GetPresContext();
   if (MOZ_UNLIKELY(!presContext)) {
     return nullptr;
   }
 
   return presContext->RefreshDriver();
 }
 
+void
+DocumentTimeline::UnregisterFromRefreshDriver()
+{
+  if (!mIsObservingRefreshDriver) {
+    return;
+  }
+
+  nsRefreshDriver* refreshDriver = GetRefreshDriver();
+  if (!refreshDriver) {
+    return;
+  }
+
+  refreshDriver->RemoveRefreshObserver(this, Flush_Style);
+  mIsObservingRefreshDriver = false;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/animation/DocumentTimeline.h
+++ b/dom/animation/DocumentTimeline.h
@@ -78,16 +78,17 @@ public:
   void WillRefresh(TimeStamp aTime) override;
 
   void NotifyRefreshDriverCreated(nsRefreshDriver* aDriver);
   void NotifyRefreshDriverDestroying(nsRefreshDriver* aDriver);
 
 protected:
   TimeStamp GetCurrentTimeStamp() const;
   nsRefreshDriver* GetRefreshDriver() const;
+  void UnregisterFromRefreshDriver();
 
   nsCOMPtr<nsIDocument> mDocument;
 
   // The most recently used refresh driver time. This is used in cases where
   // we don't have a refresh driver (e.g. because we are in a display:none
   // iframe).
   mutable TimeStamp mLastRefreshDriverTime;
   bool mIsObservingRefreshDriver;
--- a/dom/base/PostMessageEvent.cpp
+++ b/dom/base/PostMessageEvent.cpp
@@ -31,17 +31,17 @@ namespace dom {
 
 PostMessageEvent::PostMessageEvent(nsGlobalWindow* aSource,
                                    const nsAString& aCallerOrigin,
                                    nsGlobalWindow* aTargetWindow,
                                    nsIPrincipal* aProvidedPrincipal,
                                    nsIDocument* aSourceDocument,
                                    bool aTrustedCaller)
 : StructuredCloneHolder(CloningSupported, TransferringSupported,
-                        SameProcessSameThread),
+                        StructuredCloneScope::SameProcessSameThread),
   mSource(aSource),
   mCallerOrigin(aCallerOrigin),
   mTargetWindow(aTargetWindow),
   mProvidedPrincipal(aProvidedPrincipal),
   mSourceDocument(aSourceDocument),
   mTrustedCaller(aTrustedCaller)
 {
   MOZ_COUNT_CTOR(PostMessageEvent);
--- a/dom/base/StructuredCloneHolder.cpp
+++ b/dom/base/StructuredCloneHolder.cpp
@@ -141,19 +141,20 @@ const JSStructuredCloneCallbacks gCallba
   StructuredCloneCallbacksWriteTransfer,
   StructuredCloneCallbacksFreeTransfer
 };
 
 } // anonymous namespace
 
 // StructuredCloneHolderBase class
 
-StructuredCloneHolderBase::StructuredCloneHolderBase()
+StructuredCloneHolderBase::StructuredCloneHolderBase(StructuredCloneScope aScope)
+  : mStructuredCloneScope(aScope)
 #ifdef DEBUG
-  : mClearCalled(false)
+  , mClearCalled(false)
 #endif
 {}
 
 StructuredCloneHolderBase::~StructuredCloneHolderBase()
 {
 #ifdef DEBUG
   MOZ_ASSERT(mClearCalled);
 #endif
@@ -179,17 +180,17 @@ StructuredCloneHolderBase::Write(JSConte
 bool
 StructuredCloneHolderBase::Write(JSContext* aCx,
                                  JS::Handle<JS::Value> aValue,
                                  JS::Handle<JS::Value> aTransfer)
 {
   MOZ_ASSERT(!mBuffer, "Double Write is not allowed");
   MOZ_ASSERT(!mClearCalled, "This method cannot be called after Clear.");
 
-  mBuffer = new JSAutoStructuredCloneBuffer(&gCallbacks, this);
+  mBuffer = new JSAutoStructuredCloneBuffer(mStructuredCloneScope, &gCallbacks, this);
 
   if (!mBuffer->write(aCx, aValue, aTransfer, &gCallbacks, this)) {
     mBuffer = nullptr;
     return false;
   }
 
   return true;
 }
@@ -237,20 +238,20 @@ StructuredCloneHolderBase::CustomFreeTra
 {
   MOZ_CRASH("Nothing to free.");
 }
 
 // StructuredCloneHolder class
 
 StructuredCloneHolder::StructuredCloneHolder(CloningSupport aSupportsCloning,
                                              TransferringSupport aSupportsTransferring,
-                                             ContextSupport aContext)
-  : mSupportsCloning(aSupportsCloning == CloningSupported)
+                                             StructuredCloneScope aScope)
+  : StructuredCloneHolderBase(aScope)
+  , mSupportsCloning(aSupportsCloning == CloningSupported)
   , mSupportsTransferring(aSupportsTransferring == TransferringSupported)
-  , mSupportedContext(aContext)
   , mParent(nullptr)
 #ifdef DEBUG
   , mCreationThread(NS_GetCurrentThread())
 #endif
 {}
 
 StructuredCloneHolder::~StructuredCloneHolder()
 {
@@ -267,41 +268,32 @@ StructuredCloneHolder::Write(JSContext* 
 }
 
 void
 StructuredCloneHolder::Write(JSContext* aCx,
                              JS::Handle<JS::Value> aValue,
                              JS::Handle<JS::Value> aTransfer,
                              ErrorResult& aRv)
 {
-  MOZ_ASSERT_IF(mSupportedContext == SameProcessSameThread,
+  MOZ_ASSERT_IF(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread,
                 mCreationThread == NS_GetCurrentThread());
 
   if (!StructuredCloneHolderBase::Write(aCx, aValue, aTransfer)) {
     aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
     return;
   }
-
-  if (mSupportedContext != SameProcessSameThread) {
-    for (uint32_t i = 0, len = mBlobImplArray.Length(); i < len; ++i) {
-      if (!mBlobImplArray[i]->MayBeClonedToOtherThreads()) {
-        aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
-        return;
-      }
-    }
-  }
 }
 
 void
 StructuredCloneHolder::Read(nsISupports* aParent,
                             JSContext* aCx,
                             JS::MutableHandle<JS::Value> aValue,
                             ErrorResult& aRv)
 {
-  MOZ_ASSERT_IF(mSupportedContext == SameProcessSameThread,
+  MOZ_ASSERT_IF(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread,
                 mCreationThread == NS_GetCurrentThread());
   MOZ_ASSERT(aParent);
 
   mozilla::AutoRestore<nsISupports*> guard(mParent);
   mParent = aParent;
 
   if (!StructuredCloneHolderBase::Read(aCx, aValue)) {
     JS_ClearPendingException(aCx);
@@ -332,37 +324,38 @@ void
 StructuredCloneHolder::ReadFromBuffer(nsISupports* aParent,
                                       JSContext* aCx,
                                       uint64_t* aBuffer,
                                       size_t aBufferLength,
                                       uint32_t aAlgorithmVersion,
                                       JS::MutableHandle<JS::Value> aValue,
                                       ErrorResult& aRv)
 {
-  MOZ_ASSERT_IF(mSupportedContext == SameProcessSameThread,
+  MOZ_ASSERT_IF(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread,
                 mCreationThread == NS_GetCurrentThread());
 
   MOZ_ASSERT(!mBuffer, "ReadFromBuffer() must be called without a Write().");
   MOZ_ASSERT(aBuffer);
 
   mozilla::AutoRestore<nsISupports*> guard(mParent);
   mParent = aParent;
 
   if (!JS_ReadStructuredClone(aCx, aBuffer, aBufferLength, aAlgorithmVersion,
-                              aValue, &gCallbacks, this)) {
+                              mStructuredCloneScope, aValue, &gCallbacks,
+                              this)) {
     JS_ClearPendingException(aCx);
     aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
   }
 }
 
 void
 StructuredCloneHolder::MoveBufferDataToArray(FallibleTArray<uint8_t>& aArray,
                                              ErrorResult& aRv)
 {
-  MOZ_ASSERT_IF(mSupportedContext == SameProcessSameThread,
+  MOZ_ASSERT_IF(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread,
                 mCreationThread == NS_GetCurrentThread());
 
   MOZ_ASSERT(mBuffer, "MoveBuffer() cannot be called without a Write().");
 
   if (NS_WARN_IF(!aArray.SetLength(BufferSize(), mozilla::fallible))) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return;
   }
@@ -698,16 +691,21 @@ bool
 WriteBlob(JSStructuredCloneWriter* aWriter,
           Blob* aBlob,
           StructuredCloneHolder* aHolder)
 {
   MOZ_ASSERT(aWriter);
   MOZ_ASSERT(aBlob);
   MOZ_ASSERT(aHolder);
 
+  if (JS_GetStructuredCloneScope(aWriter) != JS::StructuredCloneScope::SameProcessSameThread &&
+      !aBlob->Impl()->MayBeClonedToOtherThreads()) {
+    return false;
+  }
+
   ErrorResult rv;
   RefPtr<BlobImpl> blobImpl =
     EnsureBlobForBackgroundManager(aBlob->Impl(), nullptr, rv);
   if (NS_WARN_IF(rv.Failed())) {
     rv.SuppressException();
     return false;
   }
 
@@ -1036,18 +1034,18 @@ WriteFormData(JSStructuredCloneWriter* a
 
         closure->mHolder->BlobImpls().AppendElement(blobImpl);
         return true;
       }
 
       if (aValue.IsDirectory()) {
         Directory* directory = aValue.GetAsDirectory();
 
-        if (closure->mHolder->SupportedContext() !=
-              StructuredCloneHolder::SameProcessSameThread &&
+        if (closure->mHolder->CloneScope() !=
+              StructuredCloneHolder::StructuredCloneScope::SameProcessSameThread &&
             !directory->ClonableToDifferentThreadOrProcess()) {
           return false;
         }
 
         return WriteDirectory(closure->mWriter, directory);
       }
 
       size_t charSize = sizeof(nsString::char_type);
@@ -1087,18 +1085,18 @@ StructuredCloneHolder::CustomReadHandler
     return ReadFileList(aCx, aReader, aIndex, this);
   }
 
   if (aTag == SCTAG_DOM_FORMDATA) {
     return ReadFormData(aCx, aReader, aIndex, this);
   }
 
   if (aTag == SCTAG_DOM_IMAGEBITMAP) {
-    MOZ_ASSERT(mSupportedContext == SameProcessSameThread ||
-               mSupportedContext == SameProcessDifferentThread);
+    MOZ_ASSERT(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
+               mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread);
 
     // Get the current global object.
     // This can be null.
     nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(mParent);
     // aIndex is the index of the cloned image.
     return ImageBitmap::ReadStructuredClone(aCx, aReader,
                                             parent, GetSurfaces(), aIndex);
    }
@@ -1122,17 +1120,17 @@ StructuredCloneHolder::CustomWriteHandle
       return WriteBlob(aWriter, blob, this);
     }
   }
 
   // See if this is a Directory object.
   {
     Directory* directory = nullptr;
     if (NS_SUCCEEDED(UNWRAP_OBJECT(Directory, aObj, directory))) {
-      if (mSupportedContext != SameProcessSameThread &&
+      if (mStructuredCloneScope != StructuredCloneScope::SameProcessSameThread &&
           !directory->ClonableToDifferentThreadOrProcess()) {
         return false;
       }
 
       return WriteDirectory(aWriter, directory);
     }
   }
 
@@ -1148,18 +1146,18 @@ StructuredCloneHolder::CustomWriteHandle
   {
     FormData* formData = nullptr;
     if (NS_SUCCEEDED(UNWRAP_OBJECT(FormData, aObj, formData))) {
       return WriteFormData(aWriter, formData, this);
     }
   }
 
   // See if this is an ImageBitmap object.
-  if (mSupportedContext == SameProcessSameThread ||
-      mSupportedContext == SameProcessDifferentThread) {
+  if (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
+      mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread) {
     ImageBitmap* imageBitmap = nullptr;
     if (NS_SUCCEEDED(UNWRAP_OBJECT(ImageBitmap, aObj, imageBitmap))) {
       return ImageBitmap::WriteStructuredClone(aWriter,
                                                GetSurfaces(),
                                                imageBitmap);
     }
   }
 
@@ -1198,18 +1196,18 @@ StructuredCloneHolder::CustomReadTransfe
       return false;
     }
 
     aReturnObject.set(&value.toObject());
     return true;
   }
 
   if (aTag == SCTAG_DOM_CANVAS) {
-    MOZ_ASSERT(mSupportedContext == SameProcessSameThread ||
-               mSupportedContext == SameProcessDifferentThread);
+    MOZ_ASSERT(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
+               mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread);
     MOZ_ASSERT(aContent);
     OffscreenCanvasCloneData* data =
       static_cast<OffscreenCanvasCloneData*>(aContent);
     nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(mParent);
     RefPtr<OffscreenCanvas> canvas = OffscreenCanvas::CreateFromCloneData(parent, data);
     delete data;
 
     JS::Rooted<JS::Value> value(aCx);
@@ -1218,18 +1216,18 @@ StructuredCloneHolder::CustomReadTransfe
       return false;
     }
 
     aReturnObject.set(&value.toObject());
     return true;
   }
 
   if (aTag == SCTAG_DOM_IMAGEBITMAP) {
-    MOZ_ASSERT(mSupportedContext == SameProcessSameThread ||
-               mSupportedContext == SameProcessDifferentThread);
+    MOZ_ASSERT(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
+               mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread);
     MOZ_ASSERT(aContent);
     ImageBitmapCloneData* data =
       static_cast<ImageBitmapCloneData*>(aContent);
     nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(mParent);
     RefPtr<ImageBitmap> bitmap = ImageBitmap::CreateFromCloneData(parent, data);
     delete data;
 
     JS::Rooted<JS::Value> value(aCx);
@@ -1269,18 +1267,18 @@ StructuredCloneHolder::CustomWriteTransf
 
       *aTag = SCTAG_DOM_MAP_MESSAGEPORT;
       *aOwnership = JS::SCTAG_TMO_CUSTOM;
       *aContent = nullptr;
 
       return true;
     }
 
-    if (mSupportedContext == SameProcessSameThread ||
-        mSupportedContext == SameProcessDifferentThread) {
+    if (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
+        mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread) {
       OffscreenCanvas* canvas = nullptr;
       rv = UNWRAP_OBJECT(OffscreenCanvas, aObj, canvas);
       if (NS_SUCCEEDED(rv)) {
         MOZ_ASSERT(canvas);
 
         *aExtraData = 0;
         *aTag = SCTAG_DOM_CANVAS;
         *aOwnership = JS::SCTAG_TMO_CUSTOM;
@@ -1322,28 +1320,28 @@ StructuredCloneHolder::CustomFreeTransfe
   if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
     MOZ_ASSERT(!aContent);
     MOZ_ASSERT(aExtraData < mPortIdentifiers.Length());
     MessagePort::ForceClose(mPortIdentifiers[aExtraData]);
     return;
   }
 
   if (aTag == SCTAG_DOM_CANVAS) {
-    MOZ_ASSERT(mSupportedContext == SameProcessSameThread ||
-               mSupportedContext == SameProcessDifferentThread);
+    MOZ_ASSERT(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
+               mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread);
     MOZ_ASSERT(aContent);
     OffscreenCanvasCloneData* data =
       static_cast<OffscreenCanvasCloneData*>(aContent);
     delete data;
     return;
   }
 
   if (aTag == SCTAG_DOM_IMAGEBITMAP) {
-    MOZ_ASSERT(mSupportedContext == SameProcessSameThread ||
-               mSupportedContext == SameProcessDifferentThread);
+    MOZ_ASSERT(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
+               mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread);
     MOZ_ASSERT(aContent);
     ImageBitmapCloneData* data =
       static_cast<ImageBitmapCloneData*>(aContent);
     delete data;
     return;
   }
 }
 
--- a/dom/base/StructuredCloneHolder.h
+++ b/dom/base/StructuredCloneHolder.h
@@ -26,17 +26,19 @@ namespace gfx {
 class DataSourceSurface;
 }
 
 namespace dom {
 
 class StructuredCloneHolderBase
 {
 public:
-  StructuredCloneHolderBase();
+  typedef JS::StructuredCloneScope StructuredCloneScope;
+
+  StructuredCloneHolderBase(StructuredCloneScope aScope = StructuredCloneScope::SameProcessSameThread);
   virtual ~StructuredCloneHolderBase();
 
   // These methods should be implemented in order to clone data.
   // Read more documentation in js/public/StructuredClone.h.
 
   virtual JSObject* CustomReadHandler(JSContext* aCx,
                                       JSStructuredCloneReader* aReader,
                                       uint32_t aTag,
@@ -110,16 +112,18 @@ public:
   {
     MOZ_ASSERT(mBuffer, "Write() has never been called.");
     return mBuffer->nbytes();
   }
 
 protected:
   nsAutoPtr<JSAutoStructuredCloneBuffer> mBuffer;
 
+  StructuredCloneScope mStructuredCloneScope;
+
 #ifdef DEBUG
   bool mClearCalled;
 #endif
 };
 
 class BlobImpl;
 class MessagePort;
 class MessagePortIdentifier;
@@ -134,34 +138,27 @@ public:
   };
 
   enum TransferringSupport
   {
     TransferringSupported,
     TransferringNotSupported
   };
 
-  enum ContextSupport
-  {
-    SameProcessSameThread,
-    SameProcessDifferentThread,
-    DifferentProcess
-  };
-
   // If cloning is supported, this object will clone objects such as Blobs,
   // FileList, ImageData, etc.
   // If transferring is supported, we will transfer MessagePorts and in the
   // future other transferrable objects.
-  // The ContextSupport is useful to know where the cloned/transferred data can
-  // be read and written. Additional checks about the nature of the objects
-  // will be done based on this context value because not all the objects can
-  // be sent between threads or processes.
+  // The StructuredCloneScope is useful to know where the cloned/transferred
+  // data can be read and written. Additional checks about the nature of the
+  // objects will be done based on this scope value because not all the
+  // objects can be sent between threads or processes.
   explicit StructuredCloneHolder(CloningSupport aSupportsCloning,
                                  TransferringSupport aSupportsTransferring,
-                                 ContextSupport aContextSupport);
+                                 StructuredCloneScope aStructuredCloneScope);
   virtual ~StructuredCloneHolder();
 
   // Normally you should just use Write() and Read().
 
   void Write(JSContext* aCx,
              JS::Handle<JS::Value> aValue,
              ErrorResult &aRv);
 
@@ -189,19 +186,19 @@ public:
   }
 
   nsTArray<RefPtr<BlobImpl>>& BlobImpls()
   {
     MOZ_ASSERT(mSupportsCloning, "Blobs cannot be taken/set if cloning is not supported.");
     return mBlobImplArray;
   }
 
-  ContextSupport SupportedContext() const
+  StructuredCloneScope CloneScope() const
   {
-    return mSupportedContext;
+    return mStructuredCloneScope;
   }
 
   // The parent object is set internally just during the Read(). This method
   // can be used by read functions to retrieve it.
   nsISupports* ParentDuringRead() const
   {
     return mParent;
   }
@@ -289,17 +286,16 @@ protected:
                       ErrorResult &aRv);
 
   // Use this method to free a buffer generated by MoveToBuffer().
   void FreeBuffer(uint64_t* aBuffer,
                   size_t aBufferLength);
 
   bool mSupportsCloning;
   bool mSupportsTransferring;
-  ContextSupport mSupportedContext;
 
   // Used for cloning blobs in the structured cloning algorithm.
   nsTArray<RefPtr<BlobImpl>> mBlobImplArray;
 
   // This is used for sharing the backend of ImageBitmaps.
   // The DataSourceSurface object must be thread-safely reference-counted.
   // The DataSourceSurface object will not be written ever via any ImageBitmap
   // instance, so no race condition will occur.
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -2558,17 +2558,19 @@ nsDocument::StartDocumentLoad(const char
     }
   }
 
   // If this document is being loaded by a docshell, copy its sandbox flags
   // to the document, and store the fullscreen enabled flag. These are
   // immutable after being set here.
   nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(aContainer);
 
-  if (docShell) {
+  // If this is an error page, don't inherit sandbox flags from docshell
+  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
+  if (docShell && !(loadInfo && loadInfo->GetLoadErrorPage())) {
     nsresult rv = docShell->GetSandboxFlags(&mSandboxFlags);
     NS_ENSURE_SUCCESS(rv, rv);
     WarnIfSandboxIneffective(docShell, mSandboxFlags, GetChannel());
   }
 
   // The CSP directive upgrade-insecure-requests not only applies to the
   // toplevel document, but also to nested documents. Let's propagate that
   // flag from the parent to the nested document.
--- a/dom/base/nsImageLoadingContent.cpp
+++ b/dom/base/nsImageLoadingContent.cpp
@@ -225,18 +225,25 @@ nsImageLoadingContent::OnLoadComplete(im
     MakePendingRequestCurrent();
   }
   MOZ_ASSERT(aRequest == mCurrentRequest,
              "One way or another, we should be current by now");
 
   // Fire the appropriate DOM event.
   if (NS_SUCCEEDED(aStatus)) {
     FireEvent(NS_LITERAL_STRING("load"));
+
+    // Do not fire loadend event for multipart/x-mixed-replace image streams.
+    bool isMultipart;
+    if (NS_FAILED(aRequest->GetMultipart(&isMultipart)) || !isMultipart) {
+      FireEvent(NS_LITERAL_STRING("loadend"));
+    }
   } else {
     FireEvent(NS_LITERAL_STRING("error"));
+    FireEvent(NS_LITERAL_STRING("loadend"));
   }
 
   nsCOMPtr<nsINode> thisNode = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
   nsSVGEffects::InvalidateDirectRenderingObservers(thisNode->AsElement());
 
   return NS_OK;
 }
 
@@ -623,17 +630,19 @@ nsImageLoadingContent::LoadImageWithChan
     TrackImage(req);
     ResetAnimationIfNeeded();
   } else {
     MOZ_ASSERT(!req, "Shouldn't have non-null request here");
     // If we don't have a current URI, we might as well store this URI so people
     // know what we tried (and failed) to load.
     if (!mCurrentRequest)
       aChannel->GetURI(getter_AddRefs(mCurrentURI));
+
     FireEvent(NS_LITERAL_STRING("error"));
+    FireEvent(NS_LITERAL_STRING("loadend"));
     aError.Throw(rv);
   }
   return listener.forget();
 }
 
 NS_IMETHODIMP
 nsImageLoadingContent::LoadImageWithChannel(nsIChannel* aChannel,
                                             nsIStreamListener** aListener)
@@ -659,17 +668,17 @@ nsImageLoadingContent::ForceReload(const
   // defaults to true
   bool notify = !aNotify.WasPassed() || aNotify.Value();
 
   // We keep this flag around along with the old URI even for failed requests
   // without a live request object
   ImageLoadType loadType = \
     (mCurrentRequestFlags & REQUEST_IS_IMAGESET) ? eImageLoadType_Imageset
                                                  : eImageLoadType_Normal;
-  nsresult rv = LoadImage(currentURI, true, notify, loadType, nullptr,
+  nsresult rv = LoadImage(currentURI, true, notify, loadType, true, nullptr,
                           nsIRequest::VALIDATE_ALWAYS);
   if (NS_FAILED(rv)) {
     aError.Throw(rv);
   }
 }
 
 NS_IMETHODIMP
 nsImageLoadingContent::ForceReload(bool aNotify /* = true */,
@@ -743,23 +752,27 @@ nsImageLoadingContent::LoadImage(const n
 {
   // First, get a document (needed for security checks and the like)
   nsIDocument* doc = GetOurOwnerDoc();
   if (!doc) {
     // No reason to bother, I think...
     return NS_OK;
   }
 
-  // Second, parse the URI string to get image URI
+  // Fire loadstart event
+  FireEvent(NS_LITERAL_STRING("loadstart"));
+
+  // Parse the URI string to get image URI
   nsCOMPtr<nsIURI> imageURI;
   nsresult rv = StringToURI(aNewURI, doc, getter_AddRefs(imageURI));
   if (NS_FAILED(rv)) {
-    // Cancel image requests and fire error event per spec
+    // Cancel image requests and then fire error and loadend events per spec
     CancelImageRequests(aNotify);
     FireEvent(NS_LITERAL_STRING("error"));
+    FireEvent(NS_LITERAL_STRING("loadend"));
     return NS_OK;
   }
 
   bool equal;
 
   if (aNewURI.IsEmpty() &&
       doc->GetDocumentURI() &&
       NS_SUCCEEDED(doc->GetDocumentURI()->EqualsExceptRef(imageURI, &equal)) &&
@@ -773,31 +786,38 @@ nsImageLoadingContent::LoadImage(const n
     // In light of that, just skip loading it..
     // Do make sure to drop our existing image, if any
     CancelImageRequests(aNotify);
     return NS_OK;
   }
 
   NS_TryToSetImmutable(imageURI);
 
-  return LoadImage(imageURI, aForce, aNotify, aImageLoadType, doc);
+  return LoadImage(imageURI, aForce, aNotify, aImageLoadType, false, doc);
 }
 
 nsresult
 nsImageLoadingContent::LoadImage(nsIURI* aNewURI,
                                  bool aForce,
                                  bool aNotify,
                                  ImageLoadType aImageLoadType,
+                                 bool aLoadStart,
                                  nsIDocument* aDocument,
                                  nsLoadFlags aLoadFlags)
 {
+  // Fire loadstart event if required
+  if (aLoadStart) {
+    FireEvent(NS_LITERAL_STRING("loadstart"));
+  }
+
   if (!mLoadingEnabled) {
     // XXX Why fire an error here? seems like the callers to SetLoadingEnabled
     // don't want/need it.
     FireEvent(NS_LITERAL_STRING("error"));
+    FireEvent(NS_LITERAL_STRING("loadend"));
     return NS_OK;
   }
 
   NS_ASSERTION(!aDocument || aDocument == GetOurOwnerDoc(),
                "Bogus document passed in");
   // First, get a document (needed for security checks and the like)
   if (!aDocument) {
     aDocument = GetOurOwnerDoc();
@@ -844,16 +864,17 @@ nsImageLoadingContent::LoadImage(nsIURI*
   nsContentUtils::CanLoadImage(aNewURI,
                                static_cast<nsIImageLoadingContent*>(this),
                                aDocument,
                                aDocument->NodePrincipal(),
                                &cpDecision,
                                policyType);
   if (!NS_CP_ACCEPTED(cpDecision)) {
     FireEvent(NS_LITERAL_STRING("error"));
+    FireEvent(NS_LITERAL_STRING("loadend"));
     SetBlockedRequest(aNewURI, cpDecision);
     return NS_OK;
   }
 
   nsLoadFlags loadFlags = aLoadFlags;
   int32_t corsmode = GetCORSMode();
   if (corsmode == CORS_ANONYMOUS) {
     loadFlags |= imgILoader::LOAD_CORS_ANONYMOUS;
@@ -916,17 +937,19 @@ nsImageLoadingContent::LoadImage(nsIURI*
       }
     }
   } else {
     MOZ_ASSERT(!req, "Shouldn't have non-null request here");
     // If we don't have a current URI, we might as well store this URI so people
     // know what we tried (and failed) to load.
     if (!mCurrentRequest)
       mCurrentURI = aNewURI;
+
     FireEvent(NS_LITERAL_STRING("error"));
+    FireEvent(NS_LITERAL_STRING("loadend"));
     return NS_OK;
   }
 
   return NS_OK;
 }
 
 nsresult
 nsImageLoadingContent::ForceImageState(bool aForce,
--- a/dom/base/nsImageLoadingContent.h
+++ b/dom/base/nsImageLoadingContent.h
@@ -128,23 +128,25 @@ protected:
    * URI object already available, they should use this method.
    *
    * @param aNewURI the URI to be loaded
    * @param aForce If true, make sure to load the URI.  If false, only
    *        load if the URI is different from the currently loaded URI.
    * @param aNotify If true, nsIDocumentObserver state change notifications
    *                will be sent as needed.
    * @param aImageLoadType The ImageLoadType for this request
+   * @param aLoadStart If true, dispatch "loadstart" event.
    * @param aDocument Optional parameter giving the document this node is in.
    *        This is purely a performance optimization.
    * @param aLoadFlags Optional parameter specifying load flags to use for
    *        the image load
    */
   nsresult LoadImage(nsIURI* aNewURI, bool aForce, bool aNotify,
-                     ImageLoadType aImageLoadType, nsIDocument* aDocument = nullptr,
+                     ImageLoadType aImageLoadType, bool aLoadStart = true,
+                     nsIDocument* aDocument = nullptr,
                      nsLoadFlags aLoadFlags = nsIRequest::LOAD_NORMAL);
 
   /**
    * helpers to get the document for this content (from the nodeinfo
    * and such).  Not named GetOwnerDoc/GetCurrentDoc to prevent ambiguous
    * method names in subclasses
    *
    * @return the document we belong to
@@ -258,17 +260,18 @@ private:
    * content and updates what ImageState() returns accordingly.  It will also
    * fire a ContentStatesChanged() notification as needed if aNotify is true.
    */
   void UpdateImageState(bool aNotify);
 
   /**
    * Method to fire an event once we know what's going on with the image load.
    *
-   * @param aEventType "load" or "error" depending on how things went
+   * @param aEventType "loadstart", "loadend", "load", or "error" depending on
+   *                   how things went
    */
   nsresult FireEvent(const nsAString& aEventType);
 
 protected:
   /**
    * Method to create an nsIURI object from the given string (will
    * handle getting the right charset, base, etc).  You MUST pass in a
    * non-null document to this function.
@@ -313,17 +316,17 @@ protected:
   /**
    * Switch our pending request to be our current request.
    * mPendingRequest must be non-null!
    */
   void MakePendingRequestCurrent();
 
   /**
    * Cancels and nulls-out the "current" and "pending" requests if they exist.
-   * 
+   *
    * @param aNonvisibleAction An action to take if the image is no longer
    *                          visible as a result; see |UntrackImage|.
    */
   void ClearCurrentRequest(nsresult aReason,
                            const Maybe<OnNonvisible>& aNonvisibleAction = Nothing());
   void ClearPendingRequest(nsresult aReason,
                            const Maybe<OnNonvisible>& aNonvisibleAction = Nothing());
 
--- a/dom/battery/BatteryManager.cpp
+++ b/dom/battery/BatteryManager.cpp
@@ -144,16 +144,21 @@ BatteryManager::UpdateFromBatteryInfo(co
   mRemainingTime = aBatteryInfo.remainingTime();
 
   if (!nsContentUtils::IsChromeDoc(doc) &&
       status != nsIPrincipal::APP_STATUS_CERTIFIED)
   {
     mLevel = lround(mLevel * 10.0) / 10.0;
     if (mLevel == 1.0) {
       mRemainingTime = mCharging ? kDefaultRemainingTime : kUnknownRemainingTime;
+    } else if (mRemainingTime != kUnknownRemainingTime) {
+      // Round the remaining time to a multiple of 15 minutes and never zero
+      const double MINUTES_15 = 15.0 * 60.0;
+      mRemainingTime = fmax(lround(mRemainingTime / MINUTES_15) * MINUTES_15,
+                            MINUTES_15);
     }
   }
 
   // Add some guards to make sure the values are coherent.
   if (mLevel == 1.0 && mCharging == true &&
       mRemainingTime != kDefaultRemainingTime) {
     mRemainingTime = kDefaultRemainingTime;
     NS_ERROR("Battery API: When charging and level at 1.0, remaining time "
--- a/dom/bluetooth/common/BluetoothService.cpp
+++ b/dom/bluetooth/common/BluetoothService.cpp
@@ -119,17 +119,17 @@ GetAllBluetoothActors(InfallibleTArray<B
 }
 
 } // namespace
 
 BluetoothService::ToggleBtAck::ToggleBtAck(bool aEnabled)
   : mEnabled(aEnabled)
 { }
 
-NS_METHOD
+NS_IMETHODIMP
 BluetoothService::ToggleBtAck::Run()
 {
   BluetoothService::AcknowledgeToggleBt(mEnabled);
 
   return NS_OK;
 }
 
 class BluetoothService::StartupTask final : public nsISettingsServiceCallback
--- a/dom/broadcastchannel/BroadcastChannel.cpp
+++ b/dom/broadcastchannel/BroadcastChannel.cpp
@@ -35,17 +35,17 @@ using namespace workers;
 
 class BroadcastChannelMessage final : public StructuredCloneHolder
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(BroadcastChannelMessage)
 
   BroadcastChannelMessage()
     : StructuredCloneHolder(CloningSupported, TransferringNotSupported,
-                            DifferentProcess)
+                            StructuredCloneScope::DifferentProcess)
   {}
 
 private:
   ~BroadcastChannelMessage()
   {}
 };
 
 namespace {
--- a/dom/cache/ReadStream.cpp
+++ b/dom/cache/ReadStream.cpp
@@ -53,30 +53,30 @@ public:
 
   virtual bool
   MatchId(const nsID& aId) const override;
 
   virtual bool
   HasEverBeenRead() const override;
 
   // Simulate nsIInputStream methods, but we don't actually inherit from it
-  NS_METHOD
+  nsresult
   Close();
 
-  NS_METHOD
+  nsresult
   Available(uint64_t *aNumAvailableOut);
 
-  NS_METHOD
+  nsresult
   Read(char *aBuf, uint32_t aCount, uint32_t *aNumReadOut);
 
-  NS_METHOD
+  nsresult
   ReadSegments(nsWriteSegmentFun aWriter, void *aClosure, uint32_t aCount,
                uint32_t *aNumReadOut);
 
-  NS_METHOD
+  nsresult
   IsNonBlocking(bool *aNonBlockingOut);
 
 private:
   class NoteClosedRunnable;
   class ForgetRunnable;
 
   ~Inner();
 
@@ -261,39 +261,39 @@ ReadStream::Inner::MatchId(const nsID& a
 
 bool
 ReadStream::Inner::HasEverBeenRead() const
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mOwningThread);
   return mHasEverBeenRead;
 }
 
-NS_IMETHODIMP
+nsresult
 ReadStream::Inner::Close()
 {
   // stream ops can happen on any thread
   nsresult rv = mStream->Close();
   NoteClosed();
   return rv;
 }
 
-NS_IMETHODIMP
+nsresult
 ReadStream::Inner::Available(uint64_t* aNumAvailableOut)
 {
   // stream ops can happen on any thread
   nsresult rv = mSnappyStream->Available(aNumAvailableOut);
 
   if (NS_FAILED(rv)) {
     Close();
   }
 
   return rv;
 }
 
-NS_IMETHODIMP
+nsresult
 ReadStream::Inner::Read(char* aBuf, uint32_t aCount, uint32_t* aNumReadOut)
 {
   // stream ops can happen on any thread
   MOZ_ASSERT(aNumReadOut);
 
   nsresult rv = mSnappyStream->Read(aBuf, aCount, aNumReadOut);
 
   if ((NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) ||
@@ -301,17 +301,17 @@ ReadStream::Inner::Read(char* aBuf, uint
     Close();
   }
 
   mHasEverBeenRead = true;
 
   return rv;
 }
 
-NS_IMETHODIMP
+nsresult
 ReadStream::Inner::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
                                 uint32_t aCount, uint32_t* aNumReadOut)
 {
   // stream ops can happen on any thread
   MOZ_ASSERT(aNumReadOut);
 
   if (aCount) {
     mHasEverBeenRead = true;
@@ -331,17 +331,17 @@ ReadStream::Inner::ReadSegments(nsWriteS
   // want to trigger on that.
   if (*aNumReadOut) {
     mHasEverBeenRead = true;
   }
 
   return rv;
 }
 
-NS_IMETHODIMP
+nsresult
 ReadStream::Inner::IsNonBlocking(bool* aNonBlockingOut)
 {
   // stream ops can happen on any thread
   return mSnappyStream->IsNonBlocking(aNonBlockingOut);
 }
 
 ReadStream::Inner::~Inner()
 {
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -156,16 +156,18 @@ using namespace mozilla::ipc;
 using namespace mozilla::layers;
 
 namespace mozilla {
 namespace dom {
 
 // Cap sigma to avoid overly large temp surfaces.
 const Float SIGMA_MAX = 100;
 
+const size_t MAX_STYLE_STACK_SIZE = 1024;
+
 /* Memory reporter stuff */
 static int64_t gCanvasAzureMemoryUsed = 0;
 
 // This is KIND_OTHER because it's not always clear where in memory the pixels
 // of a canvas are stored.  Furthermore, this memory will be tracked by the
 // underlying surface implementations.  See bug 655638 for details.
 class Canvas2dPixelsReporter final : public nsIMemoryReporter
 {
@@ -1955,16 +1957,22 @@ CanvasRenderingContext2D::GetSurfaceForm
 
 void
 CanvasRenderingContext2D::Save()
 {
   EnsureTarget();
   mStyleStack[mStyleStack.Length() - 1].transform = mTarget->GetTransform();
   mStyleStack.SetCapacity(mStyleStack.Length() + 1);
   mStyleStack.AppendElement(CurrentState());
+
+  if (mStyleStack.Length() > MAX_STYLE_STACK_SIZE) {
+    // This is not fast, but is better than OOMing and shouldn't be hit by
+    // reasonable code.
+    mStyleStack.RemoveElementAt(0);
+  }
 }
 
 void
 CanvasRenderingContext2D::Restore()
 {
   if (mStyleStack.Length() - 1 == 0)
     return;
 
--- a/dom/events/Event.cpp
+++ b/dom/events/Event.cpp
@@ -250,17 +250,17 @@ bool
 Event::IsChrome(JSContext* aCx) const
 {
   return mIsMainThreadEvent ?
     xpc::AccessCheck::isChrome(js::GetContextCompartment(aCx)) :
     mozilla::dom::workers::IsCurrentThreadRunningChromeWorker();
 }
 
 // nsIDOMEventInterface
-NS_METHOD
+NS_IMETHODIMP
 Event::GetType(nsAString& aType)
 {
   if (!mIsMainThreadEvent || !mEvent->mSpecifiedEventTypeString.IsEmpty()) {
     aType = mEvent->mSpecifiedEventTypeString;
     return NS_OK;
   }
   const char* name = GetEventName(mEvent->mMessage);
 
@@ -286,17 +286,17 @@ GetDOMEventTarget(nsIDOMEventTarget* aTa
 }
 
 EventTarget*
 Event::GetTarget() const
 {
   return GetDOMEventTarget(mEvent->mTarget);
 }
 
-NS_METHOD
+NS_IMETHODIMP
 Event::GetTarget(nsIDOMEventTarget** aTarget)
 {
   NS_IF_ADDREF(*aTarget = GetTarget());
   return NS_OK;
 }
 
 EventTarget*
 Event::GetCurrentTarget() const
--- a/dom/events/EventNameList.h
+++ b/dom/events/EventNameList.h
@@ -281,16 +281,20 @@ NON_IDL_EVENT(mozaccesskeynotfound,
 EVENT(loadeddata,
       eLoadedData,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(loadedmetadata,
       eLoadedMetaData,
       EventNameType_HTML,
       eBasicEventClass)
+EVENT(loadend,
+      eLoadEnd,
+      EventNameType_HTML,
+      eBasicEventClass)
 EVENT(loadstart,
       eLoadStart,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(mousedown,
       eMouseDown,
       EventNameType_All,
       eMouseEventClass)
@@ -700,26 +704,26 @@ NON_IDL_EVENT(DOMActivate,
 NON_IDL_EVENT(DOMFocusIn,
               eLegacyDOMFocusIn,
               EventNameType_HTMLXUL,
               eUIEventClass)
 NON_IDL_EVENT(DOMFocusOut,
               eLegacyDOMFocusOut,
               EventNameType_HTMLXUL,
               eUIEventClass)
-                                  
+
 NON_IDL_EVENT(DOMMouseScroll,
               eLegacyMouseLineOrPageScroll,
               EventNameType_HTMLXUL,
               eMouseScrollEventClass)
 NON_IDL_EVENT(MozMousePixelScroll,
               eLegacyMousePixelScroll,
               EventNameType_HTMLXUL,
               eMouseScrollEventClass)
-                                                
+
 NON_IDL_EVENT(open,
               eOpen,
               EventNameType_None,
               eBasicEventClass)
 
 NON_IDL_EVENT(dataavailable,
               eMediaRecorderDataAvailable,
               EventNameType_None,
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -4821,22 +4821,32 @@ HTMLInputElement::HandleTypeChange(uint8
   ValueModeType aOldValueMode = GetValueMode();
   uint8_t oldType = mType;
   nsAutoString aOldValue;
 
   if (aOldValueMode == VALUE_MODE_VALUE) {
     GetValue(aOldValue);
   }
 
+  nsTextEditorState::SelectionProperties sp;
+
+  if (GetEditorState()) {
+    sp = mInputData.mState->GetSelectionProperties();
+  }
+
   // We already have a copy of the value, lets free it and changes the type.
   FreeData();
   mType = aNewType;
 
   if (IsSingleLineTextControl()) {
+
     mInputData.mState = new nsTextEditorState(this);
+    if (!sp.IsDefault()) {
+      mInputData.mState->SetSelectionProperties(sp);
+    }
   }
 
   /**
    * The following code is trying to reproduce the algorithm described here:
    * http://www.whatwg.org/specs/web-apps/current-work/complete.html#input-type-change
    */
   switch (GetValueMode()) {
     case VALUE_MODE_DEFAULT:
--- a/dom/html/nsTextEditorState.cpp
+++ b/dom/html/nsTextEditorState.cpp
@@ -1531,16 +1531,29 @@ nsTextEditorState::GetSelectionPropertie
     HTMLInputElement* number = GetParentNumberControl(mBoundFrame);
     if (number) {
       return number->GetSelectionProperties();
     }
   }
   return mSelectionProperties;
 }
 
+void
+nsTextEditorState::SetSelectionProperties(nsTextEditorState::SelectionProperties& aProps)
+{
+  if (mBoundFrame) {
+    mBoundFrame->SetSelectionRange(aProps.GetStart(),
+                                   aProps.GetEnd(),
+                                   aProps.GetDirection());
+  } else {
+    mSelectionProperties = aProps;
+  }
+}
+
+
 HTMLInputElement*
 nsTextEditorState::GetParentNumberControl(nsFrame* aFrame) const
 {
   MOZ_ASSERT(aFrame);
   nsIContent* content = aFrame->GetContent();
   MOZ_ASSERT(content);
   nsIContent* parent = content->GetParent();
   if (!parent) {
--- a/dom/html/nsTextEditorState.h
+++ b/dom/html/nsTextEditorState.h
@@ -255,16 +255,17 @@ public:
     private:
       int32_t mStart, mEnd;
       bool mIsDirty = false;
       nsITextControlFrame::SelectionDirection mDirection;
   };
 
   bool IsSelectionCached() const;
   SelectionProperties& GetSelectionProperties();
+  void SetSelectionProperties(SelectionProperties& aProps);
   void WillInitEagerly() { mSelectionRestoreEagerInit = true; }
   bool HasNeverInitializedBefore() const { return !mEverInited; }
 
   void UpdateEditableState(bool aNotify) {
     if (mRootNode) {
       mRootNode->UpdateEditableState(aNotify);
     }
   }
--- a/dom/html/test/forms/mochitest.ini
+++ b/dom/html/test/forms/mochitest.ini
@@ -1,15 +1,16 @@
 [DEFAULT]
 support-files =
   save_restore_radio_groups.sjs
   test_input_number_data.js
   !/dom/html/test/reflect.js
 
 [test_bug1039548.html]
+[test_bug1283915.html]
 [test_bug1286509.html]
 skip-if = os == "android" || appname == "b2g" # up/down arrow keys not supported on android/b2g
 [test_button_attributes_reflection.html]
 [test_input_radio_radiogroup.html]
 [test_input_radio_required.html]
 [test_change_event.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_datalist_element.html]
new file mode 100644
--- /dev/null
+++ b/dom/html/test/forms/test_bug1283915.html
@@ -0,0 +1,70 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1283915
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1283915</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+
+  /** Test for Bug 1283915 **/
+
+  SimpleTest.waitForExplicitFinish();
+
+  function isCursorAtEnd(field){
+    is(field.selectionStart, field.value.length);
+    is(field.selectionEnd, field.value.length);
+  }
+
+  function test() {
+    var tField = document.getElementById("textField");
+    tField.focus();
+
+    synthesizeKey("a", {});
+    is(tField.value, "a");
+    isCursorAtEnd(tField);
+    document.body.offsetWidth; // frame must be created after type change
+
+    synthesizeKey("b", {});
+    is(tField.value, "ab");
+    isCursorAtEnd(tField);
+
+    synthesizeKey("c", {});
+    is(tField.value, "abc");
+    isCursorAtEnd(tField);
+
+    var nField = document.getElementById("numField");
+    nField.focus();
+
+    synthesizeKey("1", {});
+    is(nField.value, "1");
+    isCursorAtEnd(nField);
+    document.body.offsetWidth;
+
+    synthesizeKey("2", {});
+    is(nField.value, "12");
+    isCursorAtEnd(nField);
+
+    synthesizeKey("3", {});
+    is(nField.value, "123");
+    isCursorAtEnd(nField);
+
+    SimpleTest.finish();
+  }
+
+  SimpleTest.waitForFocus(test);
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1283915">Mozilla Bug 1283915</a>
+<p id="display"></p>
+<input id="textField" type="text" oninput="if (this.type !='password') this.type = 'password';">
+<input id="numField" type="text" oninput="if (this.type !='number') this.type = 'number';">
+<pre id="test">
+</pre>
+</body>
+</html>
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -144,21 +144,21 @@ class TransactionDatabaseOperationBase;
 class VersionChangeTransaction;
 
 /*******************************************************************************
  * Constants
  ******************************************************************************/
 
 // If JS_STRUCTURED_CLONE_VERSION changes then we need to update our major
 // schema version.
-static_assert(JS_STRUCTURED_CLONE_VERSION == 6,
+static_assert(JS_STRUCTURED_CLONE_VERSION == 7,
               "Need to update the major schema version.");
 
 // Major schema version. Bump for almost everything.
-const uint32_t kMajorSchemaVersion = 23;
+const uint32_t kMajorSchemaVersion = 24;
 
 // Minor schema version. Should almost always be 0 (maybe bump on release
 // branches if we have to).
 const uint32_t kMinorSchemaVersion = 0;
 
 // The schema version we store in the SQLite database is a (signed) 32-bit
 // integer. The major version is left-shifted 4 bits so the max value is
 // 0xFFFFFFF. The minor version occupies the lower 4 bits and its max is 0xF.
@@ -4097,16 +4097,29 @@ UpgradeSchemaFrom22_0To23_0(mozIStorageC
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
 nsresult
+UpgradeSchemaFrom23_0To24_0(mozIStorageConnection* aConnection)
+{
+  // The only change between 23 and 24 was a different structured clone format,
+  // but it's backwards-compatible.
+  nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(24, 0));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
 GetDatabaseFileURL(nsIFile* aDatabaseFile,
                    PersistenceType aPersistenceType,
                    const nsACString& aGroup,
                    const nsACString& aOrigin,
                    uint32_t aTelemetryId,
                    nsIFileURL** aResult)
 {
   MOZ_ASSERT(aDatabaseFile);
@@ -4602,17 +4615,17 @@ CreateStorageConnection(nsIFile* aDBFile
       }
 
       rv = stmt->Execute();
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     } else  {
       // This logic needs to change next time we change the schema!
-      static_assert(kSQLiteSchemaVersion == int32_t((23 << 4) + 0),
+      static_assert(kSQLiteSchemaVersion == int32_t((24 << 4) + 0),
                     "Upgrade function needed due to schema version increase.");
 
       while (schemaVersion != kSQLiteSchemaVersion) {
         if (schemaVersion == 4) {
           rv = UpgradeSchemaFrom4To5(connection);
         } else if (schemaVersion == 5) {
           rv = UpgradeSchemaFrom5To6(connection);
         } else if (schemaVersion == 6) {
@@ -4646,16 +4659,18 @@ CreateStorageConnection(nsIFile* aDBFile
         } else if (schemaVersion == MakeSchemaVersion(19, 0)) {
           rv = UpgradeSchemaFrom19_0To20_0(aFMDirectory, connection);
         } else if (schemaVersion == MakeSchemaVersion(20, 0)) {
           rv = UpgradeSchemaFrom20_0To21_0(connection);
         } else if (schemaVersion == MakeSchemaVersion(21, 0)) {
           rv = UpgradeSchemaFrom21_0To22_0(connection);
         } else if (schemaVersion == MakeSchemaVersion(22, 0)) {
           rv = UpgradeSchemaFrom22_0To23_0(connection, aOrigin);
+        } else if (schemaVersion == MakeSchemaVersion(23, 0)) {
+          rv = UpgradeSchemaFrom23_0To24_0(connection);
         } else {
           IDB_WARNING("Unable to open IndexedDB database, no upgrade path is "
                       "available!");
           return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
         }
 
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -75,17 +75,19 @@ struct IDBObjectStore::StructuredCloneWr
   };
 
   JSAutoStructuredCloneBuffer mCloneBuffer;
   nsTArray<BlobOrMutableFile> mBlobOrMutableFiles;
   IDBDatabase* mDatabase;
   uint64_t mOffsetToKeyProp;
 
   explicit StructuredCloneWriteInfo(IDBDatabase* aDatabase)
-    : mDatabase(aDatabase)
+    : mCloneBuffer(JS::StructuredCloneScope::SameProcessSameThread, nullptr,
+                   nullptr)
+    , mDatabase(aDatabase)
     , mOffsetToKeyProp(0)
   {
     MOZ_ASSERT(aDatabase);
 
     MOZ_COUNT_CTOR(StructuredCloneWriteInfo);
   }
 
   StructuredCloneWriteInfo(StructuredCloneWriteInfo&& aCloneWriteInfo)
@@ -1105,16 +1107,17 @@ IDBObjectStore::DeserializeValue(JSConte
     nullptr,
     nullptr,
     nullptr
   };
 
   // FIXME: Consider to use StructuredCloneHolder here and in other
   //        deserializing methods.
   if (!JS_ReadStructuredClone(aCx, data, dataLen, JS_STRUCTURED_CLONE_VERSION,
+                              JS::StructuredCloneScope::SameProcessSameThread,
                               aValue, &callbacks, &aCloneReadInfo)) {
     return false;
   }
 
   return true;
 }
 
 // static
@@ -1143,16 +1146,17 @@ IDBObjectStore::DeserializeIndexValue(JS
 
   static const JSStructuredCloneCallbacks callbacks = {
     CommonStructuredCloneReadCallback<IndexDeserializationHelper>,
     nullptr,
     nullptr
   };
 
   if (!JS_ReadStructuredClone(aCx, data, dataLen, JS_STRUCTURED_CLONE_VERSION,
+                              JS::StructuredCloneScope::SameProcessSameThread,
                               aValue, &callbacks, &aCloneReadInfo)) {
     return false;
   }
 
   return true;
 }
 
 #if !defined(MOZ_B2G)
@@ -1186,16 +1190,17 @@ IDBObjectStore::DeserializeUpgradeValue(
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr
   };
 
   if (!JS_ReadStructuredClone(aCx, data, dataLen, JS_STRUCTURED_CLONE_VERSION,
+                              JS::StructuredCloneScope::SameProcessSameThread,
                               aValue, &callbacks, &aCloneReadInfo)) {
     return false;
   }
 
   return true;
 }
 
 #endif // MOZ_B2G
--- a/dom/indexedDB/IndexedDatabaseInlines.h
+++ b/dom/indexedDB/IndexedDatabaseInlines.h
@@ -42,25 +42,29 @@ StructuredCloneFile::operator==(const St
          this->mMutableFile == aOther.mMutableFile &&
          this->mFileInfo == aOther.mFileInfo &&
          this->mMutable == aOther.mMutable;
 }
 
 inline
 StructuredCloneReadInfo::StructuredCloneReadInfo()
   : mDatabase(nullptr)
+  , mCloneBuffer(JS::StructuredCloneScope::SameProcessSameThread, nullptr,
+                 nullptr)
 {
   MOZ_COUNT_CTOR(StructuredCloneReadInfo);
 }
 
 inline
 StructuredCloneReadInfo::StructuredCloneReadInfo(
                              SerializedStructuredCloneReadInfo&& aCloneReadInfo)
   : mData(Move(aCloneReadInfo.data()))
   , mDatabase(nullptr)
+  , mCloneBuffer(JS::StructuredCloneScope::SameProcessSameThread, nullptr,
+                 nullptr)
 {
   MOZ_COUNT_CTOR(StructuredCloneReadInfo);
 }
 
 inline
 StructuredCloneReadInfo::~StructuredCloneReadInfo()
 {
   MOZ_COUNT_DTOR(StructuredCloneReadInfo);
--- a/dom/ipc/StructuredCloneData.h
+++ b/dom/ipc/StructuredCloneData.h
@@ -75,17 +75,17 @@ private:
 };
 
 class StructuredCloneData : public StructuredCloneHolder
 {
 public:
   StructuredCloneData()
     : StructuredCloneHolder(StructuredCloneHolder::CloningSupported,
                             StructuredCloneHolder::TransferringSupported,
-                            StructuredCloneHolder::DifferentProcess)
+                            StructuredCloneHolder::StructuredCloneScope::DifferentProcess)
     , mExternalData(nullptr)
     , mExternalDataLength(0)
   {}
 
   StructuredCloneData(const StructuredCloneData&) = delete;
 
   ~StructuredCloneData()
   {
--- a/dom/media/webaudio/AudioEventTimeline.cpp
+++ b/dom/media/webaudio/AudioEventTimeline.cpp
@@ -258,17 +258,17 @@ AudioEventTimeline::GetValuesAtTimeHelpe
   // SetTarget nodes can be handled no matter what their next node is (if
   // they have one)
   if (aPrevious->mType == AudioTimelineEvent::SetTarget) {
     return ExponentialApproach(TimeOf(aPrevious),
                                mLastComputedValue, aPrevious->mValue,
                                aPrevious->mTimeConstant, aTime);
   }
 
-  // SetValueCurve events can be handled no mattar what their next node is
+  // SetValueCurve events can be handled no matter what their next node is
   // (if they have one)
   if (aPrevious->mType == AudioTimelineEvent::SetValueCurve) {
     return ExtractValueFromCurve(TimeOf(aPrevious),
                                  aPrevious->mCurve, aPrevious->mCurveLength,
                                  aPrevious->mDuration, aTime);
   }
 
   // If the requested time is after all of the existing events
--- a/dom/media/webaudio/AudioEventTimeline.h
+++ b/dom/media/webaudio/AudioEventTimeline.h
@@ -336,16 +336,23 @@ public:
     return mEvents.Length();
   }
 
   template<class TimeType>
   void CleanupEventsOlderThan(TimeType aTime)
   {
     while (mEvents.Length() > 1 &&
         aTime > mEvents[1].template Time<TimeType>()) {
+
+      if (mEvents[1].mType == AudioTimelineEvent::SetTarget) {
+        mLastComputedValue = GetValuesAtTimeHelperInternal(
+                                mEvents[1].template Time<TimeType>(),
+                                &mEvents[0], nullptr);
+      }
+
       mEvents.RemoveElementAt(0);
     }
   }
 
 private:
   template<class TimeType>
   void GetValuesAtTimeHelper(TimeType aTime, float* aBuffer, const size_t aSize);
 
--- a/dom/messagechannel/SharedMessagePortMessage.h
+++ b/dom/messagechannel/SharedMessagePortMessage.h
@@ -19,17 +19,17 @@ class SharedMessagePortMessage final : p
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(SharedMessagePortMessage)
 
   nsTArray<uint8_t> mData;
 
   SharedMessagePortMessage()
     : StructuredCloneHolder(CloningSupported, TransferringSupported,
-                            DifferentProcess)
+                            StructuredCloneScope::DifferentProcess)
   {}
 
   void Read(nsISupports* aParent,
             JSContext* aCx,
             JS::MutableHandle<JS::Value> aValue,
             ErrorResult& aRv);
 
   void Write(JSContext* aCx,
--- a/dom/mobilemessage/android/SmsManager.cpp
+++ b/dom/mobilemessage/android/SmsManager.cpp
@@ -10,16 +10,18 @@
 #include "mozilla/dom/mobilemessage/SmsParent.h"
 #include "mozilla/dom/mobilemessage/SmsTypes.h"
 #include "mozilla/dom/mobilemessage/Types.h"
 #include "MobileMessageThreadInternal.h"
 #include "SmsMessageInternal.h"
 #include "mozilla/Services.h"
 #include "nsIMobileMessageDatabaseService.h"
 #include "nsIObserverService.h"
+#include "nsThreadUtils.h"
+#include "AndroidJavaWrappers.h"
 
 using namespace mozilla::dom;
 using namespace mozilla::dom::mobilemessage;
 
 namespace mozilla {
 
 /*static*/
 void
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -1540,32 +1540,32 @@ bool nsPluginInstanceOwner::AddPluginVie
     mJavaView = mInstance->GetJavaSurface();
 
     if (!mJavaView)
       return false;
 
     mJavaView = (void*)jni::GetGeckoThreadEnv()->NewGlobalRef((jobject)mJavaView);
   }
 
-  if (AndroidBridge::Bridge())
-    AndroidBridge::Bridge()->AddPluginView((jobject)mJavaView, aRect, mFullScreen);
-
-  if (mFullScreen)
+  if (mFullScreen) {
+    java::GeckoAppShell::AddFullScreenPluginView(jni::Object::Ref::From(jobject(mJavaView)));
     sFullScreenInstance = this;
+  }
 
   return true;
 }
 
 void nsPluginInstanceOwner::RemovePluginView()
 {
   if (!mInstance || !mJavaView)
     return;
 
-  java::GeckoAppShell::RemovePluginView(
-      jni::Object::Ref::From(jobject(mJavaView)), mFullScreen);
+  if (mFullScreen) {
+    java::GeckoAppShell::RemoveFullScreenPluginView(jni::Object::Ref::From(jobject(mJavaView)));
+  }
   jni::GetGeckoThreadEnv()->DeleteGlobalRef((jobject)mJavaView);
   mJavaView = nullptr;
 
   if (mFullScreen)
     sFullScreenInstance = nullptr;
 }
 
 void
--- a/dom/presentation/PresentationSessionInfo.cpp
+++ b/dom/presentation/PresentationSessionInfo.cpp
@@ -602,16 +602,18 @@ PresentationControllingInfo::Shutdown(ns
 {
   PresentationSessionInfo::Shutdown(aReason);
 
   // Close the server socket if any.
   if (mServerSocket) {
     NS_WARN_IF(NS_FAILED(mServerSocket->Close()));
     mServerSocket = nullptr;
   }
+
+  mIsReconnecting = false;
 }
 
 nsresult
 PresentationControllingInfo::GetAddress()
 {
 #if defined(MOZ_WIDGET_GONK)
   nsCOMPtr<nsINetworkManager> networkManager =
     do_GetService("@mozilla.org/network/manager;1");
@@ -677,16 +679,23 @@ PresentationControllingInfo::GetAddress(
   return NS_OK;
 }
 
 nsresult
 PresentationControllingInfo::OnGetAddress(const nsACString& aAddress)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
+  if (NS_WARN_IF(!mServerSocket)) {
+    return NS_ERROR_FAILURE;
+  }
+  if (NS_WARN_IF(!mControlChannel)) {
+    return NS_ERROR_FAILURE;
+  }
+
   // Prepare and send the offer.
   int32_t port;
   nsresult rv = mServerSocket->GetPort(&port);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   RefPtr<TCPPresentationChannelDescription> description =
@@ -755,16 +764,20 @@ PresentationControllingInfo::NotifyConne
 {
   PRES_DEBUG("%s:id[%s], role[%d]\n", __func__,
              NS_ConvertUTF16toUTF8(mSessionId).get(), mRole);
 
   MOZ_ASSERT(NS_IsMainThread());
 
   switch (mState) {
     case nsIPresentationSessionListener::STATE_CONNECTING: {
+      if (mIsReconnecting) {
+        return ContinueReconnect();
+      }
+
       nsresult rv = mControlChannel->Launch(GetSessionId(), GetUrl());
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       Unused << NS_WARN_IF(NS_FAILED(BuildTransport()));
       break;
     }
     case nsIPresentationSessionListener::STATE_TERMINATED: {
@@ -782,21 +795,20 @@ NS_IMETHODIMP
 PresentationControllingInfo::NotifyReconnected()
 {
   PRES_DEBUG("%s:id[%s], role[%d]\n", __func__,
              NS_ConvertUTF16toUTF8(mSessionId).get(), mRole);
 
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mReconnectCallback);
 
-  if (NS_WARN_IF(mState == nsIPresentationSessionListener::STATE_TERMINATED)) {
+  if (NS_WARN_IF(mState != nsIPresentationSessionListener::STATE_CONNECTING)) {
     return NS_ERROR_FAILURE;
   }
 
-  SetStateWithReason(nsIPresentationSessionListener::STATE_CONNECTING, NS_OK);
   return mReconnectCallback->NotifySuccess();
 }
 
 nsresult
 PresentationControllingInfo::BuildTransport()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
@@ -931,46 +943,81 @@ PresentationControllingInfo::OnStopListe
   }
 
   // It happens after the session is ready. Change the state to CLOSED.
   SetStateWithReason(nsIPresentationSessionListener::STATE_CLOSED, aStatus);
 
   return NS_OK;
 }
 
+/**
+ * The steps to reconnect a session are summarized below:
+ * 1. Change |mState| to CONNECTING.
+ * 2. Check whether |mControlChannel| is existed or not. Usually we have to
+ *    create a new control cahnnel.
+ * 3.1 |mControlChannel| is null, which means we have to create a new one.
+ *     |EstablishControlChannel| is called to create a new control channel.
+ *     At this point, |mControlChannel| is not able to use yet. Set
+ *     |mIsReconnecting| to true and wait until |NotifyConnected|.
+ * 3.2 |mControlChannel| is not null and is avaliable.
+ *     We can just call |ContinueReconnect| to send reconnect command.
+ * 4. |NotifyReconnected| of |nsIPresentationControlChannelListener| is called
+ *    to indicate the receiver is ready for reconnecting.
+ * 5. Once both step 3 and 4 are done, the rest is to build a new data
+ *    transport channel by following the same steps as starting a
+ *    new session.
+ */
+
 nsresult
 PresentationControllingInfo::Reconnect(nsIPresentationServiceCallback* aCallback)
 {
   if (!aCallback) {
     return NS_ERROR_INVALID_ARG;
   }
 
   mReconnectCallback = aCallback;
 
   if (NS_WARN_IF(mState == nsIPresentationSessionListener::STATE_TERMINATED)) {
     return mReconnectCallback->NotifyError(NS_ERROR_DOM_INVALID_STATE_ERR);
   }
 
+  SetStateWithReason(nsIPresentationSessionListener::STATE_CONNECTING, NS_OK);
+
   nsresult rv = NS_OK;
   if (!mControlChannel) {
     nsCOMPtr<nsIPresentationControlChannel> ctrlChannel;
     rv = mDevice->EstablishControlChannel(getter_AddRefs(ctrlChannel));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return mReconnectCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR);
     }
 
     rv = Init(ctrlChannel);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return mReconnectCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR);
     }
+
+    mIsReconnecting = true;
+  } else {
+    return ContinueReconnect();
   }
 
-  rv = mControlChannel->Reconnect(mSessionId, GetUrl());
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return mReconnectCallback->NotifyError(rv);
+  return NS_OK;
+}
+
+nsresult
+PresentationControllingInfo::ContinueReconnect()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mControlChannel);
+  MOZ_ASSERT(mReconnectCallback);
+
+  mIsReconnecting = false;
+  if (NS_WARN_IF(NS_FAILED(mControlChannel->Reconnect(mSessionId, GetUrl()))) &&
+      mReconnectCallback) {
+    return mReconnectCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR);
   }
 
   return NS_OK;
 }
 
 // nsIListNetworkAddressesListener
 NS_IMETHODIMP
 PresentationControllingInfo::OnListedNetworkAddresses(const char** aAddressArray,
--- a/dom/presentation/PresentationSessionInfo.h
+++ b/dom/presentation/PresentationSessionInfo.h
@@ -200,18 +200,21 @@ private:
   }
 
   void Shutdown(nsresult aReason) override;
 
   nsresult GetAddress();
 
   nsresult OnGetAddress(const nsACString& aAddress);
 
+  nsresult ContinueReconnect();
+
   nsCOMPtr<nsIServerSocket> mServerSocket;
   nsCOMPtr<nsIPresentationServiceCallback> mReconnectCallback;
+  bool mIsReconnecting = false;
 };
 
 // Session info with presenting browsing context (receiver side) behaviors.
 class PresentationPresentingInfo final : public PresentationSessionInfo
                                        , public PromiseNativeHandler
                                        , public nsITimerCallback
 {
 public:
--- a/dom/system/mac/CoreLocationLocationProvider.mm
+++ b/dom/system/mac/CoreLocationLocationProvider.mm
@@ -133,17 +133,17 @@ CoreLocationLocationProvider::MLSUpdate:
 NS_IMETHODIMP
 CoreLocationLocationProvider::MLSUpdate::NotifyError(uint16_t error)
 {
   mParentLocationProvider.NotifyError(error);
   return NS_OK;
 }
 class CoreLocationObjects {
 public:
-  NS_METHOD Init(CoreLocationLocationProvider* aProvider) {
+  nsresult Init(CoreLocationLocationProvider* aProvider) {
     mLocationManager = [[CLLocationManager alloc] init];
     NS_ENSURE_TRUE(mLocationManager, NS_ERROR_NOT_AVAILABLE);
 
     mLocationDelegate = [[LocationDelegate alloc] init:aProvider];
     NS_ENSURE_TRUE(mLocationDelegate, NS_ERROR_NOT_AVAILABLE);
 
     mLocationManager.desiredAccuracy = kDEFAULT_ACCURACY;
     mLocationManager.delegate = mLocationDelegate;
--- a/dom/webidl/EventHandler.webidl
+++ b/dom/webidl/EventHandler.webidl
@@ -55,16 +55,17 @@ interface GlobalEventHandlers {
            attribute EventHandler oninput;
            attribute EventHandler oninvalid;
            attribute EventHandler onkeydown;
            attribute EventHandler onkeypress;
            attribute EventHandler onkeyup;
            attribute EventHandler onload;
            attribute EventHandler onloadeddata;
            attribute EventHandler onloadedmetadata;
+           attribute EventHandler onloadend;
            attribute EventHandler onloadstart;
            attribute EventHandler onmousedown;
   [LenientThis] attribute EventHandler onmouseenter;
   [LenientThis] attribute EventHandler onmouseleave;
            attribute EventHandler onmousemove;
            attribute EventHandler onmouseout;
            attribute EventHandler onmouseover;
            attribute EventHandler onmouseup;
--- a/dom/workers/ServiceWorkerClient.cpp
+++ b/dom/workers/ServiceWorkerClient.cpp
@@ -88,17 +88,17 @@ class ServiceWorkerClientPostMessageRunn
   : public Runnable
   , public StructuredCloneHolder
 {
   uint64_t mWindowId;
 
 public:
   explicit ServiceWorkerClientPostMessageRunnable(uint64_t aWindowId)
     : StructuredCloneHolder(CloningSupported, TransferringSupported,
-                            SameProcessDifferentThread)
+                            StructuredCloneScope::SameProcessDifferentThread)
     , mWindowId(aWindowId)
   {}
 
   NS_IMETHOD
   Run() override
   {
     AssertIsOnMainThread();
     nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId);
--- a/dom/workers/ServiceWorkerEvents.cpp
+++ b/dom/workers/ServiceWorkerEvents.cpp
@@ -1086,17 +1086,17 @@ PushMessageData::Blob(ErrorResult& aRv)
       mOwner, EmptyString(), mBytes.Length(), data, aRv);
     if (blob) {
       return blob.forget();
     }
   }
   return nullptr;
 }
 
-NS_METHOD
+nsresult
 PushMessageData::EnsureDecodedText()
 {
   if (mBytes.IsEmpty() || !mDecodedText.IsEmpty()) {
     return NS_OK;
   }
   nsresult rv = BodyUtil::ConsumeText(
     mBytes.Length(),
     reinterpret_cast<uint8_t*>(mBytes.Elements()),
--- a/dom/workers/ServiceWorkerEvents.h
+++ b/dom/workers/ServiceWorkerEvents.h
@@ -209,17 +209,17 @@ public:
 
   PushMessageData(nsISupports* aOwner, nsTArray<uint8_t>&& aBytes);
 private:
   nsCOMPtr<nsISupports> mOwner;
   nsTArray<uint8_t> mBytes;
   nsString mDecodedText;
   ~PushMessageData();
 
-  NS_METHOD EnsureDecodedText();
+  nsresult EnsureDecodedText();
   uint8_t* GetContentsCopy();
 };
 
 class PushEvent final : public ExtendableEvent
 {
   RefPtr<PushMessageData> mData;
 
 protected:
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -669,17 +669,17 @@ class MessageEventRunnable final : publi
 
   RefPtr<PromiseNativeHandler> mHandler;
 
 public:
   MessageEventRunnable(WorkerPrivate* aWorkerPrivate,
                        TargetAndBusyBehavior aBehavior)
   : WorkerRunnable(aWorkerPrivate, aBehavior)
   , StructuredCloneHolder(CloningSupported, TransferringSupported,
-                          SameProcessDifferentThread)
+                          StructuredCloneScope::SameProcessDifferentThread)
   {
   }
 
   void
   SetServiceWorkerData(UniquePtr<ServiceWorkerClientInfo>&& aSource,
                        PromiseNativeHandler* aHandler)
   {
     mEventSource = Move(aSource);
--- a/dom/xhr/XMLHttpRequestWorker.cpp
+++ b/dom/xhr/XMLHttpRequestWorker.cpp
@@ -239,17 +239,17 @@ class SendRunnable final
   nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
   bool mHasUploadListeners;
 
 public:
   SendRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
                const nsAString& aStringBody)
   : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy)
   , StructuredCloneHolder(CloningSupported, TransferringNotSupported,
-                          SameProcessDifferentThread)
+                          StructuredCloneScope::SameProcessDifferentThread)
   , mStringBody(aStringBody)
   , mHasUploadListeners(false)
   {
   }
 
   void SetHaveUploadListeners(bool aHasUploadListeners)
   {
     mHasUploadListeners = aHasUploadListeners;
@@ -497,30 +497,30 @@ class EventRunnable final : public MainT
   JS::PersistentRooted<JSObject*> mScopeObj;
 
 public:
   EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType,
                 bool aLengthComputable, uint64_t aLoaded, uint64_t aTotal,
                 JS::Handle<JSObject*> aScopeObj)
   : MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy),
     StructuredCloneHolder(CloningSupported, TransferringNotSupported,
-                          SameProcessDifferentThread),
+                          StructuredCloneScope::SameProcessDifferentThread),
     mType(aType), mResponse(JS::UndefinedValue()), mLoaded(aLoaded),
     mTotal(aTotal), mEventStreamId(aProxy->mInnerEventStreamId), mStatus(0),
     mReadyState(0), mUploadEvent(aUploadEvent), mProgressEvent(true),
     mLengthComputable(aLengthComputable), mUseCachedArrayBufferResponse(false),
     mResponseTextResult(NS_OK), mStatusResult(NS_OK), mResponseResult(NS_OK),
     mScopeObj(RootingCx(), aScopeObj)
   { }
 
   EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType,
                 JS::Handle<JSObject*> aScopeObj)
   : MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy),
     StructuredCloneHolder(CloningSupported, TransferringNotSupported,
-                          SameProcessDifferentThread),
+                          StructuredCloneScope::SameProcessDifferentThread),
     mType(aType), mResponse(JS::UndefinedValue()), mLoaded(0), mTotal(0),
     mEventStreamId(aProxy->mInnerEventStreamId), mStatus(0), mReadyState(0),
     mUploadEvent(aUploadEvent), mProgressEvent(false), mLengthComputable(0),
     mUseCachedArrayBufferResponse(false), mResponseTextResult(NS_OK),
     mStatusResult(NS_OK), mResponseResult(NS_OK),
     mScopeObj(RootingCx(), aScopeObj)
   { }
 
--- a/embedding/browser/nsEmbedStream.cpp
+++ b/embedding/browser/nsEmbedStream.cpp
@@ -26,23 +26,23 @@ nsEmbedStream::~nsEmbedStream()
 }
 
 void
 nsEmbedStream::InitOwner(nsIWebBrowser* aOwner)
 {
   mOwner = aOwner;
 }
 
-NS_METHOD
+nsresult
 nsEmbedStream::Init(void)
 {
   return NS_OK;
 }
 
-NS_METHOD
+nsresult
 nsEmbedStream::OpenStream(nsIURI* aBaseURI, const nsACString& aContentType)
 {
   nsresult rv;
   NS_ENSURE_ARG_POINTER(aBaseURI);
   NS_ENSURE_TRUE(IsASCII(aContentType), NS_ERROR_INVALID_ARG);
 
   // if we're already doing a stream, return an error
   if (mOutputStream) {
@@ -63,17 +63,17 @@ nsEmbedStream::OpenStream(nsIURI* aBaseU
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   mOutputStream = outputStream;
   return rv;
 }
 
-NS_METHOD
+nsresult
 nsEmbedStream::AppendToStream(const uint8_t* aData, uint32_t aLen)
 {
   nsresult rv;
   NS_ENSURE_STATE(mOutputStream);
 
   uint32_t bytesWritten = 0;
   rv = mOutputStream->Write(reinterpret_cast<const char*>(aData),
                             aLen, &bytesWritten);
@@ -81,17 +81,17 @@ nsEmbedStream::AppendToStream(const uint
     return rv;
   }
 
   NS_ASSERTION(bytesWritten == aLen,
                "underlying buffer couldn't handle the write");
   return rv;
 }
 
-NS_METHOD
+nsresult
 nsEmbedStream::CloseStream(void)
 {
   nsresult rv = NS_OK;
 
   // NS_ENSURE_STATE returns NS_ERROR_UNEXPECTED if the condition isn't
   // satisfied; this is exactly what we want to return.
   NS_ENSURE_STATE(mOutputStream);
   mOutputStream->Close();
--- a/embedding/browser/nsEmbedStream.h
+++ b/embedding/browser/nsEmbedStream.h
@@ -13,21 +13,21 @@
 #include "nsIWebBrowser.h"
 
 class nsEmbedStream : public nsISupports
 {
 public:
   nsEmbedStream();
 
   void InitOwner(nsIWebBrowser* aOwner);
-  NS_METHOD Init(void);
+  nsresult Init(void);
 
-  NS_METHOD OpenStream(nsIURI* aBaseURI, const nsACString& aContentType);
-  NS_METHOD AppendToStream(const uint8_t* aData, uint32_t aLen);
-  NS_METHOD CloseStream(void);
+  nsresult OpenStream(nsIURI* aBaseURI, const nsACString& aContentType);
+  nsresult AppendToStream(const uint8_t* aData, uint32_t aLen);
+  nsresult CloseStream(void);
 
   NS_DECL_ISUPPORTS
 
 protected:
   virtual ~nsEmbedStream();
 
 private:
   nsIWebBrowser* mOwner;
--- a/extensions/auth/nsHttpNegotiateAuth.cpp
+++ b/extensions/auth/nsHttpNegotiateAuth.cpp
@@ -60,17 +60,37 @@ static const char kNegotiateAuthSSPI[] =
 //-----------------------------------------------------------------------------
 
 // Return false when the channel comes from a Private browsing window.
 static bool
 TestNotInPBMode(nsIHttpAuthenticableChannel *authChannel)
 {
     nsCOMPtr<nsIChannel> bareChannel = do_QueryInterface(authChannel);
     MOZ_ASSERT(bareChannel);
-    return !NS_UsePrivateBrowsing(bareChannel);
+
+    if (!NS_UsePrivateBrowsing(bareChannel)) {
+        return true;
+    }
+
+    nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+    if (!prefs) {
+        return true;
+    }
+
+    // When the "Never remember history" option is set, all channels are
+    // set PB mode flag, but here we want to make an exception, users
+    // want their credentials go out.
+    bool dontRememberHistory;
+    if (NS_SUCCEEDED(prefs->GetBoolPref("browser.privatebrowsing.autostart",
+                                        &dontRememberHistory)) &&
+        dontRememberHistory) {
+        return true;
+    }
+
+    return false;
 }
 
 NS_IMETHODIMP
 nsHttpNegotiateAuth::GetAuthFlags(uint32_t *flags)
 {
     //
     // Negotiate Auth creds should not be reused across multiple requests.
     // Only perform the negotiation when it is explicitly requested by the
--- a/gfx/2d/FilterNodeSoftware.cpp
+++ b/gfx/2d/FilterNodeSoftware.cpp
@@ -1484,24 +1484,26 @@ FilterNodeFloodSoftware::Render(const In
   int32_t stride = targetMap.GetStride();
 
   if (format == SurfaceFormat::B8G8R8A8) {
     uint32_t color = ColorToBGRA(mColor);
     for (int32_t y = 0; y < aRect.height; y++) {
       for (int32_t x = 0; x < aRect.width; x++) {
         *((uint32_t*)targetData + x) = color;
       }
+      PodZero(&targetData[aRect.width * 4], stride - aRect.width * 4);
       targetData += stride;
     }
   } else if (format == SurfaceFormat::A8) {
     uint8_t alpha = NS_lround(mColor.a * 255.0f);
     for (int32_t y = 0; y < aRect.height; y++) {
       for (int32_t x = 0; x < aRect.width; x++) {
         targetData[x] = alpha;
       }
+      PodZero(&targetData[aRect.width], stride - aRect.width);
       targetData += stride;
     }
   } else {
     gfxDevCrash(LogReason::FilterInputFormat) << "Bad format in flood render " << (int)format;
     return nullptr;
   }
 
   return target.forget();
@@ -1708,24 +1710,30 @@ static void TransferComponents(DataSourc
     return;
   }
 
   uint8_t* sourceData = sourceMap.GetData();
   int32_t sourceStride = sourceMap.GetStride();
   uint8_t* targetData = targetMap.GetData();
   int32_t targetStride = targetMap.GetStride();
 
+  MOZ_ASSERT(sourceStride <= targetStride, "target smaller than source");
+
   for (int32_t y = 0; y < size.height; y++) {
     for (int32_t x = 0; x < size.width; x++) {
       uint32_t sourceIndex = y * sourceStride + x * BytesPerPixel;
       uint32_t targetIndex = y * targetStride + x * BytesPerPixel;
       for (uint32_t i = 0; i < BytesPerPixel; i++) {
         targetData[targetIndex + i] = aLookupTables[i][sourceData[sourceIndex + i]];
       }
     }
+
+    // Zero padding to keep valgrind happy.
+    PodZero(&targetData[y * targetStride + size.width * BytesPerPixel],
+            targetStride - size.width * BytesPerPixel);
   }
 }
 
 bool
 IsAllZero(uint8_t aLookupTable[256])
 {
   for (int32_t i = 0; i < 256; i++) {
     if (aLookupTable[i] != 0) {
@@ -2420,17 +2428,17 @@ already_AddRefed<DataSourceSurface>
 FilterNodeConvolveMatrixSoftware::DoRender(const IntRect& aRect,
                                            CoordType aKernelUnitLengthX,
                                            CoordType aKernelUnitLengthY)
 {
   if (mKernelSize.width <= 0 || mKernelSize.height <= 0 ||
       mKernelMatrix.size() != uint32_t(mKernelSize.width * mKernelSize.height) ||
       !IntRect(IntPoint(0, 0), mKernelSize).Contains(mTarget) ||
       mDivisor == 0) {
-    return Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8);
+    return Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8, true);
   }
 
   IntRect srcRect = InflatedSourceRect(aRect);
 
   // Inflate the source rect by another pixel because the bilinear filtering in
   // ColorComponentAtPoint may want to access the margins.
   srcRect.Inflate(1);
 
@@ -2634,16 +2642,19 @@ FilterNodeDisplacementMapSoftware::Rende
       uint32_t targIndex = y * targetStride + 4 * x;
       int32_t sourceX = x +
         scaleOver255 * mapData[mapIndex + xChannel] + scaleAdjustment;
       int32_t sourceY = y +
         scaleOver255 * mapData[mapIndex + yChannel] + scaleAdjustment;
       *(uint32_t*)(targetData + targIndex) =
         ColorAtPoint(sourceData, sourceStride, sourceX, sourceY);
     }
+
+    // Keep valgrind happy.
+    PodZero(&targetData[y * targetStride + 4 * aRect.width], targetStride - 4 * aRect.width);
   }
 
   return target.forget();
 }
 
 void
 FilterNodeDisplacementMapSoftware::RequestFromInputsForRect(const IntRect &aRect)
 {
@@ -3530,16 +3541,19 @@ FilterNodeLightingSoftware<LightType, Li
       IntPoint pointInFilterSpace(aRect.x + x, aRect.y + y);
       Float Z = mSurfaceScale * sourceData[sourceIndex] / 255.0f;
       Point3D pt(pointInFilterSpace.x, pointInFilterSpace.y, Z);
       Point3D rayDir = mLight.GetVectorToLight(pt);
       uint32_t color = mLight.GetColor(lightColor, rayDir);
 
       *(uint32_t*)(targetData + targetIndex) = mLighting.LightPixel(normal, rayDir, color);
     }
+
+    // Zero padding to keep valgrind happy.
+    PodZero(&targetData[y * targetStride + 4 * size.width], targetStride - 4 * size.width);
   }
 
   return target.forget();
 }
 
 DiffuseLightingSoftware::DiffuseLightingSoftware()
  : mDiffuseConstant(0)
 {
--- a/gfx/2d/Polygon.h
+++ b/gfx/2d/Polygon.h
@@ -83,9 +83,9 @@ private:
   Point3DTyped<Units> mNormal;
 };
 
 typedef BasePolygon3D<UnknownUnits> Polygon3D;
 
 } // namespace gfx
 } // namespace mozilla
 
-#endif /* MOZILLA_GFX_POLYGON_H */
\ No newline at end of file
+#endif /* MOZILLA_GFX_POLYGON_H */
--- a/gfx/2d/QuartzSupport.mm
+++ b/gfx/2d/QuartzSupport.mm
@@ -2,16 +2,17 @@
 // vim:set ts=2 sts=2 sw=2 et cin:
 /* 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 "QuartzSupport.h"
 #include "nsDebug.h"
 #include "MacIOSurface.h"
+#include "mozilla/Sprintf.h"
 
 #import <QuartzCore/QuartzCore.h>
 #import <AppKit/NSOpenGL.h>
 #include <dlfcn.h>
 #include "GLDefs.h"
 
 #define IOSURFACE_FRAMEWORK_PATH \
   "/System/Library/Frameworks/IOSurface.framework/IOSurface"
@@ -590,18 +591,17 @@ void nsCARenderer::SaveToDisk(MacIOSurfa
   ::CGDataProviderRelease(dataProvider);
   ::CGColorSpaceRelease(colorSpace);
   if (!cgImage) {
     surf->Unlock();
     return;
   }
 
   char cstr[1000];
-
-  sprintf(cstr, "file:///Users/benoitgirard/debug/iosurface_%i.png", ++sSaveToDiskSequence);
+  SprintfLiteral(cstr, "file:///Users/benoitgirard/debug/iosurface_%i.png", ++sSaveToDiskSequence);
 
   CFStringRef cfStr = ::CFStringCreateWithCString(kCFAllocatorDefault, cstr, kCFStringEncodingMacRoman);
 
   printf("Exporting: %s\n", cstr);
   CFURLRef url = ::CFURLCreateWithString( nullptr, cfStr, nullptr);
   ::CFRelease(cfStr);
 
   CFStringRef type = kUTTypePNG;
--- a/gfx/2d/ScaledFontWin.cpp
+++ b/gfx/2d/ScaledFontWin.cpp
@@ -2,16 +2,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ScaledFontWin.h"
 
 #include "AutoHelpersWin.h"
 #include "Logging.h"
+#include "nsString.h"
 #include "SFNTData.h"
 
 #ifdef USE_SKIA
 #include "skia/include/ports/SkTypeface_win.h"
 #endif
 
 #ifdef USE_CAIRO_SCALED_FONT
 #include "cairo-win32.h"
@@ -63,17 +64,18 @@ ScaledFontWin::GetFontFileData(FontFileD
     }
 
     // We cast here because for VS2015 char16_t != wchar_t, even though they are
     // both 16 bit.
     if (!sfntData->GetIndexForU16Name(
           reinterpret_cast<char16_t*>(mLogFont.lfFaceName), &index, LF_FACESIZE - 1)) {
       gfxWarning() << "Failed to get index for face name.";
       gfxDevCrash(LogReason::GetFontFileDataFailed) <<
-        "Failed to get index for face name |" << mLogFont.lfFaceName << "|.";
+        "Failed to get index for face name |" <<
+        NS_ConvertUTF16toUTF8(mLogFont.lfFaceName).get() << "|.";
       return false;
     }
   }
 
   aDataCallback(fontData.get(), tableSize, index, mSize, aBaton);
   return true;
 }
 
--- a/gfx/config/gfxFeature.cpp
+++ b/gfx/config/gfxFeature.cpp
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* vim: set sts=2 ts=8 sw=2 tw=99 et: */
 /* 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 "gfxFeature.h"
+
 #include "mozilla/Preferences.h"
-#include "prprf.h"
-#include "gfxFeature.h"
+#include "mozilla/Sprintf.h"
 #include "nsString.h"
 
 namespace mozilla {
 namespace gfx {
 
 bool
 FeatureState::IsEnabled() const
 {
@@ -259,14 +261,14 @@ FeatureState::GetFailureId() const
   return mFailureId;
 }
 
 void
 FeatureState::Instance::Set(FeatureStatus aStatus, const char* aMessage /* = nullptr */)
 {
   mStatus = aStatus;
   if (aMessage) {
-    PR_snprintf(mMessage, sizeof(mMessage), "%s", aMessage);
+    SprintfLiteral(mMessage, "%s", aMessage);
   }
 }
 
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/layers/BSPTree.h
+++ b/gfx/layers/BSPTree.h
@@ -84,9 +84,9 @@ private:
                     const nsTArray<float>& dots,
                     nsTArray<gfx::Point3D>& backPoints,
                     nsTArray<gfx::Point3D>& frontPoints);
 };
 
 } // namespace layers
 } // namespace mozilla
 
-#endif /* MOZILLA_LAYERS_BSPTREE_H */
\ No newline at end of file
+#endif /* MOZILLA_LAYERS_BSPTREE_H */
--- a/gfx/layers/LayerSorter.cpp
+++ b/gfx/layers/LayerSorter.cpp
@@ -12,16 +12,17 @@
 #include "Layers.h"                     // for Layer
 #include "gfxEnv.h"                     // for gfxEnv
 #include "gfxLineSegment.h"             // for gfxLineSegment
 #include "gfxPoint.h"                   // for gfxPoint
 #include "gfxQuad.h"                    // for gfxQuad
 #include "gfxRect.h"                    // for gfxRect
 #include "gfxTypes.h"                   // for gfxFloat
 #include "mozilla/gfx/BasePoint3D.h"    // for BasePoint3D
+#include "mozilla/Sprintf.h"            // for SprintfLiteral
 #include "nsRegion.h"                   // for nsIntRegion
 #include "nsTArray.h"                   // for nsTArray, etc
 #include "limits.h"
 #include "mozilla/Assertions.h"
 
 namespace mozilla {
 namespace layers {
 
@@ -178,19 +179,19 @@ static const int RESET = 0;
 // static const int REVERSE = 7;
 // static const int HIDDEN = 8;
 
 static void SetTextColor(uint32_t aColor)
 {
   char command[13];
 
   /* Command is the control command to the terminal */
-  sprintf(command, "%c[%d;%d;%dm", 0x1B, RESET,
-          aColor + XTERM_FOREGROUND_COLOR_OFFSET,
-          BLACK + XTERM_BACKGROUND_COLOR_OFFSET);
+  SprintfLiteral(command, "%c[%d;%d;%dm", 0x1B, RESET,
+                 aColor + XTERM_FOREGROUND_COLOR_OFFSET,
+                 BLACK + XTERM_BACKGROUND_COLOR_OFFSET);
   printf("%s", command);
 }
 
 static void print_layer_internal(FILE* aFile, Layer* aLayer, uint32_t aColor)
 {
   SetTextColor(aColor);
   fprintf(aFile, "%p", aLayer);
   SetTextColor(GREEN);
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -1763,17 +1763,17 @@ APZCTreeManager::FindRootContentApzcForL
 
 AsyncPanZoomController*
 APZCTreeManager::FindRootContentOrRootApzc() const
 {
   mTreeLock.AssertCurrentThreadOwns();
 
   // Note: this is intended to find the same "root" that would be found
   // by AsyncCompositionManager::ApplyAsyncContentTransformToTree inside
-  // the MOZ_ANDROID_APZ block. That is, it should find the RCD node if there
+  // the MOZ_WIDGET_ANDROID block. That is, it should find the RCD node if there
   // is one, or the root APZC if there is not.
   // Since BreadthFirstSearch is a pre-order search, we first do a search for
   // the RCD, and then if we don't find one, we do a search for the root APZC.
   HitTestingTreeNode* resultNode = BreadthFirstSearch<ReverseIterator>(mRootNode.get(),
       [](HitTestingTreeNode* aNode) {
         AsyncPanZoomController* apzc = aNode->GetApzc();
         return apzc && apzc->IsRootContent();
       });
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -69,19 +69,19 @@
 #include "nsStyleStruct.h"              // for nsTimingFunction
 #include "nsTArray.h"                   // for nsTArray, nsTArray_Impl, etc
 #include "nsThreadUtils.h"              // for NS_IsMainThread
 #include "nsViewportInfo.h"             // for kViewportMinScale, kViewportMaxScale
 #include "prsystem.h"                   // for PR_GetPhysicalMemorySize
 #include "SharedMemoryBasic.h"          // for SharedMemoryBasic
 #include "ScrollSnap.h"                 // for ScrollSnapUtils
 #include "WheelScrollAnimation.h"
-#if defined(MOZ_ANDROID_APZ)
+#if defined(MOZ_WIDGET_ANDROID)
 #include "AndroidAPZ.h"
-#endif // defined(MOZ_ANDROID_APZ)
+#endif // defined(MOZ_WIDGET_ANDROID)
 
 #define ENABLE_APZC_LOGGING 0
 // #define ENABLE_APZC_LOGGING 1
 
 #if ENABLE_APZC_LOGGING
 #  define APZC_LOG(...) printf_stderr("APZC: " __VA_ARGS__)
 #  define APZC_LOG_FM(fm, prefix, ...) \
     { std::stringstream ss; \
@@ -100,17 +100,17 @@ namespace layers {
 typedef mozilla::layers::AllowedTouchBehavior AllowedTouchBehavior;
 typedef GeckoContentController::APZStateChange APZStateChange;
 typedef GeckoContentController::TapType TapType;
 typedef mozilla::gfx::Point Point;
 typedef mozilla::gfx::Matrix4x4 Matrix4x4;
 using mozilla::gfx::PointTyped;
 
 // Choose between platform-specific implementations.
-#ifdef MOZ_ANDROID_APZ
+#ifdef MOZ_WIDGET_ANDROID
 typedef WidgetOverscrollEffect OverscrollEffect;
 typedef AndroidSpecificState PlatformSpecificState;
 typedef AndroidFlingAnimation FlingAnimation;
 #else
 typedef GenericOverscrollEffect OverscrollEffect;
 typedef PlatformSpecificStateBase PlatformSpecificState;  // no extra state, just use the base class
 typedef GenericFlingAnimation FlingAnimation;
 #endif
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -562,17 +562,17 @@ GetRootDocumentElementFor(nsIWidget* aWi
     }
   }
   return nullptr;
 }
 
 static nsIFrame*
 UpdateRootFrameForTouchTargetDocument(nsIFrame* aRootFrame)
 {
-#if defined(MOZ_ANDROID_APZ)
+#if defined(MOZ_WIDGET_ANDROID)
   // Re-target so that the hit test is performed relative to the frame for the
   // Root Content Document instead of the Root Document which are different in
   // Android. See bug 1229752 comment 16 for an explanation of why this is necessary.
   if (nsIDocument* doc = aRootFrame->PresContext()->PresShell()->GetTouchEventTargetDocument()) {
     if (nsIPresShell* shell = doc->GetShell()) {
       if (nsIFrame* frame = shell->GetRootFrame()) {
         return frame;
       }
--- a/gfx/layers/apz/util/APZEventState.cpp
+++ b/gfx/layers/apz/util/APZEventState.cpp
@@ -449,17 +449,17 @@ APZEventState::ProcessAPZStateChange(con
   }
 }
 
 void
 APZEventState::ProcessClusterHit()
 {
   // If we hit a cluster of links then we shouldn't activate any of them,
   // as we will be showing the zoomed view. (This is only called on Fennec).
-#ifndef MOZ_ANDROID_APZ
+#ifndef MOZ_WIDGET_ANDROID
   MOZ_ASSERT(false);
 #endif
   mActiveElementManager->ClearActivation();
 }
 
 bool
 APZEventState::SendPendingTouchPreventedResponse(bool aPreventDefault)
 {
--- a/gfx/layers/apz/util/APZThreadUtils.cpp
+++ b/gfx/layers/apz/util/APZThreadUtils.cpp
@@ -1,17 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/layers/APZThreadUtils.h"
 
 #include "mozilla/layers/Compositor.h"
-#ifdef MOZ_ANDROID_APZ
+#ifdef MOZ_WIDGET_ANDROID
 #include "AndroidBridge.h"
 #endif
 
 namespace mozilla {
 namespace layers {
 
 static bool sThreadAssertionsEnabled = true;
 static MessageLoop* sControllerThread;
@@ -52,17 +52,17 @@ APZThreadUtils::AssertOnCompositorThread
   }
 }
 
 /*static*/ void
 APZThreadUtils::RunOnControllerThread(already_AddRefed<Runnable> aTask)
 {
   RefPtr<Runnable> task = aTask;
 
-#ifdef MOZ_ANDROID_APZ
+#ifdef MOZ_WIDGET_ANDROID
   // This is needed while nsWindow::ConfigureAPZControllerThread is not propper
   // implemented.
   if (AndroidBridge::IsJavaUiThread()) {
     task->Run();
   } else {
     AndroidBridge::Bridge()->PostTaskToUiThread(task.forget(), 0);
   }
 #else
@@ -78,17 +78,17 @@ APZThreadUtils::RunOnControllerThread(al
     sControllerThread->PostTask(task.forget());
   }
 #endif
 }
 
 /*static*/ bool
 APZThreadUtils::IsControllerThread()
 {
-#ifdef MOZ_ANDROID_APZ
+#ifdef MOZ_WIDGET_ANDROID
   return AndroidBridge::IsJavaUiThread();
 #else
   return sControllerThread == MessageLoop::current();
 #endif
 }
 
 NS_IMPL_ISUPPORTS(GenericTimerCallbackBase, nsITimerCallback)
 
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -88,16 +88,17 @@ ClientLayerManager::MemoryPressureObserv
 }
 
 NS_IMPL_ISUPPORTS(ClientLayerManager::MemoryPressureObserver, nsIObserver)
 
 ClientLayerManager::ClientLayerManager(nsIWidget* aWidget)
   : mPhase(PHASE_NONE)
   , mWidget(aWidget)
   , mLatestTransactionId(0)
+  , mLastPaintTime(TimeDuration::Forever())
   , mTargetRotation(ROTATION_0)
   , mRepeatTransaction(false)
   , mIsRepeatTransaction(false)
   , mTransactionIncomplete(false)
   , mCompositorMightResample(false)
   , mNeedsComposite(false)
   , mPaintSequenceNumber(0)
   , mForwarder(new ShadowLayerForwarder(this))
@@ -278,27 +279,34 @@ ClientLayerManager::EndTransactionIntern
   profiler_tracing("Paint", "Rasterize", TRACING_INTERVAL_START);
 
   NS_ASSERTION(InConstruction(), "Should be in construction phase");
   mPhase = PHASE_DRAWING;
 
   ClientLayer* root = ClientLayer::ToClientLayer(GetRoot());
 
   mTransactionIncomplete = false;
-      
+
   // Apply pending tree updates before recomputing effective
   // properties.
   GetRoot()->ApplyPendingUpdatesToSubtree();
-    
+
   mPaintedLayerCallback = aCallback;
   mPaintedLayerCallbackData = aCallbackData;
 
   GetRoot()->ComputeEffectiveTransforms(Matrix4x4());
 
-  root->RenderLayer();
+  if (gfxPrefs::AlwaysPaint() && XRE_IsContentProcess()) {
+    TimeStamp start = TimeStamp::Now();
+    root->RenderLayer();
+    mLastPaintTime = TimeStamp::Now() - start;
+  } else {
+    root->RenderLayer();
+  }
+
   if (!mRepeatTransaction && !GetRoot()->GetInvalidRegion().IsEmpty()) {
     GetRoot()->Mutated();
   }
 
   if (!mIsRepeatTransaction) {
     mAnimationReadyTime = TimeStamp::Now();
     GetRoot()->StartPendingAnimations(mAnimationReadyTime);
   }
@@ -618,16 +626,20 @@ ClientLayerManager::ForwardTransaction(b
   mLatestTransactionId = mTransactionIdAllocator->GetTransactionId();
   TimeStamp transactionStart;
   if (!mTransactionIdAllocator->GetTransactionStart().IsNull()) {
     transactionStart = mTransactionIdAllocator->GetTransactionStart();
   } else {
     transactionStart = mTransactionStart;
   }
 
+  if (gfxPrefs::AlwaysPaint() && XRE_IsContentProcess()) {
+    mForwarder->SendPaintTime(mLatestTransactionId, mLastPaintTime);
+  }
+
   // forward this transaction's changeset to our LayerManagerComposite
   bool sent;
   AutoTArray<EditReply, 10> replies;
   if (mForwarder->EndTransaction(&replies, mRegionToClear,
         mLatestTransactionId, aScheduleComposite, mPaintSequenceNumber,
         mIsRepeatTransaction, transactionStart, &sent)) {
     for (nsTArray<EditReply>::size_type i = 0; i < replies.Length(); ++i) {
       const EditReply& reply = replies[i];
--- a/gfx/layers/client/ClientLayerManager.h
+++ b/gfx/layers/client/ClientLayerManager.h
@@ -298,17 +298,17 @@ private:
                               void* aCallbackData,
                               EndTransactionFlags);
 
   bool DependsOnStaleDevice() const;
 
   LayerRefArray mKeepAlive;
 
   nsIWidget* mWidget;
-  
+
   /* PaintedLayer callbacks; valid at the end of a transaciton,
    * while rendering */
   DrawPaintedLayerCallback mPaintedLayerCallback;
   void *mPaintedLayerCallbackData;
 
   // When we're doing a transaction in order to draw to a non-default
   // target, the layers transaction is only performed in order to send
   // a PLayers:Update.  We save the original non-default target to
@@ -316,16 +316,17 @@ private:
   // mDummyTarget as the render target.  After the transaction ends,
   // we send a message to our remote side to capture the actual pixels
   // being drawn to the default target, and then copy those pixels
   // back to mShadowTarget.
   RefPtr<gfxContext> mShadowTarget;
 
   RefPtr<TransactionIdAllocator> mTransactionIdAllocator;
   uint64_t mLatestTransactionId;
+  TimeDuration mLastPaintTime;
 
   // Sometimes we draw to targets that don't natively support
   // landscape/portrait orientation.  When we need to implement that
   // ourselves, |mTargetRotation| describes the induced transform we
   // need to apply when compositing content to our target.
   ScreenRotation mTargetRotation;
 
   // Used to repeat the transaction right away (to avoid rebuilding
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -1182,40 +1182,24 @@ ClientMultiTiledLayerBuffer::ComputeProg
 
   LayerMetricsWrapper scrollAncestor;
   mPaintedLayer->GetAncestorLayers(&scrollAncestor, nullptr, nullptr);
 
   // Find out the current view transform to determine which tiles to draw
   // first, and see if we should just abort this paint. Aborting is usually
   // caused by there being an incoming, more relevant paint.
   AsyncTransform viewTransform;
-#if defined(MOZ_WIDGET_ANDROID) && !defined(MOZ_ANDROID_APZ)
-  FrameMetrics contentMetrics = scrollAncestor.Metrics();
-  bool abortPaint = false;
-  // On Android, only the primary scrollable layer is async-scrolled, and the only one
-  // that the Java-side code can provide details about. If we're tiling some other layer
-  // then we already have all the information we need about it.
-  if (contentMetrics.GetScrollId() == mManager->GetRootScrollableLayerId()) {
-    FrameMetrics compositorMetrics = contentMetrics;
-    // The ProgressiveUpdateCallback updates the compositorMetrics
-    abortPaint = mManager->ProgressiveUpdateCallback(!staleRegion.Contains(aInvalidRegion),
-                                                     compositorMetrics,
-                                                     !drawingLowPrecision);
-    viewTransform = ComputeViewTransform(contentMetrics, compositorMetrics);
-  }
-#else
   MOZ_ASSERT(mSharedFrameMetricsHelper);
 
   bool abortPaint =
     mSharedFrameMetricsHelper->UpdateFromCompositorFrameMetrics(
       scrollAncestor,
       !staleRegion.Contains(aInvalidRegion),
       drawingLowPrecision,
       viewTransform);
-#endif
 
   TILING_LOG("TILING %p: Progressive update view transform %s zoom %f abort %d\n",
       mPaintedLayer, ToString(viewTransform.mTranslation).c_str(), viewTransform.mScale.scale, abortPaint);
 
   if (abortPaint) {
     // We ignore if front-end wants to abort if this is the first,
     // non-low-precision paint, as in that situation, we're about to override
     // front-end's page/viewport metrics.
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -788,17 +788,17 @@ ExpandRootClipRect(Layer* aLayer, const 
 #endif
     ParentLayerRect rect(rootClipRect.value());
     rect.Deflate(ViewAs<ParentLayerPixel>(aFixedLayerMargins,
       PixelCastJustification::ScreenIsParentLayerForRoot));
     aLayer->AsLayerComposite()->SetShadowClipRect(Some(RoundedOut(rect)));
   }
 }
 
-#ifdef MOZ_ANDROID_APZ
+#ifdef MOZ_WIDGET_ANDROID
 static void
 MoveScrollbarForLayerMargin(Layer* aRoot, FrameMetrics::ViewID aRootScrollId,
                             const ScreenMargin& aFixedLayerMargins)
 {
   // See bug 1223928 comment 9 - once we can detect the RCD with just the
   // isRootContent flag on the metrics, we can probably move this code into
   // ApplyAsyncTransformToScrollbar rather than having it as a separate
   // adjustment on the layer tree.
@@ -925,17 +925,17 @@ AsyncCompositionManager::ApplyAsyncConte
 
           if (!layer->IsScrollInfoLayer()) {
             controller->MarkAsyncTransformAppliedToContent();
           }
 
           const ScrollMetadata& scrollMetadata = layer->GetScrollMetadata(i);
           const FrameMetrics& metrics = scrollMetadata.GetMetrics();
 
-#if defined(MOZ_ANDROID_APZ)
+#if defined(MOZ_WIDGET_ANDROID)
           // If we find a metrics which is the root content doc, use that. If not, use
           // the root layer. Since this function recurses on children first we should
           // only end up using the root layer if the entire tree was devoid of a
           // root content metrics. This is a temporary solution; in the long term we
           // should not need the root content metrics at all. See bug 1201529 comment
           // 6 for details.
           if (!(*aOutFoundRoot)) {
             *aOutFoundRoot = metrics.IsRootContent() ||       /* RCD */
@@ -1501,17 +1501,17 @@ AsyncCompositionManager::TransformShadow
     // async using Gecko). If this fails, fall back to transforming the
     // primary scrollable layer.  "Failing" here means that we don't
     // find a frame that is async scrollable.  Note that the fallback
     // code also includes Fennec which is rendered async.  Fennec uses
     // its own platform-specific async rendering that is done partially
     // in Gecko and partially in Java.
     bool foundRoot = false;
     if (ApplyAsyncContentTransformToTree(root, &foundRoot)) {
-#if defined(MOZ_ANDROID_APZ)
+#if defined(MOZ_WIDGET_ANDROID)
       MOZ_ASSERT(foundRoot);
       if (foundRoot && mFixedLayerMargins != ScreenMargin()) {
         MoveScrollbarForLayerMargin(root, mRootScrollableId, mFixedLayerMargins);
       }
 #endif
     } else {
       AutoTArray<Layer*,1> scrollableLayers;
 #ifdef MOZ_WIDGET_ANDROID
--- a/gfx/layers/composite/AsyncCompositionManager.h
+++ b/gfx/layers/composite/AsyncCompositionManager.h
@@ -241,17 +241,17 @@ private:
 
   bool mReadyForCompose;
 
   gfx::Matrix mWorldTransform;
   LayerTransformRecorder mLayerTransformRecorder;
 
   TimeStamp mPreviousFrameTimeStamp;
 
-#ifdef MOZ_ANDROID_APZ
+#ifdef MOZ_WIDGET_ANDROID
   // The following two fields are only needed on Fennec with C++ APZ, because
   // then we need to reposition the gecko scrollbar to deal with the
   // dynamic toolbar shifting content around.
   FrameMetrics::ViewID mRootScrollableId;
   ScreenMargin mFixedLayerMargins;
 #endif
 };
 
--- a/gfx/layers/composite/FPSCounter.cpp
+++ b/gfx/layers/composite/FPSCounter.cpp
@@ -389,18 +389,17 @@ static void DrawDigits(unsigned int aVal
     unsigned int digit = aValue % (divisor * 10) / divisor;
     divisor /= 10;
 
     RefPtr<TexturedEffect> texturedEffect = static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
     texturedEffect->mTextureCoords = Rect(float(digit * FontWidth) / textureWidth, 0, FontWidth / textureWidth, 1.0f);
 
     Rect drawRect = Rect(aOffsetX + n * FontWidth, aOffsetY, FontWidth, FontHeight);
     IntRect clipRect = IntRect(0, 0, 300, 100);
-    aCompositor->DrawQuad(drawRect, clipRect,
-  aEffectChain, opacity, transform);
+    aCompositor->DrawQuad(drawRect, clipRect, aEffectChain, opacity, transform);
   }
 }
 
 void FPSState::DrawFPS(TimeStamp aNow,
                        int aOffsetX, int aOffsetY,
                        unsigned int aFillRatio,
                        Compositor* aCompositor)
 {
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -7,16 +7,17 @@
 #include <stddef.h>                     // for size_t
 #include <stdint.h>                     // for uint16_t, uint32_t
 #include "CanvasLayerComposite.h"       // for CanvasLayerComposite
 #include "ColorLayerComposite.h"        // for ColorLayerComposite
 #include "Composer2D.h"                 // for Composer2D
 #include "CompositableHost.h"           // for CompositableHost
 #include "ContainerLayerComposite.h"    // for ContainerLayerComposite, etc
 #include "FPSCounter.h"                 // for FPSState, FPSCounter
+#include "PaintCounter.h"               // For PaintCounter
 #include "FrameMetrics.h"               // for FrameMetrics
 #include "GeckoProfiler.h"              // for profiler_set_frame_number, etc
 #include "ImageLayerComposite.h"        // for ImageLayerComposite
 #include "Layers.h"                     // for Layer, ContainerLayer, etc
 #include "LayerScope.h"                 // for LayerScope Tool
 #include "protobuf/LayerScopePacket.pb.h" // for protobuf (LayerScope)
 #include "PaintedLayerComposite.h"      // for PaintedLayerComposite
 #include "TiledContentHost.h"
@@ -122,16 +123,18 @@ LayerManagerComposite::LayerManagerCompo
 , mDisabledApzWarning(false)
 , mCompositor(aCompositor)
 , mInTransaction(false)
 , mIsCompositorReady(false)
 , mDebugOverlayWantsNextFrame(false)
 , mGeometryChanged(true)
 , mLastFrameMissedHWC(false)
 , mWindowOverlayChanged(false)
+, mLastPaintTime(TimeDuration::Forever())
+, mRenderStartTime(TimeStamp::Now())
 {
   mTextRenderer = new TextRenderer(aCompositor);
   MOZ_ASSERT(aCompositor);
 }
 
 LayerManagerComposite::~LayerManagerComposite()
 {
   Destroy();
@@ -143,16 +146,17 @@ LayerManagerComposite::Destroy()
 {
   if (!mDestroyed) {
     mCompositor->GetWidget()->CleanupWindowEffects();
     if (mRoot) {
       RootLayer()->Destroy();
     }
     mRoot = nullptr;
     mClonedLayerTreeProperties = nullptr;
+    mPaintCounter = nullptr;
     mDestroyed = true;
   }
 }
 
 void
 LayerManagerComposite::UpdateRenderBounds(const IntRect& aRect)
 {
   mRenderBounds = aRect;
@@ -368,16 +372,17 @@ LayerManagerComposite::PostProcessLayers
 void
 LayerManagerComposite::EndTransaction(const TimeStamp& aTimeStamp,
                                       EndTransactionFlags aFlags)
 {
   NS_ASSERTION(mInTransaction, "Didn't call BeginTransaction?");
   NS_ASSERTION(!(aFlags & END_NO_COMPOSITE),
                "Shouldn't get END_NO_COMPOSITE here");
   mInTransaction = false;
+  mRenderStartTime = TimeStamp::Now();
 
   if (!mIsCompositorReady) {
     return;
   }
   mIsCompositorReady = false;
 
 #ifdef MOZ_LAYERS_HAVE_LOG
   MOZ_LAYERS_LOG(("  ----- (beginning paint)"));
@@ -554,32 +559,48 @@ LayerManagerComposite::RootLayer() const
 #endif
 
 void
 LayerManagerComposite::InvalidateDebugOverlay(nsIntRegion& aInvalidRegion, const IntRect& aBounds)
 {
   bool drawFps = gfxPrefs::LayersDrawFPS();
   bool drawFrameCounter = gfxPrefs::DrawFrameCounter();
   bool drawFrameColorBars = gfxPrefs::CompositorDrawColorBars();
+  bool drawPaintTimes = gfxPrefs::AlwaysPaint();
 
   if (drawFps || drawFrameCounter) {
     aInvalidRegion.Or(aInvalidRegion, nsIntRect(0, 0, 256, 256));
   }
   if (drawFrameColorBars) {
     aInvalidRegion.Or(aInvalidRegion, nsIntRect(0, 0, 10, aBounds.height));
   }
+  if (drawPaintTimes) {
+    aInvalidRegion.Or(aInvalidRegion, nsIntRect(PaintCounter::GetPaintRect()));
+  }
+}
+
+void
+LayerManagerComposite::DrawPaintTimes(Compositor* aCompositor)
+{
+  if (!mPaintCounter) {
+    mPaintCounter = new PaintCounter();
+  }
+
+  TimeDuration compositeTime = TimeStamp::Now() - mRenderStartTime;
+  mPaintCounter->Draw(aCompositor, mLastPaintTime, compositeTime);
 }
 
 static uint16_t sFrameCount = 0;
 void
 LayerManagerComposite::RenderDebugOverlay(const IntRect& aBounds)
 {
   bool drawFps = gfxPrefs::LayersDrawFPS();
   bool drawFrameCounter = gfxPrefs::DrawFrameCounter();
   bool drawFrameColorBars = gfxPrefs::CompositorDrawColorBars();
+  bool drawPaintTimes = gfxPrefs::AlwaysPaint();
 
   TimeStamp now = TimeStamp::Now();
 
   if (drawFps) {
     if (!mFPS) {
       mFPS = MakeUnique<FPSState>();
     }
 
@@ -709,16 +730,20 @@ LayerManagerComposite::RenderDebugOverla
 
   }
 #endif
 
   if (drawFrameColorBars || drawFrameCounter) {
     // We intentionally overflow at 2^16.
     sFrameCount++;
   }
+
+  if (drawPaintTimes) {
+    DrawPaintTimes(mCompositor);
+  }
 }
 
 RefPtr<CompositingRenderTarget>
 LayerManagerComposite::PushGroupForLayerEffects()
 {
   // This is currently true, so just making sure that any new use of this
   // method is flagged for investigation
   MOZ_ASSERT(gfxPrefs::LayersEffectInvert() ||
--- a/gfx/layers/composite/LayerManagerComposite.h
+++ b/gfx/layers/composite/LayerManagerComposite.h
@@ -57,16 +57,17 @@ struct EffectChain;
 class ImageLayer;
 class ImageLayerComposite;
 class LayerComposite;
 class RefLayerComposite;
 class PaintedLayerComposite;
 class TextRenderer;
 class CompositingRenderTarget;
 struct FPSState;
+class PaintCounter;
 
 static const int kVisualWarningDuration = 150; // ms
 
 class LayerManagerComposite final : public LayerManager
 {
   typedef mozilla::gfx::DrawTarget DrawTarget;
   typedef mozilla::gfx::IntSize IntSize;
   typedef mozilla::gfx::SurfaceFormat SurfaceFormat;
@@ -326,16 +327,18 @@ public:
 
   // Indicate that we need to composite even if nothing in our layers has
   // changed, so that the widget can draw something different in its window
   // overlay.
   void SetWindowOverlayChanged() { mWindowOverlayChanged = true; }
 
   void ForcePresent() { mCompositor->ForcePresent(); }
 
+  void SetPaintTime(const TimeDuration& aPaintTime) { mLastPaintTime = aPaintTime; }
+
 private:
   /** Region we're clipping our current drawing to. */
   nsIntRegion mClippingRegion;
   gfx::IntRect mRenderBounds;
 
   /** Current root layer. */
   LayerComposite* RootLayer() const;
 
@@ -348,16 +351,21 @@ private:
    * Render the current layer tree to the active target.
    */
   void Render(const nsIntRegion& aInvalidRegion, const nsIntRegion& aOpaqueRegion);
 #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
   void RenderToPresentationSurface();
 #endif
 
   /**
+   * Render paint and composite times above the frame.
+   */
+  void DrawPaintTimes(Compositor* aCompositor);
+
+  /**
    * We need to know our invalid region before we're ready to render.
    */
   void InvalidateDebugOverlay(nsIntRegion& aInvalidRegion, const gfx::IntRect& aBounds);
 
   /**
    * Render debug overlays such as the FPS/FrameCounter above the frame.
    */
   void RenderDebugOverlay(const gfx::IntRect& aBounds);
@@ -404,16 +412,19 @@ private:
   RefPtr<TextRenderer> mTextRenderer;
   bool mGeometryChanged;
 
   // Testing property. If hardware composer is supported, this will return
   // true if the last frame was deemed 'too complicated' to be rendered.
   bool mLastFrameMissedHWC;
 
   bool mWindowOverlayChanged;
+  RefPtr<PaintCounter> mPaintCounter;
+  TimeDuration mLastPaintTime;
+  TimeStamp mRenderStartTime;
 };
 
 /**
  * Composite layers are for use with OMTC on the compositor thread only. There
  * must be corresponding Basic layers on the content thread. For composite
  * layers, the layer manager only maintains the layer tree, all rendering is
  * done by a Compositor (see Compositor.h). As such, composite layers are
  * platform-independent and can be used on any platform for which there is a
new file mode 100644
--- /dev/null
+++ b/gfx/layers/composite/PaintCounter.cpp
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/gfx/Point.h"          // for IntSize, Point
+#include "mozilla/gfx/Rect.h"           // for Rect
+#include "mozilla/gfx/Types.h"          // for Color, SurfaceFormat
+#include "mozilla/layers/Compositor.h"  // for Compositor
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/Effects.h"     // for Effect, EffectChain, etc
+#include "mozilla/TimeStamp.h"          // for TimeStamp, TimeDuration
+#include "mozilla/Sprintf.h"
+
+#include "SkColor.h"
+#include "mozilla/gfx/HelpersSkia.h"
+#include "skia/include/core/SkBitmapDevice.h"
+#include "PaintCounter.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+// Positioned below the chrome UI
+IntRect PaintCounter::mRect = IntRect(0, 175, 300, 60);
+
+PaintCounter::PaintCounter()
+{
+  mFormat = SurfaceFormat::B8G8R8A8;
+  mSurface = Factory::CreateDataSourceSurface(mRect.Size(), mFormat);
+  mStride = mSurface->Stride();
+
+  SkBitmap bitmap;
+  bitmap.setInfo(MakeSkiaImageInfo(mRect.Size(), mFormat), mStride);
+  bitmap.setPixels(mSurface->GetData());
+  bitmap.eraseColor(SK_ColorWHITE);
+
+  mCanvas.adopt(new SkCanvas(bitmap));
+}
+
+PaintCounter::~PaintCounter()
+{
+  mSurface = nullptr;
+  mTextureSource = nullptr;
+  mTexturedEffect = nullptr;
+}
+
+void
+PaintCounter::Draw(Compositor* aCompositor, TimeDuration aPaintTime, TimeDuration aCompositeTime) {
+  const int buffer_size = 48;
+  char buffer[buffer_size];
+  snprintf(buffer, buffer_size, "P: %.2f  C: %.2f",
+           aPaintTime.ToMilliseconds(),
+           aCompositeTime.ToMilliseconds());
+
+  SkPaint paint;
+  paint.setTextSize(32);
+  paint.setColor(SkColorSetRGB(0, 255, 0));
+  paint.setAntiAlias(true);
+
+  mCanvas->clear(SK_ColorTRANSPARENT);
+  mCanvas->drawText(buffer, strlen(buffer), 10, 30, paint);
+  mCanvas->flush();
+
+  if (!mTextureSource) {
+    mTextureSource = aCompositor->CreateDataTextureSource();
+    mTexturedEffect = CreateTexturedEffect(mFormat, mTextureSource,
+                                           SamplingFilter::POINT, true);
+    mTexturedEffect->mTextureCoords = Rect(0, 0, 1.0f, 1.0f);
+  }
+
+  mTextureSource->Update(mSurface);
+
+  EffectChain effectChain;
+  effectChain.mPrimaryEffect = mTexturedEffect;
+
+  gfx::Matrix4x4 identity;
+  Rect rect(mRect.x, mRect.y, mRect.width, mRect.height);
+  aCompositor->DrawQuad(rect, mRect, effectChain, 1.0, identity);
+}
+
+} // end namespace layers
+} // end namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/composite/PaintCounter.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_PaintCounter_h_
+#define mozilla_layers_PaintCounter_h_
+
+#include <map>                          // for std::map
+#include "mozilla/RefPtr.h"             // for already_AddRefed, RefCounted
+#include "mozilla/TimeStamp.h"          // for TimeStamp, TimeDuration
+#include "skia/include/core/SkCanvas.h"
+#include "mozilla/gfx/HelpersSkia.h"
+
+namespace mozilla {
+namespace layers {
+
+class Compositor;
+
+using namespace mozilla::gfx;
+using namespace mozilla::gl;
+
+// Keeps track and paints how long a full invalidation paint takes to rasterize
+// and composite.
+class PaintCounter {
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PaintCounter)
+
+  PaintCounter();
+  void Draw(Compositor* aCompositor, TimeDuration aPaintTime, TimeDuration aCompositeTime);
+  static IntRect GetPaintRect() { return PaintCounter::mRect; }
+
+private:
+  virtual ~PaintCounter();
+
+  SurfaceFormat mFormat;
+  RefPtrSkia<SkCanvas> mCanvas;
+  IntSize mSize;
+  int mStride;
+
+  RefPtr<DataSourceSurface> mSurface;
+  RefPtr<DataTextureSource> mTextureSource;
+  RefPtr<TexturedEffect> mTexturedEffect;
+  static IntRect mRect;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_opengl_PaintCounter_h_
--- a/gfx/layers/ipc/AsyncTransactionTracker.cpp
+++ b/gfx/layers/ipc/AsyncTransactionTracker.cpp
@@ -3,16 +3,17 @@
  */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "AsyncTransactionTracker.h"
 
 #include "mozilla/layers/ImageBridgeChild.h"  // for ImageBridgeChild
+#include "mozilla/gfx/Logging.h"
 
 namespace mozilla {
 namespace layers {
 
 void
 AsyncTransactionWaiter::WaitComplete()
 {
   MOZ_ASSERT(!InImageBridgeChildThread());
@@ -31,17 +32,17 @@ AsyncTransactionWaiter::WaitComplete()
     count++;
   }
 
   if (mWaitCount > 0) {
     printf_stderr("Timeout of waiting transaction complete.");
   }
 
   if (count == maxCount) {
-    gfxDevCrash(LogReason::AsyncTransactionTimeout) << "Bug 1244883: AsyncTransactionWaiter timed out.";
+    gfxDevCrash(gfx::LogReason::AsyncTransactionTimeout) << "Bug 1244883: AsyncTransactionWaiter timed out.";
   }
 }
 
 Atomic<uint64_t> AsyncTransactionTracker::sSerialCounter(0);
 
 AsyncTransactionTracker::AsyncTransactionTracker(AsyncTransactionWaiter* aWaiter)
     : mSerial(GetNextSerial())
     , mWaiter(aWaiter)
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -610,16 +610,17 @@ CompositorBridgeParent::CompositorBridge
   , mPauseCompositionMonitor("PauseCompositionMonitor")
   , mResumeCompositionMonitor("ResumeCompositionMonitor")
   , mResetCompositorMonitor("ResetCompositorMonitor")
   , mRootLayerTreeID(0)
   , mOverrideComposeReadiness(false)
   , mForceCompositionTask(nullptr)
   , mCompositorThreadHolder(CompositorThreadHolder::GetSingleton())
   , mCompositorScheduler(nullptr)
+  , mPaintTime(TimeDuration::Forever())
 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
   , mLastPluginUpdateLayerTreeId(0)
   , mDeferPluginWindows(false)
   , mPluginWindowsHidden(false)
 #endif
 {
   // Always run destructor on the main thread
   MOZ_ASSERT(NS_IsMainThread());
@@ -1074,16 +1075,28 @@ CompositorBridgeParent::ScheduleTask(alr
   if (time == 0) {
     MessageLoop::current()->PostTask(Move(task));
   } else {
     MessageLoop::current()->PostDelayedTask(Move(task), time);
   }
 }
 
 void
+CompositorBridgeParent::UpdatePaintTime(LayerTransactionParent* aLayerTree,
+                                        const TimeDuration& aPaintTime)
+{
+  // We get a lot of paint timings for things with empty transactions.
+  if (!mLayerManager || aPaintTime.ToMilliseconds() < 1.0) {
+    return;
+  }
+
+  mLayerManager->SetPaintTime(aPaintTime);
+}
+
+void
 CompositorBridgeParent::NotifyShadowTreeTransaction(uint64_t aId, bool aIsFirstPaint,
     bool aScheduleComposite, uint32_t aPaintSequenceNumber,
     bool aIsRepeatTransaction, bool aHitTestUpdate)
 {
   if (!aIsRepeatTransaction &&
       mLayerManager &&
       mLayerManager->GetRoot()) {
     // Process plugin data here to give time for them to update before the next
@@ -2125,16 +2138,29 @@ public:
   }
   bool DeallocPCompositorWidgetParent(PCompositorWidgetParent* aActor) override {
     // Not allowed.
     return false;
   }
 
   virtual CompositorBridgeParentIPCAllocator* AsCompositorBridgeParentIPCAllocator() override { return this; }
 
+  virtual void UpdatePaintTime(LayerTransactionParent* aLayerTree, const TimeDuration& aPaintTime) override {
+    uint64_t id = aLayerTree->GetId();
+    MOZ_ASSERT(id != 0);
+
+    CompositorBridgeParent::LayerTreeState* state =
+      CompositorBridgeParent::GetIndirectShadowTree(id);
+    if (!state || !state->mParent) {
+      return;
+    }
+
+    state->mParent->UpdatePaintTime(aLayerTree, aPaintTime);
+  }
+
 protected:
   void OnChannelConnected(int32_t pid) override {
     mCompositorThreadHolder = CompositorThreadHolder::GetSingleton();
   }
 private:
   // Private destructor, to discourage deletion outside of Release():
   virtual ~CrossProcessCompositorBridgeParent();
 
--- a/gfx/layers/ipc/CompositorBridgeParent.h
+++ b/gfx/layers/ipc/CompositorBridgeParent.h
@@ -367,16 +367,19 @@ public:
   bool ScheduleResumeOnCompositorThread();
   bool ScheduleResumeOnCompositorThread(int width, int height);
 
   virtual void ScheduleComposition();
   void NotifyShadowTreeTransaction(uint64_t aId, bool aIsFirstPaint,
       bool aScheduleComposite, uint32_t aPaintSequenceNumber,
       bool aIsRepeatTransaction, bool aHitTestUpdate);
 
+  void UpdatePaintTime(LayerTransactionParent* aLayerTree,
+                       const TimeDuration& aPaintTime) override;
+
   /**
    * Check rotation info and schedule a rendering task if needed.
    * Only can be called from compositor thread.
    */
   void ScheduleRotationOnCompositorThread(const TargetConfig& aTargetConfig, bool aIsFirstPaint);
 
   /**
    * Returns the unique layer tree identifier that corresponds to the root
@@ -625,16 +628,18 @@ protected:
 
   RefPtr<CompositorThreadHolder> mCompositorThreadHolder;
   RefPtr<CompositorVsyncScheduler> mCompositorScheduler;
   // This makes sure the compositorParent is not destroyed before receiving
   // confirmation that the channel is closed.
   // mSelfRef is cleared in DeferredDestroy which is scheduled by ActorDestroy.
   RefPtr<CompositorBridgeParent> mSelfRef;
 
+  TimeDuration mPaintTime;
+
 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
   // cached plugin data used to reduce the number of updates we request.
   uint64_t mLastPluginUpdateLayerTreeId;
   nsIntPoint mPluginsLayerOffset;
   nsIntRegion mPluginsLayerVisibleRegion;
   nsTArray<PluginWindowData> mCachedPluginData;
   // Time until which we will block composition to wait for plugin updates.
   TimeStamp mWaitForPluginsUntil;
--- a/gfx/layers/ipc/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -224,16 +224,24 @@ public:
     }
   }
 private:
   LayerTransactionParent* mLayerTransaction;
   InfallibleTArray<OpDestroy>* mActorsToDestroy;
 };
 
 bool
+LayerTransactionParent::RecvPaintTime(const uint64_t& aTransactionId,
+                                      const TimeDuration& aPaintTime)
+{
+  mShadowLayersManager->UpdatePaintTime(this, aPaintTime);
+  return true;
+}
+
+bool
 LayerTransactionParent::RecvUpdate(InfallibleTArray<Edit>&& cset,
                                    InfallibleTArray<OpDestroy>&& aToDestroy,
                                    const uint64_t& aFwdTransactionId,
                                    const uint64_t& aTransactionId,
                                    const TargetConfig& targetConfig,
                                    PluginsArray&& aPlugins,
                                    const bool& isFirstPaint,
                                    const bool& scheduleComposite,
--- a/gfx/layers/ipc/LayerTransactionParent.h
+++ b/gfx/layers/ipc/LayerTransactionParent.h
@@ -104,16 +104,19 @@ public:
     mPendingCompositorUpdates--;
   }
 
 protected:
   virtual bool RecvSyncWithCompositor() override { return true; }
 
   virtual bool RecvShutdown() override;
 
+  virtual bool RecvPaintTime(const uint64_t& aTransactionId,
+                             const TimeDuration& aPaintTime) override;
+
   virtual bool RecvUpdate(EditArray&& cset,
                           OpDestroyArray&& aToDestroy,
                           const uint64_t& aFwdTransactionId,
                           const uint64_t& aTransactionId,
                           const TargetConfig& targetConfig,
                           PluginsArray&& aPlugins,
                           const bool& isFirstPaint,
                           const bool& scheduleComposite,
--- a/gfx/layers/ipc/PLayerTransaction.ipdl
+++ b/gfx/layers/ipc/PLayerTransaction.ipdl
@@ -53,16 +53,18 @@ parent:
               uint64_t fwdTransactionId,
               uint64_t id, TargetConfig targetConfig,
               PluginWindowData[] plugins, bool isFirstPaint,
               bool scheduleComposite, uint32_t paintSequenceNumber,
               bool isRepeatTransaction, TimeStamp transactionStart,
               int32_t paintSyncId)
     returns (EditReply[] reply);
 
+  async PaintTime(uint64_t id, TimeDuration paintTime);
+
   // We don't need to send a sync transaction if
   // no transaction operate require a swap.
   async UpdateNoSwap(Edit[] cset, OpDestroy[] toDestroy,
                      uint64_t fwdTransactionId,
                      uint64_t id, TargetConfig targetConfig,
                      PluginWindowData[] plugins, bool isFirstPaint,
                      bool scheduleComposite, uint32_t paintSequenceNumber,
                      bool isRepeatTransaction, TimeStamp transactionStart,
--- a/gfx/layers/ipc/RemoteContentController.cpp
+++ b/gfx/layers/ipc/RemoteContentController.cpp
@@ -85,17 +85,17 @@ RemoteContentController::HandleTap(TapTy
     Unused << SendHandleTap(aTapType, mBrowserParent->AdjustTapToChildWidget(aPoint),
             aModifiers, aGuid, aInputBlockId, callTakeFocusForClickFromTap);
   }
 }
 
 void
 RemoteContentController::PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs)
 {
-#ifdef MOZ_ANDROID_APZ
+#ifdef MOZ_WIDGET_ANDROID
   AndroidBridge::Bridge()->PostTaskToUiThread(Move(aTask), aDelayMs);
 #else
   (MessageLoop::current() ? MessageLoop::current() : mUILoop)->
     PostDelayedTask(Move(aTask), aDelayMs);
 #endif
 }
 
 bool
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -584,16 +584,25 @@ ShadowLayerForwarder::StorePluginWidgetC
     const nsIWidget::Configuration& configuration = aConfigurations[idx];
     mPluginWindowData.AppendElement(PluginWindowData(configuration.mWindowID,
                                                      configuration.mClipRegion,
                                                      configuration.mBounds,
                                                      configuration.mVisible));
   }
 }
 
+void
+ShadowLayerForwarder::SendPaintTime(uint64_t aId, TimeDuration aPaintTime)
+{
+  if (!HasShadowManager() || !mShadowManager->IPCOpen() ||
+      !mShadowManager->SendPaintTime(aId, aPaintTime)) {
+    NS_WARNING("Could not send paint times over IPC");
+  }
+}
+
 bool
 ShadowLayerForwarder::EndTransaction(InfallibleTArray<EditReply>* aReplies,
                                      const nsIntRegion& aRegionToClear,
                                      uint64_t aId,
                                      bool aScheduleComposite,
                                      uint32_t aPaintSequenceNumber,
                                      bool aIsRepeatTransaction,
                                      const mozilla::TimeStamp& aTransactionStart,
--- a/gfx/layers/ipc/ShadowLayers.h
+++ b/gfx/layers/ipc/ShadowLayers.h
@@ -261,16 +261,21 @@ public:
                                          TextureClient* aClientOnWhite) override;
 #ifdef MOZ_WIDGET_GONK
   virtual void UseOverlaySource(CompositableClient* aCompositable,
                                 const OverlaySource& aOverlay,
                                 const nsIntRect& aPictureRect) override;
 #endif
 
   /**
+   * Used for debugging to tell the compositor how long this frame took to paint.
+   */
+  void SendPaintTime(uint64_t aId, TimeDuration aPaintTime);
+
+  /**
    * End the current transaction and forward it to LayerManagerComposite.
    * |aReplies| are directions from the LayerManagerComposite to the
    * caller of EndTransaction().
    */
   bool EndTransaction(InfallibleTArray<EditReply>* aReplies,
                       const nsIntRegion& aRegionToClear,
                       uint64_t aId,
                       bool aScheduleComposite,
--- a/gfx/layers/ipc/ShadowLayersManager.h
+++ b/gfx/layers/ipc/ShadowLayersManager.h
@@ -41,14 +41,15 @@ public:
     virtual void ApplyAsyncProperties(LayerTransactionParent* aLayerTree) = 0;
     virtual void FlushApzRepaints(const LayerTransactionParent* aLayerTree) = 0;
     virtual void GetAPZTestData(const LayerTransactionParent* aLayerTree,
                                 APZTestData* aOutData) { }
     virtual void SetConfirmedTargetAPZC(const LayerTransactionParent* aLayerTree,
                                         const uint64_t& aInputBlockId,
                                         const nsTArray<ScrollableLayerGuid>& aTargets) = 0;
     virtual CompositorBridgeParentIPCAllocator* AsCompositorBridgeParentIPCAllocator() { return nullptr; }
+    virtual void UpdatePaintTime(LayerTransactionParent* aLayerTree, const TimeDuration& aPaintTime) {}
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // mozilla_layers_ShadowLayersManager_h
--- a/gfx/layers/ipc/SharedBufferManagerChild.cpp
+++ b/gfx/layers/ipc/SharedBufferManagerChild.cpp
@@ -5,19 +5,20 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "base/task.h"                  // for NewRunnableFunction, etc
 #include "base/thread.h"                // for Thread
 #include "mozilla/gfx/Logging.h"        // for gfxDebug
 #include "mozilla/layers/SharedBufferManagerChild.h"
 #include "mozilla/layers/SharedBufferManagerParent.h"
+#include "mozilla/Sprintf.h"            // for SprintfLiteral
 #include "mozilla/StaticPtr.h"          // for StaticRefPtr
 #include "mozilla/ReentrantMonitor.h"   // for ReentrantMonitor, etc
-#include "nsThreadUtils.h"              // fo NS_IsMainThread
+#include "nsThreadUtils.h"              // for NS_IsMainThread
 
 #ifdef MOZ_WIDGET_GONK
 #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "SBMChild", ## args)
 #endif
 
 namespace mozilla {
 namespace layers {
 
@@ -140,17 +141,17 @@ SharedBufferManagerChild::StartUpOnThrea
   }
 
   sSharedBufferManagerChildThread = aThread;
   if (!aThread->IsRunning()) {
     aThread->Start();
   }
   sSharedBufferManagerChildSingleton = new SharedBufferManagerChild();
   char thrname[128];
-  base::snprintf(thrname, 128, "BufMgrParent#%d", base::Process::Current().pid());
+  SprintfLiteral(thrname, "BufMgrParent#%d", base::Process::Current().pid());
   sSharedBufferManagerParentSingleton = new SharedBufferManagerParent(
     base::Process::Current().pid(), new base::Thread(thrname));
   sSharedBufferManagerChildSingleton->ConnectAsync(sSharedBufferManagerParentSingleton);
   return true;
 }
 
 void
 SharedBufferManagerChild::DestroyManager()
--- a/gfx/layers/ipc/SharedBufferManagerParent.cpp
+++ b/gfx/layers/ipc/SharedBufferManagerParent.cpp
@@ -176,17 +176,17 @@ ConnectSharedBufferManagerInParentProces
   aManager->Open(aTransport, aOtherPid, XRE_GetIOMessageLoop(), ipc::ParentSide);
 }
 
 PSharedBufferManagerParent* SharedBufferManagerParent::Create(Transport* aTransport,
                                                               ProcessId aOtherPid)
 {
   base::Thread* thread = nullptr;
   char thrname[128];
-  base::snprintf(thrname, 128, "BufMgrParent#%d", aOtherPid);
+  SprintfLiteral(thrname, "BufMgrParent#%d", aOtherPid);
   thread = new base::Thread(thrname);
 
   SharedBufferManagerParent* manager = new SharedBufferManagerParent(aOtherPid, thread);
   if (!thread->IsRunning()) {
     thread->Start();
   }
   thread->message_loop()->PostTask(NewRunnableFunction(ConnectSharedBufferManagerInParentProcess,
                                                        manager, aTransport, aOtherPid));
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -255,17 +255,17 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk
              ]
         ]
     SOURCES += [
         'ipc/GonkNativeHandle.cpp',
         'ipc/GonkNativeHandleUtils.cpp',
         'ipc/ShadowLayerUtilsGralloc.cpp',
     ]
 
-if CONFIG['MOZ_ANDROID_APZ']:
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
     UNIFIED_SOURCES += [
         'apz/src/AndroidAPZ.cpp',
     ]
 
 UNIFIED_SOURCES += [
     'apz/public/IAPZCTreeManager.cpp',
     'apz/src/APZCTreeManager.cpp',
     'apz/src/AsyncPanZoomController.cpp',
@@ -329,16 +329,17 @@ UNIFIED_SOURCES += [
     'composite/CompositableHost.cpp',
     'composite/ContainerLayerComposite.cpp',
     'composite/ContentHost.cpp',
     'composite/FPSCounter.cpp',
     'composite/FrameUniformityData.cpp',
     'composite/ImageHost.cpp',
     'composite/ImageLayerComposite.cpp',
     'composite/LayerManagerComposite.cpp',
+    'composite/PaintCounter.cpp',
     'composite/PaintedLayerComposite.cpp',
     'composite/TextRenderer.cpp',
     'composite/TextureHost.cpp',
     'composite/TiledContentHost.cpp',
     'Compositor.cpp',
     'CopyableCanvasLayer.cpp',
     'Effects.cpp',
     'FrameMetrics.cpp',
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -680,17 +680,17 @@ CompositorOGL::BeginFrame(const nsIntReg
     mWidgetSize.height = height;
   } else {
     MakeCurrent();
   }
 
   mPixelsPerFrame = width * height;
   mPixelsFilled = 0;
 
-#if MOZ_WIDGET_ANDROID
+#ifdef MOZ_WIDGET_ANDROID
   TexturePoolOGL::Fill(gl());
 #endif
 
   // Default blend function implements "OVER"
   mGLContext->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA,
                                  LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA);
   mGLContext->fEnable(LOCAL_GL_BLEND);
 
@@ -702,23 +702,18 @@ CompositorOGL::BeginFrame(const nsIntReg
 #ifdef DEBUG
   mWindowRenderTarget = mCurrentRenderTarget;
 #endif
 
   if (aClipRectOut && !aClipRectIn) {
     aClipRectOut->SetRect(0, 0, width, height);
   }
 
-  // If the Android compositor is being used, this clear will be done in
-  // DrawWindowUnderlay. Make sure the bits used here match up with those used
-  // in mobile/android/base/gfx/LayerRenderer.java
-#ifndef MOZ_WIDGET_ANDROID
   mGLContext->fClearColor(0.0, 0.0, 0.0, 0.0);
   mGLContext->fClear(LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT);
-#endif
 }
 
 void
 CompositorOGL::CreateFBOWithTexture(const gfx::IntRect& aRect, bool aCopyFromSource,
                                     GLuint aSourceFrameBuffer,
                                     GLuint *aFBO, GLuint *aTexture)
 {
   *aTexture = CreateTexture(aRect, aCopyFromSource, aSourceFrameBuffer);
--- a/gfx/tests/gtest/TestBSPTree.cpp
+++ b/gfx/tests/gtest/TestBSPTree.cpp
@@ -1116,9 +1116,9 @@ TEST(BSPTree, TwoPlaneIntersectRotate360
     Polygon3D {
       Point3D(0.00010f, 0.00000f, 2.00000f),
       Point3D(-2.00000f, -0.00000f, 2.00000f),
       Point3D(-2.00000f, 0.00010f, -2.00000f),
       Point3D(0.00000f, 0.00010f, -2.00000f)
     }
   };
   ::RunTest(polygons, expected);
-}
\ No newline at end of file
+}
--- a/gfx/thebes/gfxDWriteFontList.cpp
+++ b/gfx/thebes/gfxDWriteFontList.cpp
@@ -8,16 +8,17 @@
 
 #include "gfxDWriteFontList.h"
 #include "gfxDWriteFonts.h"
 #include "nsUnicharUtils.h"
 #include "nsILocaleService.h"
 #include "nsServiceManagerUtils.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/Sprintf.h"
 #include "mozilla/Telemetry.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsISimpleEnumerator.h"
 
 #include "gfxGDIFontList.h"
 
@@ -571,18 +572,18 @@ gfxDWriteFontEntry::ReadCMAP(FontInfoDat
     }
 
     LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d hash: %8.8x%s\n",
                   NS_ConvertUTF16toUTF8(mName).get(),
                   charmap->SizeOfIncludingThis(moz_malloc_size_of),
                   charmap->mHash, mCharacterMap == charmap ? " new" : ""));
     if (LOG_CMAPDATA_ENABLED()) {
         char prefix[256];
-        sprintf(prefix, "(cmapdata) name: %.220s",
-                NS_ConvertUTF16toUTF8(mName).get());
+        SprintfLiteral(prefix, "(cmapdata) name: %.220s",
+                       NS_ConvertUTF16toUTF8(mName).get());
         charmap->Dump(prefix, eGfxLog_cmapdata);
     }
 
     return rv;
 }
 
 gfxFont *
 gfxDWriteFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle,
--- a/gfx/thebes/gfxFcPlatformFontList.cpp
+++ b/gfx/thebes/gfxFcPlatformFontList.cpp
@@ -8,16 +8,17 @@
 #include "gfxFcPlatformFontList.h"
 #include "gfxFont.h"
 #include "gfxFontConstants.h"
 #include "gfxFontFamilyList.h"
 #include "gfxFT2Utils.h"
 #include "gfxPlatform.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/Sprintf.h"
 #include "mozilla/TimeStamp.h"
 #include "nsGkAtoms.h"
 #include "nsILanguageAtomService.h"
 #include "nsUnicodeProperties.h"
 #include "nsUnicodeRange.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsAppDirectoryServiceDefs.h"
@@ -373,18 +374,18 @@ gfxFontconfigFontEntry::ReadCMAP(FontInf
     }
 
     LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d hash: %8.8x%s\n",
                   NS_ConvertUTF16toUTF8(mName).get(),
                   charmap->SizeOfIncludingThis(moz_malloc_size_of),
                   charmap->mHash, mCharacterMap == charmap ? " new" : ""));
     if (LOG_CMAPDATA_ENABLED()) {
         char prefix[256];
-        sprintf(prefix, "(cmapdata) name: %.220s",
-                NS_ConvertUTF16toUTF8(mName).get());
+        SprintfLiteral(prefix, "(cmapdata) name: %.220s",
+                       NS_ConvertUTF16toUTF8(mName).get());
         charmap->Dump(prefix, eGfxLog_cmapdata);
     }
 
     return rv;
 }
 
 static bool
 HasChar(FcPattern *aFont, FcChar32 aCh)
--- a/gfx/thebes/gfxGDIFont.cpp
+++ b/gfx/thebes/gfxGDIFont.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gfxGDIFont.h"
 
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/Sprintf.h"
 #include "mozilla/WindowsVersion.h"
 
 #include <algorithm>
 #include "gfxWindowsPlatform.h"
 #include "gfxContext.h"
 #include "mozilla/Preferences.h"
 #include "nsUnicodeProperties.h"
 #include "gfxFontConstants.h"
@@ -398,19 +399,19 @@ gfxGDIFont::Initialize()
     mScaledFont = cairo_scaled_font_create(mFontFace, &sizeMatrix,
                                            &ctm, fontOptions);
     cairo_font_options_destroy(fontOptions);
 
     if (!mScaledFont ||
         cairo_scaled_font_status(mScaledFont) != CAIRO_STATUS_SUCCESS) {
 #ifdef DEBUG
         char warnBuf[1024];
-        sprintf(warnBuf, "Failed to create scaled font: %s status: %d",
-                NS_ConvertUTF16toUTF8(mFontEntry->Name()).get(),
-                mScaledFont ? cairo_scaled_font_status(mScaledFont) : 0);
+        SprintfLiteral(warnBuf, "Failed to create scaled font: %s status: %d",
+                       NS_ConvertUTF16toUTF8(mFontEntry->Name()).get(),
+                       mScaledFont ? cairo_scaled_font_status(mScaledFont) : 0);
         NS_WARNING(warnBuf);
 #endif
         mIsValid = false;
     } else {
         mIsValid = true;
     }
 
 #if 0
--- a/gfx/thebes/gfxGDIFontList.cpp
+++ b/gfx/thebes/gfxGDIFontList.cpp
@@ -2,16 +2,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/DebugOnly.h"
 #include <algorithm>
 
 #include "mozilla/Logging.h"
+#include "mozilla/Sprintf.h"
 
 #include "gfxGDIFontList.h"
 #include "gfxWindowsPlatform.h"
 #include "gfxUserFontSet.h"
 #include "gfxFontUtils.h"
 #include "gfxGDIFont.h"
 
 #include "nsServiceManagerUtils.h"
@@ -70,22 +71,22 @@ public:
 
     virtual ~WinUserFontData()
     {
         DebugOnly<BOOL> success;
         success = RemoveFontMemResourceEx(mFontRef);
 #if DEBUG
         if (!success) {
             char buf[256];
-            sprintf(buf, "error deleting font handle (%p) - RemoveFontMemResourceEx failed", mFontRef);
+            SprintfLiteral(buf, "error deleting font handle (%p) - RemoveFontMemResourceEx failed", mFontRef);
             NS_ASSERTION(success, buf);
         }
 #endif
     }
-    
+
     HANDLE mFontRef;
 };
 
 BYTE 
 FontTypeToOutPrecision(uint8_t fontType)
 {
     BYTE ret;
     switch (fontType) {
@@ -197,18 +198,18 @@ GDIFontEntry::ReadCMAP(FontInfoData *aFo
     }
 
     LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d hash: %8.8x%s\n",
                   NS_ConvertUTF16toUTF8(mName).get(),
                   charmap->SizeOfIncludingThis(moz_malloc_size_of),
                   charmap->mHash, mCharacterMap == charmap ? " new" : ""));
     if (LOG_CMAPDATA_ENABLED()) {
         char prefix[256];
-        sprintf(prefix, "(cmapdata) name: %.220s",
-                NS_ConvertUTF16toUTF8(mName).get());
+        SprintfLiteral(prefix, "(cmapdata) name: %.220s",
+                       NS_ConvertUTF16toUTF8(mName).get());
         charmap->Dump(prefix, eGfxLog_cmapdata);
     }
 
     return rv;
 }
 
 bool
 GDIFontEntry::IsSymbolFont()
--- a/gfx/thebes/gfxMacFont.cpp
+++ b/gfx/thebes/gfxMacFont.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gfxMacFont.h"
 
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/Sprintf.h"
 
 #include "gfxCoreTextShaper.h"
 #include <algorithm>
 #include "gfxPlatformMac.h"
 #include "gfxContext.h"
 #include "gfxFontUtils.h"
 #include "gfxMacPlatformFontList.h"
 #include "gfxFontConstants.h"
@@ -44,18 +45,19 @@ gfxMacFont::gfxMacFont(MacOSFontEntry *a
 
     mFontFace = cairo_quartz_font_face_create_for_cgfont(mCGFont);
 
     cairo_status_t cairoerr = cairo_font_face_status(mFontFace);
     if (cairoerr != CAIRO_STATUS_SUCCESS) {
         mIsValid = false;
 #ifdef DEBUG
         char warnBuf[1024];
-        sprintf(warnBuf, "Failed to create Cairo font face: %s status: %d",
-                NS_ConvertUTF16toUTF8(GetName()).get(), cairoerr);
+        SprintfLiteral(warnBuf,
+                       "Failed to create Cairo font face: %s status: %d",
+                       NS_ConvertUTF16toUTF8(GetName()).get(), cairoerr);
         NS_WARNING(warnBuf);
 #endif
         return;
     }
 
     cairo_matrix_t sizeMatrix, ctm;
     cairo_matrix_init_identity(&ctm);
     cairo_matrix_init_scale(&sizeMatrix, mAdjustedSize, mAdjustedSize);
@@ -94,18 +96,18 @@ gfxMacFont::gfxMacFont(MacOSFontEntry *a
                                            fontOptions);
     cairo_font_options_destroy(fontOptions);
 
     cairoerr = cairo_scaled_font_status(mScaledFont);
     if (cairoerr != CAIRO_STATUS_SUCCESS) {
         mIsValid = false;
 #ifdef DEBUG
         char warnBuf[1024];
-        sprintf(warnBuf, "Failed to create scaled font: %s status: %d",
-                NS_ConvertUTF16toUTF8(GetName()).get(), cairoerr);
+        SprintfLiteral(warnBuf, "Failed to create scaled font: %s status: %d",
+                       NS_ConvertUTF16toUTF8(GetName()).get(), cairoerr);
         NS_WARNING(warnBuf);
 #endif
     }
 }
 
 gfxMacFont::~gfxMacFont()
 {
     if (mCTFont) {
@@ -214,18 +216,19 @@ gfxMacFont::InitMetrics()
     if (!upem) {
         upem = ::CGFontGetUnitsPerEm(mCGFont);
     }
 
     if (upem < 16 || upem > 16384) {
         // See http://www.microsoft.com/typography/otspec/head.htm
 #ifdef DEBUG
         char warnBuf[1024];
-        sprintf(warnBuf, "Bad font metrics for: %s (invalid unitsPerEm value)",
-                NS_ConvertUTF16toUTF8(mFontEntry->Name()).get());
+        SprintfLiteral(warnBuf,
+                       "Bad font metrics for: %s (invalid unitsPerEm value)",
+                       NS_ConvertUTF16toUTF8(mFontEntry->Name()).get());
         NS_WARNING(warnBuf);
 #endif
         return;
     }
 
     mAdjustedSize = std::max(mStyle.size, 1.0);
     mFUnitsConvFactor = mAdjustedSize / upem;
 
--- a/gfx/thebes/gfxMacPlatformFontList.mm
+++ b/gfx/thebes/gfxMacPlatformFontList.mm
@@ -59,16 +59,17 @@
 #include "nsISimpleEnumerator.h"
 #include "nsCharTraits.h"
 #include "nsCocoaFeatures.h"
 #include "nsCocoaUtils.h"
 #include "gfxFontConstants.h"
 
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/Sprintf.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/gfx/2D.h"
 
 #include <unistd.h>
 #include <time.h>
 #include <dlfcn.h>
 
 using namespace mozilla;
@@ -223,18 +224,18 @@ MacOSFontEntry::ReadCMAP(FontInfoData *a
     }
 
     LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d hash: %8.8x%s\n",
                   NS_ConvertUTF16toUTF8(mName).get(),
                   charmap->SizeOfIncludingThis(moz_malloc_size_of),
                   charmap->mHash, mCharacterMap == charmap ? " new" : ""));
     if (LOG_CMAPDATA_ENABLED()) {
         char prefix[256];
-        sprintf(prefix, "(cmapdata) name: %.220s",
-                NS_ConvertUTF16toUTF8(mName).get());
+        SprintfLiteral(prefix, "(cmapdata) name: %.220s",
+                       NS_ConvertUTF16toUTF8(mName).get());
         charmap->Dump(prefix, eGfxLog_cmapdata);
     }
 
     return rv;
 }
 
 gfxFont*
 MacOSFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold)
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -11,17 +11,16 @@
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/gfx/GPUProcessManager.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/TimeStamp.h"
 
 #include "mozilla/Logging.h"
 #include "mozilla/Services.h"
-#include "prprf.h"
 
 #include "gfxCrashReporterUtils.h"
 #include "gfxPlatform.h"
 #include "gfxPrefs.h"
 #include "gfxEnv.h"
 #include "gfxTextRun.h"
 #include "gfxConfig.h"
 #include "MediaPrefs.h"
@@ -2119,27 +2118,37 @@ gfxPlatform::InitAcceleration()
     }
   }
 
   Preferences::AddBoolVarCache(&sLayersHardwareVideoDecodingFailed,
                                "media.hardware-video-decoding.failed",
                                false);
 
   if (XRE_IsParentProcess()) {
+    FeatureState& gpuProc = gfxConfig::GetFeature(Feature::GPU_PROCESS);
     if (gfxPrefs::GPUProcessDevEnabled()) {
       // We want to hide this from about:support, so only set a default if the
       // pref is known to be true.
-      gfxConfig::SetDefaultFromPref(
-        Feature::GPU_PROCESS,
+      gpuProc.SetDefaultFromPref(
         gfxPrefs::GetGPUProcessDevEnabledPrefName(),
         true,
         gfxPrefs::GetGPUProcessDevEnabledPrefDefault());
+
+      // We require E10S - otherwise, there is very little benefit to the GPU
+      // process, since the UI process must still use acceleration for
+      // performance.
+      if (!BrowserTabsRemoteAutostart()) {
+        gpuProc.Disable(
+          FeatureStatus::Unavailable,
+          "Multi-process mode is not enabled",
+          NS_LITERAL_CSTRING("FEATURE_FAILURE_NO_E10S"));
+      }
     }
 
-    if (gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
+    if (gpuProc.IsEnabled()) {
       GPUProcessManager* gpu = GPUProcessManager::Get();
       gpu->EnableGPUProcess();
     }
   }
 
   sLayersAccelerationPrefsInitialized = true;
 }
 
@@ -2343,17 +2352,17 @@ gfxPlatform::AsyncPanZoomEnabled()
   // For XUL applications (everything but B2G on mobile and desktop, and
   // Firefox on Android) we only want to use APZ when E10S is enabled. If
   // we ever get input events off the main thread we can consider relaxing
   // this requirement.
   if (!BrowserTabsRemoteAutostart()) {
     return false;
   }
 #endif
-#ifdef MOZ_ANDROID_APZ
+#ifdef MOZ_WIDGET_ANDROID
   return true;
 #else
   return gfxPrefs::AsyncPanZoomEnabledDoNotUseDirectly();
 #endif
 }
 
 /*static*/ bool
 gfxPlatform::PerfWarnings()
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -381,16 +381,17 @@ private:
   DECL_GFX_PREF(Live, "gfx.partialpresent.force",              PartialPresent, int32_t, 0);
   DECL_GFX_PREF(Live, "gfx.perf-warnings.enabled",             PerfWarnings, bool, false);
   DECL_GFX_PREF(Live, "gfx.SurfaceTexture.detach.enabled",     SurfaceTextureDetachEnabled, bool, true);
   DECL_GFX_PREF(Live, "gfx.testing.device-reset",              DeviceResetForTesting, int32_t, 0);
   DECL_GFX_PREF(Live, "gfx.testing.device-fail",               DeviceFailForTesting, bool, false);
   DECL_GFX_PREF(Live, "gfx.ycbcr.accurate-conversion",         YCbCrAccurateConversion, bool, false);
 
   DECL_GFX_PREF(Live, "gfx.content.use-native-pushlayer",      UseNativePushLayer, bool, false);
+  DECL_GFX_PREF(Live, "gfx.content.always-paint",              AlwaysPaint, bool, false);
 
   // Disable surface sharing due to issues with compatible FBConfigs on
   // NVIDIA drivers as described in bug 1193015.
   DECL_GFX_PREF(Live, "gfx.use-glx-texture-from-pixmap",       UseGLXTextureFromPixmap, bool, false);
 
   DECL_GFX_PREF(Once, "gfx.use-iosurface-textures",            UseIOSurfaceTextures, bool, false);
 
   // These times should be in milliseconds
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -1566,16 +1566,24 @@ gfxWindowsPlatform::InitializeDevices()
 
   // If acceleration is disabled, we refuse to initialize anything.
   if (!gfxConfig::IsEnabled(Feature::HW_COMPOSITING)) {
     return;
   }
 
   MOZ_ASSERT(!InSafeMode());
 
+  // If we're the UI process, and the GPU process is enabled, then we don't
+  // initialize any DirectX devices. We do leave them enabled in gfxConfig
+  // though. If the GPU process fails to create these devices it will send
+  // a message back and we'll update their status.
+  if (gfxConfig::IsEnabled(Feature::GPU_PROCESS) && XRE_IsParentProcess()) {
+    return;
+  }
+
   // If we previously crashed initializing devices, bail out now.
   D3D11LayersCrashGuard detectCrashes;
   if (detectCrashes.Crashed()) {
     gfxConfig::SetFailed(Feature::HW_COMPOSITING,
                          FeatureStatus::CrashedOnStartup,
                          "Crashed during startup in a previous session");
     gfxConfig::SetFailed(Feature::D3D11_COMPOSITING,
                          FeatureStatus::CrashedOnStartup,
--- a/image/FrameAnimator.cpp
+++ b/image/FrameAnimator.cpp
@@ -2,17 +2,16 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "FrameAnimator.h"
 
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Move.h"
-#include "mozilla/NotNull.h"
 #include "imgIContainer.h"
 #include "LookupResult.h"
 #include "MainThreadUtils.h"
 #include "RasterImage.h"
 
 #include "pixman.h"
 
 namespace mozilla {
@@ -297,31 +296,27 @@ FrameAnimator::RequestRefresh(AnimationS
 
 LookupResult
 FrameAnimator::GetCompositedFrame(uint32_t aFrameNum)
 {
   MOZ_ASSERT(aFrameNum != 0, "First frame is never composited");
 
   // If we have a composited version of this frame, return that.
   if (mLastCompositedFrameIndex == int32_t(aFrameNum)) {
-    RefPtr<ISurfaceProvider> provider =
-      new SimpleSurfaceProvider(WrapNotNull(mCompositingFrame.get()));
-    return LookupResult(Move(provider), MatchType::EXACT);
+    return LookupResult(mCompositingFrame->DrawableRef(), MatchType::EXACT);
   }
 
   // Otherwise return the raw frame. DoBlend is required to ensure that we only
   // hit this case if the frame is not paletted and doesn't require compositing.
   LookupResult result =
     SurfaceCache::Lookup(ImageKey(mImage),
                          RasterSurfaceKey(mSize,
                                           DefaultSurfaceFlags(),
                                           aFrameNum));
-  MOZ_ASSERT(!result ||
-             !result.Provider()->DrawableRef() ||
-             !result.Provider()->DrawableRef()->GetIsPaletted(),
+  MOZ_ASSERT(!result || !result.DrawableRef()->GetIsPaletted(),
              "About to return a paletted frame");
   return result;
 }
 
 FrameTimeout
 FrameAnimator::GetTimeoutForFrame(uint32_t aFrameNum) const
 {
   RawAccessFrameRef frame = GetRawFrame(aFrameNum);
@@ -381,28 +376,18 @@ FrameAnimator::CollectSizeOfCompositingS
 RawAccessFrameRef
 FrameAnimator::GetRawFrame(uint32_t aFrameNum) const
 {
   LookupResult result =
     SurfaceCache::Lookup(ImageKey(mImage),
                          RasterSurfaceKey(mSize,
                                           DefaultSurfaceFlags(),
                                           aFrameNum));
-  if (!result) {
-    return RawAccessFrameRef();
-  }
-
-  DrawableFrameRef drawableRef = result.Provider()->DrawableRef();
-  if (!drawableRef) {
-    MOZ_ASSERT_UNREACHABLE("Animated image frames should be locked and "
-                           "in-memory; how did we lose one?");
-    return RawAccessFrameRef();
-  }
-
-  return drawableRef->RawAccessRef();
+  return result ? result.DrawableRef()->RawAccessRef()
+                : RawAccessFrameRef();
 }
 
 //******************************************************************************
 // DoBlend gets called when the timer for animation get fired and we have to
 // update the composited frame of the animation.
 bool
 FrameAnimator::DoBlend(IntRect* aDirtyRect,
                        uint32_t aPrevFrameIndex,
--- a/image/LookupResult.h
+++ b/image/LookupResult.h
@@ -8,83 +8,83 @@
  * combines a surface with relevant metadata tracked by SurfaceCache.
  */
 
 #ifndef mozilla_image_LookupResult_h
 #define mozilla_image_LookupResult_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/Move.h"
-#include "ISurfaceProvider.h"
+#include "imgFrame.h"
 
 namespace mozilla {
 namespace image {
 
 enum class MatchType : uint8_t
 {
   NOT_FOUND,  // No matching surface and no placeholder.
   PENDING,    // Found a matching placeholder, but no surface.
   EXACT,      // Found a surface that matches exactly.
   SUBSTITUTE_BECAUSE_NOT_FOUND,  // No exact match, but found a similar one.
   SUBSTITUTE_BECAUSE_PENDING     // Found a similar surface and a placeholder
                                  // for an exact match.
 };
 
 /**
  * LookupResult is the return type of SurfaceCache's Lookup*() functions. It
- * combines an ISurfaceProvider with relevant metadata tracked by SurfaceCache.
+ * combines a surface with relevant metadata tracked by SurfaceCache.
  */
 class MOZ_STACK_CLASS LookupResult
 {
 public:
   explicit LookupResult(MatchType aMatchType)
     : mMatchType(aMatchType)
   {
     MOZ_ASSERT(mMatchType == MatchType::NOT_FOUND ||
                mMatchType == MatchType::PENDING,
                "Only NOT_FOUND or PENDING make sense with no surface");
   }
 
   LookupResult(LookupResult&& aOther)
-    : mProvider(Move(aOther.mProvider))
+    : mDrawableRef(Move(aOther.mDrawableRef))
     , mMatchType(aOther.mMatchType)
   { }
 
-  LookupResult(RefPtr<ISurfaceProvider>&& aProvider, MatchType aMatchType)
-    : mProvider(Move(aProvider))
+  LookupResult(DrawableFrameRef&& aDrawableRef, MatchType aMatchType)
+    : mDrawableRef(Move(aDrawableRef))
     , mMatchType(aMatchType)
   {
-    MOZ_ASSERT(!mProvider || !(mMatchType == MatchType::NOT_FOUND ||
-                               mMatchType == MatchType::PENDING),
+    MOZ_ASSERT(!mDrawableRef || !(mMatchType == MatchType::NOT_FOUND ||
+                                  mMatchType == MatchType::PENDING),
                "Only NOT_FOUND or PENDING make sense with no surface");
-    MOZ_ASSERT(mProvider || mMatchType == MatchType::NOT_FOUND ||
-                            mMatchType == MatchType::PENDING,
+    MOZ_ASSERT(mDrawableRef || mMatchType == MatchType::NOT_FOUND ||
+                               mMatchType == MatchType::PENDING,
                "NOT_FOUND or PENDING do not make sense with a surface");
   }
 
   LookupResult& operator=(LookupResult&& aOther)
   {
     MOZ_ASSERT(&aOther != this, "Self-move-assignment is not supported");
-    mProvider = Move(aOther.mProvider);
+    mDrawableRef = Move(aOther.mDrawableRef);
     mMatchType = aOther.mMatchType;
     return *this;
   }
 
-  ISurfaceProvider* Provider() { return mProvider.get(); }
-  const ISurfaceProvider* Provider() const { return mProvider.get(); }
+  DrawableFrameRef& DrawableRef() { return mDrawableRef; }
+  const DrawableFrameRef& DrawableRef() const { return mDrawableRef; }
 
-  /// @return true if this LookupResult contains a surface provider.
-  explicit operator bool() const { return bool(mProvider); }
+  /// @return true if this LookupResult contains a surface.
+  explicit operator bool() const { return bool(mDrawableRef); }
 
   /// @return what kind of match this is (exact, substitute, etc.)
   MatchType Type() const { return mMatchType; }
 
 private:
   LookupResult(const LookupResult&) = delete;
 
-  RefPtr<ISurfaceProvider> mProvider;
+  DrawableFrameRef mDrawableRef;
   MatchType mMatchType;
 };
 
 } // namespace image
 } // namespace mozilla
 
 #endif // mozilla_image_LookupResult_h
--- a/image/RasterImage.cpp
+++ b/image/RasterImage.cpp
@@ -349,51 +349,40 @@ RasterImage::LookupFrame(uint32_t aFrame
     }
   }
 
   if (!result) {
     // We still weren't able to get a frame. Give up.
     return DrawableFrameRef();
   }
 
-  // OK, we've got an ISurfaceProvider. Ask it to give us a surface containing
-  // the frame we're looking for. (This could be computationally expensive in
-  // some cases.)
-  DrawableFrameRef drawableRef = result.Provider()->DrawableRef();
-  if (!drawableRef) {
-    // An ISurfaceProvider will only fail to give us a surface in catastrophic
-    // cases such as the operating system freeing our volatile buffers. Our best
-    // bet is to throw everything out and attempt to recover.
-    RecoverFromInvalidFrames(aSize, aFlags);
+  if (result.DrawableRef()->GetCompositingFailed()) {
     return DrawableFrameRef();
   }
 
-  if (drawableRef->GetCompositingFailed()) {
-    return DrawableFrameRef();
-  }
-
-  MOZ_ASSERT(!drawableRef->GetIsPaletted(), "Should not have a paletted frame");
+  MOZ_ASSERT(!result.DrawableRef()->GetIsPaletted(),
+             "Should not have a paletted frame");
 
   // Sync decoding guarantees that we got the frame, but if it's owned by an
   // async decoder that's currently running, the contents of the frame may not
   // be available yet. Make sure we get everything.
   if (mHasSourceData && (aFlags & FLAG_SYNC_DECODE)) {
-    drawableRef->WaitUntilFinished();
+    result.DrawableRef()->WaitUntilFinished();
   }
 
   // If we could have done some decoding in this function we need to check if
   // that decoding encountered an error and hence aborted the surface. We want
   // to avoid calling IsAborted if we weren't passed any sync decode flag because
   // IsAborted acquires the monitor for the imgFrame.
   if (aFlags & (FLAG_SYNC_DECODE | FLAG_SYNC_DECODE_IF_FAST) &&
-      drawableRef->IsAborted()) {
+    result.DrawableRef()->IsAborted()) {
     return DrawableFrameRef();
   }
 
-  return Move(drawableRef);
+  return Move(result.DrawableRef());
 }
 
 uint32_t
 RasterImage::GetCurrentFrameIndex() const
 {
   if (mAnimationState) {
     return mAnimationState->GetCurrentAnimationFrameIndex();
   }
--- a/image/SurfaceCache.cpp
+++ b/image/SurfaceCache.cpp
@@ -50,17 +50,16 @@ class SurfaceCacheImpl;
 
 ///////////////////////////////////////////////////////////////////////////////
 // Static Data
 ///////////////////////////////////////////////////////////////////////////////
 
 // The single surface cache instance.
 static StaticRefPtr<SurfaceCacheImpl> sInstance;
 
-
 ///////////////////////////////////////////////////////////////////////////////
 // SurfaceCache Implementation
 ///////////////////////////////////////////////////////////////////////////////
 
 /**
  * Cost models the cost of storing a surface in the cache. Right now, this is
  * simply an estimate of the size of the surface in bytes, but in the future it
  * may be worth taking into account the cost of rematerializing the surface as
@@ -142,27 +141,24 @@ public:
     , mImageKey(aImageKey)
     , mSurfaceKey(aSurfaceKey)
   {
     MOZ_ASSERT(aProvider || mCost == sPlaceholderCost,
                "Old-style placeholders should have trivial cost");
     MOZ_ASSERT(mImageKey, "Must have a valid image key");
   }
 
-  already_AddRefed<ISurfaceProvider> Provider() const
+  DrawableFrameRef DrawableRef() const
   {
     if (MOZ_UNLIKELY(IsPlaceholder())) {
-      MOZ_ASSERT_UNREACHABLE("Shouldn't call Provider() on a placeholder");
-      return nullptr;
+      MOZ_ASSERT_UNREACHABLE("Shouldn't call DrawableRef() on a placeholder");
+      return DrawableFrameRef();
     }
 
-    MOZ_ASSERT(mProvider);
-
-    RefPtr<ISurfaceProvider> provider = mProvider;
-    return provider.forget();
+    return mProvider->DrawableRef();
   }
 
   void SetLocked(bool aLocked)
   {
     if (IsPlaceholder()) {
       return;  // Can't lock a placeholder.
     }
 
@@ -197,51 +193,40 @@ public:
 
     void Add(CachedSurface* aCachedSurface)
     {
       MOZ_ASSERT(aCachedSurface, "Should have a CachedSurface");
 
       SurfaceMemoryCounter counter(aCachedSurface->GetSurfaceKey(),
                                    aCachedSurface->IsLocked());
 
-      if (aCachedSurface->IsPlaceholder()) {
-        mCounters.AppendElement(counter);
-        return;
-      }
-
-      RefPtr<ISurfaceProvider> provider = aCachedSurface->Provider();
-      if (!provider) {
-        MOZ_ASSERT_UNREACHABLE("Not a placeholder, but no ISurfaceProvider?");
-        mCounters.AppendElement(counter);
-        return;
-      }
+      if (!aCachedSurface->IsPlaceholder()) {
+        DrawableFrameRef surfaceRef = aCachedSurface->DrawableRef();
+        if (surfaceRef) {
+          counter.SubframeSize() = Some(surfaceRef->GetSize());
 
-      DrawableFrameRef surfaceRef = provider->DrawableRef();
-      if (!surfaceRef) {
-        mCounters.AppendElement(counter);
-        return;
+          size_t heap = 0, nonHeap = 0;
+          surfaceRef->AddSizeOfExcludingThis(mMallocSizeOf, heap, nonHeap);
+          counter.Values().SetDecodedHeap(heap);
+          counter.Values().SetDecodedNonHeap(nonHeap);
+        }
       }
 
-      counter.SubframeSize() = Some(surfaceRef->GetSize());
-      size_t heap = 0, nonHeap = 0;
-      surfaceRef->AddSizeOfExcludingThis(mMallocSizeOf, heap, nonHeap);
-      counter.Values().SetDecodedHeap(heap);
-      counter.Values().SetDecodedNonHeap(nonHeap);
-
       mCounters.AppendElement(counter);
     }
 
   private:
     nsTArray<SurfaceMemoryCounter>& mCounters;
     MallocSizeOf                    mMallocSizeOf;
   };
 
 private:
   nsExpirationState  mExpirationState;
   RefPtr<ISurfaceProvider> mProvider;
+  DrawableFrameRef   mDrawableRef;
   const Cost         mCost;
   const ImageKey     mImageKey;
   const SurfaceKey   mSurfaceKey;
 };
 
 static int64_t
 AreaOfIntSize(const IntSize& aSize) {
   return static_cast<int64_t>(aSize.width) * static_cast<int64_t>(aSize.height);
@@ -609,68 +594,81 @@ public:
       // Lookup in the per-image cache missed.
       return LookupResult(MatchType::NOT_FOUND);
     }
 
     if (surface->IsPlaceholder()) {
       return LookupResult(MatchType::PENDING);
     }
 
-    RefPtr<ISurfaceProvider> provider = surface->Provider();
-    if (!provider) {
-      MOZ_ASSERT_UNREACHABLE("Not a placeholder, but no ISurfaceProvider?");
+    DrawableFrameRef ref = surface->DrawableRef();
+    if (!ref) {
+      // The surface was released by the operating system. Remove the cache
+      // entry as well.
       Remove(surface);
       return LookupResult(MatchType::NOT_FOUND);
     }
 
     if (aMarkUsed) {
       MarkUsed(surface, cache);
     }
 
     MOZ_ASSERT(surface->GetSurfaceKey() == aSurfaceKey,
                "Lookup() not returning an exact match?");
-    return LookupResult(Move(provider), MatchType::EXACT);
+    return LookupResult(Move(ref), MatchType::EXACT);
   }
 
   LookupResult LookupBestMatch(const ImageKey         aImageKey,
                                const SurfaceKey&      aSurfaceKey)
   {
     RefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
     if (!cache) {
       // No cached surfaces for this image.
       return LookupResult(MatchType::NOT_FOUND);
     }
 
+    // Repeatedly look up the best match, trying again if the resulting surface
+    // has been freed by the operating system, until we can either lock a
+    // surface for drawing or there are no matching surfaces left.
+    // XXX(seth): This is O(N^2), but N is expected to be very small. If we
+    // encounter a performance problem here we can revisit this.
+
     RefPtr<CachedSurface> surface;
+    DrawableFrameRef ref;
     MatchType matchType = MatchType::NOT_FOUND;
-    Tie(surface, matchType) = cache->LookupBestMatch(aSurfaceKey);
-    if (!surface) {
-      return LookupResult(matchType);  // Lookup in the per-image cache missed.
-    }
+    while (true) {
+      Tie(surface, matchType) = cache->LookupBestMatch(aSurfaceKey);
+
+      if (!surface) {
+        return LookupResult(matchType);  // Lookup in the per-image cache missed.
+      }
 
-    RefPtr<ISurfaceProvider> provider = surface->Provider();
-    if (!provider) {
-      MOZ_ASSERT_UNREACHABLE("Not a placeholder, but no ISurfaceProvider?");
+      ref = surface->DrawableRef();
+      if (ref) {
+        break;
+      }
+
+      // The surface was released by the operating system. Remove the cache
+      // entry as well.
       Remove(surface);
-      return LookupResult(MatchType::NOT_FOUND);
     }
 
     MOZ_ASSERT_IF(matchType == MatchType::EXACT,
                   surface->GetSurfaceKey() == aSurfaceKey);
     MOZ_ASSERT_IF(matchType == MatchType::SUBSTITUTE_BECAUSE_NOT_FOUND ||
                   matchType == MatchType::SUBSTITUTE_BECAUSE_PENDING,
       surface->GetSurfaceKey().SVGContext() == aSurfaceKey.SVGContext() &&
       surface->GetSurfaceKey().AnimationTime() == aSurfaceKey.AnimationTime() &&
       surface->GetSurfaceKey().Flags() == aSurfaceKey.Flags());
 
     if (matchType == MatchType::EXACT) {
       MarkUsed(surface, cache);
     }
 
-    return LookupResult(Move(provider), matchType);
+    return LookupResult(Move(ref), matchType);
   }
 
   bool CanHold(const Cost aCost) const
   {
     return aCost <= mMaxCost;
   }
 
   size_t MaximumCapacity() const
--- a/image/SurfaceCache.h
+++ b/image/SurfaceCache.h
@@ -189,56 +189,57 @@ struct SurfaceCache
   static void Initialize();
 
   /**
    * Release static data. Called during imagelib module shutdown.
    */
   static void Shutdown();
 
   /**
-   * Looks up and returns the requested cache entry.
+   * Looks up the requested cache entry and returns a drawable reference to its
+   * associated surface.
    *
    * If the image associated with the cache entry is locked, then the entry will
    * be locked before it is returned.
    *
-   * This function returns an ISurfaceProvider, but it does not guarantee that
-   * the ISurfaceProvider can give the caller a drawable surface. Lookup()
-   * callers should check that ISurfaceProvider::DrawableRef() returns a
-   * non-empty value; if not, some sort of serious failure has occurred, and the
-   * best bet is to remove all existing surfaces for the image from the cache
-   * using RemoveImage() and try to recover. The most likely cause for this kind
-   * of failure is the surface's volatile buffer being freed by the operating
-   * system due to extreme memory pressure.
+   * If a matching ISurfaceProvider was found in the cache, but SurfaceCache
+   * couldn't obtain a surface from it (e.g. because it had stored its surface
+   * in a volatile buffer which was discarded by the OS) then it is
+   * automatically removed from the cache and an empty LookupResult is returned.
+   * Note that this will never happen to ISurfaceProviders associated with a
+   * locked image; SurfaceCache tells such ISurfaceProviders to keep a strong
+   * references to their data internally.
    *
    * @param aImageKey       Key data identifying which image the cache entry
    *                        belongs to.
    * @param aSurfaceKey     Key data which uniquely identifies the requested
    *                        cache entry.
-   * @return                a LookupResult which will contain an ISurfaceProvider
-   *                        for the requested surface if a matching cache entry
-   *                        was found.
+   * @return                a LookupResult, which will either contain a
+   *                        DrawableFrameRef to a surface, or an empty
+   *                        DrawableFrameRef if the cache entry was not found.
    */
   static LookupResult Lookup(const ImageKey    aImageKey,
                              const SurfaceKey& aSurfaceKey);
 
   /**
-   * Looks up and returns the best matching cache entry.
+   * Looks up the best matching cache entry and returns a drawable reference to
+   * its associated surface.
    *
    * The result may vary from the requested cache entry only in terms of size.
    *
    * @param aImageKey       Key data identifying which image the cache entry
    *                        belongs to.
    * @param aSurfaceKey     Key data which uniquely identifies the requested
    *                        cache entry.
-   * @return                a LookupResult which will contain either an
-   *                        ISurfaceProvider for a surface similar to the one
-   *                        the caller requested, or no ISurfaceProvider if no
-   *                        acceptable match was found. Callers can use
-   *                        LookupResult::IsExactMatch() to check whether the
-   *                        returned ISurfaceProvider exactly matches
+   * @return                a LookupResult, which will either contain a
+   *                        DrawableFrameRef to a surface similar to the
+   *                        the one the caller requested, or an empty
+   *                        DrawableFrameRef if no acceptable match was found.
+   *                        Callers can use LookupResult::IsExactMatch() to check
+   *                        whether the returned surface exactly matches
    *                        @aSurfaceKey.
    */
   static LookupResult LookupBestMatch(const ImageKey    aImageKey,
                                       const SurfaceKey& aSurfaceKey);
 
   /**
    * Insert an ISurfaceProvider into the cache. If an entry with the same
    * ImageKey and SurfaceKey is already in the cache, Insert returns
--- a/image/VectorImage.cpp
+++ b/image/VectorImage.cpp
@@ -854,65 +854,44 @@ VectorImage::Draw(gfxContext* aContext,
                                  : mSVGDocumentWrapper->GetCurrentTime();
   AutoSVGRenderingState autoSVGState(svgContext, animTime,
                                      mSVGDocumentWrapper->GetRootSVGElem());
 
 
   SVGDrawingParameters params(aContext, aSize, aRegion, aSamplingFilter,
                               svgContext, animTime, aFlags);
 
-  // If we have an prerasterized version of this image that matches the
-  // drawing parameters, use that.
-  RefPtr<gfxDrawable> svgDrawable = LookupCachedSurface(params);
-  if (svgDrawable) {
-    Show(svgDrawable, params);
+  if (aFlags & FLAG_BYPASS_SURFACE_CACHE) {
+    CreateSurfaceAndShow(params);
     return DrawResult::SUCCESS;
   }
 
-  // We didn't get a hit in the surface cache, so we'll need to rerasterize.
-  CreateSurfaceAndShow(params);
-  return DrawResult::SUCCESS;
-}
-
-already_AddRefed<gfxDrawable>
-VectorImage::LookupCachedSurface(const SVGDrawingParameters& aParams)
-{
-  // If we're not allowed to use a cached surface, don't attempt a lookup.
-  if (aParams.flags & FLAG_BYPASS_SURFACE_CACHE) {
-    return nullptr;
-  }
-
   LookupResult result =
     SurfaceCache::Lookup(ImageKey(this),
-                         VectorSurfaceKey(aParams.size,
-                                          aParams.svgContext,
-                                          aParams.animationTime));
-  if (!result) {
-    return nullptr;  // No matching surface.
+                         VectorSurfaceKey(params.size,
+                                          params.svgContext,
+                                          params.animationTime));
+
+  // Draw.
+  if (result) {
+    RefPtr<SourceSurface> surface = result.DrawableRef()->GetSurface();
+    if (surface) {
+      RefPtr<gfxDrawable> svgDrawable =
+        new gfxSurfaceDrawable(surface, result.DrawableRef()->GetSize());
+      Show(svgDrawable, params);
+      return DrawResult::SUCCESS;
+    }
+
+    // We lost our surface due to some catastrophic event.
+    RecoverFromLossOfSurfaces();
   }
 
-  DrawableFrameRef drawableRef = result.Provider()->DrawableRef();
-  if (!drawableRef) {
-    // Something went wrong. (Probably the OS freeing our volatile buffer due to
-    // low memory.) Attempt to recover.
-    RecoverFromLossOfSurfaces();
-    return nullptr;
-  }
+  CreateSurfaceAndShow(params);
 
-  RefPtr<SourceSurface> surface = drawableRef->GetSurface();
-  if (!surface) {
-    // Something went wrong. (Probably a GPU driver crash or device reset.)
-    // Attempt to recover.
-    RecoverFromLossOfSurfaces();
-    return nullptr;
-  }
-
-  RefPtr<gfxDrawable> svgDrawable =
-    new gfxSurfaceDrawable(surface, drawableRef->GetSize());
-  return svgDrawable.forget();
+  return DrawResult::SUCCESS;
 }
 
 void
 VectorImage::CreateSurfaceAndShow(const SVGDrawingParameters& aParams)
 {
   mSVGDocumentWrapper->UpdateViewportBounds(aParams.viewportSize);
   mSVGDocumentWrapper->FlushImageTransformInvalidation();
 
--- a/image/VectorImage.h
+++ b/image/VectorImage.h
@@ -73,24 +73,20 @@ public:
 protected:
   explicit VectorImage(ImageURL* aURI = nullptr);
   virtual ~VectorImage();
 
   virtual nsresult StartAnimation() override;
   virtual nsresult StopAnimation() override;
   virtual bool     ShouldAnimate() override;
 
-private:
-  /// Attempt to find a cached surface matching @aParams in the SurfaceCache.
-  already_AddRefed<gfxDrawable>
-    LookupCachedSurface(const SVGDrawingParameters& aParams);
-
   void CreateSurfaceAndShow(const SVGDrawingParameters& aParams);
   void Show(gfxDrawable* aDrawable, const SVGDrawingParameters& aParams);
 
+private:
   nsresult Init(const char* aMimeType, uint32_t aFlags);
 
   /**
    * In catastrophic circumstances like a GPU driver crash, we may lose our
    * surfaces even if they're locked. RecoverFromLossOfSurfaces discards all
    * existing surfaces, allowing us to recover.
    */
   void RecoverFromLossOfSurfaces();
--- a/intl/uconv/util/nsUCConstructors.cpp
+++ b/intl/uconv/util/nsUCConstructors.cpp
@@ -1,137 +1,134 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-
-
 #include "nsUCSupport.h"
 #include "nsUCConstructors.h"
 
 template<class T>
-inline NS_METHOD StabilizedQueryInterface(T* aNewObject,
+inline nsresult StabilizedQueryInterface(T* aNewObject,
                                          REFNSIID aIID,
                                          void **aResult)
 {
     NS_ADDREF(aNewObject);
     nsresult rv = aNewObject->QueryInterface(aIID, aResult);
     NS_RELEASE(aNewObject);
     return rv;
 }
 
-NS_METHOD
-CreateMultiTableDecoder(int32_t aTableCount, const uRange * aRangeArray, 
+nsresult
+CreateMultiTableDecoder(int32_t aTableCount, const uRange * aRangeArray,
                         uScanClassID * aScanClassArray,
                         uMappingTable ** aMappingTable,
                         uint32_t aMaxLengthFactor,
                         nsISupports* aOuter,
                         REFNSIID aIID,
                         void** aResult)
 {
 
   if (aOuter)
     return NS_ERROR_NO_AGGREGATION;
-  
+
   nsMultiTableDecoderSupport* decoder =
     new nsMultiTableDecoderSupport(aTableCount, aRangeArray,
                                    aScanClassArray, aMappingTable,
                                    aMaxLengthFactor);
   if (!decoder)
     return NS_ERROR_OUT_OF_MEMORY;
 
   return StabilizedQueryInterface(decoder, aIID, aResult);
 }
 
-NS_METHOD
+nsresult
 CreateMultiTableEncoder(int32_t aTableCount,
                         uScanClassID * aScanClassArray,
                         uShiftOutTable ** aShiftOutTable,
                         uMappingTable ** aMappingTable,
                         uint32_t aMaxLengthFactor,
                         nsISupports* aOuter,
                         REFNSIID aIID,
                         void** aResult)
 {
 
   if (aOuter)
     return NS_ERROR_NO_AGGREGATION;
-  
+
   nsMultiTableEncoderSupport* encoder =
     new nsMultiTableEncoderSupport(aTableCount,
                                    aScanClassArray,
                                    aShiftOutTable,
                                    aMappingTable,
                                    aMaxLengthFactor);
   if (!encoder)
     return NS_ERROR_OUT_OF_MEMORY;
 
   return StabilizedQueryInterface(encoder, aIID, aResult);
 }
 
-NS_METHOD
+nsresult
 CreateMultiTableEncoder(int32_t aTableCount,
                         uScanClassID * aScanClassArray,
                         uMappingTable ** aMappingTable,
                         uint32_t aMaxLengthFactor,
                         nsISupports* aOuter,
                         REFNSIID aIID,
                         void** aResult)
 {
   return CreateMultiTableEncoder(aTableCount, aScanClassArray,
                                  nullptr,
                                  aMappingTable, aMaxLengthFactor,
                                  aOuter, aIID, aResult);
 }
 
-NS_METHOD
+nsresult
 CreateTableEncoder(uScanClassID aScanClass,
                    uShiftOutTable * aShiftOutTable,
                    uMappingTable  * aMappingTable,
                    uint32_t aMaxLengthFactor,
                    nsISupports* aOuter,
                    REFNSIID aIID,
                    void** aResult)
 {
   if (aOuter)
     return NS_ERROR_NO_AGGREGATION;
-  
+
   nsTableEncoderSupport* encoder =
       new nsTableEncoderSupport(aScanClass,
                                 aShiftOutTable,  aMappingTable,
                                 aMaxLengthFactor);
   if (!encoder)
     return NS_ERROR_OUT_OF_MEMORY;
 
   return StabilizedQueryInterface(encoder, aIID, aResult);
 }
 
-NS_METHOD
+nsresult
 CreateTableEncoder(uScanClassID aScanClass,
                    uMappingTable  * aMappingTable,
                    uint32_t aMaxLengthFactor,
                    nsISupports* aOuter,
                    REFNSIID aIID,
                    void** aResult)
 {
     return CreateTableEncoder(aScanClass, nullptr,
                               aMappingTable, aMaxLengthFactor,
                               aOuter, aIID, aResult);
 }
 
-NS_METHOD
+nsresult
 CreateOneByteDecoder(uMappingTable * aMappingTable,
-                     
                      nsISupports* aOuter,
                      REFNSIID aIID,
                      void** aResult)
 {
     if (aOuter) return NS_ERROR_NO_AGGREGATION;
-    
+
     nsOneByteDecoderSupport* decoder =
         new nsOneByteDecoderSupport(aMappingTable);
 
     if (!decoder)
         return NS_ERROR_OUT_OF_MEMORY;
-    
+
     return StabilizedQueryInterface(decoder, aIID, aResult);
 }
--- a/intl/uconv/util/nsUCConstructors.h
+++ b/intl/uconv/util/nsUCConstructors.h
@@ -9,63 +9,62 @@
 #include <stdint.h>
 #include "nscore.h"
 #include "nsID.h"
 #include "uconvutil.h"
 
 class nsISupports;
 
 // all the useful constructors
-NS_METHOD
+nsresult
 CreateMultiTableDecoder(int32_t aTableCount,
-                        const uRange * aRangeArray, 
+                        const uRange * aRangeArray,
                         uScanClassID * aScanClassArray,
                         uMappingTable ** aMappingTable,
                         uint32_t aMaxLengthFactor,
-                        
                         nsISupports* aOuter,
                         REFNSIID aIID,
                         void** aResult);
 
-NS_METHOD
+nsresult
 CreateMultiTableEncoder(int32_t aTableCount,
                         uScanClassID * aScanClassArray,
                         uShiftOutTable ** aShiftOutTable,
                         uMappingTable  ** aMappingTable,
                         uint32_t aMaxLengthFactor,
                         nsISupports* aOuter,
                         REFNSIID aIID,
                         void** aResult);
 
-NS_METHOD
+nsresult
 CreateTableEncoder(uScanClassID aScanClass,
                    uShiftOutTable * aShiftOutTable,
                    uMappingTable  * aMappingTable,
                    uint32_t aMaxLengthFactor,
                    nsISupports* aOuter,
                    REFNSIID aIID,
                    void** aResult);
 
-NS_METHOD
+nsresult
 CreateMultiTableEncoder(int32_t aTableCount,
                         uScanClassID * aScanClassArray,
                         uMappingTable  ** aMappingTable,
                         uint32_t aMaxLengthFactor,
                         nsISupports* aOuter,
                         REFNSIID aIID,
                         void** aResult);
 
-NS_METHOD
+nsresult
 CreateTableEncoder(uScanClassID aScanClass,
                    uMappingTable  * aMappingTable,
                    uint32_t aMaxLengthFactor,
                    nsISupports* aOuter,
                    REFNSIID aIID,
                    void** aResult);
 
-NS_METHOD
+nsresult
 CreateOneByteDecoder(uMappingTable * aMappingTable,
                      nsISupports* aOuter,
                      REFNSIID aIID,
                      void** aResult);
 
                    
 #endif
--- a/ipc/glue/WindowsMessageLoop.cpp
+++ b/ipc/glue/WindowsMessageLoop.cpp
@@ -1091,39 +1091,47 @@ MessageChannel::WaitForSyncNotifyWithA11
                                             1, &mEvent, &waitResult);
     if (hr == RPC_S_CALLPENDING) {
       timedOut = true;
       break;
     }
     if (hr == S_OK) {
       if (waitResult == 0) {
         // mEvent is signaled
+        BOOL success = ::ResetEvent(mEvent);
+        if (!success) {
+          gfxDevCrash(mozilla::gfx::LogReason::MessageChannelInvalidHandle) <<
+                      "WindowsMessageChannel::WaitForSyncNotifyWithA11yReentry failed to reset event. GetLastError: " <<
+                      GetLastError();
+        }
         break;
       }
       if (waitResult == WAIT_IO_COMPLETION) {
         // APC fired, keep waiting
         continue;
       }
     }
-    NS_WARN_IF_FALSE(SUCCEEDED(hr), "CoWaitForMultipleHandles failed");
+    NS_ERROR("CoWaitForMultipleHandles failed");
+    break;
   }
 
   return WaitResponse(timedOut);
 }
 #endif
 
 bool
 MessageChannel::WaitForSyncNotify(bool aHandleWindowsMessages)
 {
   mMonitor->AssertCurrentThreadOwns();
 
   MOZ_ASSERT(gUIThreadId, "InitUIThread was not called!");
 
 #if defined(ACCESSIBILITY)
   if (IsVistaOrLater() && (mFlags & REQUIRE_A11Y_REENTRY)) {
+    MOZ_ASSERT(!(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION));
     return WaitForSyncNotifyWithA11yReentry();
   }
 #endif
 
   // Use a blocking wait if this channel does not require
   // Windows message deferral behavior.
   if (!(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION) || !aHandleWindowsMessages) {
     PRIntervalTime timeout = (kNoTimeout == mTimeoutMs) ?
--- a/js/public/StructuredClone.h
+++ b/js/public/StructuredClone.h
@@ -17,16 +17,23 @@
 
 struct JSRuntime;
 struct JSStructuredCloneReader;
 struct JSStructuredCloneWriter;
 
 // API for the HTML5 internal structured cloning algorithm.
 
 namespace JS {
+
+enum class StructuredCloneScope : uint32_t {
+    SameProcessSameThread,
+    SameProcessDifferentThread,
+    DifferentProcess
+};
+
 enum TransferableOwnership {
     /** Transferable data has not been filled in yet */
     SCTAG_TMO_UNFILLED = 0,
 
     /** Structured clone buffer does not yet own the data */
     SCTAG_TMO_UNOWNED = 1,
 
     /** All values at least this large are owned by the clone buffer */
@@ -129,39 +136,41 @@ typedef bool (*TransferStructuredCloneOp
  */
 typedef void (*FreeTransferStructuredCloneOp)(uint32_t tag, JS::TransferableOwnership ownership,
                                               void* content, uint64_t extraData, void* closure);
 
 // The maximum supported structured-clone serialization format version.
 // Increment this when anything at all changes in the serialization format.
 // (Note that this does not need to be bumped for Transferable-only changes,
 // since they are never saved to persistent storage.)
-#define JS_STRUCTURED_CLONE_VERSION 6
+#define JS_STRUCTURED_CLONE_VERSION 7
 
 struct JSStructuredCloneCallbacks {
     ReadStructuredCloneOp read;
     WriteStructuredCloneOp write;
     StructuredCloneErrorOp reportError;
     ReadTransferStructuredCloneOp readTransfer;
     TransferStructuredCloneOp writeTransfer;
     FreeTransferStructuredCloneOp freeTransfer;
 };
 
 /** Note: if the *data contains transferable objects, it can be read only once. */
 JS_PUBLIC_API(bool)
 JS_ReadStructuredClone(JSContext* cx, uint64_t* data, size_t nbytes, uint32_t version,
+                       JS::StructuredCloneScope scope,
                        JS::MutableHandleValue vp,
                        const JSStructuredCloneCallbacks* optionalCallbacks, void* closure);
 
 /**
  * Note: On success, the caller is responsible for calling
  * JS_ClearStructuredClone(*datap, nbytes, optionalCallbacks, closure).
  */
 JS_PUBLIC_API(bool)
 JS_WriteStructuredClone(JSContext* cx, JS::HandleValue v, uint64_t** datap, size_t* nbytesp,
+                        JS::StructuredCloneScope scope,
                         const JSStructuredCloneCallbacks* optionalCallbacks,
                         void* closure, JS::HandleValue transferable);
 
 JS_PUBLIC_API(bool)
 JS_ClearStructuredClone(uint64_t* data, size_t nbytes,
                         const JSStructuredCloneCallbacks* optionalCallbacks,
                         void *closure, bool freeData = true);
 
@@ -169,37 +178,33 @@ JS_PUBLIC_API(bool)
 JS_StructuredCloneHasTransferables(const uint64_t* data, size_t nbytes, bool* hasTransferable);
 
 JS_PUBLIC_API(bool)
 JS_StructuredClone(JSContext* cx, JS::HandleValue v, JS::MutableHandleValue vp,
                    const JSStructuredCloneCallbacks* optionalCallbacks, void* closure);
 
 /** RAII sugar for JS_WriteStructuredClone. */
 class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) {
+    const JS::StructuredCloneScope scope_;
     uint64_t* data_;
     size_t nbytes_;
     uint32_t version_;
     enum {
         OwnsTransferablesIfAny,
         IgnoreTransferablesIfAny,
         NoTransferables
     } ownTransferables_;
 
     const JSStructuredCloneCallbacks* callbacks_;
     void* closure_;
 
   public:
-    JSAutoStructuredCloneBuffer()
-        : data_(nullptr), nbytes_(0), version_(JS_STRUCTURED_CLONE_VERSION),
-          ownTransferables_(NoTransferables),
-          callbacks_(nullptr), closure_(nullptr)
-    {}
-
-    JSAutoStructuredCloneBuffer(const JSStructuredCloneCallbacks* callbacks, void* closure)
-        : data_(nullptr), nbytes_(0), version_(JS_STRUCTURED_CLONE_VERSION),
+    JSAutoStructuredCloneBuffer(JS::StructuredCloneScope scope,
+                                const JSStructuredCloneCallbacks* callbacks, void* closure)
+        : scope_(scope), data_(nullptr), nbytes_(0), version_(JS_STRUCTURED_CLONE_VERSION),
           ownTransferables_(NoTransferables),
           callbacks_(callbacks), closure_(closure)
     {}
 
     JSAutoStructuredCloneBuffer(JSAutoStructuredCloneBuffer&& other);
     JSAutoStructuredCloneBuffer& operator=(JSAutoStructuredCloneBuffer&& other);
 
     ~JSAutoStructuredCloneBuffer() { clear(); }
@@ -280,9 +285,12 @@ JS_PUBLIC_API(bool)
 JS_WriteString(JSStructuredCloneWriter* w, JS::HandleString str);
 
 JS_PUBLIC_API(bool)
 JS_WriteTypedArray(JSStructuredCloneWriter* w, JS::HandleValue v);
 
 JS_PUBLIC_API(bool)
 JS_ObjectNotWritten(JSStructuredCloneWriter* w, JS::HandleObject obj);
 
+JS_PUBLIC_API(JS::StructuredCloneScope)
+JS_GetStructuredCloneScope(JSStructuredCloneWriter* w);
+
 #endif  /* js_StructuredClone_h */
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -305,17 +305,18 @@ struct js::AsmJSMetadata : Metadata, Asm
     uint32_t srcEndBeforeCurly() const {
         return srcStart + srcLength;
     }
     uint32_t srcEndAfterCurly() const {
         return srcStart + srcLengthWithRightBrace;
     }
 
     AsmJSMetadata()
-      : cacheResult(CacheResult::Miss),
+      : Metadata(ModuleKind::AsmJS),
+        cacheResult(CacheResult::Miss),
         srcStart(0),
         srcBodyStart(0),
         strict(false)
     {}
     ~AsmJSMetadata() override {}
 
     const AsmJSExport& lookupAsmJSExport(uint32_t funcIndex) const {
         // The AsmJSExportVector isn't stored in sorted order so do a linear
@@ -1758,41 +1759,42 @@ class MOZ_STACK_CLASS ModuleValidator
 
         // This flows into FunctionBox, so must be tenured.
         dummyFunction_ = NewScriptedFunction(cx_, 0, JSFunction::INTERPRETED, nullptr,
                                              /* proto = */ nullptr, gc::AllocKind::FUNCTION,
                                              TenuredObject);
         if (!dummyFunction_)
             return false;
 
-        UniqueChars filename;
+        ScriptedCaller scriptedCaller;
         if (parser_.ss->filename()) {
-            filename = DuplicateString(parser_.ss->filename());
-            if (!filename)
+            scriptedCaller.line = scriptedCaller.column = 0;  // unused
+            scriptedCaller.filename = DuplicateString(parser_.ss->filename());
+            if (!scriptedCaller.filename)
                 return false;
         }
 
         CompileArgs args;
-        if (!args.initFromContext(cx_, Move(filename)))
+        if (!args.initFromContext(cx_, Move(scriptedCaller)))
             return false;
 
         auto genData = MakeUnique<ModuleGeneratorData>(args.assumptions.usesSignal, ModuleKind::AsmJS);
         if (!genData ||
             !genData->sigs.resize(MaxSigs) ||
             !genData->funcSigs.resize(MaxFuncs) ||
             !genData->funcImports.resize(MaxImports) ||
             !genData->tables.resize(MaxTables) ||
             !genData->asmJSSigToTableIndex.resize(MaxSigs))
         {
             return false;
         }
 
         genData->minMemoryLength = RoundUpToNextValidAsmJSHeapLength(0);
 
-        if (!mg_.init(Move(genData), Move(args), asmJSMetadata_.get()))
+        if (!mg_.init(Move(genData), args, asmJSMetadata_.get()))
             return false;
 
         return true;
     }
 
     ExclusiveContext* cx() const             { return cx_; }
     PropertyName* moduleFunctionName() const { return moduleFunctionName_; }
     PropertyName* globalArgumentName() const { return globalArgumentName_; }
--- a/js/src/asmjs/WasmCode.h
+++ b/js/src/asmjs/WasmCode.h
@@ -414,19 +414,20 @@ class MetadataCacheablePod
     uint32_t              startFuncIndex_;
 
   public:
     ModuleKind            kind;
     MemoryUsage           memoryUsage;
     uint32_t              minMemoryLength;
     uint32_t              maxMemoryLength;
 
-    MetadataCacheablePod() {
+    explicit MetadataCacheablePod(ModuleKind kind) {
         mozilla::PodZero(this);
         startFuncIndex_ = NO_START_FUNCTION;
+        this->kind = kind;
     }
 
     bool hasStartFunction() const {
         return startFuncIndex_ != NO_START_FUNCTION;
     }
     void initStartFuncIndex(uint32_t i) {
         MOZ_ASSERT(!hasStartFunction());
         startFuncIndex_ = i;
@@ -435,16 +436,17 @@ class MetadataCacheablePod
     uint32_t startFuncIndex() const {
         MOZ_ASSERT(hasStartFunction());
         return startFuncIndex_;
     }
 };
 
 struct Metadata : ShareableBase<Metadata>, MetadataCacheablePod
 {
+    explicit Metadata(ModuleKind kind = ModuleKind::Wasm) : MetadataCacheablePod(kind) {}
     virtual ~Metadata() {}
 
     MetadataCacheablePod& pod() { return *this; }
     const MetadataCacheablePod& pod() const { return *this; }
 
     FuncImportVector      funcImports;
     FuncExportVector      funcExports;
     SigWithIdVector       sigIds;
--- a/js/src/asmjs/WasmCompile.cpp
+++ b/js/src/asmjs/WasmCompile.cpp
@@ -1580,25 +1580,25 @@ DecodeUnknownSections(Decoder& d)
         if (!d.skipSection())
             return Fail(d, "failed to skip unknown section at end");
     }
 
     return true;
 }
 
 bool
-CompileArgs::initFromContext(ExclusiveContext* cx, UniqueChars f)
+CompileArgs::initFromContext(ExclusiveContext* cx, ScriptedCaller&& scriptedCaller)
 {
     alwaysBaseline = cx->options().wasmAlwaysBaseline();
-    filename = Move(f);
+    this->scriptedCaller = Move(scriptedCaller);
     return assumptions.initBuildIdFromContext(cx);
 }
 
 SharedModule
-wasm::Compile(const ShareableBytes& bytecode, CompileArgs&& args, UniqueChars* error)
+wasm::Compile(const ShareableBytes& bytecode, const CompileArgs& args, UniqueChars* error)
 {
     bool newFormat = args.assumptions.newFormat;
 
     auto init = js::MakeUnique<ModuleGeneratorData>(args.assumptions.usesSignal);
     if (!init)
         return nullptr;
 
     Decoder d(bytecode.begin(), bytecode.end(), error);
@@ -1623,17 +1623,17 @@ wasm::Compile(const ShareableBytes& byte
     bool memoryExported = false;
     if (!DecodeMemorySection(d, newFormat, init.get(), &memoryExported))
         return nullptr;
 
     if (!DecodeGlobalSection(d, init.get()))
         return nullptr;
 
     ModuleGenerator mg(Move(imports));
-    if (!mg.init(Move(init), Move(args)))
+    if (!mg.init(Move(init), args))
         return nullptr;
 
     if (!DecodeExportSection(d, newFormat, memoryExported, mg))
         return nullptr;
 
     if (!DecodeStartSection(d, mg))
         return nullptr;
 
--- a/js/src/asmjs/WasmCompile.h
+++ b/js/src/asmjs/WasmCompile.h
@@ -20,36 +20,50 @@
 #define wasm_compile_h
 
 #include "asmjs/WasmJS.h"
 #include "asmjs/WasmModule.h"
 
 namespace js {
 namespace wasm {
 
-// Compile the given WebAssembly bytecode with the given assumptions, settings
-// and filename into a wasm::Module.
+// Describes the JS scripted caller of a request to compile a wasm module.
+
+struct ScriptedCaller
+{
+    UniqueChars filename;
+    unsigned line;
+    unsigned column;
+};
+
+// Describes all the parameters that control wasm compilation.
 
 struct CompileArgs
 {
     Assumptions assumptions;
-    UniqueChars filename;
+    ScriptedCaller scriptedCaller;
     bool alwaysBaseline;
 
-    CompileArgs(Assumptions&& assumptions, UniqueChars filename)
+    CompileArgs(Assumptions&& assumptions, ScriptedCaller&& scriptedCaller)
       : assumptions(Move(assumptions)),
-        filename(Move(filename)),
+        scriptedCaller(Move(scriptedCaller)),
         alwaysBaseline(false)
     {}
 
     // If CompileArgs is constructed without arguments, initFromContext() must
     // be called to complete initialization.
     CompileArgs() = default;
-    bool initFromContext(ExclusiveContext* cx, UniqueChars filename);
+    bool initFromContext(ExclusiveContext* cx, ScriptedCaller&& scriptedCaller);
 };
 
+// Compile the given WebAssembly bytecode with the given arguments into a
+// wasm::Module. On success, the Module is returned. On failure, the returned
+// SharedModule pointer is null and either:
+//  - *error points to a string description of the error
+//  - *error is null and the caller should report out-of-memory.
+
 SharedModule
-Compile(const ShareableBytes& bytecode, CompileArgs&& args, UniqueChars* error);
+Compile(const ShareableBytes& bytecode, const CompileArgs& args, UniqueChars* error);
 
 }  // namespace wasm
 }  // namespace js
 
 #endif // namespace wasm_compile_h
--- a/js/src/asmjs/WasmGenerator.cpp
+++ b/js/src/asmjs/WasmGenerator.cpp
@@ -92,41 +92,46 @@ ModuleGenerator::~ModuleGenerator()
         MOZ_ASSERT(HelperThreadState().wasmCompilationInProgress);
         HelperThreadState().wasmCompilationInProgress = false;
     } else {
         MOZ_ASSERT(!outstanding_);
     }
 }
 
 bool
-ModuleGenerator::init(UniqueModuleGeneratorData shared, CompileArgs&& args,
+ModuleGenerator::init(UniqueModuleGeneratorData shared, const CompileArgs& args,
                       Metadata* maybeAsmJSMetadata)
 {
+    shared_ = Move(shared);
     alwaysBaseline_ = args.alwaysBaseline;
 
     if (!exportedFuncs_.init())
         return false;
 
     linkData_.globalDataLength = AlignBytes(InitialGlobalDataBytes, sizeof(void*));;
 
     // asm.js passes in an AsmJSMetadata subclass to use instead.
     if (maybeAsmJSMetadata) {
-        MOZ_ASSERT(shared->kind == ModuleKind::AsmJS);
         metadata_ = maybeAsmJSMetadata;
+        MOZ_ASSERT(isAsmJS());
     } else {
         metadata_ = js_new<Metadata>();
         if (!metadata_)
             return false;
+        MOZ_ASSERT(!isAsmJS());
     }
 
-    metadata_->kind = shared->kind;
-    metadata_->filename = Move(args.filename);
-    metadata_->assumptions = Move(args.assumptions);
+    if (args.scriptedCaller.filename) {
+        metadata_->filename = DuplicateString(args.scriptedCaller.filename.get());
+        if (!metadata_->filename)
+            return false;
+    }
 
-    shared_ = Move(shared);
+    if (!metadata_->assumptions.clone(args.assumptions))
+        return false;
 
     // For asm.js, the Vectors in ModuleGeneratorData are max-sized reservations
     // and will be initialized in a linear order via init* functions as the
     // module is generated. For wasm, the Vectors are correctly-sized and
     // already initialized.
 
     if (!isAsmJS()) {
         numSigs_ = shared_->sigs.length();
--- a/js/src/asmjs/WasmGenerator.h
+++ b/js/src/asmjs/WasmGenerator.h
@@ -136,17 +136,17 @@ class MOZ_STACK_CLASS ModuleGenerator
     MOZ_MUST_USE bool addFuncImport(const Sig& sig, uint32_t globalDataOffset);
     MOZ_MUST_USE bool allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOff);
     MOZ_MUST_USE bool allocateGlobal(GlobalDesc* global);
 
   public:
     explicit ModuleGenerator(ImportVector&& imports);
     ~ModuleGenerator();
 
-    MOZ_MUST_USE bool init(UniqueModuleGeneratorData shared, CompileArgs&& args,
+    MOZ_MUST_USE bool init(UniqueModuleGeneratorData shared, const CompileArgs& args,
                            Metadata* maybeAsmJSMetadata = nullptr);
 
     bool isAsmJS() const { return metadata_->kind == ModuleKind::AsmJS; }
     SignalUsage usesSignal() const { return metadata_->assumptions.usesSignal; }
     jit::MacroAssembler& masm() { return masm_; }
 
     // Memory:
     bool usesMemory() const { return UsesMemory(shared_->memoryUsage); }
--- a/js/src/asmjs/WasmJS.cpp
+++ b/js/src/asmjs/WasmJS.cpp
@@ -16,18 +16,19 @@
  * limitations under the License.
  */
 
 #include "asmjs/WasmJS.h"
 
 #include "asmjs/WasmCompile.h"
 #include "asmjs/WasmInstance.h"
 #include "asmjs/WasmModule.h"
-
+#include "builtin/Promise.h"
 #include "jit/JitOptions.h"
+#include "vm/Interpreter.h"
 
 #include "jsobjinlines.h"
 
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 using namespace js::jit;
 using namespace js::wasm;
@@ -199,48 +200,59 @@ GetImports(JSContext* cx,
         }
     }
 
     MOZ_ASSERT(globalIndex == globals.length() || !globals[globalIndex].isImport());
 
     return true;
 }
 
+static bool
+DescribeScriptedCaller(JSContext* cx, ScriptedCaller* scriptedCaller)
+{
+    // Note: JS::DescribeScriptedCaller returns whether a scripted caller was
+    // found, not whether an error was thrown. This wrapper function converts
+    // back to the more ordinary false-if-error form.
+
+    JS::AutoFilename af;
+    if (JS::DescribeScriptedCaller(cx, &af, &scriptedCaller->line, &scriptedCaller->column)) {
+        scriptedCaller->filename = DuplicateString(cx, af.get());
+        if (!scriptedCaller->filename)
+            return false;
+    }
+
+    return true;
+}
+
 bool
 wasm::Eval(JSContext* cx, Handle<TypedArrayObject*> code, HandleObject importObj,
            MutableHandleWasmInstanceObject instanceObj)
 {
     if (!CheckCompilerSupport(cx))
         return false;
 
     MutableBytes bytecode = cx->new_<ShareableBytes>();
     if (!bytecode)
         return false;
 
     if (!bytecode->append((uint8_t*)code->viewDataEither().unwrap(), code->byteLength())) {
         ReportOutOfMemory(cx);
         return false;
     }
 
-    UniqueChars filename;
-    {
-        JS::AutoFilename af;
-        if (DescribeScriptedCaller(cx, &af)) {
-            filename = DuplicateString(cx, af.get());
-            if (!filename)
-                return false;
-        }
-    }
+    ScriptedCaller scriptedCaller;
+    if (!DescribeScriptedCaller(cx, &scriptedCaller))
+        return false;
 
     CompileArgs compileArgs;
-    if (!compileArgs.initFromContext(cx, Move(filename)))
+    if (!compileArgs.initFromContext(cx, Move(scriptedCaller)))
         return false;
 
     UniqueChars error;
-    SharedModule module = Compile(*bytecode, Move(compileArgs), &error);
+    SharedModule module = Compile(*bytecode, compileArgs, &error);
     if (!module) {
         if (error)
             JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_FAIL, error.get());
         else
             ReportOutOfMemory(cx);
         return false;
     }
 
@@ -376,74 +388,72 @@ WasmModuleObject::create(ExclusiveContex
     if (!obj)
         return nullptr;
 
     obj->initReservedSlot(MODULE_SLOT, PrivateValue(&module));
     module.AddRef();
     return obj;
 }
 
+static bool
+GetCompileArgs(JSContext* cx, CallArgs callArgs, const char* name, MutableBytes* bytecode,
+               CompileArgs* compileArgs)
+{
+    if (!callArgs.requireAtLeast(cx, name, 1))
+        return false;
+
+    if (!callArgs[0].isObject()) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_BUF_ARG);
+        return false;
+    }
+
+    *bytecode = cx->new_<ShareableBytes>();
+    if (!*bytecode)
+        return false;
+
+    if (callArgs[0].toObject().is<TypedArrayObject>()) {
+        TypedArrayObject& view = callArgs[0].toObject().as<TypedArrayObject>();
+        if (!(*bytecode)->append((uint8_t*)view.viewDataEither().unwrap(), view.byteLength()))
+            return false;
+    } else if (callArgs[0].toObject().is<ArrayBufferObject>()) {
+        ArrayBufferObject& buffer = callArgs[0].toObject().as<ArrayBufferObject>();
+        if (!(*bytecode)->append(buffer.dataPointer(), buffer.byteLength()))
+            return false;
+    } else {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_BUF_ARG);
+        return false;
+    }
+
+    ScriptedCaller scriptedCaller;
+    if (!DescribeScriptedCaller(cx, &scriptedCaller))
+        return false;
+
+    if (!compileArgs->initFromContext(cx, Move(scriptedCaller)))
+        return false;
+
+    compileArgs->assumptions.newFormat = true;
+
+    return CheckCompilerSupport(cx);
+}
+
 /* static */ bool
 WasmModuleObject::construct(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs callArgs = CallArgsFromVp(argc, vp);
 
     if (!ThrowIfNotConstructing(cx, callArgs, "Module"))
         return false;
 
-    if (!callArgs.requireAtLeast(cx, "WebAssembly.Module", 1))
-        return false;
-
-    if (!callArgs.get(0).isObject()) {
-        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_BUF_ARG);
-        return false;
-    }
-
-    MutableBytes bytecode = cx->new_<ShareableBytes>();
-    if (!bytecode)
-        return false;
-
-    if (callArgs[0].toObject().is<TypedArrayObject>()) {
-        TypedArrayObject& view = callArgs[0].toObject().as<TypedArrayObject>();
-        if (!bytecode->append((uint8_t*)view.viewDataEither().unwrap(), view.byteLength())) {
-            ReportOutOfMemory(cx);
-            return false;
-        }
-    } else if (callArgs[0].toObject().is<ArrayBufferObject>()) {
-        ArrayBufferObject& buffer = callArgs[0].toObject().as<ArrayBufferObject>();
-        if (!bytecode->append(buffer.dataPointer(), buffer.byteLength())) {
-            ReportOutOfMemory(cx);
-            return false;
-        }
-    } else {
-        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_BUF_ARG);
-        return false;
-    }
-
-    UniqueChars filename;
-    {
-        JS::AutoFilename af;
-        if (DescribeScriptedCaller(cx, &af)) {
-            filename = DuplicateString(cx, af.get());
-            if (!filename)
-                return false;
-        }
-    }
-
+    MutableBytes bytecode;
     CompileArgs compileArgs;
-    if (!compileArgs.initFromContext(cx, Move(filename)))
-        return false;
-
-    compileArgs.assumptions.newFormat = true;
-
-    if (!CheckCompilerSupport(cx))
+    if (!GetCompileArgs(cx, callArgs, "WebAssembly.Module", &bytecode, &compileArgs))
         return false;
 
     UniqueChars error;
-    SharedModule module = Compile(*bytecode, Move(compileArgs), &error);
+    SharedModule module = Compile(*bytecode, compileArgs, &error);
     if (!module) {
         if (error)
             JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_FAIL, error.get());
         else
             ReportOutOfMemory(cx);
         return false;
     }
 
@@ -1122,21 +1132,123 @@ static bool
 WebAssembly_toSource(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setString(cx->names().WebAssembly);
     return true;
 }
 #endif
 
+#ifdef SPIDERMONKEY_PROMISE
+static bool
+Nop(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    args.rval().setUndefined();
+    return true;
+}
+
+static bool
+Reject(JSContext* cx, const CompileArgs& args, UniqueChars error, Handle<PromiseObject*> promise)
+{
+    if (!error) {
+        ReportOutOfMemory(cx);
+
+        RootedValue rejectionValue(cx);
+        if (!cx->getPendingException(&rejectionValue))
+            return false;
+
+        return promise->reject(cx, rejectionValue);
+    }
+
+    RootedObject stack(cx, promise->allocationSite());
+    RootedString filename(cx, JS_NewStringCopyZ(cx, args.scriptedCaller.filename.get()));
+    if (!filename)
+        return false;
+
+    unsigned line = args.scriptedCaller.line;
+    unsigned column = args.scriptedCaller.column;
+
+    RootedString message(cx, NewLatin1StringZ(cx, Move(error)));
+    if (!message)
+        return false;
+
+    RootedObject errorObj(cx,
+        ErrorObject::create(cx, JSEXN_TYPEERR, stack, filename, line, column, nullptr, message));
+    if (!errorObj)
+        return false;
+
+    RootedValue rejectionValue(cx, ObjectValue(*errorObj));
+    return promise->reject(cx, rejectionValue);
+}
+
+static bool
+Resolve(JSContext* cx, Module& module, Handle<PromiseObject*> promise)
+{
+    RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmModule).toObject());
+    RootedObject moduleObj(cx, WasmModuleObject::create(cx, module, proto));
+    if (!moduleObj)
+        return false;
+
+    RootedValue resolutionValue(cx, ObjectValue(*moduleObj));
+    return promise->resolve(cx, resolutionValue);
+}
+
+static bool
+WebAssembly_compile(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs callArgs = CallArgsFromVp(argc, vp);
+
+    RootedFunction nopFun(cx, NewNativeFunction(cx, Nop, 0, nullptr));
+    if (!nopFun)
+        return false;
+
+    Rooted<PromiseObject*> promise(cx, PromiseObject::create(cx, nopFun));
+    if (!promise)
+        return false;
+
+    MutableBytes bytecode;
+    CompileArgs compileArgs;
+    if (!GetCompileArgs(cx, callArgs, "WebAssembly.compile", &bytecode, &compileArgs)) {
+        if (!cx->isExceptionPending())
+            return false;
+
+        RootedValue rejectionValue(cx);
+        if (!GetAndClearException(cx, &rejectionValue))
+            return false;
+
+        if (!promise->reject(cx, rejectionValue))
+            return false;
+
+        callArgs.rval().setObject(*promise);
+        return true;
+    }
+
+    UniqueChars error;
+    if (SharedModule module = Compile(*bytecode, compileArgs, &error)) {
+        if (!Resolve(cx, *module, promise))
+            return false;
+    } else {
+        if (!Reject(cx, compileArgs, Move(error), promise))
+            return false;
+    }
+
+    callArgs.rval().setObject(*promise);
+    return true;
+}
+#endif
+
 static const JSFunctionSpec WebAssembly_static_methods[] =
 {
 #if JS_HAS_TOSOURCE
     JS_FN(js_toSource_str, WebAssembly_toSource, 0, 0),
 #endif
+#ifdef SPIDERMONKEY_PROMISE
+    JS_FN("compile", WebAssembly_compile, 1, 0),
+#endif
     JS_FS_END
 };
 
 const Class js::WebAssemblyClass =
 {
     js_WebAssembly_str,
     JSCLASS_HAS_CACHED_PROTO(JSProto_WebAssembly)
 };
--- a/js/src/asmjs/WasmTypes.cpp
+++ b/js/src/asmjs/WasmTypes.cpp
@@ -584,16 +584,25 @@ Assumptions::initBuildIdFromContext(Excl
     if (!cx->buildIdOp() || !cx->buildIdOp()(&buildId)) {
         ReportOutOfMemory(cx);
         return false;
     }
     return true;
 }
 
 bool
+Assumptions::clone(const Assumptions& other)
+{
+    usesSignal = other.usesSignal;
+    cpuId = other.cpuId;
+    newFormat = other.newFormat;
+    return buildId.appendAll(other.buildId);
+}
+
+bool
 Assumptions::operator==(const Assumptions& rhs) const
 {
     return usesSignal == rhs.usesSignal &&
            cpuId == rhs.cpuId &&
            buildId.length() == rhs.buildId.length() &&
            PodEqual(buildId.begin(), rhs.buildId.begin(), buildId.length()) &&
            newFormat == rhs.newFormat;
 }
--- a/js/src/asmjs/WasmTypes.h
+++ b/js/src/asmjs/WasmTypes.h
@@ -1021,16 +1021,18 @@ struct Assumptions
 
     explicit Assumptions(JS::BuildIdCharVector&& buildId);
 
     // If Assumptions is constructed without arguments, initBuildIdFromContext()
     // must be called to complete initialization.
     Assumptions();
     bool initBuildIdFromContext(ExclusiveContext* cx);
 
+    bool clone(const Assumptions& other);
+
     bool operator==(const Assumptions& rhs) const;
     bool operator!=(const Assumptions& rhs) const { return !(*this == rhs); }
 
     WASM_DECLARE_SERIALIZABLE(Assumptions)
 };
 
 // A Module can either be asm.js or wasm.
 
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -2207,17 +2207,17 @@ const JSPropertySpec CloneBufferObject::
     JS_PS_END
 };
 
 static bool
 Serialize(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    JSAutoStructuredCloneBuffer clonebuf;
+    JSAutoStructuredCloneBuffer clonebuf(JS::StructuredCloneScope::SameProcessSameThread, nullptr, nullptr);
     if (!clonebuf.write(cx, args.get(0), args.get(1)))
         return false;
 
     RootedObject obj(cx, CloneBufferObject::Create(cx, &clonebuf));
     if (!obj)
         return false;
 
     args.rval().setObject(*obj);
@@ -2249,17 +2249,20 @@ Deserialize(JSContext* cx, unsigned argc
     }
 
     bool hasTransferable;
     if (!JS_StructuredCloneHasTransferables(obj->data(), obj->nbytes(), &hasTransferable))
         return false;
 
     RootedValue deserialized(cx);
     if (!JS_ReadStructuredClone(cx, obj->data(), obj->nbytes(),
-                                JS_STRUCTURED_CLONE_VERSION, &deserialized, nullptr, nullptr)) {
+                                JS_STRUCTURED_CLONE_VERSION,
+                                JS::StructuredCloneScope::SameProcessSameThread,
+                                &deserialized, nullptr, nullptr))
+    {
         return false;
     }
     args.rval().set(deserialized);
 
     if (hasTransferable)
         obj->discard();
 
     return true;
--- a/js/src/gc/Allocator.cpp
+++ b/js/src/gc/Allocator.cpp
@@ -537,16 +537,18 @@ GCRuntime::recycleChunk(Chunk* chunk, co
 Chunk*
 GCRuntime::pickChunk(const AutoLockGC& lock,
                      AutoMaybeStartBackgroundAllocation& maybeStartBackgroundAllocation)
 {
     if (availableChunks(lock).count())
         return availableChunks(lock).head();
 
     Chunk* chunk = getOrAllocChunk(lock, maybeStartBackgroundAllocation);
+    if (!chunk)
+        return nullptr;
 
     chunk->init(rt);
     MOZ_ASSERT(chunk->info.numArenasFreeCommitted == 0);
     MOZ_ASSERT(chunk->unused());
     MOZ_ASSERT(!fullChunks(lock).contains(chunk));
     MOZ_ASSERT(!availableChunks(lock).contains(chunk));
 
     chunkAllocationSinceLastGC = true;
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -535,25 +535,24 @@ js::Nursery::maybeEndProfile(ProfileKey 
 }
 
 void
 js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason)
 {
     MOZ_ASSERT(!rt->mainThread.suppressGC);
     MOZ_RELEASE_ASSERT(CurrentThreadCanAccessRuntime(rt));
 
-    StoreBuffer& sb = rt->gc.storeBuffer;
     if (!isEnabled() || isEmpty()) {
         /*
          * Our barriers are not always exact, and there may be entries in the
          * storebuffer even when the nursery is disabled or empty. It's not
          * safe to keep these entries as they may refer to tenured cells which
          * may be freed after this point.
          */
-        sb.clear();
+        rt->gc.storeBuffer.clear();
         return;
     }
 
     rt->gc.incMinorGcNumber();
 
 #ifdef JS_GC_ZEAL
     if (rt->gc.hasZealMode(ZealMode::CheckNursery)) {
         for (auto canary = lastCanary_; canary; canary = canary->next)
@@ -562,121 +561,22 @@ js::Nursery::collect(JSRuntime* rt, JS::
     lastCanary_ = nullptr;
 #endif
 
     rt->gc.stats.beginNurseryCollection(reason);
     TraceMinorGCStart();
 
     startProfile(ProfileKey::Total);
 
-    AutoTraceSession session(rt, JS::HeapState::MinorCollecting);
-    AutoStopVerifyingBarriers av(rt, false);
-    AutoDisableProxyCheck disableStrictProxyChecking(rt);
-    mozilla::DebugOnly<AutoEnterOOMUnsafeRegion> oomUnsafeRegion;
-
-    size_t initialUsedSpace = usedSpace();
-
-    // Move objects pointed to by roots from the nursery to the major heap.
-    TenuringTracer mover(rt, this);
-
-    // Mark the store buffer. This must happen first.
-
-    maybeStartProfile(ProfileKey::CancelIonCompilations);
-    if (sb.cancelIonCompilations()) {
-        for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
-            jit::StopAllOffThreadCompilations(c);
-    }
-    maybeEndProfile(ProfileKey::CancelIonCompilations);
-
-    maybeStartProfile(ProfileKey::TraceValues);
-    sb.traceValues(mover);
-    maybeEndProfile(ProfileKey::TraceValues);
-
-    maybeStartProfile(ProfileKey::TraceCells);
-    sb.traceCells(mover);
-    maybeEndProfile(ProfileKey::TraceCells);
-
-    maybeStartProfile(ProfileKey::TraceSlots);
-    sb.traceSlots(mover);
-    maybeEndProfile(ProfileKey::TraceSlots);
-
-    maybeStartProfile(ProfileKey::TraceWholeCells);
-    sb.traceWholeCells(mover);
-    maybeEndProfile(ProfileKey::TraceWholeCells);
-
-    maybeStartProfile(ProfileKey::TraceGenericEntries);
-    sb.traceGenericEntries(&mover);
-    maybeEndProfile(ProfileKey::TraceGenericEntries);
-
-    maybeStartProfile(ProfileKey::MarkRuntime);
-    rt->gc.markRuntime(&mover, GCRuntime::TraceRuntime, session.lock);
-    maybeEndProfile(ProfileKey::MarkRuntime);
-
-    maybeStartProfile(ProfileKey::MarkDebugger);
-    {
-        gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_MARK_ROOTS);
-        Debugger::markAll(&mover);
-    }
-    maybeEndProfile(ProfileKey::MarkDebugger);
+    // The hazard analysis thinks doCollection can invalidate pointers in
+    // tenureCounts below.
+    JS::AutoSuppressGCAnalysis nogc;
 
-    maybeStartProfile(ProfileKey::ClearNewObjectCache);
-    rt->contextFromMainThread()->caches.newObjectCache.clearNurseryObjects(rt);
-    maybeEndProfile(ProfileKey::ClearNewObjectCache);
-
-    // Most of the work is done here. This loop iterates over objects that have
-    // been moved to the major heap. If these objects have any outgoing pointers
-    // to the nursery, then those nursery objects get moved as well, until no
-    // objects are left to move. That is, we iterate to a fixed point.
-    maybeStartProfile(ProfileKey::CollectToFP);
     TenureCountCache tenureCounts;
-    collectToFixedPoint(mover, tenureCounts);
-    maybeEndProfile(ProfileKey::CollectToFP);
-
-    // Sweep compartments to update the array buffer object's view lists.
-    maybeStartProfile(ProfileKey::SweepArrayBufferViewList);
-    for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
-        c->sweepAfterMinorGC();
-    maybeEndProfile(ProfileKey::SweepArrayBufferViewList);
-
-    // Update any slot or element pointers whose destination has been tenured.
-    maybeStartProfile(ProfileKey::UpdateJitActivations);
-    js::jit::UpdateJitActivationsForMinorGC(rt, &mover);
-    forwardedBuffers.finish();
-    maybeEndProfile(ProfileKey::UpdateJitActivations);
-
-    maybeStartProfile(ProfileKey::ObjectsTenuredCallback);
-    rt->gc.callObjectsTenuredCallback();
-    maybeEndProfile(ProfileKey::ObjectsTenuredCallback);
-
-    // Sweep.
-    maybeStartProfile(ProfileKey::FreeMallocedBuffers);
-    freeMallocedBuffers();
-    maybeEndProfile(ProfileKey::FreeMallocedBuffers);
-
-    maybeStartProfile(ProfileKey::Sweep);
-    sweep();
-    maybeEndProfile(ProfileKey::Sweep);
-
-    maybeStartProfile(ProfileKey::ClearStoreBuffer);
-    rt->gc.storeBuffer.clear();
-    maybeEndProfile(ProfileKey::ClearStoreBuffer);
-
-    // Make sure hashtables have been updated after the collection.
-    maybeStartProfile(ProfileKey::CheckHashTables);
-#ifdef JS_GC_ZEAL
-    if (rt->hasZealMode(ZealMode::CheckHashTablesOnMinorGC))
-        CheckHashTablesAfterMovingGC(rt);
-#endif
-    maybeEndProfile(ProfileKey::CheckHashTables);
-
-    // Resize the nursery.
-    maybeStartProfile(ProfileKey::Resize);
-    double promotionRate = mover.tenuredSize / double(initialUsedSpace);
-    maybeResizeNursery(reason, initialUsedSpace, promotionRate);
-    maybeEndProfile(ProfileKey::Resize);
+    double promotionRate = doCollection(rt, reason, tenureCounts);
 
     // If we are promoting the nursery, or exhausted the store buffer with
     // pointers to nursery things, which will force a collection well before
     // the nursery is full, look for object groups that are getting promoted
     // excessively and try to pretenure them.
     maybeStartProfile(ProfileKey::Pretenure);
     uint32_t pretenureCount = 0;
     if (promotionRate > 0.8 || reason == JS::gcreason::FULL_STORE_BUFFER) {
@@ -725,16 +625,129 @@ js::Nursery::collect(JSRuntime* rt, JS::
         fprintf(stderr, "MinorGC: %20s %5.1f%% %4u ",
                 JS::gcreason::ExplainReason(reason),
                 promotionRate * 100,
                 numChunks());
         printProfileTimes(profileTimes_);
     }
 }
 
+double
+js::Nursery::doCollection(JSRuntime* rt, JS::gcreason::Reason reason,
+                          TenureCountCache& tenureCounts)
+{
+    AutoTraceSession session(rt, JS::HeapState::MinorCollecting);
+    AutoStopVerifyingBarriers av(rt, false);
+    AutoDisableProxyCheck disableStrictProxyChecking(rt);
+    mozilla::DebugOnly<AutoEnterOOMUnsafeRegion> oomUnsafeRegion;
+
+    size_t initialUsedSpace = usedSpace();
+
+    // Move objects pointed to by roots from the nursery to the major heap.
+    TenuringTracer mover(rt, this);
+
+    // Mark the store buffer. This must happen first.
+    StoreBuffer& sb = rt->gc.storeBuffer;
+
+    maybeStartProfile(ProfileKey::CancelIonCompilations);
+    if (sb.cancelIonCompilations()) {
+        for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
+            jit::StopAllOffThreadCompilations(c);
+    }
+    maybeEndProfile(ProfileKey::CancelIonCompilations);
+
+    maybeStartProfile(ProfileKey::TraceValues);
+    sb.traceValues(mover);
+    maybeEndProfile(ProfileKey::TraceValues);
+
+    maybeStartProfile(ProfileKey::TraceCells);
+    sb.traceCells(mover);
+    maybeEndProfile(ProfileKey::TraceCells);
+
+    maybeStartProfile(ProfileKey::TraceSlots);
+    sb.traceSlots(mover);
+    maybeEndProfile(ProfileKey::TraceSlots);
+
+    maybeStartProfile(ProfileKey::TraceWholeCells);
+    sb.traceWholeCells(mover);
+    maybeEndProfile(ProfileKey::TraceWholeCells);
+
+    maybeStartProfile(ProfileKey::TraceGenericEntries);
+    sb.traceGenericEntries(&mover);
+    maybeEndProfile(ProfileKey::TraceGenericEntries);
+
+    maybeStartProfile(ProfileKey::MarkRuntime);
+    rt->gc.markRuntime(&mover, GCRuntime::TraceRuntime, session.lock);
+    maybeEndProfile(ProfileKey::MarkRuntime);
+
+    maybeStartProfile(ProfileKey::MarkDebugger);
+    {
+        gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_MARK_ROOTS);
+        Debugger::markAll(&mover);
+    }
+    maybeEndProfile(ProfileKey::MarkDebugger);
+
+    maybeStartProfile(ProfileKey::ClearNewObjectCache);
+    rt->contextFromMainThread()->caches.newObjectCache.clearNurseryObjects(rt);
+    maybeEndProfile(ProfileKey::ClearNewObjectCache);
+
+    // Most of the work is done here. This loop iterates over objects that have
+    // been moved to the major heap. If these objects have any outgoing pointers
+    // to the nursery, then those nursery objects get moved as well, until no
+    // objects are left to move. That is, we iterate to a fixed point.
+    maybeStartProfile(ProfileKey::CollectToFP);
+    collectToFixedPoint(mover, tenureCounts);
+    maybeEndProfile(ProfileKey::CollectToFP);
+
+    // Sweep compartments to update the array buffer object's view lists.
+    maybeStartProfile(ProfileKey::SweepArrayBufferViewList);
+    for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
+        c->sweepAfterMinorGC();
+    maybeEndProfile(ProfileKey::SweepArrayBufferViewList);
+
+    // Update any slot or element pointers whose destination has been tenured.
+    maybeStartProfile(ProfileKey::UpdateJitActivations);
+    js::jit::UpdateJitActivationsForMinorGC(rt, &mover);
+    forwardedBuffers.finish();
+    maybeEndProfile(ProfileKey::UpdateJitActivations);
+
+    maybeStartProfile(ProfileKey::ObjectsTenuredCallback);
+    rt->gc.callObjectsTenuredCallback();
+    maybeEndProfile(ProfileKey::ObjectsTenuredCallback);
+
+    // Sweep.
+    maybeStartProfile(ProfileKey::FreeMallocedBuffers);
+    freeMallocedBuffers();
+    maybeEndProfile(ProfileKey::FreeMallocedBuffers);
+
+    maybeStartProfile(ProfileKey::Sweep);
+    sweep();
+    maybeEndProfile(ProfileKey::Sweep);
+
+    maybeStartProfile(ProfileKey::ClearStoreBuffer);
+    rt->gc.storeBuffer.clear();
+    maybeEndProfile(ProfileKey::ClearStoreBuffer);
+
+    // Make sure hashtables have been updated after the collection.
+    maybeStartProfile(ProfileKey::CheckHashTables);
+#ifdef JS_GC_ZEAL
+    if (rt->hasZealMode(ZealMode::CheckHashTablesOnMinorGC))
+        CheckHashTablesAfterMovingGC(rt);
+#endif
+    maybeEndProfile(ProfileKey::CheckHashTables);
+
+    // Resize the nursery.
+    maybeStartProfile(ProfileKey::Resize);
+    double promotionRate = mover.tenuredSize / double(initialUsedSpace);
+    maybeResizeNursery(reason, initialUsedSpace, promotionRate);
+    maybeEndProfile(ProfileKey::Resize);
+
+    return promotionRate;
+}
+
 void
 js::Nursery::FreeMallocedBuffersTask::transferBuffersToFree(MallocedBuffersSet& buffersToFree,
                                                             const AutoLockHelperThreadState& lock)
 {
     // Transfer the contents of the source set to the task's buffers_ member by
     // swapping the sets, which also clears the source.
     MOZ_ASSERT(!isRunningWithLockHeld(lock));
     MOZ_ASSERT(buffers_.empty());
@@ -785,16 +798,17 @@ js::Nursery::sweep()
         if (!IsForwarded(obj))
             obj->zone()->removeUniqueId(obj);
         else
             MOZ_ASSERT(Forwarded(obj)->zone()->hasUniqueId(Forwarded(obj)));
     }
     cellsWithUid_.clear();
 
     runSweepActions();
+    sweepDictionaryModeObjects();
 
 #ifdef JS_GC_ZEAL
     /* Poison the nursery contents so touching a freed object will crash. */
     for (unsigned i = 0; i < numChunks(); i++)
         chunk(i).poisonAndInit(runtime(), JS_SWEPT_NURSERY_PATTERN);
 
     if (runtime()->hasZealMode(ZealMode::GenerationalGC)) {
         /* Only reset the alloc point when we are close to the end. */
@@ -966,8 +980,25 @@ js::Nursery::runSweepActions()
     // The hazard analysis doesn't know whether the thunks can GC.
     JS::AutoSuppressGCAnalysis nogc;
 
     AutoSetThreadIsSweeping threadIsSweeping;
     for (auto action = sweepActions_; action; action = action->next)
         action->thunk(action->data);
     sweepActions_ = nullptr;
 }
+
+bool
+js::Nursery::queueDictionaryModeObjectToSweep(NativeObject* obj)
+{
+    MOZ_ASSERT(IsInsideNursery(obj));
+    return dictionaryModeObjects_.append(obj);
+}
+
+void
+js::Nursery::sweepDictionaryModeObjects()
+{
+    for (auto obj : dictionaryModeObjects_) {
+        if (!IsForwarded(obj))
+            obj->sweepDictionaryListPointer();
+    }
+    dictionaryModeObjects_.clear();
+}
--- a/js/src/gc/Nursery.h
+++ b/js/src/gc/Nursery.h
@@ -173,20 +173,17 @@ class Nursery
                            uint32_t oldBytes, uint32_t newBytes);
 
     /* Free an object buffer. */
     void freeBuffer(void* buffer);
 
     /* The maximum number of bytes allowed to reside in nursery buffers. */
     static const size_t MaxNurseryBufferSize = 1024;
 
-    /*
-     * Do a minor collection, optionally specifying a list to store groups which
-     * should be pretenured afterwards.
-     */
+    /* Do a minor collection. */
     void collect(JSRuntime* rt, JS::gcreason::Reason reason);
 
     /*
      * Check if the thing at |*ref| in the Nursery has been forwarded. If so,
      * sets |*ref| to the new location of the object and returns true. Otherwise
      * returns false and leaves |*ref| unset.
      */
     MOZ_ALWAYS_INLINE MOZ_MUST_USE bool getForwardedPointer(JSObject** ref) const;
@@ -212,16 +209,18 @@ class Nursery
         MOZ_ASSERT(cellsWithUid_.initialized());
         MOZ_ASSERT(!cellsWithUid_.has(cell));
         return cellsWithUid_.put(cell);
     }
 
     using SweepThunk = void (*)(void *data);
     void queueSweepAction(SweepThunk thunk, void* data);
 
+    MOZ_MUST_USE bool queueDictionaryModeObjectToSweep(NativeObject* obj);
+
     size_t sizeOfHeapCommitted() const {
         return numChunks() * gc::ChunkSize;
     }
     size_t sizeOfMallocedBuffers(mozilla::MallocSizeOf mallocSizeOf) const {
         size_t total = 0;
         for (MallocedBuffersSet::Range r = mallocedBuffers.all(); !r.empty(); r.popFront())
             total += mallocSizeOf(r.front());
         total += mallocedBuffers.sizeOfExcludingThis(mallocSizeOf);
@@ -348,16 +347,19 @@ class Nursery
      *       stable object hashing and we have to break the cycle somehow.
      */
     using CellsWithUniqueIdSet = HashSet<gc::Cell*, PointerHasher<gc::Cell*, 3>, SystemAllocPolicy>;
     CellsWithUniqueIdSet cellsWithUid_;
 
     struct SweepAction;
     SweepAction* sweepActions_;
 
+    using NativeObjectVector = Vector<NativeObject*, 0, SystemAllocPolicy>;
+    NativeObjectVector dictionaryModeObjects_;
+
 #ifdef JS_GC_ZEAL
     struct Canary;
     Canary* lastCanary_;
 #endif
 
     NurseryChunk* allocChunk();
 
     NurseryChunk& chunk(unsigned index) const {
@@ -391,16 +393,19 @@ class Nursery
     JSRuntime* runtime() const { return runtime_; }
 
     /* Allocates a new GC thing from the tenured generation during minor GC. */
     gc::TenuredCell* allocateFromTenured(JS::Zone* zone, gc::AllocKind thingKind);
 
     /* Common internal allocator function. */
     void* allocate(size_t size);
 
+    double doCollection(JSRuntime* rt, JS::gcreason::Reason reason,
+                        gc::TenureCountCache& tenureCounts);
+
     /*
      * Move the object at |src| in the Nursery to an already-allocated cell
      * |dst| in Tenured.
      */
     void collectToFixedPoint(TenuringTracer& trc, gc::TenureCountCache& tenureCounts);
 
     /* Handle relocation of slots/elements pointers stored in Ion frames. */
     void setForwardingPointer(void* oldData, void* newData, bool direct);
@@ -414,16 +419,17 @@ class Nursery
 
     /*
      * Frees all non-live nursery-allocated things at the end of a minor
      * collection.
      */
     void sweep();
 
     void runSweepActions();
+    void sweepDictionaryModeObjects();
 
     /* Change the allocable space provided by the nursery. */
     void maybeResizeNursery(JS::gcreason::Reason reason, size_t usedSpace, double promotionRate);
     void growAllocableSpace();
     void shrinkAllocableSpace();
 
     /* Profile recording and printing. */
     void startProfile(ProfileKey key);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/class-08.js
@@ -0,0 +1,13 @@
+let g = newGlobal();
+let dbg = Debugger(g);
+dbg.onDebuggerStatement = function() {
+    // Force the constructor to return undefined, which should be replaced with
+    // |this| if the latter has been initialized.
+    return { return: undefined };
+}
+
+assertEq(g.eval(`
+    new (class extends class {} {
+        constructor() { super(); this.foo = 42; debugger; }
+    })
+`).foo, 42);
--- a/js/src/jit-test/tests/wasm/jsapi.js
+++ b/js/src/jit-test/tests/wasm/jsapi.js
@@ -26,16 +26,18 @@ assertEq(moduleDesc.enumerable, false);
 assertEq(moduleDesc.configurable, true);
 
 // 'WebAssembly.Module' constructor function
 const Module = WebAssembly.Module;
 assertEq(Module, moduleDesc.value);
 assertEq(Module.length, 1);
 assertEq(Module.name, "Module");
 assertErrorMessage(() => Module(), TypeError, /constructor without new is forbidden/);
+assertErrorMessage(() => new Module(), TypeError, /requires more than 0 arguments/);
+assertErrorMessage(() => new Module(undefined), TypeError, "first argument must be an ArrayBuffer or typed array object");
 assertErrorMessage(() => new Module(1), TypeError, "first argument must be an ArrayBuffer or typed array object");
 assertErrorMessage(() => new Module({}), TypeError, "first argument must be an ArrayBuffer or typed array object");
 assertErrorMessage(() => new Module(new Uint8Array()), /* TODO: WebAssembly.CompileError */ TypeError, /compile error/);
 assertErrorMessage(() => new Module(new ArrayBuffer()), /* TODO: WebAssembly.CompileError */ TypeError, /compile error/);
 assertEq(new Module(emptyModule) instanceof Module, true);
 assertEq(new Module(emptyModule.buffer) instanceof Module, true);
 
 // 'WebAssembly.Module.prototype' property
@@ -251,8 +253,43 @@ assertErrorMessage(() => set.call(tbl1, 
 assertErrorMessage(() => set.call(tbl1, Math.pow(2,33), null), RangeError, /out-of-range index/);
 assertErrorMessage(() => set.call(tbl1, 0, undefined), TypeError, /second argument must be null or an exported WebAssembly Function object/);
 assertErrorMessage(() => set.call(tbl1, 0, {}), TypeError, /second argument must be null or an exported WebAssembly Function object/);
 assertErrorMessage(() => set.call(tbl1, 0, function() {}), TypeError, /second argument must be null or an exported WebAssembly Function object/);
 assertErrorMessage(() => set.call(tbl1, 0, Math.sin), TypeError, /second argument must be null or an exported WebAssembly Function object/);
 assertErrorMessage(() => set.call(tbl1, {valueOf() { throw Error("hai") }}, null), Error, "hai");
 assertEq(set.call(tbl1, 0, null), undefined);
 assertEq(set.call(tbl1, 1, null), undefined);
+
+// 'WebAssembly.compile' property
+const compileDesc = Object.getOwnPropertyDescriptor(WebAssembly, 'compile');
+assertEq(typeof compileDesc.value, "function");
+assertEq(compileDesc.writable, true);
+assertEq(compileDesc.enumerable, false);
+assertEq(compileDesc.configurable, true);
+
+// 'WebAssembly.compile' function
+const compile = WebAssembly.compile;
+assertEq(compile, compileDesc.value);
+assertEq(compile.length, 1);
+assertEq(compile.name, "compile");
+function assertCompileError(args, msg) {
+    var error = null;
+    compile(...args).catch(e => error = e);
+    drainJobQueue();
+    assertEq(error instanceof TypeError, true);
+    assertEq(Boolean(error.stack.match("jsapi.js")), true);
+    assertEq(Boolean(error.message.match(msg)), true);
+}
+assertCompileError([], /requires more than 0 arguments/);
+assertCompileError([undefined], /first argument must be an ArrayBuffer or typed array object/);
+assertCompileError([1], /first argument must be an ArrayBuffer or typed array object/);
+assertCompileError([{}], /first argument must be an ArrayBuffer or typed array object/);
+assertCompileError([new Uint8Array()], /compile error/);
+assertCompileError([new ArrayBuffer()], /compile error/);
+function assertCompileSuccess(bytes) {
+    var module = null;
+    compile(bytes).then(m => module = m);
+    drainJobQueue();
+    assertEq(module instanceof Module, true);
+}
+assertCompileSuccess(emptyModule);
+assertCompileSuccess(emptyModule.buffer);
--- a/js/src/jsapi-tests/testMappedArrayBuffer.cpp
+++ b/js/src/jsapi-tests/testMappedArrayBuffer.cpp
@@ -121,17 +121,17 @@ bool TestDetachObject()
 
     return true;
 }
 
 bool TestCloneObject()
 {
     JS::RootedObject obj1(cx, CreateNewObject(8, 12));
     CHECK(obj1);
-    JSAutoStructuredCloneBuffer cloned_buffer;
+    JSAutoStructuredCloneBuffer cloned_buffer(JS::StructuredCloneScope::SameProcessSameThread, nullptr, nullptr);
     JS::RootedValue v1(cx, JS::ObjectValue(*obj1));
     CHECK(cloned_buffer.write(cx, v1, nullptr, nullptr));
     JS::RootedValue v2(cx);
     CHECK(cloned_buffer.read(cx, &v2, nullptr, nullptr));
     JS::RootedObject obj2(cx, v2.toObjectOrNull());
     CHECK(VerifyObject(obj2, 8, 12, false));
 
     return true;
@@ -159,17 +159,17 @@ bool TestTransferObject()
     JS::AutoValueVector argv(cx);
     if (!argv.append(v1))
         return false;
 
     JS::RootedObject obj(cx, JS_NewArrayObject(cx, JS::HandleValueArray::subarray(argv, 0, 1)));
     CHECK(obj);
     JS::RootedValue transferable(cx, JS::ObjectValue(*obj));
 
-    JSAutoStructuredCloneBuffer cloned_buffer;
+    JSAutoStructuredCloneBuffer cloned_buffer(JS::StructuredCloneScope::SameProcessSameThread, nullptr, nullptr);
     CHECK(cloned_buffer.write(cx, v1, transferable, nullptr, nullptr));
     JS::RootedValue v2(cx);
     CHECK(cloned_buffer.read(cx, &v2, nullptr, nullptr));
     JS::RootedObject obj2(cx, v2.toObjectOrNull());
     CHECK(VerifyObject(obj2, 8, 12, true));
     CHECK(JS_IsDetachedArrayBufferObject(obj1));
 
     return true;
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -101,22 +101,28 @@ JSObject::finalize(js::FreeOp* fop)
                 // are themselves finalized.
                 fop->freeLater(elements);
             }
         } else {
             fop->free_(elements);
         }
     }
 
+    nobj->sweepDictionaryListPointer();
+}
+
+MOZ_ALWAYS_INLINE void
+js::NativeObject::sweepDictionaryListPointer()
+{
     // For dictionary objects (which must be native), it's possible that
-    // unreachable shapes may be marked whose listp points into this object.
-    // In case this happens, null out the shape's pointer here so that a moving
-    // GC will not try to access the dead object.
-    if (nobj->shape_->listp == &nobj->shape_)
-        nobj->shape_->listp = nullptr;
+    // unreachable shapes may be marked whose listp points into this object.  In
+    // case this happens, null out the shape's pointer so that a moving GC will
+    // not try to access the dead object.
+    if (shape_->listp == &shape_)
+        shape_->listp = nullptr;
 }
 
 /* static */ inline bool
 JSObject::setSingleton(js::ExclusiveContext* cx, js::HandleObject obj)
 {
     MOZ_ASSERT_IF(cx->isJSContext(), !IsInsideNursery(obj));
 
     js::ObjectGroup* group = js::ObjectGroup::lazySingletonGroup(cx, obj->getClass(),
--- a/js/src/jspropertytree.cpp
+++ b/js/src/jspropertytree.cpp
@@ -222,24 +222,16 @@ Shape::finalize(FreeOp* fop)
 }
 
 void
 Shape::fixupDictionaryShapeAfterMovingGC()
 {
     if (!listp)
         return;
 
-    // It's possible that this shape is unreachable and that listp points to the
-    // location of a dead object in the nursery, in which case we should never
-    // touch it again.
-    if (IsInsideNursery(reinterpret_cast<Cell*>(listp))) {
-        listp = nullptr;
-        return;
-    }
-
     // The listp field either points to the parent field of the next shape in
     // the list if there is one.  Otherwise if this shape is the last in the
     // list then it points to the shape_ field of the object the list is for.
     // We can tell which it is because the base shape is owned if this is the
     // last property and not otherwise.
     bool listpPointsIntoShape = !MaybeForwarded(base())->isOwned();
 
 #ifdef DEBUG
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -1334,18 +1334,23 @@ Debugger::handleUncaughtExceptionHelper(
             RootedValue exc(cx);
             if (!cx->getPendingException(&exc))
                 return JSTRAP_ERROR;
             cx->clearPendingException();
 
             RootedValue fval(cx, ObjectValue(*uncaughtExceptionHook));
             RootedValue rv(cx);
             if (js::Call(cx, fval, object, exc, &rv)) {
-                return vp ? parseResumptionValueHelper(ac, true, rv, thisVForCheck, frame, *vp, false)
-                          : JSTRAP_CONTINUE;
+                if (vp) {
+                    JSTrapStatus status = JSTRAP_CONTINUE;
+                    if (processResumptionValue(ac, frame, thisVForCheck, rv, &status, *vp))
+                        return status;
+                } else {
+                    return JSTRAP_CONTINUE;
+                }
             }
         }
 
         if (cx->isExceptionPending()) {
             /*
              * We want to report the pending exception, but we want to let the
              * embedding handle it however it wants to.  So pretend like we're
              * starting a new script execution on our current compartment (which
@@ -1499,72 +1504,98 @@ ParseResumptionValueAsObject(JSContext* 
 
     if (hits != 1) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_RESUMPTION);
         return false;
     }
     return true;
 }
 
+static bool
+ParseResumptionValue(JSContext* cx, HandleValue rval, JSTrapStatus* statusp, MutableHandleValue vp)
+{
+    if (rval.isUndefined()) {
+        *statusp = JSTRAP_CONTINUE;
+        vp.setUndefined();
+        return true;
+    }
+    if (rval.isNull()) {
+        *statusp = JSTRAP_ERROR;
+        vp.setUndefined();
+        return true;
+    }
+    return ParseResumptionValueAsObject(cx, rval, statusp, vp);
+}
+
+static bool
+CheckResumptionValue(JSContext* cx, AbstractFramePtr frame, const Maybe<HandleValue>& maybeThisv,
+                     JSTrapStatus status, MutableHandleValue vp)
+{
+    if (maybeThisv.isSome()) {
+        const HandleValue& thisv = maybeThisv.ref();
+        if (status == JSTRAP_RETURN && vp.isPrimitive()) {
+            if (vp.isUndefined()) {
+                if (thisv.isMagic(JS_UNINITIALIZED_LEXICAL)) {
+                    return ThrowUninitializedThis(cx, frame);
+                }
+
+                vp.set(thisv);
+            } else {
+                ReportValueError(cx, JSMSG_BAD_DERIVED_RETURN, JSDVG_IGNORE_STACK, vp, nullptr);
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+bool
+Debugger::processResumptionValue(Maybe<AutoCompartment>& ac, AbstractFramePtr frame,
+                                 const Maybe<HandleValue>& maybeThisv, HandleValue rval,
+                                 JSTrapStatus* statusp, MutableHandleValue vp)
+{
+    JSContext* cx = ac->context()->asJSContext();
+
+    if (!ParseResumptionValue(cx, rval, statusp, vp) ||
+        !unwrapDebuggeeValue(cx, vp) ||
+        !CheckResumptionValue(cx, frame, maybeThisv, *statusp, vp))
+    {
+        return false;
+    }
+
+    ac.reset();
+    if (!cx->compartment()->wrap(cx, vp)) {
+        *statusp = JSTRAP_ERROR;
+        vp.setUndefined();
+    }
+
+    return true;
+}
+
 JSTrapStatus
 Debugger::parseResumptionValueHelper(Maybe<AutoCompartment>& ac, bool ok, const Value& rv,
                                      const Maybe<HandleValue>& thisVForCheck, AbstractFramePtr frame,
-                                     MutableHandleValue vp, bool callHook)
-{
-    vp.setUndefined();
+                                     MutableHandleValue vp)
+{
     if (!ok)
-        return handleUncaughtException(ac, vp, callHook, thisVForCheck, frame);
-    if (rv.isUndefined()) {
-        ac.reset();
-        return JSTRAP_CONTINUE;
-    }
-    if (rv.isNull()) {
-        ac.reset();
-        return JSTRAP_ERROR;
-    }
+        return handleUncaughtException(ac, vp, true, thisVForCheck, frame);
 
     JSContext* cx = ac->context()->asJSContext();
+    RootedValue rvRoot(cx, rv);
     JSTrapStatus status = JSTRAP_CONTINUE;
     RootedValue v(cx);
-    RootedValue rvRoot(cx, rv);
-
-    if (!ParseResumptionValueAsObject(cx, rvRoot, &status, &v) ||
-        !unwrapDebuggeeValue(cx, &v))
-    {
-        return handleUncaughtException(ac, vp, callHook, thisVForCheck, frame);
-    }
-
-    if (status == JSTRAP_RETURN && thisVForCheck.isSome() && v.isPrimitive()) {
-        if (v.isUndefined()) {
-            if (thisVForCheck.ref().isMagic(JS_UNINITIALIZED_LEXICAL)) {
-                MOZ_ALWAYS_FALSE(ThrowUninitializedThis(cx, frame));
-                return handleUncaughtException(ac, vp, callHook, thisVForCheck, frame);
-            }
-
-            v = thisVForCheck.ref();
-        } else {
-            ReportValueError(cx, JSMSG_BAD_DERIVED_RETURN, JSDVG_IGNORE_STACK, v, nullptr);
-            return handleUncaughtException(ac, vp, callHook, thisVForCheck, frame);
-        }
-    }
-
-    ac.reset();
-    if (!cx->compartment()->wrap(cx, &v)) {
-        vp.setUndefined();
-        return JSTRAP_ERROR;
-    }
+    if (!processResumptionValue(ac, frame, thisVForCheck, rvRoot, &status, &v))
+        return handleUncaughtException(ac, vp, true, thisVForCheck, frame);
     vp.set(v);
-
     return status;
 }
 
 JSTrapStatus
 Debugger::parseResumptionValue(Maybe<AutoCompartment>& ac, bool ok, const Value& rv,
-                               AbstractFramePtr frame, jsbytecode* pc, MutableHandleValue vp,
-                               bool callHook)
+                               AbstractFramePtr frame, jsbytecode* pc, MutableHandleValue vp)
 {
     JSContext* cx = ac->context()->asJSContext();
     RootedValue rootThis(cx);
     Maybe<HandleValue> thisArg;
     if (frame.debuggerNeedsCheckPrimitiveReturn()) {
         bool success;
         {
             AutoCompartment ac2(cx, frame.scopeChain());
@@ -1572,31 +1603,30 @@ Debugger::parseResumptionValue(Maybe<Aut
         }
         if (!success || !cx->compartment()->wrap(cx, &rootThis)) {
             ac.reset();
             return JSTRAP_ERROR;
         }
         MOZ_ASSERT_IF(rootThis.isMagic(), rootThis.isMagic(JS_UNINITIALIZED_LEXICAL));
         thisArg.emplace(HandleValue(rootThis));
     }
-    return parseResumptionValueHelper(ac, ok, rv, thisArg, frame, vp, callHook);
+    return parseResumptionValueHelper(ac, ok, rv, thisArg, frame, vp);
 }
 
 JSTrapStatus
 Debugger::parseResumptionValue(Maybe<AutoCompartment>& ac, bool ok, const Value& rv,
-                               const Value& thisV, AbstractFramePtr frame, MutableHandleValue vp,
-                               bool callHook)
+                               const Value& thisV, AbstractFramePtr frame, MutableHandleValue vp)
 {
     JSContext* cx = ac->context()->asJSContext();
     RootedValue rootThis(cx, thisV);
     Maybe<HandleValue> thisArg;
     if (frame.debuggerNeedsCheckPrimitiveReturn())
         thisArg.emplace(rootThis);
 
-    return parseResumptionValueHelper(ac, ok, rv, thisArg, frame, vp, callHook);
+    return parseResumptionValueHelper(ac, ok, rv, thisArg, frame, vp);
 }
 
 static bool
 CallMethodIfPresent(JSContext* cx, HandleObject obj, const char* name, size_t argc, Value* argv,
                     MutableHandleValue rval)
 {
     rval.setUndefined();
     JSAtom* atom = Atomize(cx, name, strlen(name));
@@ -1871,17 +1901,17 @@ Debugger::onTrap(JSContext* cx, MutableH
 
             RootedValue scriptFrame(cx);
             if (!dbg->getScriptFrame(cx, iter, &scriptFrame))
                 return dbg->handleUncaughtException(ac, false);
             RootedValue rv(cx);
             Rooted<JSObject*> handler(cx, bp->handler);
             bool ok = CallMethodIfPresent(cx, handler, "hit", 1, scriptFrame.address(), &rv);
             JSTrapStatus st = dbg->parseResumptionValue(ac, ok, rv,  iter.abstractFramePtr(),
-                                                        iter.pc(), vp, true);
+                                                        iter.pc(), vp);
             if (st != JSTRAP_CONTINUE)
                 return st;
 
             /* Calling JS code invalidates site. Reload it. */
             site = script->getBreakpointSite(pc);
         }
     }
 
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -543,33 +543,36 @@ class Debugger : private mozilla::Linked
      *         or JSTRAP_THROW. The interpreter will force the current frame to
      *         return or throw an exception.
      *     null - Return JSTRAP_ERROR to terminate the debuggee with an
      *         uncatchable error.
      *     anything else - Make a new TypeError the pending exception and
      *         return handleUncaughtException(ac, vp, callHook).
      */
     JSTrapStatus parseResumptionValue(mozilla::Maybe<AutoCompartment>& ac, bool OK, const Value& rv,
-                                      AbstractFramePtr frame, jsbytecode* pc, MutableHandleValue vp,
-                                      bool callHook = true);
+                                      AbstractFramePtr frame, jsbytecode* pc, MutableHandleValue vp);
 
     /*
      * When we run the onEnterFrame hook, the |this| slot hasn't been fully
      * initialized, because the initialzation happens in the function's
      * prologue. To combat this, we pass the this for the primitive return
      * check directly. When bug 1249193 is fixed, this overload should be
      * removed.
      */
     JSTrapStatus parseResumptionValue(mozilla::Maybe<AutoCompartment>& ac, bool OK, const Value& rv,
                                       const Value& thisVForCheck, AbstractFramePtr frame,
-                                      MutableHandleValue vp, bool callHook = true);
+                                      MutableHandleValue vp);
 
     JSTrapStatus parseResumptionValueHelper(mozilla::Maybe<AutoCompartment>& ac, bool ok, const Value& rv,
                                             const mozilla::Maybe<HandleValue>& thisVForCheck, AbstractFramePtr frame,
-                                            MutableHandleValue vp, bool callHook);
+                                            MutableHandleValue vp);
+
+    bool processResumptionValue(mozilla::Maybe<AutoCompartment>& ac, AbstractFramePtr frame,
+                                const mozilla::Maybe<HandleValue>& maybeThis, HandleValue rval,
+                                JSTrapStatus* statusp, MutableHandleValue vp);
 
     GlobalObject* unwrapDebuggeeArgument(JSContext* cx, const Value& v);
 
     static void traceObject(JSTracer* trc, JSObject* obj);
     void trace(JSTracer* trc);
     static void finalize(FreeOp* fop, JSObject* obj);
     void markCrossCompartmentEdges(JSTracer* tracer);
 
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -1252,16 +1252,17 @@ class NativeObject : public ShapedObject
         return privateRef(nfixed);
     }
 
     static inline NativeObject*
     copy(ExclusiveContext* cx, gc::AllocKind kind, gc::InitialHeap heap,
          HandleNativeObject templateObject);
 
     void updateShapeAfterMovingGC();
+    void sweepDictionaryListPointer();
 
     /* JIT Accessors */
     static size_t offsetOfElements() { return offsetof(NativeObject, elements_); }
     static size_t offsetOfFixedElements() {
         return sizeof(NativeObject) + sizeof(ObjectElements);
     }
 
     static size_t getFixedSlotOffset(size_t slot) {
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -495,16 +495,23 @@ js::NativeObject::toDictionaryMode(Exclu
         shape = shape->previous();
     }
 
     if (!Shape::hashify(cx, root)) {
         ReportOutOfMemory(cx);
         return false;
     }
 
+    if (IsInsideNursery(self) &&
+        !cx->asJSContext()->gc.nursery.queueDictionaryModeObjectToSweep(self))
+    {
+        ReportOutOfMemory(cx);
+        return false;
+    }
+
     MOZ_ASSERT(root->listp == nullptr);
     root->listp = &self->shape_;
     self->shape_ = root;
 
     MOZ_ASSERT(self->inDictionaryMode());
     root->base()->setSlotSpan(span);
 
     return true;
--- a/js/src/vm/String.cpp
+++ b/js/src/vm/String.cpp
@@ -6,16 +6,17 @@
 
 #include "vm/String-inl.h"
 
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/RangedPtr.h"
 #include "mozilla/TypeTraits.h"
+#include "mozilla/unused.h"
 
 #include "gc/Marking.h"
 #include "js/UbiNode.h"
 #include "vm/SPSProfiler.h"
 
 #include "jscntxtinlines.h"
 #include "jscompartmentinlines.h"
 
@@ -1264,16 +1265,27 @@ template JSFlatString*
 NewStringCopyNDontDeflate<NoGC>(ExclusiveContext* cx, const char16_t* s, size_t n);
 
 template JSFlatString*
 NewStringCopyNDontDeflate<CanGC>(ExclusiveContext* cx, const Latin1Char* s, size_t n);
 
 template JSFlatString*
 NewStringCopyNDontDeflate<NoGC>(ExclusiveContext* cx, const Latin1Char* s, size_t n);
 
+JSFlatString*
+NewLatin1StringZ(ExclusiveContext* cx, UniqueChars chars)
+{
+    JSFlatString* str = NewString<CanGC>(cx, (Latin1Char*)chars.get(), strlen(chars.get()));
+    if (!str)
+        return nullptr;
+
+    mozilla::Unused << chars.release();
+    return str;
+}
+
 template <AllowGC allowGC, typename CharT>
 JSFlatString*
 NewStringCopyN(ExclusiveContext* cx, const CharT* s, size_t n)
 {
     if (IsSame<CharT, char16_t>::value && CanStoreCharsAsLatin1(s, n))
         return NewStringDeflated<allowGC>(cx, s, n);
 
     return NewStringCopyNDontDeflate<allowGC>(cx, s, n);
--- a/js/src/vm/String.h
+++ b/js/src/vm/String.h
@@ -1169,16 +1169,20 @@ NewString(js::ExclusiveContext* cx, Char
 /* Like NewString, but doesn't try to deflate to Latin1. */
 template <js::AllowGC allowGC, typename CharT>
 extern JSFlatString*
 NewStringDontDeflate(js::ExclusiveContext* cx, CharT* chars, size_t length);
 
 extern JSLinearString*
 NewDependentString(JSContext* cx, JSString* base, size_t start, size_t length);
 
+/* Take ownership of an array of Latin1Chars. */
+extern JSFlatString*
+NewLatin1StringZ(js::ExclusiveContext* cx, UniqueChars chars);
+
 /* Copy a counted string and GC-allocate a descriptor for it. */
 template <js::AllowGC allowGC, typename CharT>
 extern JSFlatString*
 NewStringCopyN(js::ExclusiveContext* cx, const CharT* s, size_t n);
 
 template <js::AllowGC allowGC>
 inline JSFlatString*
 NewStringCopyN(ExclusiveContext* cx, const char* s, size_t n)
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -66,16 +66,17 @@ using JS::CanonicalizeNaN;
 //
 // Note that SCTAG_END_OF_KEYS is written into the serialized form and should have
 // a stable ID, it need not be at the end of the list and should not be used for
 // sizing data structures.
 
 enum StructuredDataType : uint32_t {
     /* Structured data types provided by the engine */
     SCTAG_FLOAT_MAX = 0xFFF00000,
+    SCTAG_HEADER = 0xFFF10000,
     SCTAG_NULL = 0xFFFF0000,
     SCTAG_UNDEFINED,
     SCTAG_BOOLEAN,
     SCTAG_INT32,
     SCTAG_STRING,
     SCTAG_DATE_OBJECT,
     SCTAG_REGEXP_OBJECT,
     SCTAG_ARRAY_OBJECT,
@@ -221,27 +222,29 @@ class SCInput {
     uint64_t* point;
     uint64_t* bufEnd;
 };
 
 } /* namespace js */
 
 struct JSStructuredCloneReader {
   public:
-    explicit JSStructuredCloneReader(SCInput& in, const JSStructuredCloneCallbacks* cb,
+    explicit JSStructuredCloneReader(SCInput& in, JS::StructuredCloneScope scope,
+                                     const JSStructuredCloneCallbacks* cb,
                                      void* cbClosure)
-        : in(in), objs(in.context()), allObjs(in.context()),
+        : in(in), scope(scope), objs(in.context()), allObjs(in.context()),
           callbacks(cb), closure(cbClosure) { }
 
     SCInput& input() { return in; }
     bool read(MutableHandleValue vp);
 
   private:
     JSContext* context() { return in.context(); }
 
+    bool readHeader();
     bool readTransferMap();
 
     template <typename CharT>
     JSString* readStringImpl(uint32_t nchars);
     JSString* readString(uint32_t data);
 
     bool checkDouble(double d);
     bool readTypedArray(uint32_t arrayType, uint32_t nelems, MutableHandleValue vp,
@@ -249,16 +252,18 @@ struct JSStructuredCloneReader {
     bool readDataView(uint32_t byteLength, MutableHandleValue vp);
     bool readArrayBuffer(uint32_t nbytes, MutableHandleValue vp);
     bool readV1ArrayBuffer(uint32_t arrayType, uint32_t nelems, MutableHandleValue vp);
     JSObject* readSavedFrame(uint32_t principalsTag);
     bool startRead(MutableHandleValue vp);
 
     SCInput& in;
 
+    JS::StructuredCloneScope scope;
+
     // Stack of objects with properties remaining to be read.
     AutoValueVector objs;
 
     // Stack of all objects read during this deserialization
     AutoValueVector allObjs;
 
     // The user defined callbacks that will be used for cloning.
     const JSStructuredCloneCallbacks* callbacks;
@@ -267,50 +272,54 @@ struct JSStructuredCloneReader {
     void* closure;
 
     friend bool JS_ReadTypedArray(JSStructuredCloneReader* r, MutableHandleValue vp);
 };
 
 struct JSStructuredCloneWriter {
   public:
     explicit JSStructuredCloneWriter(JSContext* cx,
+                                     JS::StructuredCloneScope scope,
                                      const JSStructuredCloneCallbacks* cb,
                                      void* cbClosure,
                                      Value tVal)
-        : out(cx), objs(out.context()),
+        : out(cx), scope(scope), objs(out.context()),
           counts(out.context()), entries(out.context()),
           memory(out.context()), callbacks(cb),
           closure(cbClosure), transferable(out.context(), tVal),
           transferableObjects(out.context(), GCHashSet<JSObject*>(cx))
     {}
 
     ~JSStructuredCloneWriter();
 
     bool init() {
         if (!memory.init()) {
             ReportOutOfMemory(context());
             return false;
         }
-        return parseTransferable() && writeTransferMap();
+        return parseTransferable() && writeHeader() && writeTransferMap();
     }
 
     bool write(HandleValue v);
 
     SCOutput& output() { return out; }
 
     bool extractBuffer(uint64_t** datap, size_t* sizep) {
         return out.extractBuffer(datap, sizep);
     }
 
+    JS::StructuredCloneScope cloneScope() const { return scope; }
+
   private:
     JSStructuredCloneWriter() = delete;
     JSStructuredCloneWriter(const JSStructuredCloneWriter&) = delete;
 
     JSContext* context() { return out.context(); }
 
+    bool writeHeader();
     bool writeTransferMap();
 
     bool writeString(uint32_t tag, JSString* str);
     bool writeArrayBuffer(HandleObject obj);
     bool writeTypedArray(HandleObject obj);
     bool writeDataView(HandleObject obj);
     bool writeSharedArrayBuffer(HandleObject obj);
     bool startObject(HandleObject obj, bool* backref);
@@ -324,16 +333,19 @@ struct JSStructuredCloneWriter {
 
     bool parseTransferable();
     bool transferOwnership();
 
     inline void checkStack();
 
     SCOutput out;
 
+    // The (address space, thread) scope within which this clone is valid.
+    JS::StructuredCloneScope scope;
+
     // Vector of objects with properties remaining to be written.
     //
     // NB: These can span multiple compartments, so the compartment must be
     // entered before any manipulation is performed.
     AutoValueVector objs;
 
     // counts[i] is the number of entries of objs[i] remaining to be written.
     // counts.length() == objs.length() and sum(counts) == entries.length().
@@ -406,29 +418,31 @@ ReportDataCloneError(JSContext* cx,
       default:
         MOZ_CRASH("Unkown errorId");
         break;
     }
 }
 
 bool
 WriteStructuredClone(JSContext* cx, HandleValue v, uint64_t** bufp, size_t* nbytesp,
+                     JS::StructuredCloneScope scope,
                      const JSStructuredCloneCallbacks* cb, void* cbClosure,
                      Value transferable)
 {
-    JSStructuredCloneWriter w(cx, cb, cbClosure, transferable);
+    JSStructuredCloneWriter w(cx, scope, cb, cbClosure, transferable);
     return w.init() && w.write(v) && w.extractBuffer(bufp, nbytesp);
 }
 
 bool
-ReadStructuredClone(JSContext* cx, uint64_t* data, size_t nbytes, MutableHandleValue vp,
+ReadStructuredClone(JSContext* cx, uint64_t* data, size_t nbytes,
+                    JS::StructuredCloneScope scope, MutableHandleValue vp,
                     const JSStructuredCloneCallbacks* cb, void* cbClosure)
 {
     SCInput in(cx, data, nbytes);
-    JSStructuredCloneReader r(in, cb, cbClosure);
+    JSStructuredCloneReader r(in, scope, cb, cbClosure);
     return r.read(vp);
 }
 
 // If the given buffer contains Transferables, free them. Note that custom
 // Transferables will use the JSStructuredCloneCallbacks::freeTransfer() to
 // delete their transferables.
 static void
 DiscardTransferables(uint64_t* buffer, size_t nbytes,
@@ -437,16 +451,24 @@ DiscardTransferables(uint64_t* buffer, s
     MOZ_ASSERT(nbytes % sizeof(uint64_t) == 0);
     uint64_t* end = buffer + nbytes / sizeof(uint64_t);
     uint64_t* point = buffer;
     if (point == end)
         return; // Empty buffer
 
     uint32_t tag, data;
     SCInput::getPair(point++, &tag, &data);
+
+    if (tag == SCTAG_HEADER) {
+        if (point == end)
+            return;
+
+        SCInput::getPair(point++, &tag, &data);
+    }
+
     if (tag != SCTAG_TRANSFER_MAP_HEADER)
         return;
 
     if (TransferableMapHeader(data) == SCTAG_TM_TRANSFERRED)
         return;
 
     // freeTransfer should not GC
     JS::AutoSuppressGCAnalysis nogc;
@@ -1259,16 +1281,22 @@ JSStructuredCloneWriter::startWrite(Hand
             return callbacks->write(context(), this, obj, closure);
         /* else fall through */
     }
 
     return reportDataCloneError(JS_SCERR_UNSUPPORTED_TYPE);
 }
 
 bool
+JSStructuredCloneWriter::writeHeader()
+{
+    return out.writePair(SCTAG_HEADER, (uint32_t)scope);
+}
+
+bool
 JSStructuredCloneWriter::writeTransferMap()
 {
     if (transferableObjects.empty())
         return true;
 
     if (!out.writePair(SCTAG_TRANSFER_MAP_HEADER, (uint32_t)SCTAG_TM_UNREAD))
         return false;
 
@@ -1302,16 +1330,18 @@ JSStructuredCloneWriter::transferOwnersh
 {
     if (transferableObjects.empty())
         return true;
 
     // Walk along the transferables and the transfer map at the same time,
     // grabbing out pointers from the transferables and stuffing them into the
     // transfer map.
     uint64_t* point = out.rawBuffer();
+    MOZ_ASSERT(uint32_t(LittleEndian::readUint64(point) >> 32) == SCTAG_HEADER);
+    point++;
     MOZ_ASSERT(uint32_t(LittleEndian::readUint64(point) >> 32) == SCTAG_TRANSFER_MAP_HEADER);
     point++;
     MOZ_ASSERT(LittleEndian::readUint64(point) == transferableObjects.count());
     point++;
 
     RootedObject obj(context());
     for (auto tr = transferableObjects.all(); !tr.empty(); tr.popFront()) {
         obj = tr.front();
@@ -1337,16 +1367,18 @@ JSStructuredCloneWriter::transferOwnersh
             Rooted<ArrayBufferObject*> arrayBuffer(context(), &CheckedUnwrap(obj)->as<ArrayBufferObject>());
             JSAutoCompartment ac(context(), arrayBuffer);
             size_t nbytes = arrayBuffer->byteLength();
 
             // Structured cloning currently only has optimizations for mapped
             // and malloc'd buffers, not asm.js-ified buffers.
             bool hasStealableContents = arrayBuffer->hasStealableContents() &&
                                         (arrayBuffer->isMapped() || arrayBuffer->hasMallocedContents());
+            if (scope == JS::StructuredCloneScope::DifferentProcess)
+                hasStealableContents = false;
 
             ArrayBufferObject::BufferContents bufContents =
                 ArrayBufferObject::stealContents(context(), arrayBuffer, hasStealableContents);
             if (!bufContents)
                 return false; // Destructor will clean up the already-transferred data.
 
             content = bufContents.data();
             tag = SCTAG_TRANSFER_MAP_ARRAY_BUFFER;
@@ -1892,16 +1924,39 @@ JSStructuredCloneReader::startRead(Mutab
 
     if (vp.isObject() && !allObjs.append(vp))
         return false;
 
     return true;
 }
 
 bool
+JSStructuredCloneReader::readHeader()
+{
+    uint32_t tag, data;
+    if (!in.getPair(&tag, &data))
+        return in.reportTruncated();
+
+    if (tag != SCTAG_HEADER) {
+        // Old structured clone buffer. We must have read it from disk or
+        // somewhere, so we can assume it's scope-compatible.
+        return true;
+    }
+
+    MOZ_ALWAYS_TRUE(in.readPair(&tag, &data));
+    if (data < uint32_t(scope)) {
+        JS_ReportErrorNumber(context(), GetErrorMessage, nullptr,
+                             JSMSG_SC_BAD_SERIALIZED_DATA, "incompatible structured clone scope");
+        return false;
+    }
+
+    return true;
+}
+
+bool
 JSStructuredCloneReader::readTransferMap()
 {
     JSContext* cx = context();
     uint64_t* headerPos = in.tell();
 
     uint32_t tag, data;
     if (!in.getPair(&tag, &data))
         return in.reportTruncated();
@@ -2066,16 +2121,19 @@ JSStructuredCloneReader::readSavedFrame(
 
     return savedFrame;
 }
 
 // Perform the whole recursive reading procedure.
 bool
 JSStructuredCloneReader::read(MutableHandleValue vp)
 {
+    if (!readHeader())
+        return false;
+
     if (!readTransferMap())
         return false;
 
     // Start out by reading in the main object and pushing it onto the 'objs'
     // stack. The data related to this object and its descendants extends from
     // here to the SCTAG_END_OF_KEYS at the end of the stream.
     if (!startRead(vp))
         return false;
@@ -2173,42 +2231,44 @@ JSStructuredCloneReader::read(MutableHan
 
     return true;
 }
 
 using namespace js;
 
 JS_PUBLIC_API(bool)
 JS_ReadStructuredClone(JSContext* cx, uint64_t* buf, size_t nbytes,
-                       uint32_t version, MutableHandleValue vp,
+                       uint32_t version, JS::StructuredCloneScope scope,
+                       MutableHandleValue vp,
                        const JSStructuredCloneCallbacks* optionalCallbacks,
                        void* closure)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
 
     if (version > JS_STRUCTURED_CLONE_VERSION) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_SC_BAD_CLONE_VERSION);
         return false;
     }
     const JSStructuredCloneCallbacks* callbacks = optionalCallbacks;
-    return ReadStructuredClone(cx, buf, nbytes, vp, callbacks, closure);
+    return ReadStructuredClone(cx, buf, nbytes, scope, vp, callbacks, closure);
 }
 
 JS_PUBLIC_API(bool)
 JS_WriteStructuredClone(JSContext* cx, HandleValue value, uint64_t** bufp, size_t* nbytesp,
+                        JS::StructuredCloneScope scope,
                         const JSStructuredCloneCallbacks* optionalCallbacks,
                         void* closure, HandleValue transferable)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, value);
 
     const JSStructuredCloneCallbacks* callbacks = optionalCallbacks;
-    return WriteStructuredClone(cx, value, bufp, nbytesp, callbacks, closure, transferable);
+    return WriteStructuredClone(cx, value, bufp, nbytesp, scope, callbacks, closure, transferable);
 }
 
 JS_PUBLIC_API(bool)
 JS_ClearStructuredClone(uint64_t* data, size_t nbytes,
                         const JSStructuredCloneCallbacks* optionalCallbacks,
                         void* closure, bool freeData)
 {
     DiscardTransferables(data, nbytes, optionalCallbacks, closure);
@@ -2242,17 +2302,17 @@ JS_StructuredClone(JSContext* cx, Handle
         return false;
       }
       vp.setString(strValue);
       return true;
     }
 
     const JSStructuredCloneCallbacks* callbacks = optionalCallbacks;
 
-    JSAutoStructuredCloneBuffer buf;
+    JSAutoStructuredCloneBuffer buf(JS::StructuredCloneScope::SameProcessSameThread, callbacks, closure);
     {
         // If we use Maybe<AutoCompartment> here, G++ can't tell that the
         // destructor is only called when Maybe::construct was called, and
         // we get warnings about using uninitialized variables.
         if (value.isObject()) {
             AutoCompartment ac(cx, &value.toObject());
             if (!buf.write(cx, value, callbacks, closure))
                 return false;
@@ -2261,25 +2321,27 @@ JS_StructuredClone(JSContext* cx, Handle
                 return false;
         }
     }
 
     return buf.read(cx, vp, callbacks, closure);
 }
 
 JSAutoStructuredCloneBuffer::JSAutoStructuredCloneBuffer(JSAutoStructuredCloneBuffer&& other)
+    : scope_(other.scope_)
 {
     ownTransferables_ = other.ownTransferables_;
     other.steal(&data_, &nbytes_, &version_, &callbacks_, &closure_);
 }
 
 JSAutoStructuredCloneBuffer&
 JSAutoStructuredCloneBuffer::operator=(JSAutoStructuredCloneBuffer&& other)
 {
     MOZ_ASSERT(&other != this);
+    MOZ_ASSERT(scope_ == other.scope_);
     clear();
     ownTransferables_ = other.ownTransferables_;
     other.steal(&data_, &nbytes_, &version_, &callbacks_, &closure_);
     return *this;
 }
 
 void
 JSAutoStructuredCloneBuffer::clear(const JSStructuredCloneCallbacks* optionalCallbacks,
@@ -2364,17 +2426,17 @@ JSAutoStructuredCloneBuffer::steal(uint6
 
 bool
 JSAutoStructuredCloneBuffer::read(JSContext* cx, MutableHandleValue vp,
                                   const JSStructuredCloneCallbacks* optionalCallbacks,
                                   void* closure)
 {
     MOZ_ASSERT(cx);
     MOZ_ASSERT(data_);
-    return !!JS_ReadStructuredClone(cx, data_, nbytes_, version_, vp,
+    return !!JS_ReadStructuredClone(cx, data_, nbytes_, version_, scope_, vp,
                                     optionalCallbacks, closure);
 }
 
 bool
 JSAutoStructuredCloneBuffer::write(JSContext* cx, HandleValue value,
                                    const JSStructuredCloneCallbacks* optionalCallbacks,
                                    void* closure)
 {
@@ -2385,16 +2447,17 @@ JSAutoStructuredCloneBuffer::write(JSCon
 bool
 JSAutoStructuredCloneBuffer::write(JSContext* cx, HandleValue value,
                                    HandleValue transferable,
                                    const JSStructuredCloneCallbacks* optionalCallbacks,
                                    void* closure)
 {
     clear();
     bool ok = JS_WriteStructuredClone(cx, value, &data_, &nbytes_,
+                                      scope_,
                                       optionalCallbacks, closure,
                                       transferable);
 
     if (ok) {
         ownTransferables_ = OwnsTransferablesIfAny;
     } else {
         data_ = nullptr;
         nbytes_ = 0;
@@ -2465,8 +2528,14 @@ JS_WriteTypedArray(JSStructuredCloneWrit
 
 JS_PUBLIC_API(bool)
 JS_ObjectNotWritten(JSStructuredCloneWriter* w, HandleObject obj)
 {
     w->memory.remove(w->memory.lookup(obj));
 
     return true;
 }
+
+JS_PUBLIC_API(JS::StructuredCloneScope)
+JS_GetStructuredCloneScope(JSStructuredCloneWriter* w)
+{
+    return w->cloneScope();
+}
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -3496,19 +3496,17 @@ public:
     {
     }
 
     // We don't actually inherit any ref counting infrastructure, but we don't
     // need an nsAutoRefCnt member, so the _INHERITED macro is a hack to avoid
     // having one.
     NS_DECL_ISUPPORTS_INHERITED
     NS_DECL_NSIXPCSCRIPTABLE
-    // This is NS_METHOD because NS_CALLBACK requires the referent to be
-    // declared __stdcall on Windows, and NS_METHOD does that.
-    static NS_METHOD Get(nsIXPCScriptable** helper)
+    static nsresult Get(nsIXPCScriptable** helper)
     {
         *helper = &singleton;
         return NS_OK;
     }
 
 private:
     static ComponentsSH singleton;
 };
--- a/js/xpconnect/src/XPCJSID.cpp
+++ b/js/xpconnect/src/XPCJSID.cpp
@@ -234,17 +234,17 @@ static void EnsureClassObjectsInitialize
 {
     if (!gClassObjectsWereInited) {
         gSharedScriptableHelperForJSIID = new SharedScriptableHelperForJSIID();
 
         gClassObjectsWereInited = true;
     }
 }
 
-NS_METHOD GetSharedScriptableHelperForJSIID(nsIXPCScriptable** helper)
+static nsresult GetSharedScriptableHelperForJSIID(nsIXPCScriptable** helper)
 {
     EnsureClassObjectsInitialized();
     nsCOMPtr<nsIXPCScriptable> temp = gSharedScriptableHelperForJSIID.get();
     temp.forget(helper);
     return NS_OK;
 }
 
 /******************************************************/
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -821,32 +821,16 @@ XPCJSRuntime::FinalizeCallback(JSFreeOp*
             // compartment being collected will be marked.
             //
             // Ideally, if NativeInterfaces from different compartments were
             // kept separate, we could sweep only the ones belonging to
             // compartments being collected. Currently, though, NativeInterfaces
             // are shared between compartments. This ought to be fixed.
             bool doSweep = !isCompartmentGC;
 
-            // We don't want to sweep the JSClasses at shutdown time.
-            // At this point there may be JSObjects using them that have
-            // been removed from the other maps.
-            if (!nsXPConnect::XPConnect()->IsShuttingDown()) {
-                for (auto i = self->mNativeScriptableSharedMap->Iter(); !i.Done(); i.Next()) {
-                    auto entry = static_cast<XPCNativeScriptableSharedMap::Entry*>(i.Get());
-                    XPCNativeScriptableShared* shared = entry->key;
-                    if (shared->IsMarked()) {
-                        shared->Unmark();
-                    } else if (doSweep) {
-                        delete shared;
-                        i.Remove();
-                    }
-                }
-            }
-
             if (doSweep) {
                 for (auto i = self->mClassInfo2NativeSetMap->Iter(); !i.Done(); i.Next()) {
                     auto entry = static_cast<ClassInfo2NativeSetMap::Entry*>(i.Get());
                     if (!entry->value->IsMarked())
                         i.Remove();
                 }
             }
 
--- a/js/xpconnect/src/XPCMaps.cpp
+++ b/js/xpconnect/src/XPCMaps.cpp
@@ -435,38 +435,39 @@ XPCNativeScriptableSharedMap::XPCNativeS
 bool
 XPCNativeScriptableSharedMap::GetNewOrUsed(uint32_t flags,
                                            char* name,
                                            XPCNativeScriptableInfo* si)
 {
     NS_PRECONDITION(name,"bad param");
     NS_PRECONDITION(si,"bad param");
 
-    XPCNativeScriptableShared key(flags, name, /* populate = */ false);
-    auto entry = static_cast<Entry*>(mTable.Add(&key, fallible));
+    RefPtr<XPCNativeScriptableShared> key =
+        new XPCNativeScriptableShared(flags, name, /* populate = */ false);
+    auto entry = static_cast<Entry*>(mTable.Add(key, fallible));
     if (!entry)
         return false;
 
-    XPCNativeScriptableShared* shared = entry->key;
+    RefPtr<XPCNativeScriptableShared> shared = entry->key;
 
     // XXX: this XPCNativeScriptableShared is heap-allocated, which means the
     // js::Class it contains is also heap-allocated. This causes problems for
     // memory reporting. See the comment above the BaseShape case in
     // StatsCellCallback() in js/src/vm/MemoryMetrics.cpp.
     //
     // When the code below is removed (bug 1265271) and there are no longer any
     // heap-allocated js::Class instances, the disabled code in
     // StatsCellCallback() should be reinstated.
     //
     if (!shared) {
-        entry->key = shared =
-            new XPCNativeScriptableShared(flags, key.TransferNameOwnership(),
-                                          /* populate = */ true);
+        shared = new XPCNativeScriptableShared(flags, key->TransferNameOwnership(),
+                                               /* populate = */ true);
+        entry->key = shared;
     }
-    si->SetScriptableShared(shared);
+    si->SetScriptableShared(shared.forget());
     return true;
 }
 
 /***************************************************************************/
 // implement XPCWrappedNativeProtoMap...
 
 // static
 XPCWrappedNativeProtoMap*
--- a/js/xpconnect/src/XPCMaps.h
+++ b/js/xpconnect/src/XPCMaps.h
@@ -492,16 +492,18 @@ private:
 
 /***************************************************************************/
 
 class XPCNativeScriptableSharedMap
 {
 public:
     struct Entry : public PLDHashEntryHdr
     {
+        // This is a weak reference that will be cleared
+        // in the XPCNativeScriptableShared destructor.
         XPCNativeScriptableShared* key;
 
         static PLDHashNumber
         Hash(const void* key);
 
         static bool
         Match(const PLDHashEntryHdr* entry, const void* key);
 
@@ -509,17 +511,17 @@ public:
     };
 
     static XPCNativeScriptableSharedMap* newMap(int length);
 
     bool GetNewOrUsed(uint32_t flags, char* name, XPCNativeScriptableInfo* si);
 
     inline uint32_t Count() { return mTable.EntryCount(); }
 
-    PLDHashTable::Iterator Iter() { return mTable.Iter(); }
+    void Remove(XPCNativeScriptableShared* key) { mTable.Remove(key); }
 
 private:
     XPCNativeScriptableSharedMap();    // no implementation
     explicit XPCNativeScriptableSharedMap(int size);
 private:
     PLDHashTable mTable;
 };
 
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -166,18 +166,18 @@ XPCWrappedNative::WrapNewGlobal(xpcObjec
     XPCNativeScriptableCreateInfo sciProto;
     XPCNativeScriptableCreateInfo sciMaybe;
     const XPCNativeScriptableCreateInfo& sciWrapper =
         GatherScriptableCreateInfo(identity, nativeHelper.GetClassInfo(),
                                    sciProto, sciMaybe);
 
     // ...and then ScriptableInfo. We need all this stuff now because it's going
     // to tell us the JSClass of the object we're going to create.
-    AutoMarkingNativeScriptableInfoPtr si(cx, XPCNativeScriptableInfo::Construct(&sciWrapper));
-    MOZ_ASSERT(si.get());
+    XPCNativeScriptableInfo* si = XPCNativeScriptableInfo::Construct(&sciWrapper);
+    MOZ_ASSERT(si);
 
     // Finally, we get to the JSClass.
     const JSClass* clasp = si->GetJSClass();
     MOZ_ASSERT(clasp->flags & JSCLASS_IS_GLOBAL);
 
     // Create the global.
     aOptions.creationOptions().setTrace(XPCWrappedNative::Trace);
     if (xpc::SharedMemoryEnabled())
@@ -617,22 +617,16 @@ XPCWrappedNative::Destroy()
 
     mMaybeScope = nullptr;
 }
 
 void
 XPCWrappedNative::UpdateScriptableInfo(XPCNativeScriptableInfo* si)
 {
     MOZ_ASSERT(mScriptableInfo, "UpdateScriptableInfo expects an existing scriptable info");
-
-    // Write barrier for incremental GC.
-    JSContext* cx = GetRuntime()->Context();
-    if (IsIncrementalBarrierNeeded(cx))
-        mScriptableInfo->Mark();
-
     mScriptableInfo = si;
 }
 
 void
 XPCWrappedNative::SetProto(XPCWrappedNativeProto* p)
 {
     MOZ_ASSERT(!IsWrapperExpired(), "bad ptr!");
 
@@ -971,21 +965,19 @@ XPCWrappedNative::SystemIsBeingShutDown(
     mFlatJSObject = nullptr;
     mFlatJSObject.unsetFlags(FLAT_JS_OBJECT_VALID);
 
     XPCWrappedNativeProto* proto = GetProto();
 
     if (HasProto())
         proto->SystemIsBeingShutDown();
 
-    if (mScriptableInfo &&
-        (!HasProto() ||
-         (proto && proto->GetScriptableInfo() != mScriptableInfo))) {
-        delete mScriptableInfo;
-    }
+    // Don't destroy mScriptableInfo here because this will release
+    // XPCNativeScriptableShared, which will destroy js::ClassOps
+    // which may still be in use by JS objects.
 
     // Cleanup the tearoffs.
     for (XPCWrappedNativeTearOff* to = &mFirstTearOff; to; to = to->GetNextTearOff()) {
         if (JSObject* jso = to->GetJSObjectPreserveColor()) {
             JS_SetPrivate(jso, nullptr);
             to->SetJSObject(nullptr);
         }
         // We leak the tearoff mNative
--- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
@@ -957,18 +957,16 @@ static const js::ObjectOps XPC_WN_Object
     nullptr,  // funToString
 };
 
 XPCNativeScriptableShared::XPCNativeScriptableShared(uint32_t aFlags,
                                                      char* aName,
                                                      bool aPopulate)
     : mFlags(aFlags)
 {
-    MOZ_COUNT_CTOR(XPCNativeScriptableShared);
-
     // Initialize the js::Class.
 
     memset(&mJSClass, 0, sizeof(mJSClass));
     mJSClass.name = aName;  // take ownership
 
     if (!aPopulate)
         return;
 
@@ -1054,16 +1052,31 @@ XPCNativeScriptableShared::XPCNativeScri
     mJSClass.ext = &XPC_WN_JSClassExtension;
 
     // Initialize the js::ObjectOps.
 
     if (mFlags.WantNewEnumerate())
         mJSClass.oOps = &XPC_WN_ObjectOpsWithEnumerate;
 }
 
+XPCNativeScriptableShared::~XPCNativeScriptableShared()
+{
+    // mJSClass.cOps will be null if |this| was created with
+    // populate=false. Otherwise, it was created with populate=true
+    // and there is a weak reference in a global map that must be
+    // removed.
+
+    if (mJSClass.cOps) {
+        XPCJSRuntime::Get()->GetNativeScriptableSharedMap()->Remove(this);
+        free((void*)mJSClass.cOps);
+    }
+
+    free((void*)mJSClass.name);
+}
+
 /***************************************************************************/
 /***************************************************************************/
 
 // Compatibility hack.
 //
 // XPConnect used to do all sorts of funny tricks to find the "correct"
 // |this| object for a given method (often to the detriment of proper
 // call/apply). When these tricks were removed, a fair amount of chrome
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -1421,50 +1421,43 @@ class XPCNativeSet final
     uint16_t                mInterfaceCount : 15;
     uint16_t                mMarked : 1;
     XPCNativeInterface*     mInterfaces[1];  // always last - object sized for array
 };
 
 /***************************************************************************/
 // XPCNativeScriptableFlags is a wrapper class that holds the flags returned
 // from calls to nsIXPCScriptable::GetScriptableFlags(). It has convenience
-// methods to check for particular bitflags. Since we also use this class as
-// a member of the gc'd class XPCNativeScriptableShared, this class holds the
-// bit and exposes the inlined methods to support marking.
-
-#define XPC_WN_SJSFLAGS_MARK_FLAG JS_BIT(31) // only high bit of 32 is set
+// methods to check for particular bitflags.
 
 class XPCNativeScriptableFlags final
 {
-private:
-    uint32_t mFlags;
-
 public:
-
     explicit XPCNativeScriptableFlags(uint32_t flags = 0) : mFlags(flags) {}
 
-    uint32_t GetFlags() const {return mFlags & ~XPC_WN_SJSFLAGS_MARK_FLAG;}
-    void     SetFlags(uint32_t flags) {mFlags = flags;}
-
-    operator uint32_t() const {return GetFlags();}
+    uint32_t GetFlags() const { return mFlags; }
+    void SetFlags(uint32_t flags) { mFlags = flags; }
+
+    operator uint32_t() const { return GetFlags(); }
 
     XPCNativeScriptableFlags(const XPCNativeScriptableFlags& r)
-        {mFlags = r.GetFlags();}
+    {
+        mFlags = r.GetFlags();
+    }
 
     XPCNativeScriptableFlags& operator= (const XPCNativeScriptableFlags& r)
-        {mFlags = r.GetFlags(); return *this;}
-
-    void Mark()       {mFlags |= XPC_WN_SJSFLAGS_MARK_FLAG;}
-    void Unmark()     {mFlags &= ~XPC_WN_SJSFLAGS_MARK_FLAG;}
-    bool IsMarked() const {return 0 != (mFlags & XPC_WN_SJSFLAGS_MARK_FLAG);}
+    {
+        mFlags = r.GetFlags();
+        return *this;
+    }
 
 #ifdef GET_IT
 #undef GET_IT
 #endif
-#define GET_IT(f_) const {return 0 != (mFlags & nsIXPCScriptable:: f_ );}
+#define GET_IT(f_) const { return 0 != (mFlags & nsIXPCScriptable:: f_ ); }
 
     bool WantPreCreate()                GET_IT(WANT_PRECREATE)
     bool WantAddProperty()              GET_IT(WANT_ADDPROPERTY)
     bool WantGetProperty()              GET_IT(WANT_GETPROPERTY)
     bool WantSetProperty()              GET_IT(WANT_SETPROPERTY)
     bool WantEnumerate()                GET_IT(WANT_ENUMERATE)
     bool WantNewEnumerate()             GET_IT(WANT_NEWENUMERATE)
     bool WantResolve()                  GET_IT(WANT_RESOLVE)
@@ -1479,54 +1472,51 @@ public:
     bool DontAskInstanceForScriptable() GET_IT(DONT_ASK_INSTANCE_FOR_SCRIPTABLE)
     bool ClassInfoInterfacesOnly()      GET_IT(CLASSINFO_INTERFACES_ONLY)
     bool AllowPropModsDuringResolve()   GET_IT(ALLOW_PROP_MODS_DURING_RESOLVE)
     bool AllowPropModsToPrototype()     GET_IT(ALLOW_PROP_MODS_TO_PROTOTYPE)
     bool IsGlobalObject()               GET_IT(IS_GLOBAL_OBJECT)
     bool DontReflectInterfaceNames()    GET_IT(DONT_REFLECT_INTERFACE_NAMES)
 
 #undef GET_IT
+
+private:
+    uint32_t mFlags;
 };
 
 /***************************************************************************/
 
 // XPCNativeScriptableShared is used to hold the JSClass and the
 // associated scriptable flags for XPCWrappedNatives. These are shared across
 // the runtime and are garbage collected by xpconnect. We *used* to just store
 // this inside the XPCNativeScriptableInfo (usually owned by instances of
 // XPCWrappedNativeProto. This had two problems... It was wasteful, and it
 // was a big problem when wrappers are reparented to different scopes (and
 // thus different protos (the DOM does this).
 
 class XPCNativeScriptableShared final
 {
 public:
+    NS_INLINE_DECL_REFCOUNTING(XPCNativeScriptableShared)
+
     const XPCNativeScriptableFlags& GetFlags() const { return mFlags; }
 
     const JSClass* GetJSClass() { return Jsvalify(&mJSClass); }
 
     XPCNativeScriptableShared(uint32_t aFlags, char* aName, bool aPopulate);
 
-    ~XPCNativeScriptableShared() {
-        free((void*)mJSClass.name);
-        free((void*)mJSClass.cOps);
-        MOZ_COUNT_DTOR(XPCNativeScriptableShared);
-    }
-
     char* TransferNameOwnership() {
         char* name = (char*)mJSClass.name;
         mJSClass.name = nullptr;
         return name;
     }
 
-    void Mark()   { mFlags.Mark(); }
-    void Unmark() { mFlags.Unmark(); }
-    bool IsMarked() const { return mFlags.IsMarked(); }
-
 private:
+    ~XPCNativeScriptableShared();
+
     XPCNativeScriptableFlags mFlags;
 
     // This is an unusual js::Class instance: its name and cOps members are
     // heap-allocated, unlike all other instances for which they are statically
     // allocated. So we must free them in the destructor.
     js::Class mJSClass;
 };
 
@@ -1536,50 +1526,47 @@ private:
 
 class XPCNativeScriptableInfo final
 {
 public:
     static XPCNativeScriptableInfo*
     Construct(const XPCNativeScriptableCreateInfo* sci);
 
     nsIXPCScriptable*
-    GetCallback() const {return mCallback;}
+    GetCallback() const { return mCallback; }
 
     const XPCNativeScriptableFlags&
-    GetFlags() const      {return mShared->GetFlags();}
+    GetFlags() const { return mShared->GetFlags(); }
 
     const JSClass*
-    GetJSClass()          {return mShared->GetJSClass();}
+    GetJSClass() { return mShared->GetJSClass(); }
 
     void
-    SetScriptableShared(XPCNativeScriptableShared* shared) {mShared = shared;}
-
-    void Mark() {
-        if (mShared)
-            mShared->Mark();
-    }
-
-    void TraceJS(JSTracer* trc) {}
-    void AutoTrace(JSTracer* trc) {}
+    SetScriptableShared(already_AddRefed<XPCNativeScriptableShared>&& shared) { mShared = shared; }
 
 protected:
     explicit XPCNativeScriptableInfo(nsIXPCScriptable* scriptable)
-        : mCallback(scriptable), mShared(nullptr)
-                               {MOZ_COUNT_CTOR(XPCNativeScriptableInfo);}
+        : mCallback(scriptable)
+    {
+        MOZ_COUNT_CTOR(XPCNativeScriptableInfo);
+    }
 public:
-    ~XPCNativeScriptableInfo() {MOZ_COUNT_DTOR(XPCNativeScriptableInfo);}
+    ~XPCNativeScriptableInfo()
+    {
+        MOZ_COUNT_DTOR(XPCNativeScriptableInfo);
+    }
 private:
 
     // disable copy ctor and assignment
     XPCNativeScriptableInfo(const XPCNativeScriptableInfo& r) = delete;
     XPCNativeScriptableInfo& operator= (const XPCNativeScriptableInfo& r) = delete;
 
 private:
-    nsCOMPtr<nsIXPCScriptable>  mCallback;
-    XPCNativeScriptableShared*  mShared;
+    nsCOMPtr<nsIXPCScriptable> mCallback;
+    RefPtr<XPCNativeScriptableShared> mShared;
 };
 
 /***************************************************************************/
 // XPCNativeScriptableCreateInfo is used in creating new wrapper and protos.
 // it abstracts out the scriptable interface pointer and the flags. After
 // creation these are factored differently using XPCNativeScriptableInfo.
 
 class MOZ_STACK_CLASS XPCNativeScriptableCreateInfo final
@@ -1660,18 +1647,16 @@ public:
     void TraceSelf(JSTracer* trc) {
         if (mJSProtoObject)
             mJSProtoObject.trace(trc, "XPCWrappedNativeProto::mJSProtoObject");
     }
 
     void TraceInside(JSTracer* trc) {
         if (trc->isMarkingTracer()) {
             mSet->Mark();
-            if (mScriptableInfo)
-                mScriptableInfo->Mark();
         }
 
         GetScope()->TraceSelf(trc);
     }
 
     void TraceJS(JSTracer* trc) {
         TraceSelf(trc);
         TraceInside(trc);
@@ -1681,20 +1666,17 @@ public:
     {
         if (JS::IsIncrementalBarrierNeeded(cx) && mJSProtoObject)
             mJSProtoObject.writeBarrierPre(cx);
     }
 
     // NOP. This is just here to make the AutoMarkingPtr code compile.
     inline void AutoTrace(JSTracer* trc) {}
 
-    // Yes, we *do* need to mark the mScriptableInfo in both cases.
-    void Mark() const
-        {mSet->Mark();
-         if (mScriptableInfo) mScriptableInfo->Mark();}
+    void Mark() const {mSet->Mark();}
 
 #ifdef DEBUG
     void ASSERT_SetNotMarked() const {mSet->ASSERT_NotMarked();}
 #endif
 
     ~XPCWrappedNativeProto();
 
 protected:
@@ -1944,26 +1926,22 @@ public:
     XPCWrappedNativeTearOff* FindTearOff(XPCNativeInterface* aInterface,
                                          bool needJSObject = false,
                                          nsresult* pError = nullptr);
     XPCWrappedNativeTearOff* FindTearOff(const nsIID& iid);
 
     void Mark() const
     {
         mSet->Mark();
-        if (mScriptableInfo) mScriptableInfo->Mark();
         if (HasProto()) GetProto()->Mark();
     }
 
-    // Yes, we *do* need to mark the mScriptableInfo in both cases.
     inline void TraceInside(JSTracer* trc) {
         if (trc->isMarkingTracer()) {
             mSet->Mark();
-            if (mScriptableInfo)
-                mScriptableInfo->Mark();
         }
         if (HasProto())
             GetProto()->TraceSelf(trc);
         else
             GetScope()->TraceSelf(trc);
         if (mFlatJSObject && JS_IsGlobalObject(mFlatJSObject))
         {
             xpc::TraceXPCGlobal(trc, mFlatJSObject);
@@ -2900,17 +2878,16 @@ class TypedAutoMarkingPtr : public AutoM
     T* mPtr;
 };
 
 typedef TypedAutoMarkingPtr<XPCNativeInterface> AutoMarkingNativeInterfacePtr;
 typedef TypedAutoMarkingPtr<XPCNativeSet> AutoMarkingNativeSetPtr;
 typedef TypedAutoMarkingPtr<XPCWrappedNative> AutoMarkingWrappedNativePtr;
 typedef TypedAutoMarkingPtr<XPCWrappedNativeTearOff> AutoMarkingWrappedNativeTearOffPtr;
 typedef TypedAutoMarkingPtr<XPCWrappedNativeProto> AutoMarkingWrappedNativeProtoPtr;
-typedef TypedAutoMarkingPtr<XPCNativeScriptableInfo> AutoMarkingNativeScriptableInfoPtr;
 
 template<class T>
 class ArrayAutoMarkingPtr : public AutoMarkingPtr
 {
   public:
     ArrayAutoMarkingPtr(JSContext* cx, nsTArray<T*>& ptr)
       : AutoMarkingPtr(cx), mPtr(ptr)
     {}
--- a/js/xpconnect/tests/chrome/test_chrometoSource.xul
+++ b/js/xpconnect/tests/chrome/test_chrometoSource.xul
@@ -20,20 +20,16 @@ var Cc = Components.classes;
 var Ci = Components.interfaces;
 Cu.import("resource://gre/modules/NetUtil.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 function inlinefunction() {
          return 42;
 }
 
-// 1 assertion in debug builds because bug 821726 added the ignoreCache switch and
-// we now write to the cache more than once for the same entry
-SimpleTest.expectAssertions(1);
-
 var src;
 src = inlinefunction.toSource();
 isnot(src.indexOf("return 42"), -1, "inline XUL script should have source");
 is(src.charAt(src.length - 1), "}", "inline XUL source should end with '}'");
 src = outoflinefunction.toSource();
 isnot(src.indexOf("return 42"), -1, "out of line XUL script should have source")
 is(src.charAt(src.length - 1), "}", "out of line XUL source should end with '}'");
 src = NetUtil.asyncFetch.toSource();
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -2722,17 +2722,17 @@ nsCSSRendering::PaintGradient(nsPresCont
 
   // If a non-repeating linear gradient is axis-aligned and there are no gaps
   // between tiles, we can optimise away most of the work by converting to a
   // repeating linear gradient and filling the whole destination rect at once.
   bool forceRepeatToCoverTiles =
     aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_LINEAR &&
     (lineStart.x == lineEnd.x) != (lineStart.y == lineEnd.y) &&
     aRepeatSize.width == aDest.width && aRepeatSize.height == aDest.height &&
-    !aGradient->mRepeating && !cellContainsFill;
+    !aGradient->mRepeating && !aSrc.IsEmpty() && !cellContainsFill;
 
   gfxMatrix matrix;
   if (forceRepeatToCoverTiles) {
     // Length of the source rectangle along the gradient axis.
     double rectLen;
     // The position of the start of the rectangle along the gradient.
     double offset;
 
@@ -2741,25 +2741,25 @@ nsCSSRendering::PaintGradient(nsPresCont
     // right way up.
     if (lineStart.x > lineEnd.x || lineStart.y > lineEnd.y) {
       std::swap(lineStart, lineEnd);
       matrix.Scale(-1, -1);
     }
 
     // Fit the gradient line exactly into the source rect.
     if (lineStart.x != lineEnd.x) {
-      rectLen = srcSize.width;
+      rectLen = aPresContext->CSSPixelsToDevPixels(aSrc.width);
       offset = ((double)aSrc.x - lineStart.x) / lineLength;
       lineStart.x = aSrc.x;
-      lineEnd.x = aSrc.x + srcSize.width;
+      lineEnd.x = aSrc.x + rectLen;
     } else {
-      rectLen = srcSize.height;
+      rectLen = aPresContext->CSSPixelsToDevPixels(aSrc.height);
       offset = ((double)aSrc.y - lineStart.y) / lineLength;
       lineStart.y = aSrc.y;
-      lineEnd.y = aSrc.y + srcSize.height;
+      lineEnd.y = aSrc.y + rectLen;
     }
 
     // Adjust gradient stop positions for the new gradient line.
     double scale = lineLength / rectLen;
     for (size_t i = 0; i < stops.Length(); i++) {
       stops[i].mPosition = (stops[i].mPosition - offset) * fabs(scale);
     }
 
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -52,16 +52,17 @@
 #include "ImageContainer.h"
 #include "nsCanvasFrame.h"
 #include "StickyScrollContainer.h"
 #include "mozilla/AnimationPerformanceWarning.h"
 #include "mozilla/AnimationUtils.h"
 #include "mozilla/EffectCompositor.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/LookAndFeel.h"
+#include "mozilla/OperatorNewExtensions.h"
 #include "mozilla/PendingAnimationTracker.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/unused.h"
 #include "ActiveLayerTracker.h"
 #include "nsContentUtils.h"
 #include "nsPrintfCString.h"
 #include "UnitTransforms.h"
@@ -104,17 +105,17 @@ SpammyLayoutWarningsEnabled()
     sValueInitialized = true;
   }
 
   return sValue;
 }
 #endif
 
 void*
-AnimatedGeometryRoot::operator new(size_t aSize, nsDisplayListBuilder* aBuilder) CPP_THROW_NEW
+AnimatedGeometryRoot::operator new(size_t aSize, nsDisplayListBuilder* aBuilder)
 {
   return aBuilder->Allocate(aSize);
 }
 
 static inline CSSAngle
 MakeCSSAngle(const nsCSSValue& aValue)
 {
   return CSSAngle(aValue.GetAngleValue(), aValue.GetUnit());
@@ -1103,30 +1104,31 @@ const DisplayItemClip*
 nsDisplayListBuilder::AllocateDisplayItemClip(const DisplayItemClip& aOriginal)
 {
   void* p = Allocate(sizeof(DisplayItemClip));
   if (!aOriginal.GetRoundedRectCount()) {
     memcpy(p, &aOriginal, sizeof(DisplayItemClip));
     return static_cast<DisplayItemClip*>(p);
   }
 
-  DisplayItemClip* c = new (p) DisplayItemClip(aOriginal);
+  DisplayItemClip* c = new (KnownNotNull, p) DisplayItemClip(aOriginal);
   mDisplayItemClipsToDestroy.AppendElement(c);
   return c;
 }
 
 DisplayItemScrollClip*
 nsDisplayListBuilder::AllocateDisplayItemScrollClip(const DisplayItemScrollClip* aParent,
                                                     nsIScrollableFrame* aScrollableFrame,
                                                     const DisplayItemClip* aClip,
                                                     bool aIsAsyncScrollable)
 {
   void* p = Allocate(sizeof(DisplayItemScrollClip));
   DisplayItemScrollClip* c =
-    new (p) DisplayItemScrollClip(aParent, aScrollableFrame, aClip, aIsAsyncScrollable);
+    new (KnownNotNull, p) DisplayItemScrollClip(aParent, aScrollableFrame,
+                                                aClip, aIsAsyncScrollable);
   mScrollClipsToDestroy.AppendElement(c);
   return c;
 }
 
 const nsIFrame*
 nsDisplayListBuilder::FindReferenceFrameFor(const nsIFrame *aFrame,
                                             nsPoint* aOffset)
 {
@@ -1758,16 +1760,21 @@ already_AddRefed<LayerManager> nsDisplay
 
   if (doBeginTransaction) {
     if (aCtx) {
       layerManager->BeginTransactionWithTarget(aCtx->ThebesContext());
     } else {
       layerManager->BeginTransaction();
     }
   }
+
+  if (XRE_IsContentProcess() && gfxPrefs::AlwaysPaint()) {
+    FrameLayerBuilder::InvalidateAllLayers(layerManager);
+  }
+
   if (widgetTransaction) {
     layerBuilder->DidBeginRetainedLayerTransaction(layerManager);
   }
 
   nsIFrame* frame = aBuilder->RootReferenceFrame();
   nsPresContext* presContext = frame->PresContext();
   nsIPresShell* presShell = presContext->GetPresShell();
   nsRootPresContext* rootPresContext = presContext->GetRootPresContext();
@@ -1836,19 +1843,17 @@ already_AddRefed<LayerManager> nsDisplay
   nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
   if (rootScrollFrame) {
     content = rootScrollFrame->GetContent();
   } else {
     // If there is no root scroll frame, pick the document element instead.
     // The only case we don't want to do this is in non-APZ fennec, where
     // we want the root xul document to get a null scroll id so that the root
     // content document gets the first non-null scroll id.
-#if !defined(MOZ_WIDGET_ANDROID) || defined(MOZ_ANDROID_APZ)
     content = document->GetDocumentElement();
-#endif
   }
 
 
   if (ensureMetricsForRootId && content) {
     ViewID scrollId = nsLayoutUtils::FindOrCreateIDFor(content);
     if (nsLayoutUtils::ContainsMetricsWithId(root, scrollId)) {
       ensureMetricsForRootId = false;
     }
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -139,17 +139,17 @@ struct AnimatedGeometryRoot
     , mParentAGR(aParent)
   {}
 
   operator nsIFrame*() { return mFrame; }
 
   nsIFrame* operator ->() const { return mFrame; }
 
   void* operator new(size_t aSize,
-                     nsDisplayListBuilder* aBuilder) CPP_THROW_NEW;
+                     nsDisplayListBuilder* aBuilder);
 
   nsIFrame* mFrame;
   AnimatedGeometryRoot* mParentAGR;
 };
 
 enum class nsDisplayListBuilderMode : uint8_t {
   PAINTING,
   EVENT_DELIVERY,
@@ -1352,17 +1352,17 @@ public:
 #ifdef MOZ_DUMP_PAINTING
     , mPainted(false)
 #endif
   {
   }
   virtual ~nsDisplayItem() {}
 
   void* operator new(size_t aSize,
-                     nsDisplayListBuilder* aBuilder) CPP_THROW_NEW {
+                     nsDisplayListBuilder* aBuilder) {
     return aBuilder->Allocate(aSize);
   }
 
 // Contains all the type integers for each display list item type
 #include "nsDisplayItemTypes.h"
 
   struct HitTestState {
     explicit HitTestState() : mInPreserves3D(false) {}
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -886,21 +886,17 @@ nsLayoutUtils::AsyncPanZoomEnabled(nsIFr
   if (!widget) {
     return false;
   }
   return widget->AsyncPanZoomEnabled();
 }
 
 float
 nsLayoutUtils::GetCurrentAPZResolutionScale(nsIPresShell* aShell) {
-#if !defined(MOZ_WIDGET_ANDROID) || defined(MOZ_ANDROID_APZ)
   return aShell ? aShell->GetCumulativeNonRootScaleResolution() : 1.0;
-#else
-  return 1.0f;
-#endif
 }
 
 // Return the maximum displayport size, based on the LayerManager's maximum
 // supported texture size. The result is in app units.
 static nscoord
 GetMaxDisplayPortSize(nsIContent* aContent)
 {
   MOZ_ASSERT(!gfxPrefs::LayersTilesEnabled(), "Do not clamp displayports if tiling is enabled");
@@ -3471,28 +3467,26 @@ nsLayoutUtils::PaintFrame(nsRenderingCon
     ViewID id = FrameMetrics::NULL_SCROLL_ID;
     if (ignoreViewportScrolling && presContext->IsRootContentDocument()) {
       if (nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame()) {
         if (nsIContent* content = rootScrollFrame->GetContent()) {
           id = nsLayoutUtils::FindOrCreateIDFor(content);
         }
       }
     }
-#if !defined(MOZ_WIDGET_ANDROID) || defined(MOZ_ANDROID_APZ)
     else if (presShell->GetDocument() && presShell->GetDocument()->IsRootDisplayDocument()
         && !presShell->GetRootScrollFrame()) {
       // In cases where the root document is a XUL document, we want to take
       // the ViewID from the root element, as that will be the ViewID of the
       // root APZC in the tree. Skip doing this in cases where we know
       // nsGfxScrollFrame::BuilDisplayList will do it instead.
       if (dom::Element* element = presShell->GetDocument()->GetDocumentElement()) {
         id = nsLayoutUtils::FindOrCreateIDFor(element);
       }
     }
-#endif
 
     nsDisplayListBuilder::AutoCurrentScrollParentIdSetter idSetter(&builder, id);
 
     PROFILER_LABEL("nsLayoutUtils", "PaintFrame::BuildDisplayList",
       js::ProfileEntry::Category::GRAPHICS);
 
     aFrame->BuildDisplayListForStackingContext(&builder, dirtyRect, &list);
   }
@@ -8474,27 +8468,25 @@ nsLayoutUtils::CalculateScrollableRectFo
   if (aScrollableFrame) {
     contentBounds = aScrollableFrame->GetScrollRange();
 
     // We ifndef the below code for Fennec because it requires special behaviour
     // on the APZC side. Because Fennec has it's own PZC implementation which doesn't
     // provide the special behaviour, this code will cause it to break. We can remove
     // the ifndef once Fennec switches over to APZ or if we add the special handling
     // to Fennec
-#if !defined(MOZ_WIDGET_ANDROID) || defined(MOZ_ANDROID_APZ)
     nsPoint scrollPosition = aScrollableFrame->GetScrollPosition();
     if (aScrollableFrame->GetScrollbarStyles().mVertical == NS_STYLE_OVERFLOW_HIDDEN) {
       contentBounds.y = scrollPosition.y;
       contentBounds.height = 0;
     }
     if (aScrollableFrame->GetScrollbarStyles().mHorizontal == NS_STYLE_OVERFLOW_HIDDEN) {
       contentBounds.x = scrollPosition.x;
       contentBounds.width = 0;
     }
-#endif
 
     contentBounds.width += aScrollableFrame->GetScrollPortRect().width;
     contentBounds.height += aScrollableFrame->GetScrollPortRect().height;
   } else {
     contentBounds = aRootFrame->GetRect();
   }
   return contentBounds;
 }
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -788,24 +788,17 @@ PresShell::PresShell()
 #endif
   mLoadBegin = TimeStamp::Now();
 
   mSelectionFlags = nsISelectionDisplay::DISPLAY_TEXT | nsISelectionDisplay::DISPLAY_IMAGES;
   mIsThemeSupportDisabled = false;
   mIsActive = true;
   mIsHidden = false;
   // FIXME/bug 735029: find a better solution to this problem
-#if defined(MOZ_WIDGET_ANDROID) && !defined(MOZ_ANDROID_APZ)
-  // The java pan/zoom code uses this to mean approximately "request a
-  // reset of pan/zoom state" which doesn't necessarily correspond
-  // with the first paint of content.
-  mIsFirstPaint = false;
-#else
   mIsFirstPaint = true;
-#endif
   mPresShellId = sNextPresShellId++;
   mFrozen = false;
   mRenderFlags = 0;
 
   mScrollPositionClampingScrollPortSizeSet = false;
 
   static bool addedSynthMouseMove = false;
   if (!addedSynthMouseMove) {
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -1923,16 +1923,20 @@ nsRefreshDriver::Tick(int64_t aNowEpoch,
   while (iter.HasMore()) {
     nsAPostRefreshObserver* observer = iter.GetNext();
     observer->DidRefresh();
   }
 
   ConfigureHighPrecision();
 
   NS_ASSERTION(mInRefresh, "Still in refresh");
+
+  if (mPresContext->IsRoot() && XRE_IsContentProcess() && gfxPrefs::AlwaysPaint()) {
+    ScheduleViewManagerFlush();
+  }
 }
 
 void
 nsRefreshDriver::BeginRefreshingImages(RequestTable& aEntries,
                                        mozilla::TimeStamp aDesired)
 {
   for (auto iter = aEntries.Iter(); !iter.Done(); iter.Next()) {
     auto req = static_cast<imgIRequest*>(iter.Get()->GetKey());
--- a/layout/generic/nsFrameList.cpp
+++ b/layout/generic/nsFrameList.cpp
@@ -20,17 +20,17 @@ namespace mozilla {
 namespace layout {
 namespace detail {
 const AlignedFrameListBytes gEmptyFrameListBytes = { 0 };
 } // namespace detail
 } // namespace layout
 } // namespace mozilla
 
 void*
-nsFrameList::operator new(size_t sz, nsIPresShell* aPresShell) CPP_THROW_NEW
+nsFrameList::operator new(size_t sz, nsIPresShell* aPresShell)
 {
   return aPresShell->AllocateByObjectID(eArenaObjectID_nsFrameList, sz);
 }
 
 void
 nsFrameList::Delete(nsIPresShell* aPresShell)
 {
   NS_PRECONDITION(this != &EmptyList(), "Shouldn't Delete() this list");
--- a/layout/generic/nsFrameList.h
+++ b/layout/generic/nsFrameList.h
@@ -71,17 +71,17 @@ public:
   nsFrameList(const nsFrameList& aOther) :
     mFirstChild(aOther.mFirstChild), mLastChild(aOther.mLastChild)
   {
   }
 
   /**
    * Infallibly allocate a nsFrameList from the shell arena.
    */
-  void* operator new(size_t sz, nsIPresShell* aPresShell) CPP_THROW_NEW;
+  void* operator new(size_t sz, nsIPresShell* aPresShell);
 
   /**
    * Deallocate this list that was allocated from the shell arena.
    * The list is required to be empty.
    */
   void Delete(nsIPresShell* aPresShell);
 
   /**
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -3573,23 +3573,16 @@ ScrollFrameHelper::ComputeScrollMetadata
   // For containerful frames, the clip is on the container layer.
   if (aClip &&
       (!gfxPrefs::LayoutUseContainersForRootFrames() || mAddClipRectToLayer)) {
     parentLayerClip = Some(aClip->GetClipRect());
   }
 
   bool isRootContent = mIsRoot && mOuter->PresContext()->IsRootContentDocument();
   bool thisScrollFrameUsesAsyncScrolling = nsLayoutUtils::UsesAsyncScrolling(mOuter);
-#if defined(MOZ_WIDGET_ANDROID) && !defined(MOZ_ANDROID_APZ)
-  // Android without apzc (aka the java pan zoom code) only uses async scrolling
-  // for the root scroll frame of the root content document.
-  if (!isRootContent) {
-    thisScrollFrameUsesAsyncScrolling = false;
-  }
-#endif
   if (!thisScrollFrameUsesAsyncScrolling) {
     if (parentLayerClip) {
       // If APZ is not enabled, we still need the displayport to be clipped
       // in the compositor.
       ParentLayerIntRect displayportClip =
         ViewAs<ParentLayerPixel>(
           parentLayerClip->ScaleToNearestPixels(
             aParameters.mXScale,
--- a/layout/generic/nsLineBox.cpp
+++ b/layout/generic/nsLineBox.cpp
@@ -142,17 +142,17 @@ nsLineBox::NoteFramesMovedFrom(nsLineBox
       // aFromLine's hash table and allocate a new hash table for that line.
       StealHashTableFrom(aFromLine, fromNewCount); // fromNewCount RemoveEntry
       aFromLine->SwitchToHashtable(); // fromNewCount PutEntry
     }
   }
 }
 
 void*
-nsLineBox::operator new(size_t sz, nsIPresShell* aPresShell) CPP_THROW_NEW
+nsLineBox::operator new(size_t sz, nsIPresShell* aPresShell)
 {
   return aPresShell->AllocateByObjectID(eArenaObjectID_nsLineBox, sz);
 }
 
 void
 nsLineBox::Destroy(nsIPresShell* aPresShell)
 {
   this->nsLineBox::~nsLineBox();
--- a/layout/generic/nsLineBox.h
+++ b/layout/generic/nsLineBox.h
@@ -203,17 +203,17 @@ class nsLineLink {
  */
 class nsLineBox final : public nsLineLink {
 private:
   nsLineBox(nsIFrame* aFrame, int32_t aCount, bool aIsBlock);
   ~nsLineBox();
   
   // Infallible overloaded new operator. Uses an arena (which comes from the
   // presShell) to perform the allocation.
-  void* operator new(size_t sz, nsIPresShell* aPresShell) CPP_THROW_NEW;
+  void* operator new(size_t sz, nsIPresShell* aPresShell);
   void operator delete(void* aPtr, size_t sz) = delete;
 
 public:
   // Use these functions to allocate and destroy line boxes
   friend nsLineBox* NS_NewLineBox(nsIPresShell* aPresShell, nsIFrame* aFrame,
                                   bool aIsBlock);
   friend nsLineBox* NS_NewLineBox(nsIPresShell* aPresShell, nsLineBox* aFromLine,
                                   nsIFrame* aFrame, int32_t aCount);
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1291528-ref.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title></title>
+    <!--
+      This CSS will hit the non-optimised linear gradient painting path, since
+      the dirty rect of div.inner will be the same as the destination rect,
+      making cellContainsFill true in PaintGradient() and skipping the optimised
+      path.
+    -->
+    <style type="text/css">
+      div.outer {
+        border: 2px solid grey;
+        padding: 0;
+        height: 18.5px;
+        width: 100px;
+      }
+
+      div.inner {
+        margin: 0;
+        padding: 0;
+        border: 0;
+        height: 18.5px;
+        width: 100px;
+        background: linear-gradient(to top, red, blue);
+      }
+    </style>
+  </head>
+  <body>
+    <div class="outer">
+      <div class="inner">
+      </div>
+    </div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1291528.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title></title>
+    <!--
+        This test makes sure that linear gradient painting paths agree around
+        non-pixel aligned dimensions: namely, the direct path which draws the
+        linear gradient repeatedly; and the optimised path which converts the
+        original gradient into a repeating gradient and fills the whole
+        destination in one go.
+
+        This CSS will hit the optimised linear gradient painting path, since the
+        dirty rect will be the size of the whole box (including border), but the
+        destination is only the size of the rect within the border.
+    -->
+    <style type="text/css">
+      div {
+        border: 2px solid grey;
+        background: linear-gradient(to top, red, blue);
+        height: 18.5px;
+        width: 100px;
+      }
+    </style>
+  </head>
+  <body>
+    <div>
+    </div>
+  </body>
+</html>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1957,16 +1957,17 @@ fuzzy(100,2000) == 1239564.html 1239564-
 == 1272997-1.html 1272997-1-ref.html
 random-if(!winWidget) == 1273154-1.html 1273154-1-ref.html # depends on Windows font
 random-if(!winWidget) == 1273154-2.html 1273154-2-ref.html # depends on Windows font
 == 1274368-1.html 1274368-1-ref.html
 != 1276161-1a.html 1276161-1-notref.html
 != 1276161-1b.html 1276161-1-notref.html
 != 1276161-1a.html 1276161-1b.html
 == 1275411-1.html 1275411-1-ref.html
+fuzzy(8,1900) == 1291528.html 1291528-ref.html
 # Buttons in 2 pages have different position and the rendering result can be
 # different, but they should use the same button style and the background color
 # should be same.  |fuzzy()| here allows the difference in border, but not
 # background color.
 fuzzy(255,1000) skip-if(!cocoaWidget) == 1294102-1.html 1294102-1-ref.html
 
 HTTP == 652991-1a.html 652991-1-ref.html
 HTTP == 652991-1b.html 652991-1-ref.html
--- a/layout/style/CounterStyleManager.cpp
+++ b/layout/style/CounterStyleManager.cpp
@@ -975,17 +975,17 @@ public:
 
   virtual CounterStyle* GetFallback() override;
 
   // DependentBuiltinCounterStyle is managed in the same way as
   // CustomCounterStyle.
   NS_IMETHOD_(MozExternalRefCountType) AddRef() override;
   NS_IMETHOD_(MozExternalRefCountType) Release() override;
 
-  void* operator new(size_t sz, nsPresContext* aPresContext) CPP_THROW_NEW
+  void* operator new(size_t sz, nsPresContext* aPresContext)
   {
     return aPresContext->PresShell()->AllocateByObjectID(
         eArenaObjectID_DependentBuiltinCounterStyle, sz);
   }
 
 private:
   void Destroy()
   {
@@ -1096,17 +1096,17 @@ public:
   }
 
   // CustomCounterStyle should be reference-counted because it may be
   // dereferenced from the manager but still referenced by nodes and
   // frames before the style change is propagated.
   NS_IMETHOD_(MozExternalRefCountType) AddRef() override;
   NS_IMETHOD_(MozExternalRefCountType) Release() override;
 
-  void* operator new(size_t sz, nsPresContext* aPresContext) CPP_THROW_NEW
+  void* operator new(size_t sz, nsPresContext* aPresContext)
   {
     return aPresContext->PresShell()->AllocateByObjectID(
         eArenaObjectID_CustomCounterStyle, sz);
   }
 
 private:
   void Destroy()
   {
--- a/layout/style/nsCSSRuleProcessor.cpp
+++ b/layout/style/nsCSSRuleProcessor.cpp
@@ -49,16 +49,17 @@
 #include "nsStyleSet.h"
 #include "mozilla/dom/Element.h"
 #include "nsNthIndexCache.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/Likely.h"
+#include "mozilla/OperatorNewExtensions.h"
 #include "mozilla/TypedEnumBits.h"
 #include "RuleProcessorCache.h"
 #include "nsIDOMMutationEvent.h"
 #include "nsIMozBrowserFrame.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
@@ -255,17 +256,17 @@ RuleHash_IdCSMatchEntry(const PLDHashEnt
 {
   return CSMatchAtoms(key, SubjectSelectorForRuleHash(hdr)->mIDList->mAtom);
 }
 
 static void
 RuleHash_InitEntry(PLDHashEntryHdr *hdr, const void *key)
 {
   RuleHashTableEntry* entry = static_cast<RuleHashTableEntry*>(hdr);
-  new (entry) RuleHashTableEntry();
+  new (KnownNotNull, entry) RuleHashTableEntry();
 }
 
 static void
 RuleHash_ClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr)
 {
   RuleHashTableEntry* entry = static_cast<RuleHashTableEntry*>(hdr);
   entry->~RuleHashTableEntry();
 }
@@ -273,17 +274,17 @@ RuleHash_ClearEntry(PLDHashTable *table,
 static void
 RuleHash_MoveEntry(PLDHashTable *table, const PLDHashEntryHdr *from,
                    PLDHashEntryHdr *to)
 {
   NS_PRECONDITION(from != to, "This is not going to work!");
   RuleHashTableEntry *oldEntry =
     const_cast<RuleHashTableEntry*>(
       static_cast<const RuleHashTableEntry*>(from));
-  RuleHashTableEntry *newEntry = new (to) RuleHashTableEntry();
+  RuleHashTableEntry *newEntry = new (KnownNotNull, to) RuleHashTableEntry();
   newEntry->mRules.SwapElements(oldEntry->mRules);
   oldEntry->~RuleHashTableEntry();
 }
 
 static bool
 RuleHash_TagTable_MatchEntry(const PLDHashEntryHdr *hdr, const void *key)
 {
   nsIAtom *match_atom = const_cast<nsIAtom*>(static_cast<const nsIAtom*>(key));
@@ -291,17 +292,17 @@ RuleHash_TagTable_MatchEntry(const PLDHa
 
   return match_atom == entry_atom;
 }
 
 static void
 RuleHash_TagTable_InitEntry(PLDHashEntryHdr *hdr, const void *key)
 {
   RuleHashTagTableEntry* entry = static_cast<RuleHashTagTableEntry*>(hdr);
-  new (entry) RuleHashTagTableEntry();
+  new (KnownNotNull, entry) RuleHashTagTableEntry();
   entry->mTag = const_cast<nsIAtom*>(static_cast<const nsIAtom*>(key));
 }
 
 static void
 RuleHash_TagTable_ClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr)
 {
   RuleHashTagTableEntry* entry = static_cast<RuleHashTagTableEntry*>(hdr);
   entry->~RuleHashTagTableEntry();
@@ -310,17 +311,17 @@ RuleHash_TagTable_ClearEntry(PLDHashTabl
 static void
 RuleHash_TagTable_MoveEntry(PLDHashTable *table, const PLDHashEntryHdr *from,
                             PLDHashEntryHdr *to)
 {
   NS_PRECONDITION(from != to, "This is not going to work!");
   RuleHashTagTableEntry *oldEntry =
     const_cast<RuleHashTagTableEntry*>(
       static_cast<const RuleHashTagTableEntry*>(from));
-  RuleHashTagTableEntry *newEntry = new (to) RuleHashTagTableEntry();
+  RuleHashTagTableEntry *newEntry = new (KnownNotNull, to) RuleHashTagTableEntry();
   newEntry->mTag.swap(oldEntry->mTag);
   newEntry->mRules.SwapElements(oldEntry->mRules);
   oldEntry->~RuleHashTagTableEntry();
 }
 
 static PLDHashNumber
 RuleHash_NameSpaceTable_HashKey(const void *key)
 {
@@ -789,28 +790,28 @@ AtomSelector_ClearEntry(PLDHashTable *ta
 {
   (static_cast<AtomSelectorEntry*>(hdr))->~AtomSelectorEntry();
 }
 
 static void
 AtomSelector_InitEntry(PLDHashEntryHdr *hdr, const void *key)
 {
   AtomSelectorEntry *entry = static_cast<AtomSelectorEntry*>(hdr);
-  new (entry) AtomSelectorEntry();
+  new (KnownNotNull, entry) AtomSelectorEntry();
   entry->mAtom = const_cast<nsIAtom*>(static_cast<const nsIAtom*>(key));
 }
 
 static void
 AtomSelector_MoveEntry(PLDHashTable *table, const PLDHashEntryHdr *from,
                        PLDHashEntryHdr *to)
 {
   NS_PRECONDITION(from != to, "This is not going to work!");
   AtomSelectorEntry *oldEntry =
     const_cast<AtomSelectorEntry*>(static_cast<const AtomSelectorEntry*>(from));
-  AtomSelectorEntry *newEntry = new (to) AtomSelectorEntry();
+  AtomSelectorEntry *newEntry = new (KnownNotNull, to) AtomSelectorEntry();
   newEntry->mAtom = oldEntry->mAtom;
   newEntry->mSelectors.SwapElements(oldEntry->mSelectors);
   oldEntry->~AtomSelectorEntry();
 }
 
 static bool
 AtomSelector_CIMatchEntry(const PLDHashEntryHdr *hdr, const void *key)
 {
@@ -3475,17 +3476,17 @@ MatchWeightEntry(const PLDHashEntryHdr *
   const RuleByWeightEntry *entry = (const RuleByWeightEntry *)hdr;
   return entry->data.mWeight == NS_PTR_TO_INT32(key);
 }
 
 static void
 InitWeightEntry(PLDHashEntryHdr *hdr, const void *key)
 {
   RuleByWeightEntry* entry = static_cast<RuleByWeightEntry*>(hdr);
-  new (entry) RuleByWeightEntry();
+  new (KnownNotNull, entry) RuleByWeightEntry();
 }
 
 static const PLDHashTableOps gRulesByWeightOps = {
     HashIntKey,
     MatchWeightEntry,
     PLDHashTable::MoveEntryStub,
     PLDHashTable::ClearEntryStub,
     InitWeightEntry
--- a/layout/style/nsCSSValue.cpp
+++ b/layout/style/nsCSSValue.cpp
@@ -2633,16 +2633,26 @@ css::URLValueData::operator==(const URLV
            (mURI && aOther.mURI &&
             NS_SUCCEEDED(mURI->Equals(aOther.mURI, &eq)) &&
             eq)) &&
           (mOriginPrincipal == aOther.mOriginPrincipal ||
            self.mOriginPrincipal.get()->Equals(other.mOriginPrincipal.get()));
 }
 
 bool
+css::URLValueData::MaybeUnresolvedURIEquals(const URLValueData& aOther) const
+{
+  if (!mURIResolved || !aOther.mURIResolved) {
+    return false;
+  }
+
+  return URIEquals(aOther);
+}
+
+bool
 css::URLValueData::URIEquals(const URLValueData& aOther) const
 {
   MOZ_ASSERT(mURIResolved && aOther.mURIResolved,
              "How do you know the URIs aren't null?");
   bool eq;
   // Cast away const so we can call nsIPrincipal::Equals.
   auto& self = *const_cast<URLValueData*>(this);
   auto& other = const_cast<URLValueData&>(aOther);
--- a/layout/style/nsCSSValue.h
+++ b/layout/style/nsCSSValue.h
@@ -112,16 +112,20 @@ struct URLValueData
   bool operator==(const URLValueData& aOther) const;
 
   // URIEquals only compares URIs and principals (unlike operator==, which
   // also compares the original strings).  URIEquals also assumes that the
   // mURI member of both URL objects is non-null.  Do NOT call this method
   // unless you're sure this is the case.
   bool URIEquals(const URLValueData& aOther) const;
 
+  // Pretty much like URIEquals, but allows comparing unresolved URIs (returning
+  // false in that case).
+  bool MaybeUnresolvedURIEquals(const URLValueData& aOther) const;
+
   nsIURI* GetURI() const;
 
   size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
   bool GetLocalURLFlag() const { return mLocalURLFlag; }
 
 private:
   // If mURIResolved is false, mURI stores the base URI.
--- a/layout/style/nsHTMLStyleSheet.cpp
+++ b/layout/style/nsHTMLStyleSheet.cpp
@@ -28,16 +28,17 @@
 #include "nsRuleWalker.h"
 #include "nsRuleData.h"
 #include "nsError.h"
 #include "nsRuleProcessorData.h"
 #include "nsCSSRuleProcessor.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/Element.h"
 #include "nsHashKeys.h"
+#include "mozilla/OperatorNewExtensions.h"
 #include "mozilla/RestyleManagerHandle.h"
 #include "mozilla/RestyleManagerHandleInlines.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 NS_IMPL_ISUPPORTS(nsHTMLStyleSheet::HTMLColorRule, nsIStyleRule)
 
@@ -262,17 +263,17 @@ LangRuleTable_MatchEntry(const PLDHashEn
   return entry->mRule->mLang == *lang;
 }
 
 static void
 LangRuleTable_InitEntry(PLDHashEntryHdr *hdr, const void *key)
 {
   const nsString *lang = static_cast<const nsString*>(key);
 
-  LangRuleTableEntry *entry = new (hdr) LangRuleTableEntry();
+  LangRuleTableEntry *entry = new (KnownNotNull, hdr) LangRuleTableEntry();
 
   // Create the unique rule for this language
   entry->mRule = new nsHTMLStyleSheet::LangRule(*lang);
 }
 
 static const PLDHashTableOps LangRuleTable_Ops = {
   LangRuleTable_HashKey,
   LangRuleTable_MatchEntry,
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -13,16 +13,17 @@
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Function.h"
 #include "mozilla/dom/AnimationEffectReadOnlyBinding.h" // for PlaybackDirection
 #include "mozilla/Likely.h"
 #include "mozilla/LookAndFeel.h"
+#include "mozilla/OperatorNewExtensions.h"
 #include "mozilla/unused.h"
 
 #include "mozilla/css/Declaration.h"
 
 #include "nsAlgorithm.h" // for clamped()
 #include "nsRuleNode.h"
 #include "nscore.h"
 #include "nsIWidget.h"
@@ -1516,17 +1517,17 @@ SetFactor(const nsCSSValue& aValue, floa
   default:
     break;
   }
 
   NS_NOTREACHED("SetFactor: inappropriate unit");
 }
 
 void*
-nsRuleNode::operator new(size_t sz, nsPresContext* aPresContext) CPP_THROW_NEW
+nsRuleNode::operator new(size_t sz, nsPresContext* aPresContext)
 {
   // Check the recycle list first.
   return aPresContext->PresShell()->AllocateByObjectID(eArenaObjectID_nsRuleNode, sz);
 }
 
 // Overridden to prevent the global delete from being called, since the memory
 // came out of an nsIArena instead of the global delete operator's heap.
 void
@@ -2231,17 +2232,17 @@ struct AutoCSSValueArray {
   AutoCSSValueArray(void* aStorage, size_t aCount) {
     MOZ_ASSERT(size_t(aStorage) % NS_ALIGNMENT_OF(nsCSSValue) == 0,
                "bad alignment from alloca");
     mCount = aCount;
     // Don't use placement new[], since it might store extra data
     // for the count (on Windows!).
     mArray = static_cast<nsCSSValue*>(aStorage);
     for (size_t i = 0; i < mCount; ++i) {
-      new (mArray + i) nsCSSValue();
+      new (KnownNotNull, mArray + i) nsCSSValue();
     }
   }
 
   ~AutoCSSValueArray() {
     for (size_t i = 0; i < mCount; ++i) {
       mArray[i].~nsCSSValue();
     }
   }
--- a/layout/style/nsRuleNode.h
+++ b/layout/style/nsRuleNode.h
@@ -31,17 +31,17 @@ struct nsCSSValuePairList;
 struct nsRuleData;
 
 struct nsInheritedStyleData
 {
   mozilla::RangedArray<void*,
                        nsStyleStructID_Inherited_Start,
                        nsStyleStructID_Inherited_Count> mStyleStructs;
 
-  void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW {
+  void* operator new(size_t sz, nsPresContext* aContext) {
     return aContext->PresShell()->
       AllocateByObjectID(mozilla::eArenaObjectID_nsInheritedStyleData, sz);
   }
 
   void DestroyStructs(uint64_t aBits, nsPresContext* aContext) {
 #define STYLE_STRUCT_INHERITED(name, checkdata_cb) \
     void *name##Data = mStyleStructs[eStyleStruct_##name]; \
     if (name##Data && !(aBits & NS_STYLE_INHERIT_BIT(name))) \
@@ -79,17 +79,17 @@ struct nsResetStyleData
   {
     for (nsStyleStructID i = nsStyleStructID_Reset_Start;
          i < nsStyleStructID_Reset_Start + nsStyleStructID_Reset_Count;
          i = nsStyleStructID(i + 1)) {
       mStyleStructs[i] = nullptr;
     }
   }
 
-  void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW {
+  void* operator new(size_t sz, nsPresContext* aContext) {
     return aContext->PresShell()->
       AllocateByObjectID(mozilla::eArenaObjectID_nsResetStyleData, sz);
   }
 
   void Destroy(uint64_t aBits, nsPresContext* aContext) {
 #define STYLE_STRUCT_RESET(name, checkdata_cb) \
     void *name##Data = mStyleStructs[eStyleStruct_##name]; \
     if (name##Data && !(aBits & NS_STYLE_INHERIT_BIT(name))) \
@@ -114,17 +114,17 @@ struct nsConditionalResetStyleData
 
   struct Entry
   {
     Entry(const mozilla::RuleNodeCacheConditions& aConditions,
           void* aStyleStruct,
           Entry* aNext)
       : mConditions(aConditions), mStyleStruct(aStyleStruct), mNext(aNext) {}
 
-    void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW {
+    void* operator new(size_t sz, nsPresContext* aContext) {
       return aContext->PresShell()->AllocateByObjectID(
           mozilla::eArenaObjectID_nsConditionalResetStyleDataEntry, sz);
     }
 
     const mozilla::RuleNodeCacheConditions mConditions;
     void* const mStyleStruct;
     Entry* const mNext;
   };
@@ -143,17 +143,17 @@ struct nsConditionalResetStyleData
     for (nsStyleStructID i = nsStyleStructID_Reset_Start;
          i < nsStyleStructID_Reset_Start + nsStyleStructID_Reset_Count;
          i = nsStyleStructID(i + 1)) {
       mEntries[i] = nullptr;
     }
     mConditionalBits = 0;
   }
 
-  void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW {
+  void* operator new(size_t sz, nsPresContext* aContext) {
     return aContext->PresShell()->AllocateByObjectID(
         mozilla::eArenaObjectID_nsConditionalResetStyleData, sz);
   }
 
   void* GetStyleData(nsStyleStructID aSID) const {
     if (mConditionalBits & GetBitForSID(aSID)) {
       return nullptr;
     }
@@ -545,17 +545,17 @@ private:
   // and rule nodes hold strong references to their parent.
   //
   // When the refcount drops to zero, we don't necessarily free the node.
   // Instead, we notify the style set, which performs periodic sweeps.
   uint32_t mRefCnt;
 
 public:
   // Infallible overloaded new operator that allocates from a presShell arena.
-  void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW;
+  void* operator new(size_t sz, nsPresContext* aContext);
   void Destroy();
 
   // Implemented in nsStyleSet.h, since it needs to know about nsStyleSet.
   inline void AddRef();
 
   // Implemented in nsStyleSet.h, since it needs to know about nsStyleSet.
   inline void Release();
 
--- a/layout/style/nsStyleContext.cpp
+++ b/layout/style/nsStyleContext.cpp
@@ -1333,17 +1333,17 @@ void nsStyleContext::List(FILE* out, int
     }
   }
 }
 #endif
 
 // Overloaded new operator. Initializes the memory to 0 and relies on an arena
 // (which comes from the presShell) to perform the allocation.
 void*
-nsStyleContext::operator new(size_t sz, nsPresContext* aPresContext) CPP_THROW_NEW
+nsStyleContext::operator new(size_t sz, nsPresContext* aPresContext)
 {
   // Check the recycle list first.
   return aPresContext->PresShell()->
     AllocateByObjectID(eArenaObjectID_nsStyleContext, sz);
 }
 
 // Overridden to prevent the global delete from being called, since the memory
 // came out of an nsIArena instead of the global delete operator's heap.
--- a/layout/style/nsStyleContext.h
+++ b/layout/style/nsStyleContext.h
@@ -76,17 +76,17 @@ public:
   // nsRuleNode.
   nsStyleContext(nsStyleContext* aParent,
                  nsPresContext* aPresContext,
                  nsIAtom* aPseudoTag,
                  mozilla::CSSPseudoElementType aPseudoType,
                  already_AddRefed<ServoComputedValues> aComputedValues,
                  bool aSkipParentDisplayBasedStyleFixup);
 
-  void* operator new(size_t sz, nsPresContext* aPresContext) CPP_THROW_NEW;
+  void* operator new(size_t sz, nsPresContext* aPresContext);
   void Destroy();
 
   // These two methods are for use by ArenaRefPtr.
   static mozilla::ArenaObjectID ArenaObjectID()
   {
     return mozilla::eArenaObjectID_nsStyleContext;
   }
   nsIPresShell* Arena();
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -55,20 +55,20 @@ EqualURIs(nsIURI *aURI1, nsIURI *aURI2)
   bool eq;
   return aURI1 == aURI2 ||    // handle null==null, and optimize
          (aURI1 && aURI2 &&
           NS_SUCCEEDED(aURI1->Equals(aURI2, &eq)) && // not equal on fail
           eq);
 }
 
 static bool
-EqualURIs(mozilla::css::URLValue *aURI1, mozilla::css::URLValue *aURI2)
+MaybeUnresolvedURIEquals(css::URLValue *aURI1, css::URLValue *aURI2)
 {
   return aURI1 == aURI2 ||    // handle null==null, and optimize
-         (aURI1 && aURI2 && aURI1->URIEquals(*aURI2));
+         (aURI1 && aURI2 && aURI1->MaybeUnresolvedURIEquals(*aURI2));
 }
 
 static
 bool EqualURIs(const FragmentOrURL* aURI1, const FragmentOrURL* aURI2)
 {
   return aURI1 == aURI2 ||    // handle null==null, and optimize
          (aURI1 && aURI2 && *aURI1 == *aURI2);
 }
@@ -3100,17 +3100,17 @@ nsStyleDisplay::nsStyleDisplay(const nsS
   mPerspectiveOrigin[1] = aSource.mPerspectiveOrigin[1];
 }
 
 nsChangeHint
 nsStyleDisplay::CalcDifference(const nsStyleDisplay& aNewData) const
 {
   nsChangeHint hint = nsChangeHint(0);
 
-  if (!EqualURIs(mBinding, aNewData.mBinding)
+  if (!MaybeUnresolvedURIEquals(mBinding, aNewData.mBinding)
       || mPosition != aNewData.mPosition
       || mDisplay != aNewData.mDisplay
       || mContain != aNewData.mContain
       || (mFloat == StyleFloat::None_) != (aNewData.mFloat == StyleFloat::None_)
       || mOverflowX != aNewData.mOverflowX
       || mOverflowY != aNewData.mOverflowY
       || mScrollBehavior != aNewData.mScrollBehavior
       || mScrollSnapTypeX != aNewData.mScrollSnapTypeX
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -175,18 +175,18 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   /**
    * Return aSize divided by the current text zoom factor (in aPresContext).
    * aSize is allowed to be negative, but the caller is expected to deal with
    * negative results.  The result is clamped to nscoord_MIN .. nscoord_MAX.
    */
   static nscoord UnZoomText(nsPresContext* aPresContext, nscoord aSize);
   static already_AddRefed<nsIAtom> GetLanguage(StyleStructContext aPresContext);
 
-  void* operator new(size_t sz, nsStyleFont* aSelf) CPP_THROW_NEW { return aSelf; }
-  void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW {
+  void* operator new(size_t sz, nsStyleFont* aSelf) { return aSelf; }
+  void* operator new(size_t sz, nsPresContext* aContext) {
     return aContext->PresShell()->
       AllocateByObjectID(mozilla::eArenaObjectID_nsStyleFont, sz);
   }
   void Destroy(nsPresContext* aContext);
 
   void EnableZoom(nsPresContext* aContext, bool aEnable);
 
   nsFont  mFont;        // [inherited]
@@ -449,18 +449,18 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
     return nsChangeHint_RepaintFrame;
   }
   static nsChangeHint DifferenceAlwaysHandledForDescendants() {
     // CalcDifference never returns the reflow hints that are sometimes
     // handled for descendants at all.
     return nsChangeHint(0);
   }
 
-  void* operator new(size_t sz, nsStyleColor* aSelf) CPP_THROW_NEW { return aSelf; }
-  void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW {
+  void* operator new(size_t sz, nsStyleColor* aSelf) { return aSelf; }
+  void* operator new(size_t sz, nsPresContext* aContext) {
     return aContext->PresShell()->
       AllocateByObjectID(mozilla::eArenaObjectID_nsStyleColor, sz);
   }
   void Destroy(nsPresContext* aContext) {
     this->~nsStyleColor();
     aContext->PresShell()->
       FreeByObjectID(mozilla::eArenaObjectID_nsStyleColor, this);
   }
@@ -822,18 +822,18 @@ struct nsStyleImageLayers {
     for (uint32_t var_ = (start_) + 1; var_-- != (uint32_