merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 21 Jul 2016 16:24:36 +0200
changeset 331151 6b180266ac16e3226be33319ff710ddfa85f5836
parent 330956 e28e856b987380f55d699092f11f6997378f79a6 (current diff)
parent 331150 34fab997a0a18bc72da09d0811ff98357fe0eb5f (diff)
child 331152 10501352b0eef4ecf19dd4d5b1d307ebb212cf5e
child 331159 8ad7dc5cba0ca3c18cdc32beaa0b0808488cabd3
child 331207 f5154aaeaec4546dd04c66ac61a0d0ee69f4dfdf
child 331238 ab54bfc55266aaadc00567341bf494e907b0e4d7
push id9858
push userjlund@mozilla.com
push dateMon, 01 Aug 2016 14:37:10 +0000
treeherdermozilla-aurora@203106ef6cb6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone50.0a1
merge mozilla-inbound to mozilla-central a=merge
browser/installer/package-manifest.in
devtools/server/actors/script.js
dom/base/nsDocument.cpp
dom/html/HTMLInputElement.cpp
dom/html/nsHTMLContentSink.cpp
dom/media/mediasource/MediaSource.cpp
gfx/thebes/gfxPlatform.cpp
gfx/thebes/gfxWindowsPlatform.cpp
layout/generic/nsBlockReflowState.cpp
layout/generic/nsBlockReflowState.h
layout/generic/nsHTMLReflowMetrics.cpp
layout/generic/nsHTMLReflowMetrics.h
layout/generic/nsHTMLReflowState.cpp
layout/generic/nsHTMLReflowState.h
layout/reftests/scrolling/fractional-scroll-area-invalidation.html
layout/reftests/scrolling/fractional-scroll-area.html
media/libcubeb/src/cubeb_audiounit.c
testing/web-platform/meta/XMLHttpRequest/setrequestheader-header-allowed.htm.ini
toolkit/components/extensions/ExtensionUtils.jsm
toolkit/components/telemetry/Histograms.json
toolkit/xre/nsAppRunner.cpp
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 911216 - followup from the backout to fix bustage
+Bug 911216 - clobber needed
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -2756,46 +2756,46 @@ KeyBinding::ToPlatformFormat(nsAString& 
     stringBundleService->CreateBundle(
       "chrome://global-platform/locale/platformKeys.properties",
       getter_AddRefs(keyStringBundle));
 
   if (!keyStringBundle)
     return;
 
   nsAutoString separator;
-  keyStringBundle->GetStringFromName(MOZ_UTF16("MODIFIER_SEPARATOR"),
+  keyStringBundle->GetStringFromName(u"MODIFIER_SEPARATOR",
                                      getter_Copies(separator));
 
   nsAutoString modifierName;
   if (mModifierMask & kControl) {
-    keyStringBundle->GetStringFromName(MOZ_UTF16("VK_CONTROL"),
+    keyStringBundle->GetStringFromName(u"VK_CONTROL",
                                        getter_Copies(modifierName));
 
     aValue.Append(modifierName);
     aValue.Append(separator);
   }
 
   if (mModifierMask & kAlt) {
-    keyStringBundle->GetStringFromName(MOZ_UTF16("VK_ALT"),
+    keyStringBundle->GetStringFromName(u"VK_ALT",
                                        getter_Copies(modifierName));
 
     aValue.Append(modifierName);
     aValue.Append(separator);
   }
 
   if (mModifierMask & kShift) {
-    keyStringBundle->GetStringFromName(MOZ_UTF16("VK_SHIFT"),
+    keyStringBundle->GetStringFromName(u"VK_SHIFT",
                                        getter_Copies(modifierName));
 
     aValue.Append(modifierName);
     aValue.Append(separator);
   }
 
   if (mModifierMask & kMeta) {
-    keyStringBundle->GetStringFromName(MOZ_UTF16("VK_META"),
+    keyStringBundle->GetStringFromName(u"VK_META",
                                        getter_Copies(modifierName));
 
     aValue.Append(modifierName);
     aValue.Append(separator);
   }
 
   aValue.Append(mKey);
 }
--- a/accessible/generic/ApplicationAccessible.cpp
+++ b/accessible/generic/ApplicationAccessible.cpp
@@ -49,17 +49,17 @@ ApplicationAccessible::Name(nsString& aN
 
   nsCOMPtr<nsIStringBundle> bundle;
   nsresult rv = bundleService->CreateBundle("chrome://branding/locale/brand.properties",
                                             getter_AddRefs(bundle));
   if (NS_FAILED(rv))
     return eNameOK;
 
   nsXPIDLString appName;
-  rv = bundle->GetStringFromName(MOZ_UTF16("brandShortName"),
+  rv = bundle->GetStringFromName(u"brandShortName",
                                  getter_Copies(appName));
   if (NS_FAILED(rv) || appName.IsEmpty()) {
     NS_WARNING("brandShortName not found, using default app name");
     appName.AssignLiteral("Gecko based application");
   }
 
   aName.Assign(appName);
   return eNameOK;
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -1125,20 +1125,36 @@ DocAccessible::CharacterDataChanged(nsID
 
 void
 DocAccessible::ContentInserted(nsIDocument* aDocument, nsIContent* aContainer,
                                nsIContent* aChild, int32_t /* unused */)
 {
 }
 
 void
-DocAccessible::ContentRemoved(nsIDocument* aDocument, nsIContent* aContainer,
-                              nsIContent* aChild, int32_t /* unused */,
-                              nsIContent* aPreviousSibling)
+DocAccessible::ContentRemoved(nsIDocument* aDocument,
+                              nsIContent* aContainerNode,
+                              nsIContent* aChildNode, int32_t /* unused */,
+                              nsIContent* aPreviousSiblingNode)
 {
+#ifdef A11Y_LOG
+  if (logging::IsEnabled(logging::eTree)) {
+    logging::MsgBegin("TREE", "DOM content removed; doc: %p", this);
+    logging::Node("container node", aContainerNode);
+    logging::Node("content node", aChildNode);
+    logging::MsgEnd();
+  }
+#endif
+  // This one and content removal notification from layout may result in
+  // double processing of same subtrees. If it pops up in profiling, then
+  // consider reusing a document node cache to reject these notifications early.
+  Accessible* container = GetAccessibleOrContainer(aContainerNode);
+  if (container) {
+    UpdateTreeOnRemoval(container, aChildNode);
+  }
 }
 
 void
 DocAccessible::ParentChainChanged(nsIContent* aContent)
 {
 }
 
 
--- a/accessible/tests/mochitest/treeupdate/a11y.ini
+++ b/accessible/tests/mochitest/treeupdate/a11y.ini
@@ -9,16 +9,17 @@ support-files =
 [test_bug852150.xhtml]
 [test_bug883708.xhtml]
 [test_bug884251.xhtml]
 [test_bug895082.html]
 [test_bug1040735.html]
 [test_bug1100602.html]
 [test_bug1175913.html]
 [test_bug1189277.html]
+[test_bug1276857.html]
 [test_canvas.html]
 [test_colorpicker.xul]
 [test_contextmenu.xul]
 [test_cssoverflow.html]
 [test_deck.xul]
 [test_doc.html]
 [test_gencontent.html]
 [test_general.html]
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_bug1276857.html
@@ -0,0 +1,143 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>DOM mutations test</title>
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="../role.js"></script>
+  <script type="application/javascript"
+          src="../events.js"></script>
+
+  <script type="application/javascript">
+    function runTest()
+    {
+      // children change will recreate the table
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, getNode('c1'))
+      ];
+
+      this.invoke = function runTest_invoke() {
+        var tree = {
+          SECTION: [ // c1
+            { TEXT_LEAF: [] }, // Some text
+            { TEXT_CONTAINER: [
+              { TEXT_LEAF: [] } // something with ..
+            ] },
+            { TEXT_LEAF: [] } // More text
+          ]
+        };
+        testAccessibleTree('c1', tree);
+
+        getNode('c1_t').querySelector('span').remove();
+      };
+
+      this.finalCheck = function runTest_finalCheck() {
+        var tree = {
+          SECTION: [ // c1
+            { TEXT_LEAF: [] }, // Some text
+            { TEXT_LEAF: [] } // More text
+          ]
+        };
+        testAccessibleTree('c1', tree);
+      };
+
+      this.getID = function runTest_getID()
+      {
+        return 'child DOM node is removed before the layout notifies the a11y about parent removal/show';
+      };
+    }
+
+    function runShadowTest()
+    {
+      // children change will recreate the table
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, 'c2')
+      ];
+
+      this.invoke = function runShadowTest_invoke() {
+        var tree = {
+          SECTION: [ // c2
+            { TEXT_LEAF: [] }, // Some text
+            { TEXT_CONTAINER: [
+              { TEXT_LEAF: [] } // something with ..
+            ] },
+            { TEXT_LEAF: [] } // More text
+          ]
+        };
+        testAccessibleTree('c2', tree);
+
+        gShadowRoot.firstElementChild.querySelector('span').remove();
+      };
+
+      this.finalCheck = function runShadowTest_finalCheck() {
+        var tree = {
+          SECTION: [ // c2
+            { TEXT_LEAF: [] }, // Some text
+            { TEXT_LEAF: [] } // More text
+          ]
+        };
+        testAccessibleTree('c2', tree);
+      };
+
+      this.getID = function runShadowTest_getID() {
+        return 'child DOM node is removed before the layout notifies the a11y about parent removal/show in shadow DOM';
+      };
+    }
+
+    //enableLogging("tree");
+    //gA11yEventDumpToConsole = true;
+
+    var gQueue = null;
+    function doTest()
+    {
+      gQueue = new eventQueue();
+      gQueue.push(new runTest());
+      gQueue.push(new runShadowTest());
+      gQueue.invoke(); // will call SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  </script>
+
+</head>
+
+<body>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <div id="c1">
+    <div id="c1_t" style="display: table" role="presentation">
+    Some text
+    <span style="display: table-cell">something with accessibles goes here</span>
+    More text
+    </div>
+  </div>
+
+  <template id="tmpl">
+    <div style="display: table" role="presentation">
+    Some text
+    <span style="display: table-cell">something with accessibles goes here</span>
+    More text
+    </div>
+  </template>
+
+  <div id="c2"><div id="c2_c" role="presentation"></div></div>
+
+  <script>
+    var gShadowRoot = document.getElementById('c2_c').createShadowRoot();
+    var tmpl = document.getElementById('tmpl');
+    gShadowRoot.appendChild(document.importNode(tmpl.content, true));
+  </script>
+</body>
+</html>
--- a/addon-sdk/source/lib/sdk/event/chrome.js
+++ b/addon-sdk/source/lib/sdk/event/chrome.js
@@ -52,14 +52,14 @@ function observe(topic) {
   // observerChannel (since third argument is `true`). There for if it
   // will be GC-ed with all it's event listeners once no other references
   // will be held.
   addObserver(observerChannel, topic, true);
 
   // We need to remove any observer added once the add-on is unloaded;
   // otherwise we'll get a "dead object" exception.
   // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1001833
-  unload(() => removeObserver(observerChannel, topic));
+  unload(() => removeObserver(observerChannel, topic), { weak: true });
 
   return observerChannel;
 }
 
 exports.observe = observe;
--- a/addon-sdk/source/lib/sdk/system/unload.js
+++ b/addon-sdk/source/lib/sdk/system/unload.js
@@ -5,26 +5,43 @@
 // Parts of this module were taken from narwhal:
 //
 // http://narwhaljs.org
 
 module.metadata = {
   "stability": "experimental"
 };
 
+const { Cu } = require('chrome');
 const { on, off } = require('./events');
 const unloadSubject = require('@loader/unload');
 
 const observers = [];
 const unloaders = [];
 
-var when = exports.when = function when(observer) {
-  if (observers.indexOf(observer) != -1)
-    return;
-  observers.unshift(observer);
+function WeakObserver(inner) {
+  this._inner = Cu.getWeakReference(inner);
+}
+
+Object.defineProperty(WeakObserver.prototype, 'value', {
+  get: function() { this._inner.get() }
+});
+
+var when = exports.when = function when(observer, opts) {
+  opts = opts || {};
+  for (var i = 0; i < observers.length; ++i) {
+    if (observers[i] === observer || observers[i].value === observer) {
+      return;
+    }
+  }
+  if (opts.weak) {
+    observers.unshift(new WeakObserver(observer));
+  } else {
+    observers.unshift(observer);
+  }
 };
 
 var ensure = exports.ensure = function ensure(obj, destructorName) {
   if (!destructorName)
     destructorName = "unload";
   if (!(destructorName in obj))
     throw new Error("object has no '" + destructorName + "' property");
 
@@ -50,17 +67,22 @@ var ensure = exports.ensure = function e
   unloaders.push(unloadWrapper);
 
   obj[destructorName] = unloadWrapper;
 };
 
 function unload(reason) {
   observers.forEach(function(observer) {
     try {
-      observer(reason);
+      if (observer instanceof WeakObserver) {
+        observer = observer.value;
+      }
+      if (typeof observer === 'function') {
+        observer(reason);
+      }
     }
     catch (error) {
       console.exception(error);
     }
   });
 }
 
 when(function(reason) {
--- a/addon-sdk/source/test/leak/jetpack-package.ini
+++ b/addon-sdk/source/test/leak/jetpack-package.ini
@@ -1,6 +1,7 @@
 [DEFAULT]
 support-files =
   leak-utils.js
 
 [test-leak-window-events.js]
 [test-leak-event-dom-closed-window.js]
+[test-leak-event-chrome.js]
--- a/addon-sdk/source/test/leak/leak-utils.js
+++ b/addon-sdk/source/test/leak/leak-utils.js
@@ -26,16 +26,17 @@ function gc() {
           resolve();
         }
       }
     }
 
     Cu.schedulePreciseGC(genGCCallback());
   });
 }
+exports.gc = gc;
 
 // Execute the given test function and verify that we did not leak windows
 // in the process.  The test function must return a promise or be a generator.
 // If the promise is resolved, or generator completes, with an sdk loader
 // object then it will be unloaded after the memory measurements.
 exports.asyncWindowLeakTest = function*(assert, asyncTestFunc) {
 
   // SelfSupportBackend periodically tries to open windows.  This can
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/leak/test-leak-event-chrome.js
@@ -0,0 +1,41 @@
+/* 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 { gc } = require("./leak-utils");
+const { Loader } = require("sdk/test/loader");
+const { Cu } = require("chrome");
+const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
+
+exports["test sdk/event/chrome does not leak when not referenced"] = function*(assert) {
+  let loader = Loader(module);
+  let { observe } = loader.require("sdk/event/chrome");
+  let { on } = loader.require("sdk/event/core");
+
+  let gotFooEvent = false;
+  on(observe("test-foo"), "data", function(evt) {
+    gotFooEvent = true;
+  });
+
+  let bar = observe("test-bar");
+  let barPromise = new Promise(resolve => {
+    on(bar, "data", function(evt) {
+      assert.ok(!gotFooEvent, "should not have gotten test-foo event");
+      resolve();
+    });
+  });
+
+  // This should clear the test-foo observer channel because we are not
+  // holding a reference to it above.
+  yield gc();
+
+  Services.obs.notifyObservers(null, "test-foo", null);
+  Services.obs.notifyObservers(null, "test-bar", null);
+
+  yield barPromise;
+
+  loader.unload();
+}
+
+require("sdk/test").run(exports);
--- a/browser/app/moz.build
+++ b/browser/app/moz.build
@@ -51,16 +51,21 @@ if CONFIG['MOZ_SANDBOX'] and CONFIG['OS_
         '/security/sandbox/chromium',
         '/security/sandbox/chromium-shim',
     ]
 
     USE_LIBS += [
         'sandbox_s',
     ]
 
+    DELAYLOAD_DLLS += [
+        'winmm.dll',
+        'user32.dll',
+    ]
+
 # Control the default heap size.
 # This is the heap returned by GetProcessHeap().
 # As we use the CRT heap, the default size is too large and wastes VM.
 #
 # The default heap size is 1MB on Win32.
 # The heap will grow if need be.
 #
 # Set it to 256k.  See bug 127069.
--- a/browser/app/nsBrowserApp.cpp
+++ b/browser/app/nsBrowserApp.cpp
@@ -317,16 +317,30 @@ sizeof(XPCOM_DLL) - 1))
 #endif
   }
 
   return rv;
 }
 
 int main(int argc, char* argv[], char* envp[])
 {
+  mozilla::TimeStamp start = mozilla::TimeStamp::Now();
+
+#ifdef HAS_DLL_BLOCKLIST
+  DllBlocklist_Initialize();
+
+#ifdef DEBUG
+  // In order to be effective against AppInit DLLs, the blocklist must be
+  // initialized before user32.dll is loaded into the process (bug 932100).
+  if (GetModuleHandleA("user32.dll")) {
+    fprintf(stderr, "DLL blocklist was unable to intercept AppInit DLLs.\n");
+  }
+#endif
+#endif
+
 #ifdef MOZ_BROWSER_CAN_BE_CONTENTPROC
   // We are launching as a content process, delegate to the appropriate
   // main
   if (argc > 1 && IsArg(argv[1], "contentproc")) {
 #if defined(XP_WIN) && defined(MOZ_SANDBOX)
     // We need to initialize the sandbox TargetServices before InitXPCOMGlue
     // because we might need the sandbox broker to give access to some files.
     if (IsSandboxedProcess() && !sandboxing::GetInitializedTargetServices()) {
@@ -344,32 +358,19 @@ int main(int argc, char* argv[], char* e
 
     // InitXPCOMGlue calls NS_LogInit, so we need to balance it here.
     NS_LogTerm();
 
     return result;
   }
 #endif
 
-  mozilla::TimeStamp start = mozilla::TimeStamp::Now();
 
   nsIFile *xreDirectory;
 
-#ifdef HAS_DLL_BLOCKLIST
-  DllBlocklist_Initialize();
-
-#ifdef DEBUG
-  // In order to be effective against AppInit DLLs, the blocklist must be
-  // initialized before user32.dll is loaded into the process (bug 932100).
-  if (GetModuleHandleA("user32.dll")) {
-    fprintf(stderr, "DLL blocklist was unable to intercept AppInit DLLs.\n");
-  }
-#endif
-#endif
-
   nsresult rv = InitXPCOMGlue(argv[0], &xreDirectory);
   if (NS_FAILED(rv)) {
     return 255;
   }
 
   XRE_StartupTimelineRecord(mozilla::StartupTimeline::START, start);
 
 #ifdef MOZ_BROWSER_CAN_BE_CONTENTPROC
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -940,17 +940,17 @@ pref("dom.ipc.plugins.sandbox-level.flas
 // This controls the strength of the Windows content process sandbox for testing
 // purposes. This will require a restart.
 // On windows these levels are:
 // See - security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
 // SetSecurityLevelForContentProcess() for what the different settings mean.
 #if defined(NIGHTLY_BUILD)
 pref("security.sandbox.content.level", 2);
 #else
-pref("security.sandbox.content.level", 0);
+pref("security.sandbox.content.level", 1);
 #endif
 
 #if defined(MOZ_STACKWALKING)
 // This controls the depth of stack trace that is logged when Windows sandbox
 // logging is turned on.  This is only currently available for the content
 // process because the only other sandbox (for GMP) has too strict a policy to
 // allow stack tracing.  This does not require a restart to take effect.
 pref("security.sandbox.windows.log.stackTraceDepth", 0);
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -6119,28 +6119,44 @@
           // the tabbar (half a tab)
           let endScreenY = bo.screenY + 1.5 * bo.height;
           if (eY < endScreenY && eY > window.screenY)
             return;
         }
 
         // screen.availLeft et. al. only check the screen that this window is on,
         // but we want to look at the screen the tab is being dropped onto.
-        var sX = {}, sY = {}, sWidth = {}, sHeight = {};
-        Cc["@mozilla.org/gfx/screenmanager;1"]
-          .getService(Ci.nsIScreenManager)
-          .screenForRect(eX, eY, 1, 1)
-          .GetAvailRect(sX, sY, sWidth, sHeight);
+        var screen = Cc["@mozilla.org/gfx/screenmanager;1"]
+                       .getService(Ci.nsIScreenManager)
+                       .screenForRect(eX, eY, 1, 1);
+        var fullX = {}, fullY = {}, fullWidth = {}, fullHeight = {};
+        var availX = {}, availY = {}, availWidth = {}, availHeight = {};
+        // get full screen rect and available rect, both in desktop pix
+        screen.GetRectDisplayPix(fullX, fullY, fullWidth, fullHeight);
+        screen.GetAvailRectDisplayPix(availX, availY, availWidth, availHeight);
+
+        // scale factor to convert desktop pixels to CSS px
+        var scaleFactor =
+          screen.contentsScaleFactor / screen.defaultCSSScaleFactor;
+        // synchronize CSS-px top-left coordinates with the screen's desktop-px
+        // coordinates, to ensure uniqueness across multiple screens
+        // (compare the equivalent adjustments in nsGlobalWindow::GetScreenXY()
+        // and related methods)
+        availX.value = (availX.value - fullX.value) * scaleFactor + fullX.value;
+        availY.value = (availY.value - fullY.value) * scaleFactor + fullY.value;
+        availWidth.value *= scaleFactor;
+        availHeight.value *= scaleFactor;
+
         // ensure new window entirely within screen
-        var winWidth = Math.min(window.outerWidth, sWidth.value);
-        var winHeight = Math.min(window.outerHeight, sHeight.value);
-        var left = Math.min(Math.max(eX - draggedTab._dragData.offsetX, sX.value),
-                            sX.value + sWidth.value - winWidth);
-        var top = Math.min(Math.max(eY - draggedTab._dragData.offsetY, sY.value),
-                           sY.value + sHeight.value - winHeight);
+        var winWidth = Math.min(window.outerWidth, availWidth.value);
+        var winHeight = Math.min(window.outerHeight, availHeight.value);
+        var left = Math.min(Math.max(eX - draggedTab._dragData.offsetX, availX.value),
+                            availX.value + availWidth.value - winWidth);
+        var top = Math.min(Math.max(eY - draggedTab._dragData.offsetY, availY.value),
+                           availY.value + availHeight.value - winHeight);
 
         delete draggedTab._dragData;
 
         if (this.tabbrowser.tabs.length == 1) {
           // resize _before_ move to ensure the window fits the new screen.  if
           // the window is too large for its screen, the window manager may do
           // automatic repositioning.
           window.resizeTo(winWidth, winHeight);
--- a/browser/components/contextualidentity/test/browser/browser_forgetaboutsite.js
+++ b/browser/components/contextualidentity/test/browser/browser_forgetaboutsite.js
@@ -238,25 +238,25 @@ function* test_storage_cleared() {
   for (let userContextId of Object.keys(USER_CONTEXTS)) {
     // Load the page in 3 different contexts and set the local storage
     // which should only be visible in that context.
     let value = USER_CONTEXTS[userContextId];
 
     // Open our tab in the given user context.
     let tabInfo = yield* openTabInUserContext(TEST_URL+ "file_set_storages.html?" + value, userContextId);
 
-    // Check that the local storage has been set correctly.
-    let win = tabInfo.browser.contentWindow;
-    Assert.equal(win.localStorage.getItem("userContext"), USER_CONTEXTS[userContextId], "Check the local storage value");
+    // Check that the storages has been set correctly.
+    yield ContentTask.spawn(tabInfo.browser, { userContext: USER_CONTEXTS[userContextId] }, function* (arg) {
+      // Check that the local storage has been set correctly.
+      Assert.equal(content.localStorage.getItem("userContext"), arg.userContext, "Check the local storage value");
 
-    // Check that the session storage has been set correctly.
-    Assert.equal(win.sessionStorage.getItem("userContext"), USER_CONTEXTS[userContextId], "Check the session storage value");
+      // Check that the session storage has been set correctly.
+      Assert.equal(content.sessionStorage.getItem("userContext"), arg.userContext, "Check the session storage value");
 
-    // Check that the indexedDB has been set correctly.
-    yield ContentTask.spawn(tabInfo.browser, { userContext: USER_CONTEXTS[userContextId] }, function* (arg) {
+      // Check that the indexedDB has been set correctly.
       let request = content.indexedDB.open("idb", 1);
 
       let db = yield new Promise(done => {
         request.onsuccess = event => {
           done(event.target.result);
         };
       });
 
@@ -280,26 +280,26 @@ function* test_storage_cleared() {
   // Forget the site.
   ForgetAboutSite.removeDataFromDomain(TEST_HOST);
 
   // Open the tab again without setting the localStorage and check that the
   // local storage has been cleared or not.
   for (let userContextId of Object.keys(USER_CONTEXTS)) {
     // Open our tab in the given user context without setting local storage.
     let tabInfo = yield* openTabInUserContext(TEST_URL+ "file_set_storages.html", userContextId);
-    let win = tabInfo.browser.contentWindow;
-
-    // Check that does the local storage be cleared or not.
-    Assert.ok(!win.localStorage.getItem("userContext"), "The local storage has been cleared");
 
-    // Check that does the session storage be cleared or not.
-    Assert.ok(!win.sessionStorage.getItem("userContext"), "The session storage has been cleared");
+    // Check that do storages be cleared or not.
+    yield ContentTask.spawn(tabInfo.browser, null, function* () {
+      // Check that does the local storage be cleared or not.
+      Assert.ok(!content.localStorage.getItem("userContext"), "The local storage has been cleared");
 
-    // Check that does the indexedDB be cleared or not.
-    yield ContentTask.spawn(tabInfo.browser, null, function* () {
+      // Check that does the session storage be cleared or not.
+      Assert.ok(!content.sessionStorage.getItem("userContext"), "The session storage has been cleared");
+
+      // Check that does the indexedDB be cleared or not.
       let request = content.indexedDB.open("idb", 1);
 
       let db = yield new Promise(done => {
         request.onsuccess = event => {
           done(event.target.result);
         };
       });
       try {
--- a/browser/components/shell/nsGNOMEShellService.cpp
+++ b/browser/components/shell/nsGNOMEShellService.cpp
@@ -279,17 +279,17 @@ nsGNOMEShellService::SetDefaultBrowser(b
       do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIStringBundle> brandBundle;
     rv = bundleService->CreateBundle(BRAND_PROPERTIES, getter_AddRefs(brandBundle));
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsString brandShortName;
-    brandBundle->GetStringFromName(MOZ_UTF16("brandShortName"),
+    brandBundle->GetStringFromName(u"brandShortName",
                                    getter_Copies(brandShortName));
 
     // use brandShortName as the application id.
     NS_ConvertUTF16toUTF8 id(brandShortName);
     nsCOMPtr<nsIGIOMimeApp> appInfo;
     rv = giovfs->CreateAppFromCommand(mAppPath,
                                       id,
                                       getter_AddRefs(appInfo));
@@ -397,17 +397,17 @@ nsGNOMEShellService::SetDesktopBackgroun
   nsString brandName;
   nsCID bundleCID = NS_STRINGBUNDLESERVICE_CID;
   nsCOMPtr<nsIStringBundleService> bundleService(do_GetService(bundleCID));
   if (bundleService) {
     nsCOMPtr<nsIStringBundle> brandBundle;
     rv = bundleService->CreateBundle(BRAND_PROPERTIES,
                                      getter_AddRefs(brandBundle));
     if (NS_SUCCEEDED(rv) && brandBundle) {
-      rv = brandBundle->GetStringFromName(MOZ_UTF16("brandShortName"),
+      rv = brandBundle->GetStringFromName(u"brandShortName",
                                           getter_Copies(brandName));
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
   // build the file name
   filePath.Append('/');
   filePath.Append(NS_ConvertUTF16toUTF8(brandName));
--- a/browser/components/shell/nsWindowsShellService.cpp
+++ b/browser/components/shell/nsWindowsShellService.cpp
@@ -989,17 +989,17 @@ nsWindowsShellService::SetDesktopBackgro
   nsCOMPtr<nsIStringBundle> shellBundle;
   rv = bundleService->CreateBundle(SHELLSERVICE_PROPERTIES,
                                    getter_AddRefs(shellBundle));
   NS_ENSURE_SUCCESS(rv, rv);
  
   // e.g. "Desktop Background.bmp"
   nsString fileLeafName;
   rv = shellBundle->GetStringFromName
-                      (MOZ_UTF16("desktopBackgroundLeafNameWin"),
+                      (u"desktopBackgroundLeafNameWin",
                        getter_Copies(fileLeafName));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // get the profile root directory
   nsCOMPtr<nsIFile> file;
   rv = NS_GetSpecialDirectory(NS_APP_APPLICATION_REGISTRY_DIR,
                               getter_AddRefs(file));
   NS_ENSURE_SUCCESS(rv, rv);
--- a/browser/installer/Makefile.in
+++ b/browser/installer/Makefile.in
@@ -116,16 +116,19 @@ LPROJ_ROOT := $(subst -,_,$(AB_CD))
 endif
 endif
 DEFINES += -DLPROJ_ROOT=$(LPROJ_ROOT)
 
 DEFINES += -DMOZ_ICU_VERSION=$(MOZ_ICU_VERSION)
 ifdef MOZ_SYSTEM_ICU
 DEFINES += -DMOZ_SYSTEM_ICU
 endif
+ifdef MOZ_ICU_DATA_ARCHIVE
+DEFINES += -DMOZ_ICU_DATA_ARCHIVE
+endif
 DEFINES += -DMOZ_ICU_DBG_SUFFIX=$(MOZ_ICU_DBG_SUFFIX)
 DEFINES += -DICU_DATA_FILE=$(ICU_DATA_FILE)
 ifdef CLANG_CXX
 DEFINES += -DCLANG_CXX
 endif
 ifdef CLANG_CL
 DEFINES += -DCLANG_CL
 endif
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -96,17 +96,17 @@
 @BINPATH@/@MSVC_C_RUNTIME_DLL@
 @BINPATH@/@MSVC_CXX_RUNTIME_DLL@
 #endif
 #if MOZ_PACKAGE_WIN_UCRT_DLLS
 @BINPATH@/api-ms-win-*.dll
 @BINPATH@/ucrtbase.dll
 #endif
 #endif
-#ifndef MOZ_SYSTEM_ICU
+#ifdef MOZ_ICU_DATA_ARCHIVE
 @RESPATH@/@ICU_DATA_FILE@
 #endif
 #ifdef MOZ_GTK3
 @BINPATH@/@DLL_PREFIX@mozgtk@DLL_SUFFIX@
 @BINPATH@/gtk2/@DLL_PREFIX@mozgtk@DLL_SUFFIX@
 #endif
 
 [browser]
--- a/build/autoconf/icu.m4
+++ b/build/autoconf/icu.m4
@@ -77,17 +77,20 @@ if test -n "$USE_ICU"; then
 
     # TODO: the l is actually endian-dependent
     # We could make this set as 'l' or 'b' for little or big, respectively,
     # but we'd need to check in a big-endian version of the file.
     ICU_DATA_FILE="icudt${version}l.dat"
 
     dnl We won't build ICU data as a separate file when building
     dnl JS standalone so that embedders don't have to deal with it.
-    if test -z "$JS_STANDALONE" -a -z "$MOZ_SYSTEM_ICU"; then
+    dnl We also don't do it on Windows because sometimes the file goes
+    dnl missing -- possibly due to overzealous antivirus software? --
+    dnl which prevents the browser from starting up :(
+    if test -z "$JS_STANDALONE" -a -z "$MOZ_SYSTEM_ICU" -a "$OS_TARGET" != WINNT; then
         MOZ_ICU_DATA_ARCHIVE=1
     else
         MOZ_ICU_DATA_ARCHIVE=
     fi
 fi
 
 AC_SUBST(MOZ_ICU_VERSION)
 AC_SUBST(ENABLE_INTL_API)
--- a/build/autoconf/sanitize.m4
+++ b/build/autoconf/sanitize.m4
@@ -18,18 +18,18 @@ if test -n "$MOZ_ASAN"; then
         MOZ_CLANG_RT_ASAN_LIB=clang_rt.asan_dynamic-i386.dll
         # We use MOZ_PATH_PROG in order to get a Windows style path.
         MOZ_PATH_PROG(MOZ_CLANG_RT_ASAN_LIB_PATH, $MOZ_CLANG_RT_ASAN_LIB)
         if test -z "$MOZ_CLANG_RT_ASAN_LIB_PATH"; then
             AC_MSG_ERROR([Couldn't find $MOZ_CLANG_RT_ASAN_LIB.  It should be available in the same location as clang-cl.])
         fi
         AC_SUBST(MOZ_CLANG_RT_ASAN_LIB_PATH)
     fi
-    CFLAGS="-fsanitize=address -Dxmalloc=myxmalloc -fPIC $CFLAGS"
-    CXXFLAGS="-fsanitize=address -Dxmalloc=myxmalloc -fPIC $CXXFLAGS"
+    CFLAGS="-fsanitize=address $CFLAGS"
+    CXXFLAGS="-fsanitize=address $CXXFLAGS"
     LDFLAGS="-fsanitize=address $LDFLAGS"
     AC_DEFINE(MOZ_ASAN)
     MOZ_PATH_PROG(LLVM_SYMBOLIZER, llvm-symbolizer)
 fi
 AC_SUBST(MOZ_ASAN)
 
 dnl ========================================================
 dnl = Use Memory Sanitizer
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -208,21 +208,17 @@ def get_compiler_info(compiler, language
     '''
     # Note: MSVC doesn't expose __STDC_VERSION__. It does expose __STDC__,
     # but only when given the -Za option, which disables compiler
     # extensions.
     # Note: We'd normally do a version check for clang, but versions of clang
     # in Xcode have a completely different versioning scheme despite exposing
     # the version with the same defines.
     # So instead, we make things such that the version is missing when the
-    # clang used is below the minimum supported version (currently clang 3.4).
-    # Normally, we'd use __has_feature, but there are unfortunately no C++11
-    # differences in clang 3.4. However, it supports the 2013-08-28 draft of
-    # the ISO WG21 SG10 feature test macro recommendations, and thus exposes
-    # new __cpp_* macros that older clang versions didn't.
+    # clang used is below the minimum supported version (currently clang 3.6).
     # We then only include the version information when the C++ compiler
     # matches the feature check, so that an unsupported version of clang would
     # have no version number.
     check = dedent('''\
         #if defined(_MSC_VER)
         #if defined(__clang__)
         %COMPILER clang-cl
         %VERSION _MSC_FULL_VER
@@ -533,17 +529,17 @@ def compiler(language, host_or_target, c
             raise FatalCheckError(
                 'Only GCC 4.8 or newer is supported (found version %s).'
                 % info.version)
 
         # If you want to bump the version check here search for
         # __cpp_static_assert above, and see the associated comment.
         if info.type == 'clang' and not info.version:
             raise FatalCheckError(
-                'Only clang/llvm 3.4 or newer is supported.')
+                'Only clang/llvm 3.6 or newer is supported.')
 
         if info.type == 'msvc':
             if info.version < '19.00.23918':
                 raise FatalCheckError(
                     'This version (%s) of the MSVC compiler is not '
                     'supported.\n'
                     'You must install Visual C++ 2015 Update 2 or newer in '
                     'order to build.\n'
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -1009,17 +1009,17 @@ nsScriptSecurityManager::CheckLoadURIFla
                              nsIProtocolHandler::URI_LOADABLE_BY_SUBSUMERS,
                              &hasSubsumersFlag);
     NS_ENSURE_SUCCESS(rv, rv);
     if (!hasFlags && !hasSubsumersFlag) {
         nsXPIDLString message;
         NS_ConvertASCIItoUTF16 ucsTargetScheme(targetScheme);
         const char16_t* formatStrings[] = { ucsTargetScheme.get() };
         rv = sStrBundle->
-            FormatStringFromName(MOZ_UTF16("ProtocolFlagError"),
+            FormatStringFromName(u"ProtocolFlagError",
                                  formatStrings,
                                  ArrayLength(formatStrings),
                                  getter_Copies(message));
         if (NS_SUCCEEDED(rv)) {
             nsCOMPtr<nsIConsoleService> console(
               do_GetService("@mozilla.org/consoleservice;1"));
             NS_ENSURE_TRUE(console, NS_ERROR_FAILURE);
 
--- a/devtools/server/actors/script.js
+++ b/devtools/server/actors/script.js
@@ -14,17 +14,16 @@ const { EnvironmentActor } = require("de
 const { FrameActor } = require("devtools/server/actors/frame");
 const { ObjectActor, createValueGrip, longStringGrip } = require("devtools/server/actors/object");
 const { SourceActor, getSourceURL } = require("devtools/server/actors/source");
 const { DebuggerServer } = require("devtools/server/main");
 const { ActorClassWithSpec } = require("devtools/shared/protocol");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const { assert, dumpn, update, fetch } = DevToolsUtils;
 const promise = require("promise");
-const PromiseDebugging = require("PromiseDebugging");
 const xpcInspector = require("xpcInspector");
 const { DevToolsWorker } = require("devtools/shared/worker/worker");
 const object = require("sdk/util/object");
 const { threadSpec } = require("devtools/shared/specs/script");
 
 const { defer, resolve, reject, all } = promise;
 
 loader.lazyGetter(this, "Debugger", () => {
--- a/devtools/shared/heapsnapshot/DeserializedNode.cpp
+++ b/devtools/shared/heapsnapshot/DeserializedNode.cpp
@@ -54,17 +54,17 @@ DeserializedStackFrame::getParentStackFr
 } // namespace mozilla
 
 namespace JS {
 namespace ubi {
 
 using mozilla::devtools::DeserializedEdge;
 
 const char16_t Concrete<DeserializedNode>::concreteTypeName[] =
-  MOZ_UTF16("mozilla::devtools::DeserializedNode");
+  u"mozilla::devtools::DeserializedNode";
 
 const char16_t*
 Concrete<DeserializedNode>::typeName() const
 {
   return get().typeName;
 }
 
 Node::Size
--- a/devtools/shared/heapsnapshot/HeapSnapshot.cpp
+++ b/devtools/shared/heapsnapshot/HeapSnapshot.cpp
@@ -730,17 +730,17 @@ PopulateCompartmentsWithGlobals(Compartm
 // Add the given set of globals as explicit roots in the given roots
 // list. Returns false on OOM failure.
 static bool
 AddGlobalsAsRoots(AutoObjectVector& globals, ubi::RootList& roots)
 {
   unsigned length = globals.length();
   for (unsigned i = 0; i < length; i++) {
     if (!roots.addRoot(ubi::Node(globals[i].get()),
-                       MOZ_UTF16("heap snapshot global")))
+                       u"heap snapshot global"))
     {
       return false;
     }
   }
   return true;
 }
 
 // Choose roots and limits for a traversal, given `boundaries`. Set `roots` to
--- a/devtools/shared/heapsnapshot/tests/gtest/DeserializedNodeUbiNodes.cpp
+++ b/devtools/shared/heapsnapshot/tests/gtest/DeserializedNodeUbiNodes.cpp
@@ -32,17 +32,17 @@ struct MockDeserializedNode : public Des
 size_t fakeMallocSizeOf(const void*) {
   EXPECT_TRUE(false);
   MOZ_ASSERT_UNREACHABLE("fakeMallocSizeOf should never be called because "
                          "DeserializedNodes report the deserialized size.");
   return 0;
 }
 
 DEF_TEST(DeserializedNodeUbiNodes, {
-    const char16_t* typeName = MOZ_UTF16("TestTypeName");
+    const char16_t* typeName = u"TestTypeName";
     const char* className = "MyObjectClassName";
     const char* filename = "my-cool-filename.js";
 
     NodeId id = uint64_t(1) << 33;
     uint64_t size = uint64_t(1) << 60;
     MockDeserializedNode mocked(id, typeName, size);
     mocked.coarseType = JS::ubi::CoarseType::Script;
     mocked.jsObjectClassName = className;
--- a/devtools/shared/heapsnapshot/tests/gtest/DeserializedStackFrameUbiStackFrames.cpp
+++ b/devtools/shared/heapsnapshot/tests/gtest/DeserializedStackFrameUbiStackFrames.cpp
@@ -19,18 +19,18 @@ struct MockDeserializedStackFrame : publ
 {
   MockDeserializedStackFrame() : DeserializedStackFrame() { }
 };
 
 DEF_TEST(DeserializedStackFrameUbiStackFrames, {
     StackFrameId id = uint64_t(1) << 42;
     uint32_t line = 1337;
     uint32_t column = 9; // 3 space tabs!?
-    const char16_t* source = MOZ_UTF16("my-javascript-file.js");
-    const char16_t* functionDisplayName = MOZ_UTF16("myFunctionName");
+    const char16_t* source = u"my-javascript-file.js";
+    const char16_t* functionDisplayName = u"myFunctionName";
 
     MockDeserializedStackFrame mocked;
     mocked.id = id;
     mocked.line = line;
     mocked.column = column;
     mocked.source = source;
     mocked.functionDisplayName = functionDisplayName;
 
--- a/devtools/shared/heapsnapshot/tests/gtest/DevTools.h
+++ b/devtools/shared/heapsnapshot/tests/gtest/DevTools.h
@@ -180,17 +180,17 @@ protected:
 
 public:
   static const char16_t concreteTypeName[];
   static void construct(void* storage, FakeNode* ptr) {
     new (storage) Concrete(ptr);
   }
 };
 
-const char16_t Concrete<FakeNode>::concreteTypeName[] = MOZ_UTF16("FakeNode");
+const char16_t Concrete<FakeNode>::concreteTypeName[] = u"FakeNode";
 
 } // namespace ubi
 } // namespace JS
 
 void AddEdge(FakeNode& node, FakeNode& referent, const char16_t* edgeName = nullptr) {
   char16_t* ownedEdgeName = nullptr;
   if (edgeName) {
     ownedEdgeName = NS_strdup(edgeName);
--- a/devtools/shared/heapsnapshot/tests/gtest/SerializesEdgeNames.cpp
+++ b/devtools/shared/heapsnapshot/tests/gtest/SerializesEdgeNames.cpp
@@ -11,18 +11,18 @@ using testing::Field;
 using testing::IsNull;
 using testing::Property;
 using testing::Return;
 
 DEF_TEST(SerializesEdgeNames, {
     FakeNode node;
     FakeNode referent;
 
-    const char16_t edgeName[] = MOZ_UTF16("edge name");
-    const char16_t emptyStr[] = MOZ_UTF16("");
+    const char16_t edgeName[] = u"edge name";
+    const char16_t emptyStr[] = u"";
 
     AddEdge(node, referent, edgeName);
     AddEdge(node, referent, emptyStr);
     AddEdge(node, referent, nullptr);
 
     ::testing::NiceMock<MockWriter> writer;
 
     // Should get the node with edges once.
--- a/devtools/shared/heapsnapshot/tests/gtest/SerializesTypeNames.cpp
+++ b/devtools/shared/heapsnapshot/tests/gtest/SerializesTypeNames.cpp
@@ -10,17 +10,17 @@
 using testing::Property;
 using testing::Return;
 
 DEF_TEST(SerializesTypeNames, {
     FakeNode node;
 
     ::testing::NiceMock<MockWriter> writer;
     EXPECT_CALL(writer, writeNode(Property(&JS::ubi::Node::typeName,
-                                           UTF16StrEq(MOZ_UTF16("FakeNode"))),
+                                           UTF16StrEq(u"FakeNode")),
                                   _))
       .Times(1)
       .WillOnce(Return(true));
 
     JS::AutoCheckCannotGC noGC(rt);
     ASSERT_TRUE(WriteHeapGraph(cx,
                                JS::ubi::Node(&node),
                                writer,
--- a/devtools/shared/worker/loader.js
+++ b/devtools/shared/worker/loader.js
@@ -312,22 +312,16 @@ function WorkerDebuggerLoader(options) {
 }
 
 this.WorkerDebuggerLoader = WorkerDebuggerLoader;
 
 // The following APIs rely on the use of Components, and the worker debugger
 // does not provide alternative definitions for them. Consequently, they are
 // stubbed out both on the main thread and worker threads.
 
-var PromiseDebugging = {
-  getState: function () {
-    throw new Error("PromiseDebugging is not available in workers!");
-  }
-};
-
 var chrome = {
   CC: undefined,
   Cc: undefined,
   ChromeWorker: undefined,
   Cm: undefined,
   Ci: undefined,
   Cu: undefined,
   Cr: undefined,
@@ -491,17 +485,16 @@ this.worker = new WorkerDebuggerLoader({
     "reportError": reportError,
     "rpc": rpc,
     "setImmediate": setImmediate,
     "URL": URL,
   },
   loadSubScript: loadSubScript,
   modules: {
     "Debugger": Debugger,
-    "PromiseDebugging": PromiseDebugging,
     "Services": Object.create(null),
     "chrome": chrome,
     "xpcInspector": xpcInspector
   },
   paths: {
     // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
     "": "resource://gre/modules/commonjs/",
     // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
--- a/docshell/base/nsDSURIContentListener.cpp
+++ b/docshell/base/nsDSURIContentListener.cpp
@@ -443,17 +443,17 @@ nsDSURIContentListener::CheckFrameOption
   while (tokenizer.hasMoreTokens()) {
     const nsSubstring& tok = tokenizer.nextToken();
     if (!CheckOneFrameOptionsPolicy(httpChannel, tok)) {
       // cancel the load and display about:blank
       httpChannel->Cancel(NS_BINDING_ABORTED);
       if (mDocShell) {
         nsCOMPtr<nsIWebNavigation> webNav(do_QueryObject(mDocShell));
         if (webNav) {
-          webNav->LoadURI(MOZ_UTF16("about:blank"),
+          webNav->LoadURI(u"about:blank",
                           0, nullptr, nullptr, nullptr);
         }
       }
       return false;
     }
   }
 
   return true;
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -5068,17 +5068,17 @@ nsDocShell::DisplayLoadError(nsresult aE
       nsCOMPtr<Element> element = do_QueryInterface(handler);
       element->GetAttribute(NS_LITERAL_STRING("crashedPageTitle"), messageStr);
     }
 
     // DisplayLoadError requires a non-empty messageStr to proceed and call
     // LoadErrorPage. If the page doesn't have a title, we will use a blank
     // space which will be trimmed and thus treated as empty by the front-end.
     if (messageStr.IsEmpty()) {
-      messageStr.AssignLiteral(MOZ_UTF16(" "));
+      messageStr.AssignLiteral(u" ");
     }
   } else {
     // Errors requiring simple formatting
     switch (aError) {
       case NS_ERROR_MALFORMED_URI:
         // URI is malformed
         error.AssignLiteral("malformedURI");
         break;
@@ -7683,17 +7683,17 @@ nsDocShell::EndPageLoad(nsIWebProgress* 
       DisplayLoadError(aStatus, url, nullptr, aChannel);
       return NS_OK;
     } else if (aStatus == NS_ERROR_INVALID_SIGNATURE) {
       // NS_ERROR_INVALID_SIGNATURE indicates a content-signature error.
       // This currently only happens in case a remote about page fails.
       // We have to load a fallback in this case.
       // XXX: We always load about blank here, firefox has to overwrite this if
       // it wants to display something else.
-      return LoadURI(MOZ_UTF16("about:blank"),  // URI string
+      return LoadURI(u"about:blank",            // URI string
                      nsIChannel::LOAD_NORMAL,   // Load flags
                      nullptr,                   // Referring URI
                      nullptr,                   // Post data stream
                      nullptr);                  // Headers stream
     }
 
     // Handle iframe document not loading error because source was
     // a tracking URL. We make a note of this iframe node by including
@@ -13006,38 +13006,38 @@ nsDocShell::ConfirmRepost(bool* aRepost)
   rv = stringBundleService->CreateBundle(kBrandBundleURL,
                                          getter_AddRefs(brandBundle));
   NS_ENSURE_SUCCESS(rv, rv);
 
   NS_ASSERTION(prompter && brandBundle && appBundle,
                "Unable to set up repost prompter.");
 
   nsXPIDLString brandName;
-  rv = brandBundle->GetStringFromName(MOZ_UTF16("brandShortName"),
+  rv = brandBundle->GetStringFromName(u"brandShortName",
                                       getter_Copies(brandName));
 
   nsXPIDLString msgString, button0Title;
   if (NS_FAILED(rv)) { // No brand, use the generic version.
-    rv = appBundle->GetStringFromName(MOZ_UTF16("confirmRepostPrompt"),
+    rv = appBundle->GetStringFromName(u"confirmRepostPrompt",
                                       getter_Copies(msgString));
   } else {
     // Brand available - if the app has an override file with formatting, the
     // app name will be included. Without an override, the prompt will look
     // like the generic version.
     const char16_t* formatStrings[] = { brandName.get() };
-    rv = appBundle->FormatStringFromName(MOZ_UTF16("confirmRepostPrompt"),
+    rv = appBundle->FormatStringFromName(u"confirmRepostPrompt",
                                          formatStrings,
                                          ArrayLength(formatStrings),
                                          getter_Copies(msgString));
   }
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  rv = appBundle->GetStringFromName(MOZ_UTF16("resendButton.label"),
+  rv = appBundle->GetStringFromName(u"resendButton.label",
                                     getter_Copies(button0Title));
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   int32_t buttonPressed;
   // The actual value here is irrelevant, but we can't pass an invalid
   // bool through XPConnect.
--- a/docshell/test/browser/browser_timelineMarkers-frame-04.js
+++ b/docshell/test/browser/browser_timelineMarkers-frame-04.js
@@ -45,17 +45,21 @@ if (Services.prefs.getBoolPref("javascri
       markers = markers.filter(m => (m.name == "Javascript" &&
                                      m.causeName == "promise callback"));
       ok(markers.length > 0, "Found a Javascript marker");
 
       let frame = markers[0].stack;
       ok(frame.asyncParent !== null, "Parent frame has async parent");
       is(frame.asyncParent.asyncCause, "promise callback",
          "Async parent has correct cause");
-      is(frame.asyncParent.functionDisplayName, "do_promise",
+      let asyncFrame = frame.asyncParent;
+      // Skip over self-hosted parts of our Promise implementation.
+      while (asyncFrame.source === 'self-hosted')
+        asyncFrame = asyncFrame.parent;
+      is(asyncFrame.functionDisplayName, "do_promise",
          "Async parent has correct function name");
     }
   }, {
     desc: "Async stack trace on Javascript marker with script",
     searchFor: (markers) => {
       return markers.some(m => (m.name == "Javascript" &&
                                 m.causeName == "promise callback"));
     },
@@ -66,15 +70,19 @@ if (Services.prefs.getBoolPref("javascri
       markers = markers.filter(m => (m.name == "Javascript" &&
                                      m.causeName == "promise callback"));
       ok(markers.length > 0, "Found a Javascript marker");
 
       let frame = markers[0].stack;
       ok(frame.asyncParent !== null, "Parent frame has async parent");
       is(frame.asyncParent.asyncCause, "promise callback",
          "Async parent has correct cause");
-      is(frame.asyncParent.functionDisplayName, "do_promise_script",
+      let asyncFrame = frame.asyncParent;
+      // Skip over self-hosted parts of our Promise implementation.
+      while (asyncFrame.source === 'self-hosted')
+        asyncFrame = asyncFrame.parent;
+      is(asyncFrame.functionDisplayName, "do_promise_script",
          "Async parent has correct function name");
     }
   });
 }
 
 timelineContentTest(TESTS);
--- a/docshell/test/browser/browser_timelineMarkers-frame-05.js
+++ b/docshell/test/browser/browser_timelineMarkers-frame-05.js
@@ -86,20 +86,32 @@ if (Services.prefs.getBoolPref("javascri
     searchFor: "ConsoleTime",
     setup: function(docShell) {
       let resolver = makePromise();
       resolvePromise(resolver);
     },
     check: function(markers) {
       markers = markers.filter(m => m.name == "ConsoleTime");
       ok(markers.length > 0, "Promise marker includes stack");
-
+      ok(markers[0].stack.functionDisplayName == "testConsoleTime",
+         "testConsoleTime is on the stack");
       let frame = markers[0].endStack;
-      ok(frame.parent.asyncParent !== null, "Parent frame has async parent");
-      is(frame.parent.asyncParent.asyncCause, "promise callback",
+      ok(frame.functionDisplayName == "testConsoleTimeEnd",
+         "testConsoleTimeEnd is on the stack");
+
+      frame = frame.parent;
+      ok(frame.functionDisplayName == "makePromise/<",
+         "makePromise/< is on the stack");
+      let asyncFrame = frame.asyncParent;
+      ok(asyncFrame !== null, "Frame has async parent");
+      is(asyncFrame.asyncCause, "promise callback",
          "Async parent has correct cause");
-      is(frame.parent.asyncParent.functionDisplayName, "makePromise",
+      // Skip over self-hosted parts of our Promise implementation.
+      while (asyncFrame.source === 'self-hosted') {
+        asyncFrame = asyncFrame.parent;
+      }
+      is(asyncFrame.functionDisplayName, "makePromise",
          "Async parent has correct function name");
     }
   });
 }
 
 timelineContentTest(TESTS);
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -75,18 +75,18 @@ public:
     AudioChannelService::GetAudioChannelString(mAudioChannel, name);
 
     nsAutoCString topic;
     topic.Assign("audiochannel-activity-");
     topic.Append(NS_ConvertUTF16toUTF8(name));
 
     observerService->NotifyObservers(wrapper, topic.get(),
                                      mActive
-                                       ? MOZ_UTF16("active")
-                                       : MOZ_UTF16("inactive"));
+                                       ? u"active"
+                                       : u"inactive");
 
     MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
            ("NotifyChannelActiveRunnable, type = %d, active = %d\n",
             mAudioChannel, mActive));
 
     return NS_OK;
   }
 
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -983,17 +983,17 @@ public:
    * namespace ID must not be kNameSpaceID_Unknown and the name must not be
    * null.  Note that this can only return info on attributes that actually
    * live on this element (and is only virtual to handle XUL prototypes).  That
    * is, this should only be called from methods that only care about attrs
    * that effectively live in mAttrsAndChildren.
    */
   virtual nsAttrInfo GetAttrInfo(int32_t aNamespaceID, nsIAtom* aName) const;
 
-  virtual void NodeInfoChanged(mozilla::dom::NodeInfo* aOldNodeInfo)
+  virtual void NodeInfoChanged()
   {
   }
 
   /**
    * Parse a string into an nsAttrValue for a CORS attribute.  This
    * never fails.  The resulting value is an enumerated value whose
    * GetEnumValue() returns one of the above constants.
    */
--- a/dom/base/EventSource.cpp
+++ b/dom/base/EventSource.cpp
@@ -873,21 +873,21 @@ EventSource::ConsoleError()
   nsresult rv = mSrc->GetSpec(targetSpec);
   NS_ENSURE_SUCCESS(rv, rv);
 
   NS_ConvertUTF8toUTF16 specUTF16(targetSpec);
   const char16_t *formatStrings[] = { specUTF16.get() };
 
   if (mReadyState == CONNECTING) {
     rv = PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
-                             MOZ_UTF16("connectionFailure"),
+                             u"connectionFailure",
                              formatStrings, ArrayLength(formatStrings));
   } else {
     rv = PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
-                             MOZ_UTF16("netInterrupt"),
+                             u"netInterrupt",
                              formatStrings, ArrayLength(formatStrings));
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 nsresult
--- a/dom/base/NodeIterator.cpp
+++ b/dom/base/NodeIterator.cpp
@@ -176,17 +176,18 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
     NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMNodeIterator)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(NodeIterator)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(NodeIterator)
 
 NS_IMETHODIMP NodeIterator::GetRoot(nsIDOMNode * *aRoot)
 {
-    NS_ADDREF(*aRoot = Root()->AsDOMNode());
+    nsCOMPtr<nsIDOMNode> root = Root()->AsDOMNode();
+    root.forget(aRoot);
     return NS_OK;
 }
 
 NS_IMETHODIMP NodeIterator::GetWhatToShow(uint32_t *aWhatToShow)
 {
     *aWhatToShow = WhatToShow();
     return NS_OK;
 }
--- a/dom/base/WebSocket.cpp
+++ b/dom/base/WebSocket.cpp
@@ -545,21 +545,21 @@ WebSocketImpl::ConsoleError()
     }
   }
 
   NS_ConvertUTF8toUTF16 specUTF16(mURI);
   const char16_t* formatStrings[] = { specUTF16.get() };
 
   if (mWebSocket->ReadyState() < WebSocket::OPEN) {
     PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
-                        MOZ_UTF16("connectionFailure"),
+                        u"connectionFailure",
                         formatStrings, ArrayLength(formatStrings));
   } else {
     PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
-                        MOZ_UTF16("netInterrupt"),
+                        u"netInterrupt",
                         formatStrings, ArrayLength(formatStrings));
   }
   /// todo some specific errors - like for message too large
   return NS_OK;
 }
 
 void
 WebSocketImpl::FailConnection(uint16_t aReasonCode,
@@ -1594,18 +1594,18 @@ WebSocketImpl::Init(JSContext* aCx,
 
     // upgrade the request from ws:// to wss:// and mark as secure
     mURI.ReplaceSubstring("ws://", "wss://");
     if (NS_WARN_IF(mURI.Find("wss://") != 0)) {
       return;
     }
     mSecure = true;
 
-    const char16_t* params[] = { reportSpec.get(), MOZ_UTF16("wss") };
-    CSP_LogLocalizedStr(MOZ_UTF16("upgradeInsecureRequest"),
+    const char16_t* params[] = { reportSpec.get(), u"wss" };
+    CSP_LogLocalizedStr(u"upgradeInsecureRequest",
                         params, ArrayLength(params),
                         EmptyString(), // aSourceFile
                         EmptyString(), // aScriptSample
                         0, // aLineNumber
                         0, // aColumnNumber
                         nsIScriptError::warningFlag, "CSP",
                         mInnerWindowID);
   }
@@ -2628,26 +2628,19 @@ public:
     : MainThreadWorkerRunnable(aWorkerPrivate)
     , mImpl(aImpl)
   {
   }
 
   bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
     aWorkerPrivate->AssertIsOnWorkerThread();
-    aWorkerPrivate->ModifyBusyCountFromWorker(true);
     return !NS_FAILED(mImpl->CancelInternal());
   }
 
-  void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
-               bool aRunResult) override
-  {
-    aWorkerPrivate->ModifyBusyCountFromWorker(false);
-  }
-
 private:
   RefPtr<WebSocketImpl> mImpl;
 };
 
 } // namespace
 
 // Window closed, stop/reload button pressed, user navigated away from page, etc.
 NS_IMETHODIMP
@@ -2777,31 +2770,29 @@ public:
     , mWebSocketImpl(aImpl)
     , mEvent(Move(aEvent))
   {
   }
 
   bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
     aWorkerPrivate->AssertIsOnWorkerThread();
-    aWorkerPrivate->ModifyBusyCountFromWorker(true);
 
     // No messages when disconnected.
     if (mWebSocketImpl->mDisconnectingOrDisconnected) {
       NS_WARNING("Dispatching a WebSocket event after the disconnection!");
       return true;
     }
 
     return !NS_FAILED(mEvent->Run());
   }
 
   void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
                bool aRunResult) override
   {
-    aWorkerPrivate->ModifyBusyCountFromWorker(false);
   }
 
   bool
   PreDispatch(WorkerPrivate* aWorkerPrivate) override
   {
     // We don't call WorkerRunnable::PreDispatch because it would assert the
     // wrong thing about which thread we're on.  We're on whichever thread the
     // channel implementation is running on (probably the main thread or socket
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -681,22 +681,22 @@ nsContentUtils::InitializeModifierString
   nsXPIDLString shiftModifier;
   nsXPIDLString metaModifier;
   nsXPIDLString osModifier;
   nsXPIDLString altModifier;
   nsXPIDLString controlModifier;
   nsXPIDLString modifierSeparator;
   if (bundle) {
     //macs use symbols for each modifier key, so fetch each from the bundle, which also covers i18n
-    bundle->GetStringFromName(MOZ_UTF16("VK_SHIFT"), getter_Copies(shiftModifier));
-    bundle->GetStringFromName(MOZ_UTF16("VK_META"), getter_Copies(metaModifier));
-    bundle->GetStringFromName(MOZ_UTF16("VK_WIN"), getter_Copies(osModifier));
-    bundle->GetStringFromName(MOZ_UTF16("VK_ALT"), getter_Copies(altModifier));
-    bundle->GetStringFromName(MOZ_UTF16("VK_CONTROL"), getter_Copies(controlModifier));
-    bundle->GetStringFromName(MOZ_UTF16("MODIFIER_SEPARATOR"), getter_Copies(modifierSeparator));
+    bundle->GetStringFromName(u"VK_SHIFT", getter_Copies(shiftModifier));
+    bundle->GetStringFromName(u"VK_META", getter_Copies(metaModifier));
+    bundle->GetStringFromName(u"VK_WIN", getter_Copies(osModifier));
+    bundle->GetStringFromName(u"VK_ALT", getter_Copies(altModifier));
+    bundle->GetStringFromName(u"VK_CONTROL", getter_Copies(controlModifier));
+    bundle->GetStringFromName(u"MODIFIER_SEPARATOR", getter_Copies(modifierSeparator));
   }
   //if any of these don't exist, we get  an empty string
   sShiftText = new nsString(shiftModifier);
   sMetaText = new nsString(metaModifier);
   sOSText = new nsString(osModifier);
   sAltText = new nsString(altModifier);
   sControlText = new nsString(controlModifier);
   sModifierSeparator = new nsString(modifierSeparator);
@@ -5117,17 +5117,17 @@ nsContentUtils::GetWindowProviderForCont
 already_AddRefed<nsPIDOMWindowOuter>
 nsContentUtils::GetMostRecentNonPBWindow()
 {
   nsCOMPtr<nsIWindowMediator> windowMediator =
     do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
   nsCOMPtr<nsIWindowMediator_44> wm = do_QueryInterface(windowMediator);
 
   nsCOMPtr<mozIDOMWindowProxy> window;
-  wm->GetMostRecentNonPBWindow(MOZ_UTF16("navigator:browser"),
+  wm->GetMostRecentNonPBWindow(u"navigator:browser",
                                getter_AddRefs(window));
   nsCOMPtr<nsPIDOMWindowOuter> pwindow;
   pwindow = do_QueryInterface(window);
 
   return pwindow.forget();
 }
 
 /* static */
@@ -6386,26 +6386,22 @@ nsContentUtils::PlatformToDOMLineBreaks(
   }
 }
 
 bool
 nsContentUtils::PlatformToDOMLineBreaks(nsString& aString, const fallible_t& aFallible)
 {
   if (aString.FindChar(char16_t('\r')) != -1) {
     // Windows linebreaks: Map CRLF to LF:
-    if (!aString.ReplaceSubstring(MOZ_UTF16("\r\n"),
-                                  MOZ_UTF16("\n"),
-                                  aFallible)) {
+    if (!aString.ReplaceSubstring(u"\r\n", u"\n", aFallible)) {
       return false;
     }
 
     // Mac linebreaks: Map any remaining CR to LF:
-    if (!aString.ReplaceSubstring(MOZ_UTF16("\r"),
-                                  MOZ_UTF16("\n"),
-                                  aFallible)) {
+    if (!aString.ReplaceSubstring(u"\r", u"\n", aFallible)) {
       return false;
     }
   }
 
   return true;
 }
 
 void
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -3021,17 +3021,17 @@ GetFormattedTimeString(PRTime aTime, nsA
   char formatedTime[24];
   if (snprintf_literal(formatedTime, "%02d/%02d/%04d %02d:%02d:%02d",
                        prtime.tm_month + 1, prtime.tm_mday, int(prtime.tm_year),
                        prtime.tm_hour     ,  prtime.tm_min,  prtime.tm_sec)) {
     CopyASCIItoUTF16(nsDependentCString(formatedTime), aFormattedTimeString);
   } else {
     // If we for whatever reason failed to find the last modified time
     // (or even the current time), fall back to what NS4.x returned.
-    aFormattedTimeString.AssignLiteral(MOZ_UTF16("01/01/1970 00:00:00"));
+    aFormattedTimeString.AssignLiteral(u"01/01/1970 00:00:00");
   }
 }
 
 void
 nsIDocument::GetLastModified(nsAString& aLastModified) const
 {
   if (!mLastModified.IsEmpty()) {
     aLastModified.Assign(mLastModified);
@@ -9738,26 +9738,26 @@ nsDocument::GetReadyState(nsAString& aRe
   return NS_OK;
 }
 
 void
 nsIDocument::GetReadyState(nsAString& aReadyState) const
 {
   switch(mReadyState) {
   case READYSTATE_LOADING :
-    aReadyState.AssignLiteral(MOZ_UTF16("loading"));
+    aReadyState.AssignLiteral(u"loading");
     break;
   case READYSTATE_INTERACTIVE :
-    aReadyState.AssignLiteral(MOZ_UTF16("interactive"));
+    aReadyState.AssignLiteral(u"interactive");
     break;
   case READYSTATE_COMPLETE :
-    aReadyState.AssignLiteral(MOZ_UTF16("complete"));
+    aReadyState.AssignLiteral(u"complete");
     break;
   default:
-    aReadyState.AssignLiteral(MOZ_UTF16("uninitialized"));
+    aReadyState.AssignLiteral(u"uninitialized");
   }
 }
 
 namespace {
 
 struct SuppressArgs
 {
   nsIDocument::SuppressionType mWhat;
--- a/dom/base/nsGenericDOMDataNode.h
+++ b/dom/base/nsGenericDOMDataNode.h
@@ -167,23 +167,23 @@ public:
 
   NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker) override;
   NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const;
   virtual nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute,
                                               int32_t aModType) const;
 
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override
   {
-    *aResult = CloneDataNode(aNodeInfo, true);
+    nsCOMPtr<nsINode> result = CloneDataNode(aNodeInfo, true);
+    result.forget(aResult);
+
     if (!*aResult) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
-    NS_ADDREF(*aResult);
-
     return NS_OK;
   }
 
   nsresult SplitData(uint32_t aOffset, nsIContent** aReturn,
                      bool aCloneAfterOriginal = true);
 
   // WebIDL API
   // Our XPCOM GetData is just fine for WebIDL
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -9480,17 +9480,17 @@ nsGlobalWindow::FindOuter(const nsAStrin
   if (aString.IsEmpty() || aShowDialog) {
     // See if the find dialog is already up using nsIWindowMediator
     nsCOMPtr<nsIWindowMediator> windowMediator =
       do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
 
     nsCOMPtr<mozIDOMWindowProxy> findDialog;
 
     if (windowMediator) {
-      windowMediator->GetMostRecentWindow(MOZ_UTF16("findInPage"),
+      windowMediator->GetMostRecentWindow(u"findInPage",
                                           getter_AddRefs(findDialog));
     }
 
     if (findDialog) {
       // The Find dialog is already open, bring it to the top.
       auto* piFindDialog = nsPIDOMWindowOuter::From(findDialog);
       aError = piFindDialog->Focus();
     } else if (finder) {
@@ -11114,17 +11114,17 @@ nsGlobalWindow::ShowSlowScriptDialog()
       if (NS_IS_LOW_SURROGATE(filenameUTF16[cutStart + cutLength])) {
         // Likewise, don't drop a trailing low surrogate here.  We want to
         // increase cutLength, since it might be 0 already so we can't very well
         // decrease it.
         ++cutLength;
       }
 
       // Insert U+2026 HORIZONTAL ELLIPSIS
-      filenameUTF16.Replace(cutStart, cutLength, NS_LITERAL_STRING("\x2026"));
+      filenameUTF16.Replace(cutStart, cutLength, NS_LITERAL_STRING(u"\x2026"));
     }
     const char16_t *formatParams[] = { filenameUTF16.get() };
     rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
                                                "KillScriptLocation",
                                                formatParams,
                                                scriptLocation);
 
     if (NS_SUCCEEDED(rv) && scriptLocation) {
@@ -11532,17 +11532,17 @@ nsGlobalWindow::Observe(nsISupports* aSu
     event->SetTrusted(true);
 
     bool dummy;
     return DispatchEvent(event, &dummy);
   }
 #endif // MOZ_B2G
 
   if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
-    MOZ_ASSERT(!NS_strcmp(aData, MOZ_UTF16("intl.accept_languages")));
+    MOZ_ASSERT(!NS_strcmp(aData, u"intl.accept_languages"));
     MOZ_ASSERT(IsInnerWindow());
 
     // The user preferred languages have changed, we need to fire an event on
     // Window object and invalidate the cache for navigator.languages. It is
     // done for every change which can be a waste of cycles but those should be
     // fairly rare.
     // We MUST invalidate navigator.languages before sending the event in the
     // very likely situation where an event handler will try to read its value.
--- a/dom/base/nsHTMLContentSerializer.cpp
+++ b/dom/base/nsHTMLContentSerializer.cpp
@@ -149,19 +149,19 @@ nsHTMLContentSerializer::SerializeHTMLAt
         valueStr = NS_LITERAL_STRING("text/html; charset=") +
           NS_ConvertASCIItoUTF16(mCharset);
       }
     }
 
     nsDependentAtomString nameStr(attrName);
     nsAutoString prefix;
     if (namespaceID == kNameSpaceID_XML) {
-      prefix.AssignLiteral(MOZ_UTF16("xml"));
+      prefix.AssignLiteral(u"xml");
     } else if (namespaceID == kNameSpaceID_XLink) {
-      prefix.AssignLiteral(MOZ_UTF16("xlink"));
+      prefix.AssignLiteral(u"xlink");
     }
 
     // Expand shorthand attribute.
     if (aNamespace == kNameSpaceID_XHTML &&
         namespaceID == kNameSpaceID_None &&
         IsShorthandAttr(attrName, aTagName) &&
         valueStr.IsEmpty()) {
       valueStr = nameStr;
--- a/dom/base/nsISelectionPrivate.idl
+++ b/dom/base/nsISelectionPrivate.idl
@@ -10,19 +10,19 @@ interface nsIDOMNode;
 interface nsISelectionListener;
 interface nsIContent;
 interface nsINode;
 
 %{C++
 class nsIFrame;
 struct nsPoint;
 struct ScrollAxis;
-template<class T> class nsTArray;
 #include "nsDirection.h"
 #include "nsIPresShell.h" // TODO: Remove this include
+#include "nsTArrayForwardDeclare.h"
 #include "mozilla/EventForwards.h"
 %}
 
 [ptr] native nsIFrame(nsIFrame);
 [ptr] native RangeArray(nsTArray<nsRange*>);
 [ref] native constTextRangeStyleRef(const mozilla::TextRangeStyle);
 [ref] native nsPointRef(nsPoint);
 native nsDirection(nsDirection);
@@ -47,17 +47,17 @@ interface nsISelectionPrivate : nsISelec
     */
     [noscript] void endBatchChanges();
 
     DOMString  toStringWithFormat(in string formatType, in unsigned long flags, in int32_t wrapColumn);
     void  addSelectionListener(in nsISelectionListener newListener);
     void  removeSelectionListener(in nsISelectionListener listenerToRemove);
 
     /* Table selection stuff
-       We should probably move this and table-related 
+       We should probably move this and table-related
        items in nsFrameSelection  to a
        new nsITableSelection interface
     */
     const long TABLESELECTION_NONE     = 0;
     const long TABLESELECTION_CELL     = 1;
     const long TABLESELECTION_ROW      = 2;
     const long TABLESELECTION_COLUMN   = 3;
     const long TABLESELECTION_TABLE    = 4;
@@ -90,17 +90,17 @@ interface nsISelectionPrivate : nsISelec
     [noscript] void setTextRangeStyle(in nsIDOMRange range,
                       in constTextRangeStyleRef textRangeStyle);
 
     /**
      * Get the direction of the selection.
      */
     [noscript, notxpcom] nsDirection getSelectionDirection();
     [noscript, notxpcom] void setSelectionDirection(in nsDirection aDirection);
-    
+
     /**
      * Returns the type of the selection (see nsISelectionController for
      * available constants).
      */
     readonly attribute short type;
 
     /**
      * Return array of ranges intersecting with the given DOM interval.
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -1616,18 +1616,18 @@ nsJSContext::EndCycleCollectionCallback(
     }
 
     nsCString gcMsg;
     if (aResults.mForcedGC) {
       gcMsg.AssignLiteral(", forced a GC");
     }
 
     NS_NAMED_MULTILINE_LITERAL_STRING(kFmt,
-      MOZ_UTF16("CC(T+%.1f)[%s] max pause: %lums, total time: %lums, slices: %lu, suspected: %lu, visited: %lu RCed and %lu%s GCed, collected: %lu RCed and %lu GCed (%lu|%lu|%lu waiting for GC)%s\n")
-      MOZ_UTF16("ForgetSkippable %lu times before CC, min: %lu ms, max: %lu ms, avg: %lu ms, total: %lu ms, max sync: %lu ms, removed: %lu"));
+      u"CC(T+%.1f)[%s] max pause: %lums, total time: %lums, slices: %lu, suspected: %lu, visited: %lu RCed and %lu%s GCed, collected: %lu RCed and %lu GCed (%lu|%lu|%lu waiting for GC)%s\n"
+      u"ForgetSkippable %lu times before CC, min: %lu ms, max: %lu ms, avg: %lu ms, total: %lu ms, max sync: %lu ms, removed: %lu");
     nsString msg;
     msg.Adopt(nsTextFormatter::smprintf(kFmt.get(), double(delta) / PR_USEC_PER_SEC,
                                         ProcessNameForCollectorLog(),
                                         gCCStats.mMaxSliceTime, gCCStats.mTotalSliceTime,
                                         aResults.mNumSlices, gCCStats.mSuspected,
                                         aResults.mVisitedRefCounted, aResults.mVisitedGCed, mergeMsg.get(),
                                         aResults.mFreedRefCounted, aResults.mFreedGCed,
                                         sCCollectedWaitingForGC, sCCollectedZonesWaitingForGC, sLikelyShortLivingObjectsNeedingGC,
@@ -1643,41 +1643,41 @@ nsJSContext::EndCycleCollectionCallback(
       do_GetService(NS_CONSOLESERVICE_CONTRACTID);
     if (cs) {
       cs->LogStringMessage(msg.get());
     }
   }
 
   if (sPostGCEventsToObserver) {
     NS_NAMED_MULTILINE_LITERAL_STRING(kJSONFmt,
-       MOZ_UTF16("{ \"timestamp\": %llu, ")
-         MOZ_UTF16("\"duration\": %lu, ")
-         MOZ_UTF16("\"max_slice_pause\": %lu, ")
-         MOZ_UTF16("\"total_slice_pause\": %lu, ")
-         MOZ_UTF16("\"max_finish_gc_duration\": %lu, ")
-         MOZ_UTF16("\"max_sync_skippable_duration\": %lu, ")
-         MOZ_UTF16("\"suspected\": %lu, ")
-         MOZ_UTF16("\"visited\": { ")
-             MOZ_UTF16("\"RCed\": %lu, ")
-             MOZ_UTF16("\"GCed\": %lu }, ")
-         MOZ_UTF16("\"collected\": { ")
-             MOZ_UTF16("\"RCed\": %lu, ")
-             MOZ_UTF16("\"GCed\": %lu }, ")
-         MOZ_UTF16("\"waiting_for_gc\": %lu, ")
-         MOZ_UTF16("\"zones_waiting_for_gc\": %lu, ")
-         MOZ_UTF16("\"short_living_objects_waiting_for_gc\": %lu, ")
-         MOZ_UTF16("\"forced_gc\": %d, ")
-         MOZ_UTF16("\"forget_skippable\": { ")
-             MOZ_UTF16("\"times_before_cc\": %lu, ")
-             MOZ_UTF16("\"min\": %lu, ")
-             MOZ_UTF16("\"max\": %lu, ")
-             MOZ_UTF16("\"avg\": %lu, ")
-             MOZ_UTF16("\"total\": %lu, ")
-             MOZ_UTF16("\"removed\": %lu } ")
-       MOZ_UTF16("}"));
+       u"{ \"timestamp\": %llu, "
+         u"\"duration\": %lu, "
+         u"\"max_slice_pause\": %lu, "
+         u"\"total_slice_pause\": %lu, "
+         u"\"max_finish_gc_duration\": %lu, "
+         u"\"max_sync_skippable_duration\": %lu, "
+         u"\"suspected\": %lu, "
+         u"\"visited\": { "
+             u"\"RCed\": %lu, "
+             u"\"GCed\": %lu }, "
+         u"\"collected\": { "
+             u"\"RCed\": %lu, "
+             u"\"GCed\": %lu }, "
+         u"\"waiting_for_gc\": %lu, "
+         u"\"zones_waiting_for_gc\": %lu, "
+         u"\"short_living_objects_waiting_for_gc\": %lu, "
+         u"\"forced_gc\": %d, "
+         u"\"forget_skippable\": { "
+             u"\"times_before_cc\": %lu, "
+             u"\"min\": %lu, "
+             u"\"max\": %lu, "
+             u"\"avg\": %lu, "
+             u"\"total\": %lu, "
+             u"\"removed\": %lu } "
+       u"}");
     nsString json;
     json.Adopt(nsTextFormatter::smprintf(kJSONFmt.get(), endCCTime, ccNowDuration,
                                          gCCStats.mMaxSliceTime,
                                          gCCStats.mTotalSliceTime,
                                          gCCStats.mMaxGCDuration,
                                          gCCStats.mMaxSkippableDuration,
                                          gCCStats.mSuspected,
                                          aResults.mVisitedRefCounted, aResults.mVisitedGCed,
--- a/dom/base/nsNodeInfoManager.cpp
+++ b/dom/base/nsNodeInfoManager.cpp
@@ -269,19 +269,18 @@ nsNodeInfoManager::GetNodeInfo(const nsA
   }
 #endif
 
   NodeInfo::NodeInfoInner tmpKey(aName, aPrefix, aNamespaceID, aNodeType);
 
   void *node = PL_HashTableLookup(mNodeInfoHash, &tmpKey);
 
   if (node) {
-    NodeInfo* nodeInfo = static_cast<NodeInfo *>(node);
-
-    NS_ADDREF(*aNodeInfo = nodeInfo);
+    RefPtr<NodeInfo> nodeInfo = static_cast<NodeInfo*>(node);
+    nodeInfo.forget(aNodeInfo);
 
     return NS_OK;
   }
 
   nsCOMPtr<nsIAtom> nameAtom = NS_Atomize(aName);
   NS_ENSURE_TRUE(nameAtom, NS_ERROR_OUT_OF_MEMORY);
 
   RefPtr<NodeInfo> newNodeInfo =
--- a/dom/base/nsNodeUtils.cpp
+++ b/dom/base/nsNodeUtils.cpp
@@ -395,17 +395,17 @@ nsNodeUtils::CloneNodeImpl(nsINode *aNod
   *aResult = nullptr;
 
   nsCOMPtr<nsINode> newNode;
   nsCOMArray<nsINode> nodesWithProperties;
   nsresult rv = Clone(aNode, aDeep, nullptr, nodesWithProperties,
                       getter_AddRefs(newNode));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  newNode.swap(*aResult);
+  newNode.forget(aResult);
   return NS_OK;
 }
 
 /* static */
 nsresult
 nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
                            nsNodeInfoManager *aNewNodeInfoManager,
                            JS::Handle<JSObject*> aReparentScope,
@@ -501,17 +501,17 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNod
     if (aNode->IsElement()) {
       Element* element = aNode->AsElement();
       oldDoc->ClearBoxObjectFor(element);
       wasRegistered = oldDoc->UnregisterActivityObserver(element);
     }
 
     aNode->mNodeInfo.swap(newNodeInfo);
     if (elem) {
-      elem->NodeInfoChanged(newNodeInfo);
+      elem->NodeInfoChanged();
     }
 
     nsIDocument* newDoc = aNode->OwnerDoc();
     if (newDoc) {
       // XXX what if oldDoc is null, we don't know if this should be
       // registered or not! Can that really happen?
       if (wasRegistered) {
         newDoc->RegisterActivityObserver(aNode->AsElement());
--- a/dom/base/nsPlainTextSerializer.cpp
+++ b/dom/base/nsPlainTextSerializer.cpp
@@ -440,17 +440,17 @@ nsPlainTextSerializer::AppendDocumentSta
 nsresult
 nsPlainTextSerializer::DoOpenContainer(nsIAtom* aTag)
 {
   // Check if we need output current node as placeholder character and ignore
   // child nodes.
   if (ShouldReplaceContainerWithPlaceholder(mElement->NodeInfo()->NameAtom())) {
     if (mIgnoredChildNodeLevel == 0) {
       // Serialize current node as placeholder character
-      Write(NS_LITERAL_STRING("\xFFFC"));
+      Write(NS_LITERAL_STRING(u"\xFFFC"));
     }
     // Ignore child nodes.
     mIgnoredChildNodeLevel++;
     return NS_OK;
   }
   if (IsIgnorableRubyAnnotation(aTag)) {
     // Ignorable ruby annotation shouldn't be replaced by a placeholder
     // character, neither any of its descendants.
@@ -596,17 +596,17 @@ nsPlainTextSerializer::DoOpenContainer(n
   else if (aTag == nsGkAtoms::td || aTag == nsGkAtoms::th) {
     // We must make sure that the content of two table cells get a
     // space between them.
 
     // To make the separation between cells most obvious and
     // importable, we use a TAB.
     if (GetLastBool(mHasWrittenCellsForRow)) {
       // Bypass |Write| so that the TAB isn't compressed away.
-      AddToLine(MOZ_UTF16("\t"), 1);
+      AddToLine(u"\t", 1);
       mInWhitespace = true;
     }
     else if (mHasWrittenCellsForRow.IsEmpty()) {
       // We don't always see a <tr> (nor a <table>) before the <td> if we're
       // copying part of a table
       PushBool(mHasWrittenCellsForRow, true); // will never be popped
     }
     else {
@@ -1113,17 +1113,17 @@ nsPlainTextSerializer::DoAddLeaf(nsIAtom
     while (line.Length() < width) {
       line.Append(char16_t('-'));
     }
     Write(line);
 
     EnsureVerticalSpace(0);
   }
   else if (mFlags & nsIDocumentEncoder::OutputNonTextContentAsPlaceholder) {
-    Write(NS_LITERAL_STRING("\xFFFC"));
+    Write(NS_LITERAL_STRING(u"\xFFFC"));
   }
   else if (aTag == nsGkAtoms::img) {
     /* Output (in decreasing order of preference)
        alt, title or nothing */
     // See <http://www.w3.org/TR/REC-html40/struct/objects.html#edef-IMG>
     nsAutoString imageDescription;
     if (NS_SUCCEEDED(GetAttributeValue(nsGkAtoms::alt,
                                        imageDescription))) {
@@ -1212,17 +1212,17 @@ nsPlainTextSerializer::Output(nsString& 
   }
   mOutputString->Append(aString);
 }
 
 static bool
 IsSpaceStuffable(const char16_t *s)
 {
   if (s[0] == '>' || s[0] == ' ' || s[0] == kNBSP ||
-      nsCRT::strncmp(s, MOZ_UTF16("From "), 5) == 0)
+      nsCRT::strncmp(s, u"From ", 5) == 0)
     return true;
   else
     return false;
 }
 
 /**
  * This function adds a piece of text to the current stored line. If we are
  * wrapping text and the stored line will become too long, a suitable
@@ -2026,9 +2026,8 @@ int32_t GetUnicharStringWidth(const char
   for (;*pwcs && n-- > 0; pwcs++)
     if ((w = GetUnicharWidth(*pwcs)) < 0)
       ++width; // Taking 1 as the width of non-printable character, for bug# 94475.
     else
       width += w;
 
   return width;
 }
-
--- a/dom/base/test/browser_use_counters.js
+++ b/dom/base/test/browser_use_counters.js
@@ -6,17 +6,22 @@ var {Promise: promise} = Cu.import("reso
 
 const gHttpTestRoot = "http://example.com/browser/dom/base/test/";
 
 /**
  * Enable local telemetry recording for the duration of the tests.
  */
 var gOldContentCanRecord = false;
 add_task(function* test_initialize() {
-   gOldContentCanRecord = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
+  // Because canRecordExtended is a per-process variable, we need to make sure
+  // that all of the pages load in the same content process. Limit the number
+  // of content processes to at most 1 (or 0 if e10s is off entirely).
+  yield SpecialPowers.pushPrefEnv({ set: [[ "dom.ipc.processCount", 1 ]] });
+
+  gOldContentCanRecord = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
     let telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry);
     let old = telemetry.canRecordExtended;
     telemetry.canRecordExtended = true;
     return old;
   });
   info("canRecord for content: " + gOldContentCanRecord);
 });
 
--- a/dom/bindings/test/test_promise_rejections_from_jsimplemented.html
+++ b/dom/bindings/test/test_promise_rejections_from_jsimplemented.html
@@ -20,105 +20,114 @@ https://bugzilla.mozilla.org/show_bug.cg
     is(exn.name, name,
        "Should have the right exception name in test " + testNumber);
     is("filename" in exn ? exn.filename : exn.fileName, filename,
        "Should have the right file name in test " + testNumber);
     is(exn.message, message,
        "Should have the right message in test " + testNumber);
     is(exn.code, code, "Should have the right .code in test " + testNumber);
     if (message === "") {
-      is(exn.name, "NS_ERROR_UNEXPECTED",
+      is(exn.name, "InternalError",
          "Should have one of our synthetic exceptions in test " + testNumber);
     }
     is(exn.stack, stack, "Should have the right stack in test " + testNumber);
   }
 
   function ensurePromiseFail(testNumber, value) {
     ok(false, "Test " + testNumber + " should not have a fulfilled promise");
   }
 
   function doTest() {
     var t = new TestInterfaceJS();
     /* Async parent frames from pushPrefEnv don't show up in e10s.  */
     var isE10S = !SpecialPowers.isMainProcess();
     var asyncStack = SpecialPowers.getBoolPref("javascript.options.asyncstack");
     var ourFile = location.href;
-    var parentFrame = (asyncStack && !isE10S) ? `Async*@${ourFile}:121:3
+    var unwrapError = "Promise rejection value is a non-unwrappable cross-compartment wrapper.";
+    var parentFrame = (asyncStack && !isE10S) ? `Async*@${ourFile}:130:3
 ` : "";
 
     Promise.all([
       t.testPromiseWithThrowingChromePromiseInit().then(
           ensurePromiseFail.bind(null, 1),
-          checkExn.bind(null, 48, "NS_ERROR_UNEXPECTED", "", undefined,
-                        ourFile, 1,
-                        `doTest@${ourFile}:48:7
+          checkExn.bind(null, 49, "InternalError", unwrapError,
+                        undefined, ourFile, 1,
+                        `doTest@${ourFile}:49:7
 ` +
                         parentFrame)),
       t.testPromiseWithThrowingContentPromiseInit(function() {
           thereIsNoSuchContentFunction1();
         }).then(
           ensurePromiseFail.bind(null, 2),
-          checkExn.bind(null, 56, "ReferenceError",
+          checkExn.bind(null, 57, "ReferenceError",
                         "thereIsNoSuchContentFunction1 is not defined",
                         undefined, ourFile, 2,
-                        `doTest/<@${ourFile}:56:11
-doTest@${ourFile}:55:7
+                        `doTest/<@${ourFile}:57:11
+doTest@${ourFile}:56:7
 ` +
                         parentFrame)),
       t.testPromiseWithThrowingChromeThenFunction().then(
           ensurePromiseFail.bind(null, 3),
-          checkExn.bind(null, 0, "NS_ERROR_UNEXPECTED", "", undefined, "", 3, "")),
+          checkExn.bind(null, 0, "InternalError", unwrapError, undefined, "", 3, asyncStack ? (`Async*doTest@${ourFile}:67:7
+` +
+                        parentFrame) : "")),
       t.testPromiseWithThrowingContentThenFunction(function() {
           thereIsNoSuchContentFunction2();
         }).then(
           ensurePromiseFail.bind(null, 4),
-          checkExn.bind(null, 70, "ReferenceError",
+          checkExn.bind(null, 73, "ReferenceError",
                         "thereIsNoSuchContentFunction2 is not defined",
                         undefined, ourFile, 4,
-                        `doTest/<@${ourFile}:70:11
+                        `doTest/<@${ourFile}:73:11
 ` +
-                        (asyncStack ? `Async*doTest@${ourFile}:69:7
+                        (asyncStack ? `Async*doTest@${ourFile}:72:7
 ` : "") +
                         parentFrame)),
       t.testPromiseWithThrowingChromeThenable().then(
           ensurePromiseFail.bind(null, 5),
-          checkExn.bind(null, 0, "NS_ERROR_UNEXPECTED", "", undefined, "", 5, "")),
+          checkExn.bind(null, 0, "InternalError", unwrapError, undefined, "", 5, asyncStack ? (`Async*doTest@${ourFile}:84:7
+` +
+                        parentFrame) : "")),
       t.testPromiseWithThrowingContentThenable({
             then: function() { thereIsNoSuchContentFunction3(); }
         }).then(
           ensurePromiseFail.bind(null, 6),
-          checkExn.bind(null, 85, "ReferenceError",
+          checkExn.bind(null, 90, "ReferenceError",
                         "thereIsNoSuchContentFunction3 is not defined",
                         undefined, ourFile, 6,
-                        `doTest/<.then@${ourFile}:85:32
-`)),
+                        `doTest/<.then@${ourFile}:90:32
+` + (asyncStack ? `Async*doTest@${ourFile}:89:7\n` + parentFrame : ""))),
       t.testPromiseWithDOMExceptionThrowingPromiseInit().then(
           ensurePromiseFail.bind(null, 7),
-          checkExn.bind(null, 93, "NotFoundError",
+          checkExn.bind(null, 98, "NotFoundError",
                         "We are a second DOMException",
                         DOMException.NOT_FOUND_ERR, ourFile, 7,
-                        `doTest@${ourFile}:93:7
+                        `doTest@${ourFile}:98:7
 ` +
                         parentFrame)),
       t.testPromiseWithDOMExceptionThrowingThenFunction().then(
           ensurePromiseFail.bind(null, 8),
-          checkExn.bind(null, asyncStack ? 101 : 0, "NetworkError",
+          checkExn.bind(null, asyncStack ? 106 : 0, "NetworkError",
                          "We are a third DOMException",
                         DOMException.NETWORK_ERR, asyncStack ? ourFile : "", 8,
-                        (asyncStack ? `Async*doTest@${ourFile}:101:7
+                        (asyncStack ? `Async*doTest@${ourFile}:106:7
 ` +
                          parentFrame : ""))),
       t.testPromiseWithDOMExceptionThrowingThenable().then(
           ensurePromiseFail.bind(null, 9),
-          checkExn.bind(null, 0, "TypeMismatchError",
+          checkExn.bind(null, asyncStack ? 114 : 0, "TypeMismatchError",
                         "We are a fourth DOMException",
-                         DOMException.TYPE_MISMATCH_ERR, "", 9, "")),
+                        DOMException.TYPE_MISMATCH_ERR,
+                        asyncStack ? ourFile : "", 9,
+                        (asyncStack ? `Async*doTest@${ourFile}:114:7
+` +
+                         parentFrame : ""))),
     ]).then(SimpleTest.finish,
-            function() {
-              ok(false, "One of our catch statements totally failed");
+            function(err) {
+              ok(false, "One of our catch statements totally failed with err" + err + ', stack: ' + (err ? err.stack : ''));
               SimpleTest.finish();
             });
   }
 
   SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]},
                             doTest);
   </script>
 </head>
--- a/dom/camera/GonkCameraControl.cpp
+++ b/dom/camera/GonkCameraControl.cpp
@@ -1297,17 +1297,17 @@ nsGonkCameraControl::StopRecordingImpl()
     ~RecordingComplete() { }
 
     NS_IMETHODIMP
     Run()
     {
       MOZ_ASSERT(NS_IsMainThread());
 
       nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-      obs->NotifyObservers(mFile, "file-watcher-notify", MOZ_UTF16("modified"));
+      obs->NotifyObservers(mFile, "file-watcher-notify", u"modified");
       return NS_OK;
     }
 
   private:
     RefPtr<DeviceStorageFile> mFile;
   };
 
   ReentrantMonitorAutoEnter mon(mRecorderMonitor);
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -1557,18 +1557,18 @@ CanvasRenderingContext2D::EnsureTarget(c
     } else {
       mTarget = mBufferProvider->BorrowDrawTarget(IntRect(0, 0, mWidth, mHeight));
     }
 
     ScheduleStableStateCallback();
 
     if (mTarget) {
       // Restore clip and transform.
-      mTarget->SetTransform(CurrentState().transform);
       for (uint32_t i = 0; i < mStyleStack.Length(); i++) {
+        mTarget->SetTransform(mStyleStack[i].transform);
         for (uint32_t c = 0; c < mStyleStack[i].clipsPushed.Length(); c++) {
           mTarget->PushClip(mStyleStack[i].clipsPushed[c]);
         }
       }
       return mRenderingMode;
     } else {
       mBufferProvider = nullptr;
     }
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -931,17 +931,17 @@ protected:
                      miterLimit(10.0f),
                      globalAlpha(1.0f),
                      shadowBlur(0.0),
                      dashOffset(0.0f),
                      op(mozilla::gfx::CompositionOp::OP_OVER),
                      fillRule(mozilla::gfx::FillRule::FILL_WINDING),
                      lineCap(mozilla::gfx::CapStyle::BUTT),
                      lineJoin(mozilla::gfx::JoinStyle::MITER_OR_BEVEL),
-                     filterString(MOZ_UTF16("none")),
+                     filterString(u"none"),
                      updateFilterOnWriteOnly(false),
                      imageSmoothingEnabled(true),
                      fontExplicitLanguage(false)
     { }
 
     ContextState(const ContextState& aOther)
         : fontGroup(aOther.fontGroup),
           fontLanguage(aOther.fontLanguage),
new file mode 100644
--- /dev/null
+++ b/dom/canvas/crashtests/1287515-1.html
@@ -0,0 +1,7 @@
+<canvas id='i0'></canvas>
+<script>
+var c=document.getElementById('i0').getContext('2d');
+c.lineWidth=Number.MAX_SAFE_INTEGER;
+c.setLineDash([1]);
+c.strokeRect(1,1,0,Number.MIN_SAFE_INTEGER);
+</script>
--- a/dom/canvas/crashtests/crashtests.list
+++ b/dom/canvas/crashtests/crashtests.list
@@ -24,8 +24,9 @@ load 1161277-1.html
 load 1183363.html
 load 1190705.html
 load 1223740-1.html
 load 1225381-1.html
 skip-if(azureCairo) load 1229983-1.html
 load 1229932-1.html
 load 1244850-1.html
 load 1246775-1.html
+skip-if(d2d) load 1287515-1.html
--- a/dom/devicestorage/DeviceStorageStatics.cpp
+++ b/dom/devicestorage/DeviceStorageStatics.cpp
@@ -698,19 +698,19 @@ DeviceStorageStatics::Observe(nsISupport
     if (NS_WARN_IF(!sInstance)) {
       return NS_OK;
     }
 
     // 'disk-space-watcher' notifications are sent when there is a modification
     // of a file in a specific location while a low device storage situation
     // exists or after recovery of a low storage situation. For Firefox OS,
     // these notifications are specific for apps storage.
-    if (!NS_strcmp(aData, MOZ_UTF16("full"))) {
+    if (!NS_strcmp(aData, u"full")) {
       sInstance->mLowDiskSpace = true;
-    } else if (!NS_strcmp(aData, MOZ_UTF16("free"))) {
+    } else if (!NS_strcmp(aData, u"free")) {
       sInstance->mLowDiskSpace = false;
     } else {
       return NS_OK;
     }
 
 
     uint32_t i = mListeners.Length();
     DS_LOG_INFO("disk space %d (%u)", sInstance->mLowDiskSpace, i);
--- a/dom/events/TextComposition.cpp
+++ b/dom/events/TextComposition.cpp
@@ -36,17 +36,17 @@
 #undef TextRangeArray
 #undef Comment
 #endif
 
 using namespace mozilla::widget;
 
 namespace mozilla {
 
-#define IDEOGRAPHIC_SPACE (NS_LITERAL_STRING("\x3000"))
+#define IDEOGRAPHIC_SPACE (NS_LITERAL_STRING(u"\x3000"))
 
 /******************************************************************************
  * TextComposition
  ******************************************************************************/
 
 bool TextComposition::sHandlingSelectionEvent = false;
 
 TextComposition::TextComposition(nsPresContext* aPresContext,
--- a/dom/filehandle/ActorsParent.h
+++ b/dom/filehandle/ActorsParent.h
@@ -8,23 +8,23 @@
 #include "mozilla/dom/FileHandleStorage.h"
 #include "mozilla/dom/PBackgroundMutableFileParent.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "nsAutoPtr.h"
 #include "nsClassHashtable.h"
 #include "nsCOMPtr.h"
 #include "nsHashKeys.h"
 #include "nsString.h"
+#include "nsTArrayForwardDeclare.h"
 #include "nsTHashtable.h"
 
 template <class> struct already_AddRefed;
 class nsIFile;
 class nsIRunnable;
 class nsIThreadPool;
-template <class> class nsTArray;
 
 namespace mozilla {
 
 namespace ipc {
 
 class PBackgroundParent;
 
 } // namespace ipc
--- a/dom/geolocation/nsGeolocation.cpp
+++ b/dom/geolocation/nsGeolocation.cpp
@@ -955,17 +955,17 @@ nsGeolocationService::StartDevice(nsIPri
       NS_FAILED(rv = mProvider->Watch(this))) {
 
     NotifyError(nsIDOMGeoPositionError::POSITION_UNAVAILABLE);
     return rv;
   }
 
   obs->NotifyObservers(mProvider,
                        "geolocation-device-events",
-                       MOZ_UTF16("starting"));
+                       u"starting");
 
   return NS_OK;
 }
 
 void
 nsGeolocationService::StopDisconnectTimer()
 {
   if (mDisconnectTimer) {
@@ -1044,17 +1044,17 @@ nsGeolocationService::StopDevice()
     return;
   }
 
   mHigherAccuracy = false;
 
   mProvider->Shutdown();
   obs->NotifyObservers(mProvider,
                        "geolocation-device-events",
-                       MOZ_UTF16("shutdown"));
+                       u"shutdown");
 }
 
 StaticRefPtr<nsGeolocationService> nsGeolocationService::sService;
 
 already_AddRefed<nsGeolocationService>
 nsGeolocationService::GetGeolocationService()
 {
   RefPtr<nsGeolocationService> result;
--- a/dom/html/HTMLFormElement.cpp
+++ b/dom/html/HTMLFormElement.cpp
@@ -927,22 +927,22 @@ HTMLFormElement::DoSecureToInsecureSubmi
     getter_AddRefs(stringBundle));
   if (NS_FAILED(rv)) {
     return rv;
   }
   nsAutoString title;
   nsAutoString message;
   nsAutoString cont;
   stringBundle->GetStringFromName(
-    MOZ_UTF16("formPostSecureToInsecureWarning.title"), getter_Copies(title));
+    u"formPostSecureToInsecureWarning.title", getter_Copies(title));
   stringBundle->GetStringFromName(
-    MOZ_UTF16("formPostSecureToInsecureWarning.message"),
+    u"formPostSecureToInsecureWarning.message",
     getter_Copies(message));
   stringBundle->GetStringFromName(
-    MOZ_UTF16("formPostSecureToInsecureWarning.continue"),
+    u"formPostSecureToInsecureWarning.continue",
     getter_Copies(cont));
   int32_t buttonPressed;
   bool checkState = false; // this is unused (ConfirmEx requires this parameter)
   rv = prompt->ConfirmEx(title.get(), message.get(),
                          (nsIPrompt::BUTTON_TITLE_IS_STRING *
                           nsIPrompt::BUTTON_POS_0) +
                          (nsIPrompt::BUTTON_TITLE_CANCEL *
                           nsIPrompt::BUTTON_POS_1),
@@ -1752,17 +1752,17 @@ HTMLFormElement::GetActionURL(nsIURI** a
 
     // let's log a message to the console that we are upgrading a request
     nsAutoCString scheme;
     rv = actionURL->GetScheme(scheme);
     NS_ENSURE_SUCCESS(rv, rv);
     NS_ConvertUTF8toUTF16 reportScheme(scheme);
 
     const char16_t* params[] = { reportSpec.get(), reportScheme.get() };
-    CSP_LogLocalizedStr(MOZ_UTF16("upgradeInsecureRequest"),
+    CSP_LogLocalizedStr(u"upgradeInsecureRequest",
                         params, ArrayLength(params),
                         EmptyString(), // aSourceFile
                         EmptyString(), // aScriptSample
                         0, // aLineNumber
                         0, // aColumnNumber
                         nsIScriptError::warningFlag, "CSP",
                         document->InnerWindowID());
   }
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -7833,25 +7833,25 @@ HTMLInputElement::SetFilePickerFiltersFr
 
     int32_t filterMask = 0;
     nsString filterName;
     nsString extensionListStr;
 
     // First, check for image/audio/video filters...
     if (token.EqualsLiteral("image/*")) {
       filterMask = nsIFilePicker::filterImages;
-      filterBundle->GetStringFromName(MOZ_UTF16("imageFilter"),
+      filterBundle->GetStringFromName(u"imageFilter",
                                       getter_Copies(extensionListStr));
     } else if (token.EqualsLiteral("audio/*")) {
       filterMask = nsIFilePicker::filterAudio;
-      filterBundle->GetStringFromName(MOZ_UTF16("audioFilter"),
+      filterBundle->GetStringFromName(u"audioFilter",
                                       getter_Copies(extensionListStr));
     } else if (token.EqualsLiteral("video/*")) {
       filterMask = nsIFilePicker::filterVideo;
-      filterBundle->GetStringFromName(MOZ_UTF16("videoFilter"),
+      filterBundle->GetStringFromName(u"videoFilter",
                                       getter_Copies(extensionListStr));
     } else if (token.First() == '.') {
       if (token.Contains(';') || token.Contains('*')) {
         // Ignore this filter as it contains reserved characters
         continue;
       }
       extensionListStr = NS_LITERAL_STRING("*") + token;
       filterName = extensionListStr;
--- a/dom/html/ImageDocument.cpp
+++ b/dom/html/ImageDocument.cpp
@@ -780,17 +780,17 @@ ImageDocument::UpdateTitleAndCharset()
   }
 
   nsXPIDLString status;
   if (mImageIsResized) {
     nsAutoString ratioStr;
     ratioStr.AppendInt(NSToCoordFloor(GetRatio() * 100));
 
     const char16_t* formatString[1] = { ratioStr.get() };
-    mStringBundle->FormatStringFromName(MOZ_UTF16("ScaledImage"),
+    mStringBundle->FormatStringFromName(u"ScaledImage",
                                         formatString, 1,
                                         getter_Copies(status));
   }
 
   static const char* const formatNames[4] = 
   {
     "ImageTitleWithNeitherDimensionsNorFile",
     "ImageTitleWithoutDimensions",
--- a/dom/html/PluginDocument.cpp
+++ b/dom/html/PluginDocument.cpp
@@ -163,17 +163,17 @@ PluginDocument::StartDocumentLoad(const 
                                   bool                aReset,
                                   nsIContentSink*     aSink)
 {
   // do not allow message panes to host full-page plugins
   // returning an error causes helper apps to take over
   nsCOMPtr<nsIDocShellTreeItem> dsti (do_QueryInterface(aContainer));
   if (dsti) {
     bool isMsgPane = false;
-    dsti->NameEquals(MOZ_UTF16("messagepane"), &isMsgPane);
+    dsti->NameEquals(u"messagepane", &isMsgPane);
     if (isMsgPane) {
       return NS_ERROR_FAILURE;
     }
   }
 
   nsresult rv =
     MediaDocument::StartDocumentLoad(aCommand, aChannel, aLoadGroup, aContainer,
                                      aDocListener, aReset, aSink);
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -1299,21 +1299,21 @@ MapLangAttributeInto(const nsMappedAttri
     if (lang->GetUnit() == eCSSUnit_Null) {
       lang->SetStringValue(langValue->GetStringValue(), eCSSUnit_Ident);
     }
   }
   if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Text)) {
     nsCSSValue* emphasisPos = aData->ValueForTextEmphasisPosition();
     if (emphasisPos->GetUnit() == eCSSUnit_Null) {
       const nsAString& lang = langValue->GetStringValue();
-      if (nsStyleUtil::MatchesLanguagePrefix(lang, MOZ_UTF16("zh"))) {
+      if (nsStyleUtil::MatchesLanguagePrefix(lang, u"zh")) {
         emphasisPos->SetIntValue(NS_STYLE_TEXT_EMPHASIS_POSITION_DEFAULT_ZH,
                                 eCSSUnit_Enumerated);
-      } else if (nsStyleUtil::MatchesLanguagePrefix(lang, MOZ_UTF16("ja")) ||
-                 nsStyleUtil::MatchesLanguagePrefix(lang, MOZ_UTF16("mn"))) {
+      } else if (nsStyleUtil::MatchesLanguagePrefix(lang, u"ja") ||
+                 nsStyleUtil::MatchesLanguagePrefix(lang, u"mn")) {
         // This branch is currently no part of the spec.
         // See bug 1040668 comment 69 and comment 75.
         emphasisPos->SetIntValue(NS_STYLE_TEXT_EMPHASIS_POSITION_DEFAULT,
                                 eCSSUnit_Enumerated);
       }
     }
   }
 }
@@ -3077,17 +3077,17 @@ nsGenericHTMLFormElementWithState::Resto
     history->RemoveState(mStateKey);
     return result;
   }
 
   return false;
 }
 
 void
-nsGenericHTMLFormElementWithState::NodeInfoChanged(mozilla::dom::NodeInfo* aOldNodeInfo)
+nsGenericHTMLFormElementWithState::NodeInfoChanged()
 {
   mStateKey.SetIsVoid(true);
 }
 
 nsSize
 nsGenericHTMLElement::GetWidthHeightForImage(RefPtr<imgRequestProxy>& aImageRequest)
 {
   nsSize size(0,0);
--- a/dom/html/nsGenericHTMLElement.h
+++ b/dom/html/nsGenericHTMLElement.h
@@ -1395,17 +1395,17 @@ public:
    *         value of RestoreState() otherwise.
    */
   bool RestoreFormControlState();
 
   /**
    * Called when we have been cloned and adopted, and the information of the
    * node has been changed.
    */
-  virtual void NodeInfoChanged(mozilla::dom::NodeInfo* aOldNodeInfo) override;
+  virtual void NodeInfoChanged() override;
 
 protected:
   /* Generates the state key for saving the form state in the session if not
      computed already. The result is stored in mStateKey on success */
   nsresult GenerateStateKey();
 
   /* Used to store the key to that element in the session. Is void until
      GenerateStateKey has been used */
--- a/dom/html/nsHTMLContentSink.cpp
+++ b/dom/html/nsHTMLContentSink.cpp
@@ -167,18 +167,16 @@ protected:
   // Boolean indicating whether we've seen a <head> tag that might have had
   // attributes once already.
   bool mHaveSeenHead;
 
   // Boolean indicating whether we've notified insertion of our root content
   // yet.  We want to make sure to only do this once.
   bool mNotifiedRootInsertion;
 
-  mozilla::dom::NodeInfo* mNodeInfoCache[NS_HTML_TAG_MAX + 1];
-
   nsresult FlushTags() override;
 
   // Routines for tags that require special handling
   nsresult CloseHTML();
   nsresult OpenBody();
   nsresult CloseBody();
 
   void CloseHeadContext();
@@ -212,17 +210,17 @@ public:
   void DidAddContent(nsIContent* aContent);
   void UpdateChildCounts();
 
 private:
   // Function to check whether we've notified for the current content.
   // What this actually does is check whether we've notified for all
   // of the parent's kids.
   bool HaveNotifiedForCurrentContent() const;
-  
+
 public:
   HTMLContentSink* mSink;
   int32_t mNotifyLevel;
 
   struct Node {
     nsHTMLTag mType;
     nsGenericHTMLElement* mContent;
     uint32_t mNumFlushed;
@@ -245,17 +243,17 @@ NS_NewHTMLElement(Element** aResult, alr
   RefPtr<mozilla::dom::NodeInfo> nodeInfo = aNodeInfo;
 
   nsIParserService* parserService = nsContentUtils::GetParserService();
   if (!parserService)
     return NS_ERROR_OUT_OF_MEMORY;
 
   nsIAtom *name = nodeInfo->NameAtom();
 
-  NS_ASSERTION(nodeInfo->NamespaceEquals(kNameSpaceID_XHTML), 
+  NS_ASSERTION(nodeInfo->NamespaceEquals(kNameSpaceID_XHTML),
                "Trying to HTML elements that don't have the XHTML namespace");
 
   // Per the Custom Element specification, unknown tags that are valid custom
   // element names should be HTMLElement instead of HTMLUnknownElement.
   int32_t tag = parserService->HTMLCaseSensitiveAtomTagToId(name);
   if ((tag == eHTMLTag_userdefined &&
       nsContentUtils::IsCustomElementName(name)) ||
       aIs) {
@@ -669,44 +667,32 @@ HTMLContentSink::~HTMLContentSink()
 
   if (mCurrentContext == mHeadContext) {
     mCurrentContext = nullptr;
   }
 
   delete mCurrentContext;
 
   delete mHeadContext;
-
-  for (i = 0; uint32_t(i) < ArrayLength(mNodeInfoCache); ++i) {
-    NS_IF_RELEASE(mNodeInfoCache[i]);
-  }
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLContentSink)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLContentSink, nsContentSink)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mHTMLDocument)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoot)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mBody)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mHead)
-  for (uint32_t i = 0; i < ArrayLength(tmp->mNodeInfoCache); ++i) {
-    NS_IF_RELEASE(tmp->mNodeInfoCache[i]);
-  }
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLContentSink,
                                                   nsContentSink)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHTMLDocument)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBody)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHead)
-  for (uint32_t i = 0; i < ArrayLength(tmp->mNodeInfoCache); ++i) {
-    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mNodeInfoCache[i]");
-    cb.NoteNativeChild(tmp->mNodeInfoCache[i],
-                       NS_CYCLE_COLLECTION_PARTICIPANT(NodeInfo));
-  }
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLContentSink)
   NS_INTERFACE_TABLE_BEGIN
     NS_INTERFACE_TABLE_ENTRY(HTMLContentSink, nsIContentSink)
     NS_INTERFACE_TABLE_ENTRY(HTMLContentSink, nsIHTMLContentSink)
   NS_INTERFACE_TABLE_END
 NS_INTERFACE_TABLE_TAIL_INHERITING(nsContentSink)
@@ -716,17 +702,17 @@ NS_IMPL_RELEASE_INHERITED(HTMLContentSin
 
 nsresult
 HTMLContentSink::Init(nsIDocument* aDoc,
                       nsIURI* aURI,
                       nsISupports* aContainer,
                       nsIChannel* aChannel)
 {
   NS_ENSURE_TRUE(aContainer, NS_ERROR_NULL_POINTER);
-  
+
   nsresult rv = nsContentSink::Init(aDoc, aURI, aContainer, aChannel);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   aDoc->AddObserver(this);
   mIsDocumentObserver = true;
   mHTMLDocument = do_QueryInterface(aDoc);
@@ -834,17 +820,17 @@ HTMLContentSink::DidBuildModel(bool aTer
   // Make sure we no longer respond to document mutations.  We've flushed all
   // our notifications out, so there's no need to do anything else here.
 
   // XXXbz I wonder whether we could End() our contexts here too, or something,
   // just to make sure we no longer notify...  Or is the mIsDocumentObserver
   // thing sufficient?
   mDocument->RemoveObserver(this);
   mIsDocumentObserver = false;
-  
+
   mDocument->EndLoad();
 
   DropParserAndPerfHint();
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -895,17 +881,17 @@ HTMLContentSink::OpenBody()
   mBody = mCurrentContext->mStack[mCurrentContext->mStackPos - 1].mContent;
 
   if (mCurrentContext->mStackPos > 1) {
     int32_t parentIndex    = mCurrentContext->mStackPos - 2;
     nsGenericHTMLElement *parent = mCurrentContext->mStack[parentIndex].mContent;
     int32_t numFlushed     = mCurrentContext->mStack[parentIndex].mNumFlushed;
     int32_t childCount = parent->GetChildCount();
     NS_ASSERTION(numFlushed < childCount, "Already notified on the body?");
-    
+
     int32_t insertionPoint =
       mCurrentContext->mStack[parentIndex].mInsertionPoint;
 
     // XXX: I have yet to see a case where numFlushed is non-zero and
     // insertionPoint is not -1, but this code will try to handle
     // those cases too.
 
     uint32_t oldUpdates = mUpdatesInNotification;
@@ -1091,17 +1077,17 @@ HTMLContentSink::FlushPendingNotificatio
 
 nsresult
 HTMLContentSink::FlushTags()
 {
   if (!mNotifiedRootInsertion) {
     NotifyRootInsertion();
     return NS_OK;
   }
-  
+
   return mCurrentContext ? mCurrentContext->FlushTags() : NS_OK;
 }
 
 NS_IMETHODIMP
 HTMLContentSink::SetDocumentCharset(nsACString& aCharset)
 {
   MOZ_ASSERT_UNREACHABLE("<meta charset> case doesn't occur with about:blank");
   return NS_ERROR_NOT_IMPLEMENTED;
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -787,17 +787,17 @@ nsHTMLDocument::StartDocumentLoad(const 
     nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
     NS_ASSERTION(NS_SUCCEEDED(rv) && bundleService, "The bundle service could not be loaded");
     nsCOMPtr<nsIStringBundle> bundle;
     rv = bundleService->CreateBundle("chrome://global/locale/browser.properties",
                                      getter_AddRefs(bundle));
     NS_ASSERTION(NS_SUCCEEDED(rv) && bundle, "chrome://global/locale/browser.properties could not be loaded");
     nsXPIDLString title;
     if (bundle) {
-      bundle->GetStringFromName(MOZ_UTF16("plainText.wordWrap"), getter_Copies(title));
+      bundle->GetStringFromName(u"plainText.wordWrap", getter_Copies(title));
     }
     SetSelectedStyleSheetSet(title);
   }
 
   // parser the content of the URI
   mParser->Parse(uri, nullptr, (void *)this);
 
   return rv;
--- a/dom/html/test/test_bug332246.html
+++ b/dom/html/test/test_bug332246.html
@@ -32,44 +32,40 @@ https://bugzilla.mozilla.org/show_bug.cg
 </div>
 
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 /** Test for Bug 332246 **/
 
-function isWithFuzz(itIs, itShouldBe, fuzz, description) {
-  ok(Math.abs(itIs - itShouldBe) <= fuzz, `${description} - expected a value between ${itShouldBe - fuzz} and ${itShouldBe + fuzz}, got ${itIs}`);
-}
-
 var a1 = document.getElementById('a1');
 var a2 = document.getElementById('a2');
-isWithFuzz(a1.scrollHeight, 400, 1, "Wrong a1.scrollHeight");
+is(a1.scrollHeight, 400, "Wrong a1.scrollHeight");
 is(a1.offsetHeight, 100, "Wrong a1.offsetHeight");
 a2.scrollIntoView(true);
 is(a1.scrollTop, 100, "Wrong scrollTop value after a2.scrollIntoView(true)");
 a2.scrollIntoView(false);
 is(a1.scrollTop, 200, "Wrong scrollTop value after a2.scrollIntoView(false)");
 
 var b1 = document.getElementById('b1');
 var b2 = document.getElementById('b2');
-isWithFuzz(b1.scrollHeight, 420, 1, "Wrong b1.scrollHeight");
+is(b1.scrollHeight, 420, "Wrong b1.scrollHeight");
 is(b1.offsetHeight, 100, "Wrong b1.offsetHeight");
 b2.scrollIntoView(true);
 is(b1.scrollTop, 100, "Wrong scrollTop value after b2.scrollIntoView(true)");
 b2.scrollIntoView(false);
 is(b1.scrollTop, 220, "Wrong scrollTop value after b2.scrollIntoView(false)");
 
 var c1 = document.getElementById('c1');
 var c2 = document.getElementById('c2');
-isWithFuzz(c1.scrollHeight, 320, 1, "Wrong c1.scrollHeight");
+is(c1.scrollHeight, 320, "Wrong c1.scrollHeight");
 is(c1.offsetHeight, 100, "Wrong c1.offsetHeight");
 c2.scrollIntoView(true);
 is(c1.scrollTop, 100, "Wrong scrollTop value after c2.scrollIntoView(true)");
 c2.scrollIntoView(false);
-isWithFuzz(c1.scrollTop, 220, 1, "Wrong scrollTop value after c2.scrollIntoView(false)");
+is(c1.scrollTop, 220, "Wrong scrollTop value after c2.scrollIntoView(false)");
 
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/indexedDB/IDBEvents.cpp
+++ b/dom/indexedDB/IDBEvents.cpp
@@ -14,24 +14,24 @@
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::indexedDB;
 
 namespace mozilla {
 namespace dom {
 namespace indexedDB {
 
-const char16_t* kAbortEventType = MOZ_UTF16("abort");
-const char16_t* kBlockedEventType = MOZ_UTF16("blocked");
-const char16_t* kCompleteEventType = MOZ_UTF16("complete");
-const char16_t* kErrorEventType = MOZ_UTF16("error");
-const char16_t* kSuccessEventType = MOZ_UTF16("success");
-const char16_t* kUpgradeNeededEventType = MOZ_UTF16("upgradeneeded");
-const char16_t* kVersionChangeEventType = MOZ_UTF16("versionchange");
-const char16_t* kCloseEventType = MOZ_UTF16("close");
+const char16_t* kAbortEventType = u"abort";
+const char16_t* kBlockedEventType = u"blocked";
+const char16_t* kCompleteEventType = u"complete";
+const char16_t* kErrorEventType = u"error";
+const char16_t* kSuccessEventType = u"success";
+const char16_t* kUpgradeNeededEventType = u"upgradeneeded";
+const char16_t* kVersionChangeEventType = u"versionchange";
+const char16_t* kCloseEventType = u"close";
 
 already_AddRefed<nsIDOMEvent>
 CreateGenericEvent(EventTarget* aOwner,
                    const nsDependentString& aType,
                    Bubbles aBubbles,
                    Cancelable aCancelable)
 {
   RefPtr<Event> event = new Event(aOwner, nullptr, nullptr);
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -1249,30 +1249,29 @@ ContentChild::RecvInitCompositor(Endpoin
 }
 
 bool
 ContentChild::RecvInitImageBridge(Endpoint<PImageBridgeChild>&& aEndpoint)
 {
   return ImageBridgeChild::InitForContent(Move(aEndpoint));
 }
 
+bool
+ContentChild::RecvInitVRManager(Endpoint<PVRManagerChild>&& aEndpoint)
+{
+  return gfx::VRManagerChild::InitForContent(Move(aEndpoint));
+}
+
 PSharedBufferManagerChild*
 ContentChild::AllocPSharedBufferManagerChild(mozilla::ipc::Transport* aTransport,
                                               base::ProcessId aOtherProcess)
 {
   return SharedBufferManagerChild::StartUpInChildProcess(aTransport, aOtherProcess);
 }
 
-gfx::PVRManagerChild*
-ContentChild::AllocPVRManagerChild(Transport* aTransport,
-                                   ProcessId aOtherProcess)
-{
-  return gfx::VRManagerChild::StartUpInChildProcess(aTransport, aOtherProcess);
-}
-
 PBackgroundChild*
 ContentChild::AllocPBackgroundChild(Transport* aTransport,
                                     ProcessId aOtherProcess)
 {
   return BackgroundChild::Alloc(aTransport, aOtherProcess);
 }
 
 PProcessHangMonitorChild*
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -147,29 +147,27 @@ public:
   AllocPAPZChild(const TabId& aTabId) override;
   bool
   DeallocPAPZChild(PAPZChild* aActor) override;
 
   bool
   RecvInitCompositor(Endpoint<PCompositorBridgeChild>&& aEndpoint) override;
   bool
   RecvInitImageBridge(Endpoint<PImageBridgeChild>&& aEndpoint) override;
+  bool
+  RecvInitVRManager(Endpoint<PVRManagerChild>&& aEndpoint) override;
 
   PSharedBufferManagerChild*
   AllocPSharedBufferManagerChild(mozilla::ipc::Transport* aTransport,
                                   base::ProcessId aOtherProcess) override;
 
   PProcessHangMonitorChild*
   AllocPProcessHangMonitorChild(Transport* aTransport,
                                 ProcessId aOtherProcess) override;
 
-  PVRManagerChild*
-  AllocPVRManagerChild(Transport* aTransport,
-                       ProcessId aOtherProcess) override;
-
   virtual bool RecvSetProcessSandbox(const MaybeFileDesc& aBroker) override;
 
   PBackgroundChild*
   AllocPBackgroundChild(Transport* aTransport, ProcessId aOtherProcess)
                         override;
 
   virtual PBrowserChild* AllocPBrowserChild(const TabId& aTabId,
                                             const IPCTabContext& aContext,
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -270,18 +270,16 @@ using namespace mozilla::system;
 #ifdef XP_WIN
 #include "mozilla/widget/AudioSession.h"
 #endif
 
 #ifdef MOZ_CRASHREPORTER
 #include "nsThread.h"
 #endif
 
-#include "VRManagerParent.h"            // for VRManagerParent
-
 // For VP9Benchmark::sBenchmarkFpsPref
 #include "Benchmark.h"
 
 static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
 
 #if defined(XP_WIN)
 // e10s forced enable pref, defined in nsAppRunner.cpp
 extern const char* kForceEnableE10sPref;
@@ -2444,18 +2442,21 @@ ContentParent::InitInternal(ProcessPrior
         Endpoint<PImageBridgeChild> endpoint;
         DebugOnly<bool> opened =
           gpm->CreateContentImageBridge(OtherPid(), &endpoint);
         MOZ_ASSERT(opened);
         Unused << SendInitImageBridge(Move(endpoint));
       }
 
       {
-        DebugOnly<bool> opened = gfx::PVRManager::Open(this);
+        Endpoint<PVRManagerChild> endpoint;
+        DebugOnly<bool> opened =
+          gpm->CreateContentVRManager(OtherPid(), &endpoint);
         MOZ_ASSERT(opened);
+        Unused << SendInitVRManager(Move(endpoint));
       }
     }
 #ifdef MOZ_WIDGET_GONK
     DebugOnly<bool> opened = PSharedBufferManager::Open(this);
     MOZ_ASSERT(opened);
 #endif
   }
 
@@ -3236,23 +3237,16 @@ ContentParent::AllocPAPZParent(const Tab
 }
 
 bool
 ContentParent::DeallocPAPZParent(PAPZParent* aActor)
 {
   return true;
 }
 
-gfx::PVRManagerParent*
-ContentParent::AllocPVRManagerParent(Transport* aTransport,
-                                     ProcessId aOtherProcess)
-{
-  return gfx::VRManagerParent::CreateCrossProcess(aTransport, aOtherProcess);
-}
-
 PBackgroundParent*
 ContentParent::AllocPBackgroundParent(Transport* aTransport,
                                       ProcessId aOtherProcess)
 {
   return BackgroundParent::Alloc(this, aTransport, aOtherProcess);
 }
 
 PProcessHangMonitorParent*
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -752,20 +752,16 @@ private:
   PBackgroundParent*
   AllocPBackgroundParent(Transport* aTransport, ProcessId aOtherProcess)
                          override;
 
   PProcessHangMonitorParent*
   AllocPProcessHangMonitorParent(Transport* aTransport,
                                  ProcessId aOtherProcess) override;
 
-  PVRManagerParent*
-  AllocPVRManagerParent(Transport* aTransport,
-                        ProcessId aOtherProcess) override;
-
   virtual bool RecvGetProcessAttributes(ContentParentId* aCpId,
                                         bool* aIsForApp,
                                         bool* aIsForBrowser) override;
 
   virtual bool
   RecvGetXPCOMProcessAttributes(bool* aIsOffline,
                                 bool* aIsConnected,
                                 bool* aIsLangRTL,
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -382,17 +382,16 @@ struct BlobURLRegistrationData
 
 prio(normal upto urgent) sync protocol PContent
 {
     parent spawns PPluginModule;
 
     parent opens PProcessHangMonitor;
     parent opens PSharedBufferManager;
     parent opens PGMPService;
-    parent opens PVRManager;
     child opens PBackground;
 
     manages PAPZ;
     manages PBlob;
     manages PBluetooth;
     manages PBrowser;
     manages PCellBroadcast;
     manages PContentPermissionRequest;
@@ -467,16 +466,17 @@ both:
     // ignored and should be null/zero.
     async PWebBrowserPersistDocument(nullable PBrowser aBrowser,
                                      uint64_t aOuterWindowID);
 
 child:
     // Give the content process its endpoints to the compositor.
     async InitCompositor(Endpoint<PCompositorBridgeChild> compositor);
     async InitImageBridge(Endpoint<PImageBridgeChild> bridge);
+    async InitVRManager(Endpoint<PVRManagerChild> endpoint);
 
     /**
      * Enable system-level sandboxing features, if available.  Can
      * usually only be performed zero or one times.  The child may
      * abnormally exit if this fails; the details are OS-specific.
      */
     async SetProcessSandbox(MaybeFileDesc aBroker);
 
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -797,17 +797,16 @@ TabChild::Init()
     LayoutDeviceIntRect(0, 0, 0, 0),
     nullptr                  // HandleWidgetEvent
   );
 
   baseWindow->InitWindow(0, mPuppetWidget, 0, 0, 0, 0);
   baseWindow->Create();
 
   // Set the tab context attributes then pass to docShell
-  SetPrivateBrowsingAttributes(mChromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW);
   NotifyTabContextUpdated();
 
   // IPC uses a WebBrowser object for which DNS prefetching is turned off
   // by default. But here we really want it, so enable it explicitly
   nsCOMPtr<nsIWebBrowserSetup> webBrowserSetup =
     do_QueryInterface(baseWindow);
   if (webBrowserSetup) {
     webBrowserSetup->SetProperty(nsIWebBrowserSetup::SETUP_ALLOW_DNS_PREFETCH,
@@ -819,18 +818,17 @@ TabChild::Init()
 
   nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
   MOZ_ASSERT(docShell);
 
   docShell->SetAffectPrivateSessionLifetime(
       mChromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME);
   nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(WebNavigation());
   MOZ_ASSERT(loadContext);
-  loadContext->SetPrivateBrowsing(
-      mChromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW);
+  loadContext->SetPrivateBrowsing(OriginAttributesRef().mPrivateBrowsingId > 0);
   loadContext->SetRemoteTabs(
       mChromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW);
 
   // Few lines before, baseWindow->Create() will end up creating a new
   // window root in nsGlobalWindow::SetDocShell.
   // Then this chrome event handler, will be inherited to inner windows.
   // We want to also set it to the docshell so that inner windows
   // and any code that has access to the docshell
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -2666,17 +2666,17 @@ TabParent::RecvAudioChannelActivityNotif
   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
   if (os) {
     nsAutoCString topic;
     topic.Assign("audiochannel-activity-");
     topic.Append(AudioChannelService::GetAudioChannelTable()[aAudioChannel].tag);
 
     os->NotifyObservers(NS_ISUPPORTS_CAST(nsITabParent*, this),
                         topic.get(),
-                        aActive ? MOZ_UTF16("active") : MOZ_UTF16("inactive"));
+                        aActive ? u"active" : u"inactive");
   }
 
   return true;
 }
 
 already_AddRefed<nsFrameLoader>
 TabParent::GetFrameLoader(bool aUseCachedFrameLoaderAfterDestroy) const
 {
--- a/dom/mathml/nsMathMLElement.cpp
+++ b/dom/mathml/nsMathMLElement.cpp
@@ -1082,18 +1082,17 @@ nsMathMLElement::SetAttr(int32_t aNameSp
   // is important here!  The attribute is not set until SetAttr returns, and
   // we will need the updated attribute value because notifying the document
   // that content states have changed will call IntrinsicState, which will try
   // to get updated information about the visitedness from Link.
   if (aName == nsGkAtoms::href &&
       (aNameSpaceID == kNameSpaceID_None ||
        aNameSpaceID == kNameSpaceID_XLink)) {
     if (aNameSpaceID == kNameSpaceID_XLink) {
-      WarnDeprecated(MOZ_UTF16("xlink:href"),
-                     MOZ_UTF16("href"), OwnerDoc());
+      WarnDeprecated(u"xlink:href", u"href", OwnerDoc());
     }
     Link::ResetLinkState(!!aNotify, true);
   }
 
   return rv;
 }
 
 nsresult
--- a/dom/media/CubebUtils.cpp
+++ b/dom/media/CubebUtils.cpp
@@ -142,17 +142,17 @@ void InitBrandName()
   nsXPIDLString brandName;
   nsCOMPtr<nsIStringBundleService> stringBundleService =
     mozilla::services::GetStringBundleService();
   if (stringBundleService) {
     nsCOMPtr<nsIStringBundle> brandBundle;
     nsresult rv = stringBundleService->CreateBundle(kBrandBundleURL,
                                            getter_AddRefs(brandBundle));
     if (NS_SUCCEEDED(rv)) {
-      rv = brandBundle->GetStringFromName(MOZ_UTF16("brandShortName"),
+      rv = brandBundle->GetStringFromName(u"brandShortName",
                                           getter_Copies(brandName));
       NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
           "Could not get the program name for a cubeb stream.");
     }
   }
   /* cubeb expects a c-string. */
   const char* ascii = NS_LossyConvertUTF16toASCII(brandName).get();
   sBrandName = new char[brandName.Length() + 1];
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -835,24 +835,24 @@ NS_IMETHODIMP
 MediaDevice::GetType(nsAString& aType)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
 VideoDevice::GetType(nsAString& aType)
 {
-  aType.AssignLiteral(MOZ_UTF16("video"));
+  aType.AssignLiteral(u"video");
   return NS_OK;
 }
 
 NS_IMETHODIMP
 AudioDevice::GetType(nsAString& aType)
 {
-  aType.AssignLiteral(MOZ_UTF16("audio"));
+  aType.AssignLiteral(u"audio");
   return NS_OK;
 }
 
 NS_IMETHODIMP
 MediaDevice::GetId(nsAString& aID)
 {
   aID.Assign(mID);
   return NS_OK;
@@ -3047,17 +3047,17 @@ MediaManager::Observe(nsISupports* aSubj
   } else if (!strcmp(aTopic, "getUserMedia:response:deny")) {
     nsString errorMessage(NS_LITERAL_STRING("NotAllowedError"));
 
     if (aSubject) {
       nsCOMPtr<nsISupportsString> msg(do_QueryInterface(aSubject));
       MOZ_ASSERT(msg);
       msg->GetData(errorMessage);
       if (errorMessage.IsEmpty())
-        errorMessage.AssignLiteral(MOZ_UTF16("InternalError"));
+        errorMessage.AssignLiteral(u"InternalError");
     }
 
     nsString key(aData);
     RefPtr<GetUserMediaTask> task;
     mActiveCallbacks.Remove(key, getter_AddRefs(task));
     if (task) {
       task->Denied(errorMessage);
     }
--- a/dom/media/eme/EMEUtils.cpp
+++ b/dom/media/eme/EMEUtils.cpp
@@ -75,20 +75,20 @@ ParseKeySystem(const nsAString& aExpecte
     NS_WARNING("Invalid version in EME keySystem string");
     return false;
   }
   aOutCDMVersion = version;
 
   return true;
 }
 
-static const char16_t* sKeySystems[] = {
-  MOZ_UTF16("org.w3.clearkey"),
-  MOZ_UTF16("com.adobe.primetime"),
-  MOZ_UTF16("com.widevine.alpha"),
+static const char16_t *const sKeySystems[] = {
+  u"org.w3.clearkey",
+  u"com.adobe.primetime",
+  u"com.widevine.alpha",
 };
 
 bool
 ParseKeySystem(const nsAString& aInputKeySystem,
                nsAString& aOutKeySystem,
                int32_t& aOutCDMVersion)
 {
   for (const char16_t* keySystem : sKeySystems) {
--- a/dom/media/gtest/TestEME.cpp
+++ b/dom/media/gtest/TestEME.cpp
@@ -13,48 +13,48 @@ using namespace mozilla;
 struct ParseKeySystemTestCase {
   const char16_t* mInputKeySystemString;
   int32_t mOutCDMVersion;
   bool mShouldPass;
 };
 
 const ParseKeySystemTestCase ParseKeySystemTests[] = {
   {
-    MOZ_UTF16("org.w3.clearkey"),
+    u"org.w3.clearkey",
     NO_CDM_VERSION,
     true,
   }, {
-    MOZ_UTF16("org.w3.clearkey.123"),
+    u"org.w3.clearkey.123",
     123,
     true,
   }, {
-    MOZ_UTF16("org.w3.clearkey.-1"),
+    u"org.w3.clearkey.-1",
     NO_CDM_VERSION,
     false,
   }, {
-    MOZ_UTF16("org.w3.clearkey.NaN"),
+    u"org.w3.clearkey.NaN",
     NO_CDM_VERSION,
     false,
   }, {
-    MOZ_UTF16("org.w3.clearkey.0"),
+    u"org.w3.clearkey.0",
     0,
     true,
   }, {
-    MOZ_UTF16("org.w3.clearkey.123567890123567890123567890123567890123567890"),
+    u"org.w3.clearkey.123567890123567890123567890123567890123567890",
     NO_CDM_VERSION,
     false,
   }, {
-    MOZ_UTF16("org.w3.clearkey.0.1"),
+    u"org.w3.clearkey.0.1",
     NO_CDM_VERSION,
     false,
   }
 };
 
 TEST(EME, EMEParseKeySystem) {
-  const nsAutoString clearkey(MOZ_UTF16("org.w3.clearkey"));
+  const nsAutoString clearkey(u"org.w3.clearkey");
   for (const ParseKeySystemTestCase& test : ParseKeySystemTests) {
     nsAutoString keySystem;
     int32_t version;
     bool rv = ParseKeySystem(nsDependentString(test.mInputKeySystemString),
                              keySystem,
                              version);
     EXPECT_EQ(rv, test.mShouldPass) << "parse should succeed if expected to";
     if (!test.mShouldPass) {
--- a/dom/media/webrtc/MediaEngineDefault.cpp
+++ b/dom/media/webrtc/MediaEngineDefault.cpp
@@ -54,17 +54,17 @@ MediaEngineDefaultVideoSource::MediaEngi
 }
 
 MediaEngineDefaultVideoSource::~MediaEngineDefaultVideoSource()
 {}
 
 void
 MediaEngineDefaultVideoSource::GetName(nsAString& aName) const
 {
-  aName.AssignLiteral(MOZ_UTF16("Default Video Device"));
+  aName.AssignLiteral(u"Default Video Device");
   return;
 }
 
 void
 MediaEngineDefaultVideoSource::GetUUID(nsACString& aUUID) const
 {
   aUUID.AssignLiteral("1041FCBD-3F12-4F7B-9E9B-1EC556DD5676");
   return;
@@ -382,17 +382,17 @@ MediaEngineDefaultAudioSource::MediaEngi
 }
 
 MediaEngineDefaultAudioSource::~MediaEngineDefaultAudioSource()
 {}
 
 void
 MediaEngineDefaultAudioSource::GetName(nsAString& aName) const
 {
-  aName.AssignLiteral(MOZ_UTF16("Default Audio Device"));
+  aName.AssignLiteral(u"Default Audio Device");
   return;
 }
 
 void
 MediaEngineDefaultAudioSource::GetUUID(nsACString& aUUID) const
 {
   aUUID.AssignLiteral("B7CBD7C1-53EF-42F9-8353-73F61C70C092");
   return;
--- a/dom/media/webrtc/MediaEngineTabVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineTabVideoSource.cpp
@@ -117,17 +117,17 @@ MediaEngineTabVideoSource::InitRunnable:
   nsCOMPtr<nsIRunnable> start(new StartRunnable(mVideoSource));
   start->Run();
   return NS_OK;
 }
 
 void
 MediaEngineTabVideoSource::GetName(nsAString_internal& aName) const
 {
-  aName.AssignLiteral(MOZ_UTF16("&getUserMedia.videoSource.tabShare;"));
+  aName.AssignLiteral(u"&getUserMedia.videoSource.tabShare;");
 }
 
 void
 MediaEngineTabVideoSource::GetUUID(nsACString_internal& aUuid) const
 {
   aUuid.AssignLiteral("tab");
 }
 
--- a/dom/network/interfaces/nsITCPSocketCallback.idl
+++ b/dom/network/interfaces/nsITCPSocketCallback.idl
@@ -7,17 +7,17 @@
  * to highly privileged apps. It provides a buffered, non-blocking
  * interface for sending. For receiving, it uses an asynchronous,
  * event handler based interface.
  */
 
 #include "domstubs.idl"
 
 %{C++
-template<class T> class InfallibleTArray;
+#include "nsTArrayForwardDeclare.h"
 %}
 [ref] native nsUint8TArrayRef(InfallibleTArray<uint8_t>);
 [ptr] native JSContextPtr(JSContext);
 
 
 /*
  * This interface is implemented in TCPSocket.cpp as an internal interface
  * for use in cross-process socket implementation.
--- a/dom/notification/Notification.cpp
+++ b/dom/notification/Notification.cpp
@@ -834,21 +834,21 @@ NotificationTelemetryService::RecordSend
 
 NS_IMETHODIMP
 NotificationTelemetryService::Observe(nsISupports* aSubject,
                                       const char* aTopic,
                                       const char16_t* aData)
 {
   uint32_t capability;
   if (strcmp("perm-changed", aTopic) ||
-      !NS_strcmp(MOZ_UTF16("cleared"), aData) ||
+      !NS_strcmp(u"cleared", aData) ||
       !GetNotificationPermission(aSubject, &capability)) {
     return NS_OK;
   }
-  if (!NS_strcmp(MOZ_UTF16("deleted"), aData)) {
+  if (!NS_strcmp(u"deleted", aData)) {
     if (capability == nsIPermissionManager::DENY_ACTION) {
       Telemetry::Accumulate(
         Telemetry::WEB_NOTIFICATION_PERMISSION_REMOVED, 0);
     } else if (capability == nsIPermissionManager::ALLOW_ACTION) {
       Telemetry::Accumulate(
         Telemetry::WEB_NOTIFICATION_PERMISSION_REMOVED, 1);
     }
   }
--- a/dom/performance/PerformanceObserver.cpp
+++ b/dom/performance/PerformanceObserver.cpp
@@ -129,21 +129,21 @@ PerformanceObserver::QueueEntry(Performa
   aEntry->GetEntryType(entryType);
   if (!mEntryTypes.Contains<nsString>(entryType)) {
     return;
   }
 
   mQueuedEntries.AppendElement(aEntry);
 }
 
-static const char16_t* sValidTypeNames[4] = {
-  MOZ_UTF16("mark"),
-  MOZ_UTF16("measure"),
-  MOZ_UTF16("resource"),
-  MOZ_UTF16("server")
+static const char16_t *const sValidTypeNames[4] = {
+  u"mark",
+  u"measure",
+  u"resource",
+  u"server"
 };
 
 void
 PerformanceObserver::Observe(const PerformanceObserverInit& aOptions,
                              ErrorResult& aRv)
 {
   if (aOptions.mEntryTypes.IsEmpty()) {
     aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
--- a/dom/plugins/ipc/PluginHangUIParent.cpp
+++ b/dom/plugins/ipc/PluginHangUIParent.cpp
@@ -382,17 +382,17 @@ PluginHangUIParent::GetHangUIOwnerWindow
   windowHandle = nullptr;
 
   nsresult rv;
   nsCOMPtr<nsIWindowMediator> winMediator(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID,
                                                         &rv));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<mozIDOMWindowProxy> navWin;
-  rv = winMediator->GetMostRecentWindow(MOZ_UTF16("navigator:browser"),
+  rv = winMediator->GetMostRecentWindow(u"navigator:browser",
                                         getter_AddRefs(navWin));
   NS_ENSURE_SUCCESS(rv, rv);
   if (!navWin) {
     return NS_ERROR_FAILURE;
   }
 
   nsPIDOMWindowOuter* win = nsPIDOMWindowOuter::From(navWin);
   nsCOMPtr<nsIWidget> widget = WidgetUtils::DOMWindowToWidget(win);
--- a/dom/presentation/PresentationDeviceManager.cpp
+++ b/dom/presentation/PresentationDeviceManager.cpp
@@ -176,17 +176,17 @@ PresentationDeviceManager::AddDevice(nsI
   MOZ_ASSERT(NS_IsMainThread());
 
   if (NS_WARN_IF(mDevices.Contains(aDevice))) {
     return NS_ERROR_FAILURE;
   }
 
   mDevices.AppendElement(aDevice);
 
-  NotifyDeviceChange(aDevice, MOZ_UTF16("add"));
+  NotifyDeviceChange(aDevice, u"add");
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PresentationDeviceManager::RemoveDevice(nsIPresentationDevice* aDevice)
 {
   NS_ENSURE_ARG(aDevice);
@@ -194,32 +194,32 @@ PresentationDeviceManager::RemoveDevice(
 
   int32_t index = mDevices.IndexOf(aDevice);
   if (NS_WARN_IF(index < 0)) {
     return NS_ERROR_FAILURE;
   }
 
   mDevices.RemoveElementAt(index);
 
-  NotifyDeviceChange(aDevice, MOZ_UTF16("remove"));
+  NotifyDeviceChange(aDevice, u"remove");
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PresentationDeviceManager::UpdateDevice(nsIPresentationDevice* aDevice)
 {
   NS_ENSURE_ARG(aDevice);
   MOZ_ASSERT(NS_IsMainThread());
 
   if (NS_WARN_IF(!mDevices.Contains(aDevice))) {
     return NS_ERROR_FAILURE;
   }
 
-  NotifyDeviceChange(aDevice, MOZ_UTF16("update"));
+  NotifyDeviceChange(aDevice, u"update");
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PresentationDeviceManager::OnSessionRequest(nsIPresentationDevice* aDevice,
                                             const nsAString& aUrl,
                                             const nsAString& aPresentationId,
--- a/dom/quota/ActorsChild.cpp
+++ b/dom/quota/ActorsChild.cpp
@@ -140,17 +140,19 @@ QuotaUsageRequestChild::HandleResponse(n
 }
 
 void
 QuotaUsageRequestChild::HandleResponse(const UsageResponse& aResponse)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mRequest);
 
-  mRequest->SetResult(aResponse.usage(), aResponse.fileUsage());
+  mRequest->SetResult(aResponse.usage(),
+                      aResponse.fileUsage(),
+                      aResponse.limit());
 }
 
 void
 QuotaUsageRequestChild::ActorDestroy(ActorDestroyReason aWhy)
 {
   AssertIsOnOwningThread();
 
   if (mRequest) {
--- a/dom/quota/ActorsParent.cpp
+++ b/dom/quota/ActorsParent.cpp
@@ -1003,21 +1003,26 @@ private:
   virtual bool
   RecvStopIdleMaintenance() override;
 };
 
 class GetUsageOp final
   : public NormalOriginOperationBase
   , public PQuotaUsageRequestParent
 {
+  // If mGetGroupUsage is false, we use mUsageInfo to record the origin usage
+  // and the file usage. Otherwise, we use it to record the group usage and the
+  // limit.
   UsageInfo mUsageInfo;
 
   const UsageParams mParams;
+  nsCString mSuffix;
   nsCString mGroup;
   bool mIsApp;
+  bool mGetGroupUsage;
 
 public:
   explicit GetUsageOp(const UsageRequestParams& aParams);
 
   bool
   Init(Quota* aQuota);
 
 private:
@@ -4578,16 +4583,50 @@ QuotaManager::GetGroupLimit() const
   uint64_t x = std::min<uint64_t>(mTemporaryStorageLimit * .20, 2 GB);
 
   // In low-storage situations, make an exception (while not exceeding the total
   // storage limit).
   return std::min<uint64_t>(mTemporaryStorageLimit,
                             std::max<uint64_t>(x, 10 MB));
 }
 
+void
+QuotaManager::GetGroupUsageAndLimit(const nsACString& aGroup,
+                                    UsageInfo* aUsageInfo)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aUsageInfo);
+
+  {
+    MutexAutoLock lock(mQuotaMutex);
+
+    aUsageInfo->SetLimit(GetGroupLimit());
+    aUsageInfo->ResetUsage();
+
+    GroupInfoPair* pair;
+    if (!mGroupInfoPairs.Get(aGroup, &pair)) {
+      return;
+    }
+
+    // Calculate temporary group usage
+    RefPtr<GroupInfo> temporaryGroupInfo =
+      pair->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY);
+    if (temporaryGroupInfo) {
+      aUsageInfo->AppendToDatabaseUsage(temporaryGroupInfo->mUsage);
+    }
+
+    // Calculate default group usage
+    RefPtr<GroupInfo> defaultGroupInfo =
+      pair->LockedGetGroupInfo(PERSISTENCE_TYPE_DEFAULT);
+    if (defaultGroupInfo) {
+      aUsageInfo->AppendToDatabaseUsage(defaultGroupInfo->mUsage);
+    }
+  }
+}
+
 // static
 void
 QuotaManager::GetStorageId(PersistenceType aPersistenceType,
                            const nsACString& aOrigin,
                            Client::Type aClientType,
                            nsACString& aDatabaseId)
 {
   nsAutoCString str;
@@ -5840,16 +5879,17 @@ Quota::RecvStopIdleMaintenance()
   return true;
 }
 
 GetUsageOp::GetUsageOp(const UsageRequestParams& aParams)
   : NormalOriginOperationBase(Nullable<PersistenceType>(),
                               OriginScope::FromNull(),
                               /* aExclusive */ false)
   , mParams(aParams.get_UsageParams())
+  , mGetGroupUsage(aParams.get_UsageParams().getGroupUsage())
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aParams.type() == UsageRequestParams::TUsageParams);
 }
 
 bool
 GetUsageOp::Init(Quota* aQuota)
 {
@@ -5875,17 +5915,17 @@ GetUsageOp::DoInitOnMainThread()
   nsCOMPtr<nsIPrincipal> principal =
     PrincipalInfoToPrincipal(principalInfo, &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // Figure out which origin we're dealing with.
   nsCString origin;
-  rv = QuotaManager::GetInfoFromPrincipal(principal, nullptr, &mGroup, &origin,
+  rv = QuotaManager::GetInfoFromPrincipal(principal, &mSuffix, &mGroup, &origin,
                                           &mIsApp);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   mOriginScope.SetFromOrigin(origin);
 
   return NS_OK;
@@ -5990,22 +6030,44 @@ GetUsageOp::AddToUsage(QuotaManager* aQu
 
   return NS_OK;
 }
 
 nsresult
 GetUsageOp::DoDirectoryWork(QuotaManager* aQuotaManager)
 {
   AssertIsOnIOThread();
+  MOZ_ASSERT(mUsageInfo.TotalUsage() == 0);
 
   PROFILER_LABEL("Quota", "GetUsageOp::DoDirectoryWork",
                  js::ProfileEntry::Category::OTHER);
 
+  nsresult rv;
+
+  if (mGetGroupUsage) {
+    nsCOMPtr<nsIFile> directory;
+
+    // Ensure origin is initialized first. It will initialize all origins for
+    // temporary storage including origins belonging to our group.
+    rv = aQuotaManager->EnsureOriginIsInitialized(PERSISTENCE_TYPE_TEMPORARY,
+                                                  mSuffix, mGroup,
+                                                  mOriginScope.GetOrigin(),
+                                                  mIsApp,
+                                                  getter_AddRefs(directory));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    // Get cached usage and limit (the method doesn't have to stat any files).
+    aQuotaManager->GetGroupUsageAndLimit(mGroup, &mUsageInfo);
+
+    return NS_OK;
+  }
+
   // Add all the persistent/temporary/default storage files we care about.
-  nsresult rv;
   for (const PersistenceType type : kAllPersistenceTypes) {
     rv = AddToUsage(aQuotaManager, type);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   return NS_OK;
@@ -6024,18 +6086,27 @@ GetUsageOp::SendResults()
     if (mUsageInfo.Canceled()) {
       mResultCode = NS_ERROR_FAILURE;
     }
 
     UsageRequestResponse response;
 
     if (NS_SUCCEEDED(mResultCode)) {
       UsageResponse usageResponse;
+
+      // We'll get the group usage when mGetGroupUsage is true and get the
+      // origin usage when mGetGroupUsage is false.
       usageResponse.usage() = mUsageInfo.TotalUsage();
-      usageResponse.fileUsage() = mUsageInfo.FileUsage();
+
+      if (mGetGroupUsage) {
+        usageResponse.limit() = mUsageInfo.Limit();
+      } else {
+        usageResponse.fileUsage() = mUsageInfo.FileUsage();
+      }
+
       response = usageResponse;
     } else {
       response = mResultCode;
     }
 
     Unused << PQuotaUsageRequestParent::Send__delete__(this, response);
   }
 }
--- a/dom/quota/PQuota.ipdl
+++ b/dom/quota/PQuota.ipdl
@@ -15,16 +15,17 @@ using mozilla::dom::quota::PersistenceTy
 
 namespace mozilla {
 namespace dom {
 namespace quota {
 
 struct UsageParams
 {
   PrincipalInfo principalInfo;
+  bool getGroupUsage;
 };
 
 union UsageRequestParams
 {
   UsageParams;
 };
 
 struct ClearOriginParams
--- a/dom/quota/PQuotaUsageRequest.ipdl
+++ b/dom/quota/PQuotaUsageRequest.ipdl
@@ -7,16 +7,17 @@ include protocol PQuota;
 namespace mozilla {
 namespace dom {
 namespace quota {
 
 struct UsageResponse
 {
   uint64_t usage;
   uint64_t fileUsage;
+  uint64_t limit;
 };
 
 union UsageRequestResponse
 {
   nsresult;
   UsageResponse;
 };
 
--- a/dom/quota/QuotaManager.h
+++ b/dom/quota/QuotaManager.h
@@ -342,16 +342,20 @@ public:
     MOZ_ASSERT(aPersistenceType == PERSISTENCE_TYPE_DEFAULT);
 
     return mDefaultStoragePath;
   }
 
   uint64_t
   GetGroupLimit() const;
 
+  void
+  GetGroupUsageAndLimit(const nsACString& aGroup,
+                        UsageInfo* aUsageInfo);
+
   static void
   GetStorageId(PersistenceType aPersistenceType,
                const nsACString& aOrigin,
                Client::Type aClientType,
                nsACString& aDatabaseId);
 
   static nsresult
   GetInfoFromPrincipal(nsIPrincipal* aPrincipal,
--- a/dom/quota/QuotaManagerService.cpp
+++ b/dom/quota/QuotaManagerService.cpp
@@ -492,39 +492,41 @@ NS_IMPL_ADDREF(QuotaManagerService)
 NS_IMPL_RELEASE_WITH_DESTROY(QuotaManagerService, Destroy())
 NS_IMPL_QUERY_INTERFACE(QuotaManagerService,
                         nsIQuotaManagerService,
                         nsIObserver)
 
 NS_IMETHODIMP
 QuotaManagerService::GetUsageForPrincipal(nsIPrincipal* aPrincipal,
                                           nsIQuotaUsageCallback* aCallback,
+                                          bool aGetGroupUsage,
                                           nsIQuotaUsageRequest** _retval)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aPrincipal);
   MOZ_ASSERT(aCallback);
   MOZ_ASSERT(nsContentUtils::IsCallerChrome());
 
   RefPtr<UsageRequest> request = new UsageRequest(aPrincipal, aCallback);
 
   UsageParams params;
 
   PrincipalInfo& principalInfo = params.principalInfo();
-
   nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (principalInfo.type() != PrincipalInfo::TContentPrincipalInfo &&
       principalInfo.type() != PrincipalInfo::TSystemPrincipalInfo) {
     return NS_ERROR_UNEXPECTED;
   }
 
+  params.getGroupUsage() = aGetGroupUsage;
+
   nsAutoPtr<PendingRequestInfo> info(new UsageRequestInfo(request, params));
 
   rv = InitiateRequest(info);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   request.forget(_retval);
--- a/dom/quota/QuotaRequests.cpp
+++ b/dom/quota/QuotaRequests.cpp
@@ -87,21 +87,22 @@ RequestBase::GetResultCode(nsresult* aRe
     return NS_ERROR_FAILURE;
   }
 
   *aResultCode = mResultCode;
   return NS_OK;
 }
 
 UsageRequest::UsageRequest(nsIPrincipal* aPrincipal,
-	                         nsIQuotaUsageCallback* aCallback)
+                           nsIQuotaUsageCallback* aCallback)
   : RequestBase(aPrincipal)
   , mCallback(aCallback)
   , mUsage(0)
   , mFileUsage(0)
+  , mLimit(0)
   , mBackgroundActor(nullptr)
   , mCanceled(false)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aPrincipal);
   MOZ_ASSERT(aCallback);
 }
 
@@ -120,23 +121,24 @@ UsageRequest::SetBackgroundActor(QuotaUs
   mBackgroundActor = aBackgroundActor;
 
   if (mCanceled) {
     mBackgroundActor->SendCancel();
   }
 }
 
 void
-UsageRequest::SetResult(uint64_t aUsage, uint64_t aFileUsage)
+UsageRequest::SetResult(uint64_t aUsage, uint64_t aFileUsage, uint64_t aLimit)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(!mHaveResultOrErrorCode);
 
   mUsage = aUsage;
   mFileUsage = aFileUsage;
+  mLimit = aLimit;
   mHaveResultOrErrorCode = true;
 
   FireCallback();
 }
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(UsageRequest, RequestBase, mCallback)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(UsageRequest)
@@ -169,16 +171,30 @@ UsageRequest::GetFileUsage(uint64_t* aFi
     return NS_ERROR_FAILURE;
   }
 
   *aFileUsage = mFileUsage;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+UsageRequest::GetLimit(uint64_t* aLimit)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aLimit);
+
+  if (!mHaveResultOrErrorCode) {
+    return NS_ERROR_FAILURE;
+  }
+
+  *aLimit = mLimit;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 UsageRequest::GetCallback(nsIQuotaUsageCallback** aCallback)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aCallback);
 
   NS_IF_ADDREF(*aCallback = mCallback);
   return NS_OK;
 }
--- a/dom/quota/QuotaRequests.h
+++ b/dom/quota/QuotaRequests.h
@@ -69,16 +69,19 @@ class UsageRequest final
   : public RequestBase
   , public nsIQuotaUsageRequest
 {
   nsCOMPtr<nsIQuotaUsageCallback> mCallback;
 
   uint64_t mUsage;
   uint64_t mFileUsage;
 
+  // Group Limit.
+  uint64_t mLimit;
+
   QuotaUsageRequestChild* mBackgroundActor;
 
   bool mCanceled;
 
 public:
   UsageRequest(nsIPrincipal* aPrincipal,
                nsIQuotaUsageCallback* aCallback);
 
@@ -89,17 +92,17 @@ public:
   ClearBackgroundActor()
   {
     AssertIsOnOwningThread();
 
     mBackgroundActor = nullptr;
   }
 
   void
-  SetResult(uint64_t aUsage, uint64_t aFileUsage);
+  SetResult(uint64_t aUsage, uint64_t aFileUsage, uint64_t aLimit);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_FORWARD_NSIQUOTAREQUESTBASE(RequestBase::)
   NS_DECL_NSIQUOTAUSAGEREQUEST
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(UsageRequest, RequestBase)
 
 private:
   ~UsageRequest();
--- a/dom/quota/UsageInfo.h
+++ b/dom/quota/UsageInfo.h
@@ -13,17 +13,17 @@
 #include "mozilla/CheckedInt.h"
 
 BEGIN_QUOTA_NAMESPACE
 
 class UsageInfo
 {
 public:
   UsageInfo()
-  : mCanceled(false), mDatabaseUsage(0), mFileUsage(0)
+  : mCanceled(false), mDatabaseUsage(0), mFileUsage(0), mLimit(0)
   { }
 
   virtual ~UsageInfo()
   { }
 
   bool
   Canceled()
   {
@@ -47,29 +47,41 @@ public:
   }
 
   void
   AppendToFileUsage(uint64_t aUsage)
   {
     IncrementUsage(&mFileUsage, aUsage);
   }
 
+  void
+  SetLimit(uint64_t aLimit)
+  {
+    mLimit = aLimit;
+  }
+
   uint64_t
   DatabaseUsage()
   {
     return mDatabaseUsage;
   }
 
   uint64_t
   FileUsage()
   {
     return mFileUsage;
   }
 
   uint64_t
+  Limit()
+  {
+    return mLimit;
+  }
+
+  uint64_t
   TotalUsage()
   {
     uint64_t totalUsage = mDatabaseUsage;
     IncrementUsage(&totalUsage, mFileUsage);
     return totalUsage;
   }
 
   void
@@ -93,13 +105,14 @@ public:
   }
 
 protected:
   mozilla::Atomic<bool> mCanceled;
 
 private:
   uint64_t mDatabaseUsage;
   uint64_t mFileUsage;
+  uint64_t mLimit;
 };
 
 END_QUOTA_NAMESPACE
 
 #endif // mozilla_dom_quota_usageinfo_h__
--- a/dom/quota/nsIQuotaManagerService.idl
+++ b/dom/quota/nsIQuotaManagerService.idl
@@ -17,20 +17,26 @@ interface nsIQuotaManagerService : nsISu
   /**
    * Schedules an asynchronous callback that will return the total amount of
    * disk space being used by storages for the given origin.
    *
    * @param aPrincipal
    *        A principal for the origin whose usage is being queried.
    * @param aCallback
    *        The callback that will be called when the usage is available.
+   * @param aGetGroupUsage
+   *        An optional flag to indicate whether getting group usage and limit
+   *        or origin usage and file usage. The default value is false.
+   * Note:  Origin usage here represents total usage of an origin. However,
+   *        group usage here represents only non-persistent usage of a group.
    */
   nsIQuotaUsageRequest
   getUsageForPrincipal(in nsIPrincipal aPrincipal,
-                       in nsIQuotaUsageCallback aCallback);
+                       in nsIQuotaUsageCallback aCallback,
+                       [optional] in boolean aGetGroupUsage);
 
   /**
    * Removes all storages. The files may not be deleted immediately depending
    * on prohibitive concurrent operations.
    * Be careful, this removes *all* the data that has ever been stored!
    *
    * If the dom.quotaManager.testing preference is not true the call will be
    * a no-op.
--- a/dom/quota/nsIQuotaRequests.idl
+++ b/dom/quota/nsIQuotaRequests.idl
@@ -20,16 +20,18 @@ interface nsIQuotaRequestBase : nsISuppo
 
 [scriptable, uuid(166e28e6-cf6d-4927-a6d7-b51bca9d3469)]
 interface nsIQuotaUsageRequest : nsIQuotaRequestBase
 {
   readonly attribute unsigned long long usage;
 
   readonly attribute unsigned long long fileUsage;
 
+  readonly attribute unsigned long long limit;
+
   attribute nsIQuotaUsageCallback callback;
 
   void
   cancel();
 };
 
 [scriptable, uuid(22890e3e-ff25-4372-9684-d901060e2f6c)]
 interface nsIQuotaRequest : nsIQuotaRequestBase
--- a/dom/security/ContentVerifier.cpp
+++ b/dom/security/ContentVerifier.cpp
@@ -2,374 +2,229 @@
 /* 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 "ContentVerifier.h"
 
 #include "mozilla/fallible.h"
 #include "mozilla/Logging.h"
-#include "mozilla/Preferences.h"
-#include "mozilla/StaticPtr.h"
-#include "nsCharSeparatedTokenizer.h"
+#include "MainThreadUtils.h"
 #include "nsIInputStream.h"
 #include "nsIRequest.h"
-#include "nssb64.h"
-#include "nsSecurityHeaderParser.h"
 #include "nsServiceManagerUtils.h"
 #include "nsStringStream.h"
-#include "nsThreadUtils.h"
 
 using namespace mozilla;
 
 static LazyLogModule gContentVerifierPRLog("ContentVerifier");
 #define CSV_LOG(args) MOZ_LOG(gContentVerifierPRLog, LogLevel::Debug, args)
 
-// Content-Signature prefix
-const nsLiteralCString kPREFIX = NS_LITERAL_CSTRING("Content-Signature:\x00");
-
-NS_IMPL_ISUPPORTS(ContentVerifier, nsIStreamListener, nsISupports);
+NS_IMPL_ISUPPORTS(ContentVerifier,
+                  nsIContentSignatureReceiverCallback,
+                  nsIStreamListener);
 
 nsresult
-ContentVerifier::Init(const nsAString& aContentSignatureHeader)
+ContentVerifier::Init(const nsACString& aContentSignatureHeader,
+                      nsIRequest* aRequest, nsISupports* aContext)
 {
-  mVks = Preferences::GetString("browser.newtabpage.remote.keys");
-
-  if (aContentSignatureHeader.IsEmpty() || mVks.IsEmpty()) {
-    CSV_LOG(
-      ("Content-Signature header and verification keys must not be empty!\n"));
+  MOZ_ASSERT(NS_IsMainThread());
+  if (aContentSignatureHeader.IsEmpty()) {
+    CSV_LOG(("Content-Signature header must not be empty!\n"));
     return NS_ERROR_INVALID_SIGNATURE;
   }
 
-  nsresult rv = ParseContentSignatureHeader(aContentSignatureHeader);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_SIGNATURE);
-  return CreateContext();
+  // initialise the content signature "service"
+  nsresult rv;
+  mVerifier =
+    do_CreateInstance("@mozilla.org/security/contentsignatureverifier;1", &rv);
+  if (NS_FAILED(rv) || !mVerifier) {
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+
+  // Keep references to the request and context. We need them in FinishSignature
+  // and the ContextCreated callback.
+  mContentRequest = aRequest;
+  mContentContext = aContext;
+
+  rv = mVerifier->CreateContextWithoutCertChain(
+    this, aContentSignatureHeader,
+    NS_LITERAL_CSTRING("remote-newtab-signer.mozilla.org"));
+  if (NS_FAILED(rv)){
+    mVerifier = nullptr;
+  }
+  return rv;
 }
 
 /**
  * Implement nsIStreamListener
  * We buffer the entire content here and kick off verification
  */
 NS_METHOD
 AppendNextSegment(nsIInputStream* aInputStream, void* aClosure,
                   const char* aRawSegment, uint32_t aToOffset, uint32_t aCount,
                   uint32_t* outWrittenCount)
 {
   FallibleTArray<nsCString>* decodedData =
     static_cast<FallibleTArray<nsCString>*>(aClosure);
-  nsAutoCString segment(aRawSegment, aCount);
+  nsDependentCSubstring segment(aRawSegment, aCount);
   if (!decodedData->AppendElement(segment, fallible)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
   *outWrittenCount = aCount;
   return NS_OK;
 }
 
+void
+ContentVerifier::FinishSignature()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  nsCOMPtr<nsIStreamListener> nextListener;
+  nextListener.swap(mNextListener);
+
+  // Verify the content:
+  // If this fails, we return an invalid signature error to load a fallback page.
+  // If everthing is good, we return a new stream to the next listener and kick
+  // that one off.
+  bool verified = false;
+  nsresult rv = NS_OK;
+
+  // If the content signature check fails, stop the load
+  // and return a signature error. NSS resources are freed by the
+  // ContentSignatureVerifier on destruction.
+  if (NS_FAILED(mVerifier->End(&verified)) || !verified) {
+    CSV_LOG(("failed to verify content\n"));
+    (void)nextListener->OnStopRequest(mContentRequest, mContentContext,
+                                      NS_ERROR_INVALID_SIGNATURE);
+    return;
+  }
+  CSV_LOG(("Successfully verified content signature.\n"));
+
+  // We emptied the input stream so we have to create a new one from mContent
+  // to hand it to the consuming listener.
+  uint64_t offset = 0;
+  for (uint32_t i = 0; i < mContent.Length(); ++i) {
+    nsCOMPtr<nsIInputStream> oInStr;
+    rv = NS_NewCStringInputStream(getter_AddRefs(oInStr), mContent[i]);
+    if (NS_FAILED(rv)) {
+      break;
+    }
+    // let the next listener know that there is data in oInStr
+    rv = nextListener->OnDataAvailable(mContentRequest, mContentContext, oInStr,
+                                       offset, mContent[i].Length());
+    offset += mContent[i].Length();
+    if (NS_FAILED(rv)) {
+      break;
+    }
+  }
+
+  // propagate OnStopRequest and return
+  nextListener->OnStopRequest(mContentRequest, mContentContext, rv);
+}
+
 NS_IMETHODIMP
 ContentVerifier::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
 {
+  MOZ_CRASH("This OnStartRequest should've never been called!");
   return NS_OK;
 }
 
 NS_IMETHODIMP
 ContentVerifier::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
                                nsresult aStatus)
 {
-  // Verify the content:
-  // If this fails, we return an invalid signature error to load a fallback page.
-  // If everthing is good, we return a new stream to the next listener and kick
-  // that one of.
-  CSV_LOG(("VerifySignedContent, b64signature: %s\n", mSignature.get()));
-  CSV_LOG(("VerifySignedContent, key: \n[\n%s\n]\n", mKey.get()));
-  bool verified = false;
-  nsresult rv = End(&verified);
-  if (NS_FAILED(rv) || !verified || NS_FAILED(aStatus)) {
-    // cancel the request and return error
-    if (NS_FAILED(aStatus)) {
-      rv = aStatus;
-    } else {
-      rv = NS_ERROR_INVALID_SIGNATURE;
-    }
-    CSV_LOG(("failed to verify content\n"));
-    mNextListener->OnStartRequest(aRequest, aContext);
-    mNextListener->OnStopRequest(aRequest, aContext, rv);
-    return NS_ERROR_INVALID_SIGNATURE;
-  }
-  CSV_LOG(("Successfully verified content signature.\n"));
-
-  // start the next listener
-  rv = mNextListener->OnStartRequest(aRequest, aContext);
-  if (NS_SUCCEEDED(rv)) {
-    // We emptied aInStr so we have to create a new one from buf to hand it
-    // to the consuming listener.
-    for (uint32_t i = 0; i < mContent.Length(); ++i) {
-      nsCOMPtr<nsIInputStream> oInStr;
-      rv = NS_NewCStringInputStream(getter_AddRefs(oInStr), mContent[i]);
-      if (NS_FAILED(rv)) {
-        break;
-      }
-      // let the next listener know that there is data in oInStr
-      rv = mNextListener->OnDataAvailable(aRequest, aContext, oInStr, 0,
-                                          mContent[i].Length());
-      if (NS_FAILED(rv)) {
-        break;
-      }
-    }
+  // If we don't have a next listener, we handed off this request already.
+  // Return, there's nothing to do here.
+  if (!mNextListener) {
+    return NS_OK;
   }
 
-  // propagate OnStopRequest and return
-  return mNextListener->OnStopRequest(aRequest, aContext, rv);
+  if (NS_FAILED(aStatus)) {
+    CSV_LOG(("Stream failed\n"));
+    nsCOMPtr<nsIStreamListener> nextListener;
+    nextListener.swap(mNextListener);
+    return nextListener->OnStopRequest(aRequest, aContext, aStatus);
+  }
+
+  mContentRead = true;
+
+  // If the ContentSignatureVerifier is initialised, finish the verification.
+  if (mContextCreated) {
+    FinishSignature();
+    return aStatus;
+  }
+
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 ContentVerifier::OnDataAvailable(nsIRequest* aRequest, nsISupports* aContext,
                                  nsIInputStream* aInputStream, uint64_t aOffset,
                                  uint32_t aCount)
 {
   // buffer the entire stream
   uint32_t read;
   nsresult rv = aInputStream->ReadSegments(AppendNextSegment, &mContent, aCount,
                                            &read);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  // update the signature verifier
-  return Update(mContent[mContent.Length()-1]);
-}
-
-/**
- * ContentVerifier logic and utils
- */
-
-nsresult
-ContentVerifier::GetVerificationKey(const nsAString& aKeyId)
-{
-  // get verification keys from the pref and see if we have |aKeyId|
-  nsCharSeparatedTokenizer tokenizerVK(mVks, ';');
-  while (tokenizerVK.hasMoreTokens()) {
-    nsDependentSubstring token = tokenizerVK.nextToken();
-    nsCharSeparatedTokenizer tokenizerKey(token, '=');
-    nsString prefKeyId;
-    if (tokenizerKey.hasMoreTokens()) {
-      prefKeyId = tokenizerKey.nextToken();
-    }
-    nsString key;
-    if (tokenizerKey.hasMoreTokens()) {
-      key = tokenizerKey.nextToken();
-    }
-    if (prefKeyId.Equals(aKeyId)) {
-      mKey.Assign(NS_ConvertUTF16toUTF8(key));
-      return NS_OK;
-    }
-  }
-
-  // we didn't find the appropriate key
-  return NS_ERROR_INVALID_SIGNATURE;
-}
-
-nsresult
-ContentVerifier::ParseContentSignatureHeader(
-  const nsAString& aContentSignatureHeader)
-{
-  // We only support p384 ecdsa according to spec
-  NS_NAMED_LITERAL_CSTRING(keyid_var, "keyid");
-  NS_NAMED_LITERAL_CSTRING(signature_var, "p384ecdsa");
-
-  nsAutoString contentSignature;
-  nsAutoString keyId;
-  nsAutoCString header = NS_ConvertUTF16toUTF8(aContentSignatureHeader);
-  nsSecurityHeaderParser parser(header.get());
-  nsresult rv = parser.Parse();
-  if (NS_FAILED(rv)) {
-    CSV_LOG(("ContentVerifier: could not parse ContentSignature header\n"));
-    return NS_ERROR_INVALID_SIGNATURE;
-  }
-  LinkedList<nsSecurityHeaderDirective>* directives = parser.GetDirectives();
-
-  for (nsSecurityHeaderDirective* directive = directives->getFirst();
-       directive != nullptr; directive = directive->getNext()) {
-    CSV_LOG(("ContentVerifier: found directive %s\n", directive->mName.get()));
-    if (directive->mName.Length() == keyid_var.Length() &&
-        directive->mName.EqualsIgnoreCase(keyid_var.get(),
-                                          keyid_var.Length())) {
-      if (!keyId.IsEmpty()) {
-        CSV_LOG(("ContentVerifier: found two keyIds\n"));
-        return NS_ERROR_INVALID_SIGNATURE;
-      }
-
-      CSV_LOG(("ContentVerifier: found a keyid directive\n"));
-      keyId = NS_ConvertUTF8toUTF16(directive->mValue);
-      rv = GetVerificationKey(keyId);
-      NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_SIGNATURE);
-    }
-    if (directive->mName.Length() == signature_var.Length() &&
-        directive->mName.EqualsIgnoreCase(signature_var.get(),
-                                          signature_var.Length())) {
-      if (!contentSignature.IsEmpty()) {
-        CSV_LOG(("ContentVerifier: found two ContentSignatures\n"));
-        return NS_ERROR_INVALID_SIGNATURE;
-      }
-
-      CSV_LOG(("ContentVerifier: found a ContentSignature directive\n"));
-      contentSignature = NS_ConvertUTF8toUTF16(directive->mValue);
-      mSignature = directive->mValue;
-    }
-  }
-
-  // we have to ensure that we found a key and a signature at this point
-  if (mKey.IsEmpty()) {
-    CSV_LOG(("ContentVerifier: got a Content-Signature header but didn't find "
-             "an appropriate key.\n"));
-    return NS_ERROR_INVALID_SIGNATURE;
-  }
-  if (mSignature.IsEmpty()) {
-    CSV_LOG(("ContentVerifier: got a Content-Signature header but didn't find "
-             "a signature.\n"));
-    return NS_ERROR_INVALID_SIGNATURE;
+  // Update the signature verifier if the context has been created.
+  if (mContextCreated) {
+    return mVerifier->Update(mContent.LastElement());
   }
 
   return NS_OK;
 }
 
-/**
- * Parse signature, public key, and algorithm data for input to verification
- * functions in VerifyData and CreateContext.
- *
- * https://datatracker.ietf.org/doc/draft-thomson-http-content-signature/
- * If aSignature is a content signature, the function returns
- * NS_ERROR_INVALID_SIGNATURE if anything goes wrong. Only p384 with sha384
- * is supported and aSignature is a raw signature (r||s).
- */
-nsresult
-ContentVerifier::ParseInput(ScopedSECKEYPublicKey& aPublicKeyOut,
-                            ScopedSECItem& aSignatureItemOut,
-                            SECOidTag& aOidOut,
-                            const nsNSSShutDownPreventionLock&)
+NS_IMETHODIMP
+ContentVerifier::ContextCreated(bool successful)
 {
-  // Base 64 decode the key
-  ScopedSECItem keyItem(::SECITEM_AllocItem(nullptr, nullptr, 0));
-  if (!keyItem ||
-      !NSSBase64_DecodeBuffer(nullptr, keyItem,
-                              mKey.get(),
-                              mKey.Length())) {
-    return NS_ERROR_INVALID_SIGNATURE;
-  }
+  MOZ_ASSERT(NS_IsMainThread());
+  if (!successful) {
+    // If we don't have a next listener, the request has been handed off already.
+    if (!mNextListener) {
+      return NS_OK;
+    }
+    // Get local reference to mNextListener and null it to ensure that we don't
+    // call it twice.
+    nsCOMPtr<nsIStreamListener> nextListener;
+    nextListener.swap(mNextListener);
+
+    // Make sure that OnStartRequest was called and we have a request.
+    MOZ_ASSERT(mContentRequest);
 
-  // Extract the public key from the keyItem
-  ScopedCERTSubjectPublicKeyInfo pki(
-    SECKEY_DecodeDERSubjectPublicKeyInfo(keyItem));
-  if (!pki) {
-    return NS_ERROR_INVALID_SIGNATURE;
-  }
-  aPublicKeyOut = SECKEY_ExtractPublicKey(pki.get());
+    // In this case something went wrong with the cert. Let's stop this load.
+    CSV_LOG(("failed to get a valid cert chain\n"));
+    if (mContentRequest && nextListener) {
+      mContentRequest->Cancel(NS_ERROR_INVALID_SIGNATURE);
+      nsresult rv = nextListener->OnStopRequest(mContentRequest, mContentContext,
+                                                NS_ERROR_INVALID_SIGNATURE);
+      mContentRequest = nullptr;
+      mContentContext = nullptr;
+      return rv;
+    }
 
-  // in case we were not able to extract a key
-  if (!aPublicKeyOut) {
-    return NS_ERROR_INVALID_SIGNATURE;
-  }
-
-  // Base 64 decode the signature
-  ScopedSECItem rawSignatureItem(::SECITEM_AllocItem(nullptr, nullptr, 0));
-  if (!rawSignatureItem ||
-      !NSSBase64_DecodeBuffer(nullptr, rawSignatureItem,
-                              mSignature.get(),
-                              mSignature.Length())) {
-    return NS_ERROR_INVALID_SIGNATURE;
+    // We should never get here!
+    MOZ_ASSERT_UNREACHABLE(
+      "ContentVerifier was used without getting OnStartRequest!");
+    return NS_OK;
   }
 
-  // get signature object and oid
-  if (!aSignatureItemOut) {
-    return NS_ERROR_INVALID_SIGNATURE;
-  }
-  // We have a raw ecdsa signature r||s so we have to DER-encode it first
-  // Note that we have to check rawSignatureItem->len % 2 here as
-  // DSAU_EncodeDerSigWithLen asserts this
-  if (rawSignatureItem->len == 0 || rawSignatureItem->len % 2 != 0) {
-    return NS_ERROR_INVALID_SIGNATURE;
-  }
-  if (DSAU_EncodeDerSigWithLen(aSignatureItemOut, rawSignatureItem,
-                               rawSignatureItem->len) != SECSuccess) {
-    return NS_ERROR_INVALID_SIGNATURE;
-  }
-  aOidOut = SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE;
-
-  return NS_OK;
-}
-
-/**
- * Create a context for a signature verification.
- * It sets signature, public key, and algorithms that should be used to verify
- * the data. It also updates the verification buffer with the content-signature
- * prefix.
- */
-nsresult
-ContentVerifier::CreateContext()
-{
-  nsNSSShutDownPreventionLock locker;
-  if (isAlreadyShutDown()) {
-    return NS_ERROR_INVALID_SIGNATURE;
+  // In this case the content verifier is initialised and we have to feed it
+  // the buffered content.
+  mContextCreated = true;
+  for (size_t i = 0; i < mContent.Length(); ++i) {
+    if (NS_FAILED(mVerifier->Update(mContent[i]))) {
+      // Bail out if this fails. We can't return an error here, but if this
+      // failed, NS_ERROR_INVALID_SIGNATURE is returned in FinishSignature.
+      break;
+    }
   }
 
-  // Bug 769521: We have to change b64 url to regular encoding as long as we
-  // don't have a b64 url decoder. This should change soon, but in the meantime
-  // we have to live with this.
-  mSignature.ReplaceChar('-', '+');
-  mSignature.ReplaceChar('_', '/');
-
-  ScopedSECKEYPublicKey publicKey;
-  ScopedSECItem signatureItem(::SECITEM_AllocItem(nullptr, nullptr, 0));
-  SECOidTag oid;
-  nsresult rv = ParseInput(publicKey, signatureItem, oid, locker);
-  if (NS_FAILED(rv)) {
-    return NS_ERROR_INVALID_SIGNATURE;
-  }
-
-  mCx = UniqueVFYContext(VFY_CreateContext(publicKey, signatureItem, oid, NULL));
-  if (!mCx) {
-    return NS_ERROR_INVALID_SIGNATURE;
-  }
-
-  if (VFY_Begin(mCx.get()) != SECSuccess) {
-    return NS_ERROR_INVALID_SIGNATURE;
-  }
-
-  // add the prefix to the verification buffer
-  return Update(kPREFIX);
-}
-
-/**
- * Add data to the context that should be verified.
- */
-nsresult
-ContentVerifier::Update(const nsACString& aData)
-{
-  nsNSSShutDownPreventionLock locker;
-  if (isAlreadyShutDown()) {
-    return NS_ERROR_INVALID_SIGNATURE;
-  }
-
-  if (!aData.IsEmpty()) {
-    if (VFY_Update(mCx.get(),
-                   (const unsigned char*)nsPromiseFlatCString(aData).get(),
-                   aData.Length()) != SECSuccess) {
-      return NS_ERROR_INVALID_SIGNATURE;
-    }
+  // We read all content, let's verify the signature.
+  if (mContentRead) {
+    FinishSignature();
   }
 
   return NS_OK;
 }
-
-/**
- * Finish signature verification and return the result in _retval.
- */
-nsresult
-ContentVerifier::End(bool* _retval)
-{
-  nsNSSShutDownPreventionLock locker;
-  if (isAlreadyShutDown()) {
-    return NS_ERROR_INVALID_SIGNATURE;
-  }
-
-  *_retval = (VFY_End(mCx.get()) == SECSuccess);
-
-  return NS_OK;
-}
\ No newline at end of file
--- a/dom/security/ContentVerifier.h
+++ b/dom/security/ContentVerifier.h
@@ -2,97 +2,63 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_ContentVerifier_h
 #define mozilla_dom_ContentVerifier_h
 
 #include "nsCOMPtr.h"
+#include "nsIContentSignatureVerifier.h"
 #include "nsIObserver.h"
 #include "nsIStreamListener.h"
-#include "nsNSSShutDown.h"
 #include "nsString.h"
 #include "nsTArray.h"
-#include "ScopedNSSTypes.h"
 
 /**
  * Mediator intercepting OnStartRequest in nsHttpChannel, blocks until all
  * data is read from the input stream, verifies the content signature and
  * releases the request to the next listener if the verification is successful.
  * If the verification fails or anything else goes wrong, a
  * NS_ERROR_INVALID_SIGNATURE is thrown.
  */
 class ContentVerifier : public nsIStreamListener
-                      , public nsNSSShutDownObject
+                      , public nsIContentSignatureReceiverCallback
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSISTREAMLISTENER
   NS_DECL_NSIREQUESTOBSERVER
+  NS_DECL_NSICONTENTSIGNATURERECEIVERCALLBACK
 
   explicit ContentVerifier(nsIStreamListener* aMediatedListener,
                            nsISupports* aMediatedContext)
     : mNextListener(aMediatedListener)
-    , mContext(aMediatedContext)
-    , mCx(nullptr) {}
-
-  nsresult Init(const nsAString& aContentSignatureHeader);
+    , mContextCreated(false)
+    , mContentRead(false) {}
 
-  // nsNSSShutDownObject
-  virtual void virtualDestroyNSSReference() override
-  {
-    destructorSafeDestroyNSSReference();
-  }
+  nsresult Init(const nsACString& aContentSignatureHeader, nsIRequest* aRequest,
+                nsISupports* aContext);
 
 protected:
-  virtual ~ContentVerifier()
-  {
-    nsNSSShutDownPreventionLock locker;
-    if (isAlreadyShutDown()) {
-      return;
-    }
-    destructorSafeDestroyNSSReference();
-    shutdown(calledFromObject);
-  }
-
-  void destructorSafeDestroyNSSReference()
-  {
-    mCx = nullptr;
-  }
+  virtual ~ContentVerifier() {}
 
 private:
-  nsresult ParseContentSignatureHeader(const nsAString& aContentSignatureHeader);
-  nsresult GetVerificationKey(const nsAString& aKeyId);
-
-  // utility function to parse input before put into verification functions
-  nsresult ParseInput(mozilla::ScopedSECKEYPublicKey& aPublicKeyOut,
-                      mozilla::ScopedSECItem& aSignatureItemOut,
-                      SECOidTag& aOidOut,
-                      const nsNSSShutDownPreventionLock&);
+  void FinishSignature();
 
-  // create a verifier context and store it in mCx
-  nsresult CreateContext();
-
-  // Adds data to the context that was used to generate the signature.
-  nsresult Update(const nsACString& aData);
-
-  // Finalises the signature and returns the result of the signature
-  // verification.
-  nsresult End(bool* _retval);
-
+  // buffered content to verify
+  FallibleTArray<nsCString> mContent;
   // content and next listener for nsIStreamListener
   nsCOMPtr<nsIStreamListener> mNextListener;
-  nsCOMPtr<nsISupports> mContext;
-
-  // verifier context for incrementel verifications
-  mozilla::UniqueVFYContext mCx;
-  // buffered content to verify
-  FallibleTArray<nsCString> mContent;
-  // signature to verify
-  nsCString mSignature;
-  // verification key
-  nsCString mKey;
-  // verification key preference
-  nsString mVks;
+  // the verifier
+  nsCOMPtr<nsIContentSignatureVerifier> mVerifier;
+  // holding a pointer to the content request and context to resume/cancel it
+  nsCOMPtr<nsIRequest> mContentRequest;
+  nsCOMPtr<nsISupports> mContentContext;
+  // Semaphors to indicate that the verifying context was created, the entire
+  // content was read resp. The context gets created by ContentSignatureVerifier
+  // and mContextCreated is set in the ContextCreated callback. The content is
+  // read, i.e. mContentRead is set, when the content OnStopRequest is called.
+  bool mContextCreated;
+  bool mContentRead;
 };
 
 #endif /* mozilla_dom_ContentVerifier_h */
--- a/dom/security/nsCSPContext.cpp
+++ b/dom/security/nsCSPContext.cpp
@@ -887,17 +887,17 @@ nsCSPContext::SendReports(nsISupports* a
   for (uint32_t r = 0; r < reportURIs.Length(); r++) {
     nsAutoCString reportURICstring = NS_ConvertUTF16toUTF8(reportURIs[r]);
     // try to create a new uri from every report-uri string
     rv = NS_NewURI(getter_AddRefs(reportURI), reportURIs[r]);
     if (NS_FAILED(rv)) {
       const char16_t* params[] = { reportURIs[r].get() };
       CSPCONTEXTLOG(("Could not create nsIURI for report URI %s",
                      reportURICstring.get()));
-      logToConsole(MOZ_UTF16("triedToSendReport"), params, ArrayLength(params),
+      logToConsole(u"triedToSendReport", params, ArrayLength(params),
                    aSourceFile, aScriptSample, aLineNum, 0, nsIScriptError::errorFlag);
       continue; // don't return yet, there may be more URIs
     }
 
     // try to create a new channel for every report-uri
     nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL | nsIChannel::LOAD_CLASSIFY_URI;
     if (doc) {
       rv = NS_NewChannel(getter_AddRefs(reportChannel),
@@ -928,17 +928,17 @@ nsCSPContext::SendReports(nsISupports* a
 
     // log a warning to console if scheme is not http or https
     bool isHttpScheme =
       (NS_SUCCEEDED(reportURI->SchemeIs("http", &isHttpScheme)) && isHttpScheme) ||
       (NS_SUCCEEDED(reportURI->SchemeIs("https", &isHttpScheme)) && isHttpScheme);
 
     if (!isHttpScheme) {
       const char16_t* params[] = { reportURIs[r].get() };
-      logToConsole(MOZ_UTF16("reportURInotHttpsOrHttp2"), params, ArrayLength(params),
+      logToConsole(u"reportURInotHttpsOrHttp2", params, ArrayLength(params),
                    aSourceFile, aScriptSample, aLineNum, 0, nsIScriptError::errorFlag);
       continue;
     }
 
     // make sure this is an anonymous request (no cookies) so in case the
     // policy URI is injected, it can't be abused for CSRF.
     nsLoadFlags flags;
     rv = reportChannel->GetLoadFlags(&flags);
@@ -991,17 +991,17 @@ nsCSPContext::SendReports(nsISupports* a
     // AsyncOpen should not fail, but could if there's no load group (like if
     // SetRequestContext is not given a channel).  This should fail quietly and
     // not return an error since it's really ok if reports don't go out, but
     // it's good to log the error locally.
 
     if (NS_FAILED(rv)) {
       const char16_t* params[] = { reportURIs[r].get() };
       CSPCONTEXTLOG(("AsyncOpen failed for report URI %s", params[0]));
-      logToConsole(MOZ_UTF16("triedToSendReport"), params, ArrayLength(params),
+      logToConsole(u"triedToSendReport", params, ArrayLength(params),
                    aSourceFile, aScriptSample, aLineNum, 0, nsIScriptError::errorFlag);
     } else {
       CSPCONTEXTLOG(("Sent violation report to URI %s", reportURICstring.get()));
     }
   }
   return NS_OK;
 }
 
@@ -1076,18 +1076,18 @@ class CSPReportSenderRunnable final : pu
       } else if (blockedString) {
         blockedString->GetData(blockedDataStr);
       }
 
       if (blockedDataStr.Length() > 0) {
         nsString blockedDataChar16 = NS_ConvertUTF8toUTF16(blockedDataStr);
         const char16_t* params[] = { mViolatedDirective.get(),
                                      blockedDataChar16.get() };
-        mCSPContext->logToConsole(mReportOnlyFlag ? MOZ_UTF16("CSPROViolationWithURI") :
-                                                    MOZ_UTF16("CSPViolationWithURI"),
+        mCSPContext->logToConsole(mReportOnlyFlag ? u"CSPROViolationWithURI" :
+                                                    u"CSPViolationWithURI",
                                   params, ArrayLength(params), mSourceFile, mScriptSample,
                                   mLineNum, 0, nsIScriptError::errorFlag);
       }
       return NS_OK;
     }
 
   private:
     nsCOMPtr<nsISupports>   mBlockedContentSource;
@@ -1350,17 +1350,17 @@ nsCSPContext::GetCSPSandboxFlags(uint32_
       // continue the loop checking for an enforcement policy.
       nsAutoString policy;
       mPolicies[i]->toString(policy);
 
       CSPCONTEXTLOG(("nsCSPContext::GetCSPSandboxFlags, report only policy, ignoring sandbox in: %s",
                     policy.get()));
 
       const char16_t* params[] = { policy.get() };
-      logToConsole(MOZ_UTF16("ignoringReportOnlyDirective"), params, ArrayLength(params),
+      logToConsole(u"ignoringReportOnlyDirective", params, ArrayLength(params),
                    EmptyString(), EmptyString(), 0, 0, nsIScriptError::warningFlag);
     }
   }
 
   return NS_OK;
 }
 
 /* ========== CSPViolationReportListener implementation ========== */
@@ -1446,17 +1446,17 @@ CSPReportRedirectSink::AsyncOnChannelRed
   nsCOMPtr<nsIURI> uri;
   rv = aOldChannel->GetURI(getter_AddRefs(uri));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
   NS_ASSERTION(observerService, "Observer service required to log CSP violations");
   observerService->NotifyObservers(uri,
                                    CSP_VIOLATION_TOPIC,
-                                   MOZ_UTF16("denied redirect while sending violation report"));
+                                   u"denied redirect while sending violation report");
 
   return NS_BINDING_REDIRECTED;
 }
 
 NS_IMETHODIMP
 CSPReportRedirectSink::GetInterface(const nsIID& aIID, void** aResult)
 {
   if (aIID.Equals(NS_GET_IID(nsINetworkInterceptController)) &&
--- a/dom/security/nsCSPParser.cpp
+++ b/dom/security/nsCSPParser.cpp
@@ -1156,48 +1156,48 @@ nsCSPParser::directive()
 
   CSPPARSERLOG(("nsCSPParser::directive, mCurToken: %s, mCurValue: %s",
                NS_ConvertUTF16toUTF8(mCurToken).get(),
                NS_ConvertUTF16toUTF8(mCurValue).get()));
 
   // Make sure that the directive-srcs-array contains at least
   // one directive and one src.
   if (mCurDir.Length() < 1) {
-    const char16_t* params[] = { MOZ_UTF16("directive missing") };
+    const char16_t* params[] = { u"directive missing" };
     logWarningErrorToConsole(nsIScriptError::warningFlag, "failedToParseUnrecognizedSource",
                              params, ArrayLength(params));
     return;
   }
 
   // Try to create a new CSPDirective
   nsCSPDirective* cspDir = directiveName();
   if (!cspDir) {
     // if we can not create a CSPDirective, we can skip parsing the srcs for that array
     return;
   }
 
   // special case handling for block-all-mixed-content, which is only specified
   // by a directive name but does not include any srcs.
   if (cspDir->equals(nsIContentSecurityPolicy::BLOCK_ALL_MIXED_CONTENT)) {
     if (mCurDir.Length() > 1) {
-      const char16_t* params[] = { MOZ_UTF16("block-all-mixed-content") };
+      const char16_t* params[] = { u"block-all-mixed-content" };
       logWarningErrorToConsole(nsIScriptError::warningFlag,
                                "ignoreSrcForDirective",
                                params, ArrayLength(params));
     }
     // add the directive and return
     mPolicy->addDirective(cspDir);
     return;
   }
 
   // special case handling for upgrade-insecure-requests, which is only specified
   // by a directive name but does not include any srcs.
   if (cspDir->equals(nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE)) {
     if (mCurDir.Length() > 1) {
-      const char16_t* params[] = { MOZ_UTF16("upgrade-insecure-requests") };
+      const char16_t* params[] = { u"upgrade-insecure-requests" };
       logWarningErrorToConsole(nsIScriptError::warningFlag,
                                "ignoreSrcForDirective",
                                params, ArrayLength(params));
     }
     // add the directive and return
     mPolicy->addUpgradeInsecDir(static_cast<nsUpgradeInsecureDirective*>(cspDir));
     return;
   }
@@ -1228,17 +1228,17 @@ nsCSPParser::directive()
   // Ignore unsafe-inline within script-src or style-src if nonce
   // or hash is specified, see:
   // http://www.w3.org/TR/CSP2/#directive-script-src
   if ((cspDir->equals(nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE) ||
        cspDir->equals(nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE)) &&
       mHasHashOrNonce && mUnsafeInlineKeywordSrc) {
     mUnsafeInlineKeywordSrc->invalidate();
     // log to the console that unsafe-inline will be ignored
-    const char16_t* params[] = { MOZ_UTF16("'unsafe-inline'") };
+    const char16_t* params[] = { u"'unsafe-inline'" };
     logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringSrcWithinScriptStyleSrc",
                              params, ArrayLength(params));
   }
 
   // Add the newly created srcs to the directive and add the directive to the policy
   cspDir->addSrcs(srcs);
   mPolicy->addDirective(cspDir);
 }
--- a/dom/security/nsCSPUtils.cpp
+++ b/dom/security/nsCSPUtils.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsAttrValue.h"
+#include "nsCharSeparatedTokenizer.h"
 #include "nsContentUtils.h"
 #include "nsCSPUtils.h"
 #include "nsDebug.h"
 #include "nsIConsoleService.h"
 #include "nsICryptoHash.h"
 #include "nsIScriptError.h"
 #include "nsIServiceManager.h"
 #include "nsIStringBundle.h"
--- a/dom/security/nsMixedContentBlocker.cpp
+++ b/dom/security/nsMixedContentBlocker.cpp
@@ -694,17 +694,17 @@ nsMixedContentBlocker::ShouldLoad(bool a
   if (docShell->GetDocument()->GetBlockAllMixedContent(isPreload)) {
     // log a message to the console before returning.
     nsAutoCString spec;
     rv = aContentLocation->GetSpec(spec);
     NS_ENSURE_SUCCESS(rv, rv);
     NS_ConvertUTF8toUTF16 reportSpec(spec);
 
     const char16_t* params[] = { reportSpec.get()};
-    CSP_LogLocalizedStr(MOZ_UTF16("blockAllMixedContent"),
+    CSP_LogLocalizedStr(u"blockAllMixedContent",
                         params, ArrayLength(params),
                         EmptyString(), // aSourceFile
                         EmptyString(), // aScriptSample
                         0, // aLineNumber
                         0, // aColumnNumber
                         nsIScriptError::errorFlag, "CSP",
                         docShell->GetDocument()->InnerWindowID());
     *aDecision = REJECT_REQUEST;
--- a/dom/security/test/contentverifier/browser.ini
+++ b/dom/security/test/contentverifier/browser.ini
@@ -5,12 +5,15 @@ support-files =
   file_about_newtab_bad.html
   file_about_newtab_bad_csp.html
   file_about_newtab_bad_csp_signature
   file_about_newtab_good_signature
   file_about_newtab_bad_signature
   file_about_newtab_broken_signature
   file_about_newtab_sri.html
   file_about_newtab_sri_signature
+  goodChain.pem
+  head.js
   script.js
   style.css
 
 [browser_verify_content_about_newtab.js]
+[browser_verify_content_about_newtab2.js]
--- a/dom/security/test/contentverifier/browser_verify_content_about_newtab.js
+++ b/dom/security/test/contentverifier/browser_verify_content_about_newtab.js
@@ -1,243 +1,20 @@
-/*
- * Test Content-Signature for remote about:newtab
- *  - Bug 1226928 - allow about:newtab to load remote content
- *
- * This tests content-signature verification on remote about:newtab in the
- * following cases (see TESTS, all failed loads display about:blank fallback):
- * - good case (signature should verify and correct page is displayed)
- * - reload of newtab when the siganture was invalidated after the last correct
- *   load
- * - malformed content-signature header
- * - malformed keyid directive
- * - malformed p384ecdsa directive
- * - wrong signature (this is not a siganture for the delivered document)
- * - invalid signature (this is not even a signature)
- * - loading a file that doesn't fit the key or signature
- * - cache poisoning (load a malicious remote page not in newtab, subsequent
- *   newtab load has to load the fallback)
- */
-
-const ABOUT_NEWTAB_URI = "about:newtab";
-
-const BASE = "https://example.com/browser/dom/security/test/contentverifier/file_contentserver.sjs?";
-const URI_GOOD = BASE + "sig=good&key=good&file=good&header=good";
-
-const INVALIDATE_FILE = BASE + "invalidateFile=yep";
-const VALIDATE_FILE = BASE + "validateFile=yep";
-
-const URI_HEADER_BASE = BASE + "sig=good&key=good&file=good&header=";
-const URI_ERROR_HEADER = URI_HEADER_BASE + "error";
-const URI_KEYERROR_HEADER = URI_HEADER_BASE + "errorInKeyid";
-const URI_SIGERROR_HEADER = URI_HEADER_BASE + "errorInSignature";
-const URI_NO_HEADER = URI_HEADER_BASE + "noHeader";
-
-const URI_BAD_SIG = BASE + "sig=bad&key=good&file=good&header=good";
-const URI_BROKEN_SIG = BASE + "sig=broken&key=good&file=good&header=good";
-const URI_BAD_KEY = BASE + "sig=good&key=bad&file=good&header=good";
-const URI_BAD_FILE = BASE + "sig=good&key=good&file=bad&header=good";
-const URI_BAD_ALL = BASE + "sig=bad&key=bad&file=bad&header=bad";
-const URI_BAD_CSP = BASE + "sig=bad-csp&key=good&file=bad-csp&header=good";
-
-const URI_BAD_FILE_CACHED = BASE + "sig=good&key=good&file=bad&header=good&cached=true";
-
-const GOOD_ABOUT_STRING = "Just a fully good testpage for Bug 1226928";
-const BAD_ABOUT_STRING = "Just a bad testpage for Bug 1226928";
-const ABOUT_BLANK = "<head></head><body></body>";
-
-const URI_CLEANUP = BASE + "cleanup=true";
-const CLEANUP_DONE = "Done";
-
-const URI_SRI = BASE + "sig=sri&key=good&file=sri&header=good";
-const STYLESHEET_WITHOUT_SRI_BLOCKED = "Stylesheet without SRI blocked";
-const STYLESHEET_WITH_SRI_BLOCKED = "Stylesheet with SRI blocked";
-const STYLESHEET_WITH_SRI_LOADED = "Stylesheet with SRI loaded";
-const SCRIPT_WITHOUT_SRI_BLOCKED = "Script without SRI blocked";
-const SCRIPT_WITH_SRI_BLOCKED = "Script with SRI blocked";
-const SCRIPT_WITH_SRI_LOADED = "Script with SRI loaded";
-
-const CSP_TEST_SUCCESS_STRING = "CSP violation test succeeded.";
-
-// Needs to sync with pref "security.signed_content.CSP.default".
-const SIGNED_CONTENT_CSP = `{"csp-policies":[{"report-only":false,"script-src":["https://example.com","'unsafe-inline'"],"style-src":["https://example.com"]}]}`;
 
 const TESTS = [
   // { newtab (aboutURI) or regular load (url) : url,
   //   testStrings : expected strings in the loaded page }
   { "aboutURI" : URI_GOOD, "testStrings" : [GOOD_ABOUT_STRING] },
   { "aboutURI" : URI_ERROR_HEADER, "testStrings" : [ABOUT_BLANK] },
-  { "aboutURI" : URI_KEYERROR_HEADER, "testStrings" : [ABOUT_BLANK] },
-  { "aboutURI" : URI_SIGERROR_HEADER, "testStrings" : [ABOUT_BLANK] },
-  { "aboutURI" : URI_NO_HEADER, "testStrings" : [ABOUT_BLANK] },
-  { "aboutURI" : URI_BAD_SIG, "testStrings" : [ABOUT_BLANK] },
-  { "aboutURI" : URI_BROKEN_SIG, "testStrings" : [ABOUT_BLANK] },
-  { "aboutURI" : URI_BAD_KEY, "testStrings" : [ABOUT_BLANK] },
-  { "aboutURI" : URI_BAD_FILE, "testStrings" : [ABOUT_BLANK] },
-  { "aboutURI" : URI_BAD_ALL, "testStrings" : [ABOUT_BLANK] },
   { "url" : URI_BAD_FILE_CACHED, "testStrings" : [BAD_ABOUT_STRING] },
   { "aboutURI" : URI_BAD_FILE_CACHED, "testStrings" : [ABOUT_BLANK] },
   { "aboutURI" : URI_GOOD, "testStrings" : [GOOD_ABOUT_STRING] },
   { "aboutURI" : URI_SRI, "testStrings" : [
     STYLESHEET_WITHOUT_SRI_BLOCKED,
     STYLESHEET_WITH_SRI_LOADED,
     SCRIPT_WITHOUT_SRI_BLOCKED,
     SCRIPT_WITH_SRI_LOADED,
     ]},
   { "aboutURI" : URI_BAD_CSP, "testStrings" : [CSP_TEST_SUCCESS_STRING] },
   { "url" : URI_CLEANUP, "testStrings" : [CLEANUP_DONE] },
 ];
 
-var browser = null;
-var aboutNewTabService = Cc["@mozilla.org/browser/aboutnewtab-service;1"]
-                           .getService(Ci.nsIAboutNewTabService);
-
-function pushPrefs(...aPrefs) {
-  return new Promise((resolve) => {
-    SpecialPowers.pushPrefEnv({"set": aPrefs}, resolve);
-  });
-}
-
-/*
- * run tests with input from TESTS
- */
-function doTest(aExpectedStrings, reload, aUrl, aNewTabPref) {
-  // set about:newtab location for this test if it's a newtab test
-  if (aNewTabPref) {
-    aboutNewTabService.newTabURL = aNewTabPref;
-  }
-
-  // set prefs
-  yield pushPrefs(
-      ["browser.newtabpage.remote.content-signing-test", true],
-      ["browser.newtabpage.remote", true], [
-        "browser.newtabpage.remote.keys",
-        "RemoteNewTabNightlyv0=BO9QHuP6E2eLKybql8iuD4o4Np9YFDfW3D+k" +
-        "a70EcXXTqZcikc7Am1CwyP1xBDTpEoe6gb9SWzJmaDW3dNh1av2u90VkUM" +
-        "B7aHIrImjTjLNg/1oC8GRcTKM4+WzbKF00iA==;OtherKey=eKQJ2fNSId" +
-        "CFzL6N326EzZ/5LCeFU5eyq3enwZ5MLmvOw+3gycr4ZVRc36/EiSPsQYHE" +
-        "3JvJs1EKs0QCaguHFOZsHwqXMPicwp/gLdeYbuOmN2s1SEf/cxw8GtcxSA" +
-        "kG;RemoteNewTab=MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE4k3FmG7dFo" +
-        "Ot3Tuzl76abTRtK8sb/r/ibCSeVKa96RbrOX2ciscz/TT8wfqBYS/8cN4z" +
-        "Me1+f7wRmkNrCUojZR1ZKmYM2BeiUOMlMoqk2O7+uwsn1DwNQSYP58TkvZt6"
-      ]);
-
-  if (aNewTabPref === URI_BAD_CSP) {
-    // Use stricter CSP to test CSP violation.
-    yield pushPrefs(["security.signed_content.CSP.default", "script-src 'self'; style-src 'self'"]);
-  } else {
-    // Use weaker CSP to test normal content.
-    yield pushPrefs(["security.signed_content.CSP.default", "script-src 'self' 'unsafe-inline'; style-src 'self'"]);
-  }
-
-  // start the test
-  yield BrowserTestUtils.withNewTab({
-      gBrowser,
-      url: aUrl,
-    },
-    function * (browser) {
-      // check if everything's set correct for testing
-      ok(Services.prefs.getBoolPref(
-          "browser.newtabpage.remote.content-signing-test"),
-          "sanity check: remote newtab signing test should be used");
-      ok(Services.prefs.getBoolPref("browser.newtabpage.remote"),
-          "sanity check: remote newtab should be used");
-      // we only check this if we really do a newtab test
-      if (aNewTabPref) {
-        ok(aboutNewTabService.overridden,
-            "sanity check: default URL for about:newtab should be overriden");
-        is(aboutNewTabService.newTabURL, aNewTabPref,
-            "sanity check: default URL for about:newtab should return the new URL");
-      }
-
-      // Every valid remote newtab page must have built-in CSP.
-      let shouldHaveCSP = ((aUrl === ABOUT_NEWTAB_URI) &&
-                          (aNewTabPref === URI_GOOD || aNewTabPref === URI_SRI));
-
-      if (shouldHaveCSP) {
-        is(browser.contentDocument.nodePrincipal.cspJSON, SIGNED_CONTENT_CSP,
-           "Valid remote newtab page must have built-in CSP.");
-      }
-
-      yield ContentTask.spawn(
-          browser, aExpectedStrings, function * (aExpectedStrings) {
-            for (let expectedString of aExpectedStrings) {
-              ok(content.document.documentElement.innerHTML.includes(expectedString),
-                 "Expect the following value in the result\n" + expectedString +
-                 "\nand got " + content.document.documentElement.innerHTML);
-            }
-          });
-
-      // for good test cases we check if a reload fails if the remote page
-      // changed from valid to invalid in the meantime
-      if (reload) {
-        yield BrowserTestUtils.withNewTab({
-            gBrowser,
-            url: INVALIDATE_FILE,
-          },
-          function * (browser2) {
-            yield ContentTask.spawn(browser2, null, function * () {
-              ok(content.document.documentElement.innerHTML.includes("Done"),
-                 "Expect the following value in the result\n" + "Done" +
-                 "\nand got " + content.document.documentElement.innerHTML);
-            });
-          }
-        );
-
-        browser.reload();
-        yield BrowserTestUtils.browserLoaded(browser);
-
-        let expectedStrings = [ABOUT_BLANK];
-        if (aNewTabPref == URI_SRI) {
-          expectedStrings = [
-            STYLESHEET_WITHOUT_SRI_BLOCKED,
-            STYLESHEET_WITH_SRI_BLOCKED,
-            SCRIPT_WITHOUT_SRI_BLOCKED,
-            SCRIPT_WITH_SRI_BLOCKED
-          ];
-        }
-        yield ContentTask.spawn(browser, expectedStrings,
-          function * (expectedStrings) {
-            for (let expectedString of expectedStrings) {
-              ok(content.document.documentElement.innerHTML.includes(expectedString),
-                 "Expect the following value in the result\n" + expectedString +
-                 "\nand got " + content.document.documentElement.innerHTML);
-            }
-          }
-        );
-
-        yield BrowserTestUtils.withNewTab({
-            gBrowser,
-            url: VALIDATE_FILE,
-          },
-          function * (browser2) {
-            yield ContentTask.spawn(browser2, null, function * () {
-              ok(content.document.documentElement.innerHTML.includes("Done"),
-                 "Expect the following value in the result\n" + "Done" +
-                 "\nand got " + content.document.documentElement.innerHTML);
-              });
-          }
-        );
-      }
-    }
-  );
-}
-
-add_task(function * test() {
-  // run tests from TESTS
-  for (let i = 0; i < TESTS.length; i++) {
-    let testCase = TESTS[i];
-    let url = "", aNewTabPref = "";
-    let reload = false;
-    var aExpectedStrings = testCase.testStrings;
-    if (testCase.aboutURI) {
-      url = ABOUT_NEWTAB_URI;
-      aNewTabPref = testCase.aboutURI;
-      if (aNewTabPref == URI_GOOD || aNewTabPref == URI_SRI) {
-        reload = true;
-      }
-    } else {
-      url = testCase.url;
-    }
-
-    yield doTest(aExpectedStrings, reload, url, aNewTabPref);
-  }
-});
+add_task(runTests);
new file mode 100644
--- /dev/null
+++ b/dom/security/test/contentverifier/browser_verify_content_about_newtab2.js
@@ -0,0 +1,19 @@
+
+const TESTS = [
+  // { newtab (aboutURI) or regular load (url) : url,
+  //   testStrings : expected strings in the loaded page }
+  { "aboutURI" : URI_GOOD, "testStrings" : [GOOD_ABOUT_STRING] },
+  { "aboutURI" : URI_ERROR_HEADER, "testStrings" : [ABOUT_BLANK] },
+  { "aboutURI" : URI_KEYERROR_HEADER, "testStrings" : [ABOUT_BLANK] },
+  { "aboutURI" : URI_SIGERROR_HEADER, "testStrings" : [ABOUT_BLANK] },
+  { "aboutURI" : URI_NO_HEADER, "testStrings" : [ABOUT_BLANK] },
+  { "aboutURI" : URI_BAD_SIG, "testStrings" : [ABOUT_BLANK] },
+  { "aboutURI" : URI_BROKEN_SIG, "testStrings" : [ABOUT_BLANK] },
+  { "aboutURI" : URI_BAD_X5U, "testStrings" : [ABOUT_BLANK] },
+  { "aboutURI" : URI_HTTP_X5U, "testStrings" : [ABOUT_BLANK] },
+  { "aboutURI" : URI_BAD_FILE, "testStrings" : [ABOUT_BLANK] },
+  { "aboutURI" : URI_BAD_ALL, "testStrings" : [ABOUT_BLANK] },
+  { "url" : URI_CLEANUP, "testStrings" : [CLEANUP_DONE] },
+];
+
+add_task(runTests);
--- a/dom/security/test/contentverifier/file_about_newtab_bad_csp_signature
+++ b/dom/security/test/contentverifier/file_about_newtab_bad_csp_signature
@@ -1,1 +1,1 @@
-8qXVAqzuF3TsF6C750u_v_JiRu90WJXf_0xT9x0S4Fgmvolgtfu-KSWq3lYpmk2dxO8u64zaHM3iguZdWAqcSL82RFtV7OPiprt16omCbHCKfVi-Bt_rXILRlexgmRl_
\ No newline at end of file
+oiypz3lb-IyJsmKNsnlp2zDrqncste8yONn9WUE6ksgJWMhSEQ9lp8vRqN0W3JPwJb6uSk16RI-tDv7uy0jxon5jL1BZpqlqIpvimg7FCQEedMKoHZwtE9an-e95sOTd
\ No newline at end of file
--- a/dom/security/test/contentverifier/file_about_newtab_good_signature
+++ b/dom/security/test/contentverifier/file_about_newtab_good_signature
@@ -1,1 +1,1 @@
-XBKzej3i6TAFZc3VZsuCekn-4dYWJBE4-b3OOtKrOV-JIzIvAnAhnOV1aj-kEm07kh-FciIxV-Xk2QUQlRQzHO7oW7E4mXkMKkbbAcvL0CFrItTObhfhKnBnpAE9ql1O
\ No newline at end of file
+-mqpvTYdZX4HYQDW1nScojL7ICw5yj8UF2gzxyLbSCx9UIfHH-gWZ40F_PFtqjHxoC1J3dHDb3VedVhOYczdaLrNKbRvPrlnkdGx7Rl8qEBrtZpF1py1Z9uAGoCrgUHa
\ No newline at end of file
--- a/dom/security/test/contentverifier/file_about_newtab_sri_signature
+++ b/dom/security/test/contentverifier/file_about_newtab_sri_signature
@@ -1,1 +1,1 @@
-i5jOnrZWwyNwrTcIjfJ6fUR-8MhhvhtMvQbdrUD7j8aHTybNolv25v9NwJAT6rVU6kgkxmD_st9Kla086CQmzYQdLhKfzgLbTDXz0-1j23fQnyjsP1_4MNIu2xTea11p
\ No newline at end of file
+yoIyAYiiEzdP1zpkRy3KaqdsjUy62Notku89cytwVwcH0x6fKsMCdM-df1wbk9N28CSTaIOW5kcSenFy5K3nU-zPIoqZDjQo6aSjF8hF6lrw1a1xbhfl9K3g4YJsuWsO
\ No newline at end of file
--- a/dom/security/test/contentverifier/file_contentserver.sjs
+++ b/dom/security/test/contentverifier/file_contentserver.sjs
@@ -1,37 +1,51 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
 // sjs for remote about:newtab (bug 1226928)
+"use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 Cu.import("resource://gre/modules/NetUtil.jsm");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.importGlobalProperties(["URLSearchParams"]);
 
 const path = "browser/dom/security/test/contentverifier/";
 
 const goodFileName = "file_about_newtab.html";
 const goodFileBase = path + goodFileName;
 const goodFile = FileUtils.getDir("TmpD", [], true);
 goodFile.append(goodFileName);
 const goodSignature = path + "file_about_newtab_good_signature";
-const goodKeyId = "RemoteNewTab";
+const goodX5UString = "\"https://example.com/browser/dom/security/test/contentverifier/file_contentserver.sjs?x5u=default\"";
 
 const scriptFileName = "script.js";
 const cssFileName = "style.css";
 const badFile = path + "file_about_newtab_bad.html";
 const brokenSignature = path + "file_about_newtab_broken_signature";
 const badSignature = path + "file_about_newtab_bad_signature";
-const badKeyId = "OldRemoteNewTabKey";
+const badX5UString = "\"https://example.com/browser/dom/security/test/contentverifier/file_contentserver.sjs?x5u=bad\"";
+const httpX5UString = "\"http://example.com/browser/dom/security/test/contentverifier/file_contentserver.sjs?x5u=default\"";
 
 const sriFile = path + "file_about_newtab_sri.html";
 const sriSignature = path + "file_about_newtab_sri_signature";
 
 const badCspFile = path + "file_about_newtab_bad_csp.html";
 const badCspSignature = path + "file_about_newtab_bad_csp_signature";
 
+// This cert chain is copied from
+// security/manager/ssl/tests/unit/test_content_signing/
+// using the certificates
+// * content_signing_remote_newtab_ee.pem
+// * content_signing_int.pem
+// * content_signing_root.pem
+const goodCertChainPath = path + "goodChain.pem";
+
 const tempFileNames = [goodFileName, scriptFileName, cssFileName];
 
 // we copy the file to serve as newtab to a temp directory because
 // we modify it during tests.
 setupTestFiles();
 
 function setupTestFiles() {
   for (let fileName of tempFileNames) {
@@ -110,24 +124,25 @@ function cleanupTestFiles() {
  * sig=good&key=good&file=good&header=good&cached=no to serve pages with
  * content signatures
  *
  * it further handles invalidateFile=yep and validateFile=yep to change the
  * served file
  */
 function handleRequest(request, response) {
   let params = new URLSearchParams(request.queryString);
-  let keyType = params.get("key");
+  let x5uType = params.get("x5u");
   let signatureType = params.get("sig");
   let fileType = params.get("file");
   let headerType = params.get("header");
   let cached = params.get("cached");
   let invalidateFile = params.get("invalidateFile");
   let validateFile = params.get("validateFile");
   let resource = params.get("resource");
+  let x5uParam = params.get("x5u");
 
   if (params.get("cleanup")) {
     cleanupTestFiles();
     response.setHeader("Content-Type", "text/html", false);
     response.write("Done");
     return;
   }
 
@@ -166,36 +181,46 @@ function handleRequest(request, response
         r = "Error";
       }
     }
     response.setHeader("Content-Type", "text/html", false);
     response.write(r);
     return;
   }
 
+  // we have to return the certificate chain on request for the x5u parameter
+  if (x5uParam && x5uParam == "default") {
+    response.setHeader("Cache-Control", "max-age=216000", false);
+    response.setHeader("Content-Type", "text/plain", false);
+    response.write(loadFile(getFileName(goodCertChainPath, "CurWorkD")));
+    return;
+  }
+
   // avoid confusing cache behaviours
   if (!cached) {
     response.setHeader("Cache-Control", "no-cache", false);
   } else {
     response.setHeader("Cache-Control", "max-age=3600", false);
   }
 
   // send HTML to test allowed/blocked behaviours
   response.setHeader("Content-Type", "text/html", false);
 
   // set signature header and key for Content-Signature header
   /* By default a good content-signature header is returned. Any broken return
    * value has to be indicated in the url.
    */
   let csHeader = "";
-  let keyId = goodKeyId;
+  let x5uString = goodX5UString;
   let signature = goodSignature;
   let file = goodFile;
-  if (keyType == "bad") {
-    keyId = badKeyId;
+  if (x5uType == "bad") {
+    x5uString = badX5UString;
+  } else if (x5uType == "http") {
+    x5uString = httpX5UString;
   }
   if (signatureType == "bad") {
     signature = badSignature;
   } else if (signatureType == "broken") {
     signature = brokenSignature;
   } else if (signatureType == "sri") {
     signature = sriSignature;
   } else if (signatureType == "bad-csp") {
@@ -206,29 +231,29 @@ function handleRequest(request, response
   } else if (fileType == "sri") {
     file = getFileName(sriFile, "CurWorkD");
   } else if (fileType == "bad-csp") {
     file = getFileName(badCspFile, "CurWorkD");
   }
 
   if (headerType == "good") {
     // a valid content-signature header
-    csHeader = "keyid=" + keyId + ";p384ecdsa=" +
+    csHeader = "x5u=" + x5uString + ";p384ecdsa=" +
                loadFile(getFileName(signature, "CurWorkD"));
   } else if (headerType == "error") {
     // this content-signature header is missing ; before p384ecdsa
-    csHeader = "keyid=" + keyId + "p384ecdsa=" +
+    csHeader = "x5u=" + x5uString + "p384ecdsa=" +
                loadFile(getFileName(signature, "CurWorkD"));
-  } else if (headerType == "errorInKeyid") {
+  } else if (headerType == "errorInX5U") {
     // this content-signature header is missing the keyid directive
-    csHeader = "keid=" + keyId + ";p384ecdsa=" +
+    csHeader = "x6u=" + x5uString + ";p384ecdsa=" +
                loadFile(getFileName(signature, "CurWorkD"));
   } else if (headerType == "errorInSignature") {
     // this content-signature header is missing the p384ecdsa directive
-    csHeader = "keyid=" + keyId + ";p385ecdsa=" +
+    csHeader = "x5u=" + x5uString + ";p385ecdsa=" +
                loadFile(getFileName(signature, "CurWorkD"));
   }
 
   if (csHeader) {
     response.setHeader("Content-Signature", csHeader, false);
   }
   let result = loadFile(file);
 
new file mode 100644
--- /dev/null
+++ b/dom/security/test/contentverifier/goodChain.pem
@@ -0,0 +1,51 @@
+-----BEGIN CERTIFICATE-----
+MIICSTCCATOgAwIBAgIUWQzTTfKLNZgX5ngi/ENiI2DO2kowCwYJKoZIhvcNAQEL
+MBExDzANBgNVBAMMBmludC1DQTAiGA8yMDE0MTEyNzAwMDAwMFoYDzIwMTcwMjA0
+MDAwMDAwWjAUMRIwEAYDVQQDDAllZS1pbnQtQ0EwdjAQBgcqhkjOPQIBBgUrgQQA
+IgNiAAShaHJDNitcexiJ83kVRhWhxz+0je6GPgIpFdtgjiUt5LcTLajOmOgxU05q
+nAwLCcjWOa3oMgbluoE0c6EfozDgXajJbkOD/ieHPalxA74oiM/wAvBa9xof3cyD
+dKpuqc6jRDBCMBMGA1UdJQQMMAoGCCsGAQUFBwMDMCsGA1UdEQQkMCKCIHJlbW90
+ZS1uZXd0YWItc2lnbmVyLm1vemlsbGEub3JnMAsGCSqGSIb3DQEBCwOCAQEAc2nE
+feYpA8WFyiPfZi56NgVgc8kXSKRNgplDtBHXK7gT7ICNQTSKkt+zHxnS9tAoXoix
+OGKsyp/8LNIYGMr4vHVNyOGnxuiLzAYjmDxXhp3t36xOFlU5Y7UaKf9G4feMXrNH
++q1SPYlP84keo1MaC5yhTZTTmJMKkRBsCbIVhfDnL3BUczxVZmk9F+7qK/trL222
+RoAaTZW5hdXUZrX630CYs1sQHWgL0B5rg2y9bwFk7toQ34JbjS0Z25e/MZUtFz19
+5tSjAZQHlLE6fAYZ3knrxF9xVMJCZf7gQqVphJzBtgy9yvTAtlMsrf6XS6sRRngz
+27HBxIpd4tYniYrtfg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIC0TCCAbugAwIBAgIULYyr3v/0zZ+XiR22NH7hOcnj2FcwCwYJKoZIhvcNAQEL
+MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTQxMTI3MDAwMDAwWhgPMjAxNzAyMDQwMDAw
+MDBaMBExDzANBgNVBAMMBmludC1DQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72x
+nAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lM
+wmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF
+4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20
+yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xx
+j5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMlMCMwDAYDVR0TBAUwAwEB/zAT
+BgNVHSUEDDAKBggrBgEFBQcDAzALBgkqhkiG9w0BAQsDggEBADfRBKSM08JF6vqz
+0EA+KNc0XIEAWApuHuwX6XXWeLgo6QN4E/9qfrsaO+C366WT+JDsjDOi40wW46SA
+XbguxtZQeZasNDUWp/leZix4RSJoHB7OllG1rgZJfN76zKVaXRGUmyQObkMMOJZe
+wIA0OBURT8ik9Z89pD0IWrqscds71Edfjt0hHgg63wVvIaklReZXvFOD3VmSCPNn
+2wB6ZzECcbhJpnzxZdsoMSGH0C6apYnNNTjqZjO90JVm/Ph/7nbi/KncYXA6ccl6
+Jz2mfiAquWIua2+CzBGbqjZVSATTpWCp+cXQJE1xka+hWUaL5HPTq1bTULRFlauZ
+HGl5lJk=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICzTCCAbegAwIBAgIUIVkGGA8HiO3RIKGjdOjVi+d6EVkwCwYJKoZIhvcNAQEL
+MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTQxMTI3MDAwMDAwWhgPMjAxNzAyMDQwMDAw
+MDBaMA0xCzAJBgNVBAMMAmNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptu
+Gobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO
+7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgf
+qDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/yt
+HSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcx
+uLP+SSP6clHEMdUDrNoYCjXtjQIDAQABoyUwIzAMBgNVHRMEBTADAQH/MBMGA1Ud
+JQQMMAoGCCsGAQUFBwMDMAsGCSqGSIb3DQEBCwOCAQEAlpbRzRIPnf43AwGfMvKP
+zOtntRy2nE9GlmY9I00uioHUnUrPLs8aw3UDtyiDWMGqcYysXGx9EX2Vk0POS4gf
+G6PA95F6GxTtbzIEZmTPVuzA/cfc9HU3HXDPqh+dySJ8/Ta4c4vX1lgeGGAvstNe
+q+9DaCGXs8MqMF8KtXNmOm3eS9q622hKEvTVEoxqj1t365kwKHaNpbObddQ6Xcny
+akvfh2L+8QbJSflcm8fL/JTup/2/cRG1ytOsaiXEr9JBEITOtQO0Ot/4Qzq+MJjv
+weaJ3hZ0c+cTy3tEvt+I7+lnW4Q5dB7aLR2/BZfLubhxz1SUVMuHfLH64fc0Uf1Q
+Nw==
+-----END CERTIFICATE-----
new file mode 100644
--- /dev/null
+++ b/dom/security/test/contentverifier/head.js
@@ -0,0 +1,210 @@
+/*
+ * Test Content-Signature for remote about:newtab
+ *  - Bug 1226928 - allow about:newtab to load remote content
+ *
+ * This tests content-signature verification on remote about:newtab in the
+ * following cases (see TESTS, all failed loads display about:blank fallback):
+ * - good case (signature should verify and correct page is displayed)
+ * - reload of newtab when the siganture was invalidated after the last correct
+ *   load
+ * - malformed content-signature header
+ * - malformed keyid directive
+ * - malformed p384ecdsa directive
+ * - wrong signature (this is not a siganture for the delivered document)
+ * - invalid signature (this is not even a signature)
+ * - loading a file that doesn't fit the key or signature
+ * - cache poisoning (load a malicious remote page not in newtab, subsequent
+ *   newtab load has to load the fallback)
+ */
+
+const ABOUT_NEWTAB_URI = "about:newtab";
+
+const BASE = "https://example.com/browser/dom/security/test/contentverifier/file_contentserver.sjs?";
+const URI_GOOD = BASE + "sig=good&x5u=good&file=good&header=good";
+
+const INVALIDATE_FILE = BASE + "invalidateFile=yep";
+const VALIDATE_FILE = BASE + "validateFile=yep";
+
+const URI_HEADER_BASE = BASE + "sig=good&x5u=good&file=good&header=";
+const URI_ERROR_HEADER = URI_HEADER_BASE + "error";
+const URI_KEYERROR_HEADER = URI_HEADER_BASE + "errorInX5U";
+const URI_SIGERROR_HEADER = URI_HEADER_BASE + "errorInSignature";
+const URI_NO_HEADER = URI_HEADER_BASE + "noHeader";
+
+const URI_BAD_SIG = BASE + "sig=bad&x5u=good&file=good&header=good";
+const URI_BROKEN_SIG = BASE + "sig=broken&x5u=good&file=good&header=good";
+const URI_BAD_X5U = BASE + "sig=good&x5u=bad&file=good&header=good";
+const URI_HTTP_X5U = BASE + "sig=good&x5u=http&file=good&header=good";
+const URI_BAD_FILE = BASE + "sig=good&x5u=good&file=bad&header=good";
+const URI_BAD_ALL = BASE + "sig=bad&x5u=bad&file=bad&header=bad";
+const URI_BAD_CSP = BASE + "sig=bad-csp&x5u=good&file=bad-csp&header=good";
+
+const URI_BAD_FILE_CACHED = BASE + "sig=good&x5u=good&file=bad&header=good&cached=true";
+
+const GOOD_ABOUT_STRING = "Just a fully good testpage for Bug 1226928";
+const BAD_ABOUT_STRING = "Just a bad testpage for Bug 1226928";
+const ABOUT_BLANK = "<head></head><body></body>";
+
+const URI_CLEANUP = BASE + "cleanup=true";
+const CLEANUP_DONE = "Done";
+
+const URI_SRI = BASE + "sig=sri&x5u=good&file=sri&header=good";
+const STYLESHEET_WITHOUT_SRI_BLOCKED = "Stylesheet without SRI blocked";
+const STYLESHEET_WITH_SRI_BLOCKED = "Stylesheet with SRI blocked";
+const STYLESHEET_WITH_SRI_LOADED = "Stylesheet with SRI loaded";
+const SCRIPT_WITHOUT_SRI_BLOCKED = "Script without SRI blocked";
+const SCRIPT_WITH_SRI_BLOCKED = "Script with SRI blocked";
+const SCRIPT_WITH_SRI_LOADED = "Script with SRI loaded";
+
+const CSP_TEST_SUCCESS_STRING = "CSP violation test succeeded.";
+
+// Needs to sync with pref "security.signed_content.CSP.default".
+const SIGNED_CONTENT_CSP = `{"csp-policies":[{"report-only":false,"script-src":["https://example.com","'unsafe-inline'"],"style-src":["https://example.com"]}]}`;
+
+var browser = null;
+var aboutNewTabService = Cc["@mozilla.org/browser/aboutnewtab-service;1"]
+                           .getService(Ci.nsIAboutNewTabService);
+
+function pushPrefs(...aPrefs) {
+  return new Promise((resolve) => {
+    SpecialPowers.pushPrefEnv({"set": aPrefs}, resolve);
+  });
+}
+
+/*
+ * run tests with input from TESTS
+ */
+function doTest(aExpectedStrings, reload, aUrl, aNewTabPref) {
+  // set about:newtab location for this test if it's a newtab test
+  if (aNewTabPref) {
+    aboutNewTabService.newTabURL = aNewTabPref;
+  }
+
+  // set prefs
+  yield pushPrefs(
+      ["browser.newtabpage.remote.content-signing-test", true],
+      ["browser.newtabpage.remote", true],
+      ["security.content.signature.root_hash",
+       "65:AE:D8:1E:B5:12:AE:B0:6B:38:58:BC:7C:47:35:3D:D4:EA:25:F1:63:DA:08:BB:86:3A:2E:97:39:66:8F:55"]);
+
+  if (aNewTabPref === URI_BAD_CSP) {
+    // Use stricter CSP to test CSP violation.
+    yield pushPrefs(["security.signed_content.CSP.default", "script-src 'self'; style-src 'self'"]);
+  } else {
+    // Use weaker CSP to test normal content.
+    yield pushPrefs(["security.signed_content.CSP.default", "script-src 'self' 'unsafe-inline'; style-src 'self'"]);
+  }
+
+  // start the test
+  yield BrowserTestUtils.withNewTab({
+      gBrowser,
+      url: aUrl,
+    },
+    function * (browser) {
+      // check if everything's set correct for testing
+      ok(Services.prefs.getBoolPref(
+          "browser.newtabpage.remote.content-signing-test"),
+          "sanity check: remote newtab signing test should be used");
+      ok(Services.prefs.getBoolPref("browser.newtabpage.remote"),
+          "sanity check: remote newtab should be used");
+      // we only check this if we really do a newtab test
+      if (aNewTabPref) {
+        ok(aboutNewTabService.overridden,
+            "sanity check: default URL for about:newtab should be overriden");
+        is(aboutNewTabService.newTabURL, aNewTabPref,
+            "sanity check: default URL for about:newtab should return the new URL");
+      }
+
+      // Every valid remote newtab page must have built-in CSP.
+      let shouldHaveCSP = ((aUrl === ABOUT_NEWTAB_URI) &&
+                          (aNewTabPref === URI_GOOD || aNewTabPref === URI_SRI));
+
+      if (shouldHaveCSP) {
+        is(browser.contentDocument.nodePrincipal.cspJSON, SIGNED_CONTENT_CSP,
+           "Valid remote newtab page must have built-in CSP.");
+      }
+
+      yield ContentTask.spawn(
+          browser, aExpectedStrings, function * (aExpectedStrings) {
+            for (let expectedString of aExpectedStrings) {
+              ok(content.document.documentElement.innerHTML.includes(expectedString),
+                 "Expect the following value in the result\n" + expectedString +
+                 "\nand got " + content.document.documentElement.innerHTML);
+            }
+          });
+
+      // for good test cases we check if a reload fails if the remote page
+      // changed from valid to invalid in the meantime
+      if (reload) {
+        yield BrowserTestUtils.withNewTab({
+            gBrowser,
+            url: INVALIDATE_FILE,
+          },
+          function * (browser2) {
+            yield ContentTask.spawn(browser2, null, function * () {
+              ok(content.document.documentElement.innerHTML.includes("Done"),
+                 "Expect the following value in the result\n" + "Done" +
+                 "\nand got " + content.document.documentElement.innerHTML);
+            });
+          }
+        );
+
+        browser.reload();
+        yield BrowserTestUtils.browserLoaded(browser);
+
+        let expectedStrings = [ABOUT_BLANK];
+        if (aNewTabPref == URI_SRI) {
+          expectedStrings = [
+            STYLESHEET_WITHOUT_SRI_BLOCKED,
+            STYLESHEET_WITH_SRI_BLOCKED,
+            SCRIPT_WITHOUT_SRI_BLOCKED,
+            SCRIPT_WITH_SRI_BLOCKED
+          ];
+        }
+        yield ContentTask.spawn(browser, expectedStrings,
+          function * (expectedStrings) {
+            for (let expectedString of expectedStrings) {
+              ok(content.document.documentElement.innerHTML.includes(expectedString),
+                 "Expect the following value in the result\n" + expectedString +
+                 "\nand got " + content.document.documentElement.innerHTML);
+            }
+          }
+        );
+
+        yield BrowserTestUtils.withNewTab({
+            gBrowser,
+            url: VALIDATE_FILE,
+          },
+          function * (browser2) {
+            yield ContentTask.spawn(browser2, null, function * () {
+              ok(content.document.documentElement.innerHTML.includes("Done"),
+                 "Expect the following value in the result\n" + "Done" +
+                 "\nand got " + content.document.documentElement.innerHTML);
+              });
+          }
+        );
+      }
+    }
+  );
+}
+
+function runTests() {
+  // run tests from TESTS
+  for (let i = 0; i < TESTS.length; i++) {
+    let testCase = TESTS[i];
+    let url = "", aNewTabPref = "";
+    let reload = false;
+    var aExpectedStrings = testCase.testStrings;
+    if (testCase.aboutURI) {
+      url = ABOUT_NEWTAB_URI;
+      aNewTabPref = testCase.aboutURI;
+      if (aNewTabPref == URI_GOOD || aNewTabPref == URI_SRI) {
+        reload = true;
+      }
+    } else {
+      url = testCase.url;
+    }
+
+    yield doTest(aExpectedStrings, reload, url, aNewTabPref);
+  }
+}
--- a/dom/storage/DOMStorage.cpp
+++ b/dom/storage/DOMStorage.cpp
@@ -213,18 +213,18 @@ DOMStorage::BroadcastChangeNotification(
   // Note, this DOM event should never reach JS. It is cloned later in
   // nsGlobalWindow.
   RefPtr<StorageEvent> event =
     StorageEvent::Constructor(nullptr, NS_LITERAL_STRING("storage"), dict);
 
   RefPtr<StorageNotifierRunnable> r =
     new StorageNotifierRunnable(event,
                                 GetType() == LocalStorage
-                                  ? MOZ_UTF16("localStorage")
-                                  : MOZ_UTF16("sessionStorage"));
+                                  ? u"localStorage"
+                                  : u"sessionStorage");
   NS_DispatchToMainThread(r);
 }
 
 static const char kPermissionType[] = "cookie";
 static const char kStorageEnabled[] = "dom.storage.enabled";
 
 // static, public
 bool
--- a/dom/svg/SVGLength.cpp
+++ b/dom/svg/SVGLength.cpp
@@ -20,17 +20,17 @@ namespace mozilla {
 static void GetUnitString(nsAString& unit, uint16_t unitType);
 static uint16_t GetUnitTypeForString(const nsAString& unitStr);
 
 void
 SVGLength::GetValueAsString(nsAString &aValue) const
 {
   char16_t buf[24];
   nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
-                            MOZ_UTF16("%g"),
+                            u"%g",
                             (double)mValue);
   aValue.Assign(buf);
 
   nsAutoString unitString;
   GetUnitString(unitString, mUnit);
   aValue.Append(unitString);
 }
 
--- a/dom/svg/SVGNumberList.cpp
+++ b/dom/svg/SVGNumberList.cpp
@@ -28,17 +28,17 @@ SVGNumberList::GetValueAsString(nsAStrin
 {
   aValue.Truncate();
   char16_t buf[24];
   uint32_t last = mNumbers.Length() - 1;
   for (uint32_t i = 0; i < mNumbers.Length(); ++i) {
     // Would like to use aValue.AppendPrintf("%f", mNumbers[i]), but it's not
     // possible to always avoid trailing zeros.
     nsTextFormatter::snprintf(buf, ArrayLength(buf),
-                              MOZ_UTF16("%g"),
+                              u"%g",
                               double(mNumbers[i]));
     // We ignore OOM, since it's not useful for us to return an error.
     aValue.Append(buf);
     if (i != last) {
       aValue.Append(' ');
     }
   }
 }
--- a/dom/svg/SVGPathSegUtils.cpp
+++ b/dom/svg/SVGPathSegUtils.cpp
@@ -32,51 +32,51 @@ SVGPathSegUtils::GetValueAsString(const 
   uint32_t type = DecodeType(aSeg[0]);
   char16_t typeAsChar = GetPathSegTypeAsLetter(type);
 
   // Special case arcs:
   if (IsArcType(type)) {
     bool largeArcFlag = aSeg[4] != 0.0f;
     bool sweepFlag = aSeg[5] != 0.0f;
     nsTextFormatter::ssprintf(aValue,
-                              MOZ_UTF16("%c%g,%g %g %d,%d %g,%g"),
+                              u"%c%g,%g %g %d,%d %g,%g",
                               typeAsChar, aSeg[1], aSeg[2], aSeg[3],
                               largeArcFlag, sweepFlag, aSeg[6], aSeg[7]);
   } else {
 
     switch (ArgCountForType(type)) {
     case 0:
       aValue = typeAsChar;
       break;
 
     case 1:
-      nsTextFormatter::ssprintf(aValue, MOZ_UTF16("%c%g"),
+      nsTextFormatter::ssprintf(aValue, u"%c%g",
                                 typeAsChar, aSeg[1]);
       break;
 
     case 2:
-      nsTextFormatter::ssprintf(aValue, MOZ_UTF16("%c%g,%g"),
+      nsTextFormatter::ssprintf(aValue, u"%c%g,%g",
                                 typeAsChar, aSeg[1], aSeg[2]);
       break;
 
     case 4:
-      nsTextFormatter::ssprintf(aValue, MOZ_UTF16("%c%g,%g %g,%g"),
+      nsTextFormatter::ssprintf(aValue, u"%c%g,%g %g,%g",
                                 typeAsChar, aSeg[1], aSeg[2], aSeg[3], aSeg[4]);
       break;
 
     case 6:
       nsTextFormatter::ssprintf(aValue,
-                                MOZ_UTF16("%c%g,%g %g,%g %g,%g"),
+                                u"%c%g,%g %g,%g %g,%g",
                                 typeAsChar, aSeg[1], aSeg[2], aSeg[3], aSeg[4],
                                 aSeg[5], aSeg[6]);
       break;
 
     default:
       MOZ_ASSERT(false, "Unknown segment type");
-      aValue = MOZ_UTF16("<unknown-segment-type>");
+      aValue = u"<unknown-segment-type>";
       return;
     }
   }
   
   // nsTextFormatter::ssprintf is one of the nsTextFormatter methods that
   // randomly appends '\0' to its output string, which means that the length
   // of the output string is one too long. We need to manually remove that '\0'
   // until nsTextFormatter is fixed.
--- a/dom/svg/SVGPointList.cpp
+++ b/dom/svg/SVGPointList.cpp
@@ -27,17 +27,17 @@ SVGPointList::GetValueAsString(nsAString
 {
   aValue.Truncate();
   char16_t buf[50];
   uint32_t last = mItems.Length() - 1;
   for (uint32_t i = 0; i < mItems.Length(); ++i) {
     // Would like to use aValue.AppendPrintf("%f,%f", item.mX, item.mY),
     // but it's not possible to always avoid trailing zeros.
     nsTextFormatter::snprintf(buf, ArrayLength(buf),
-                              MOZ_UTF16("%g,%g"),
+                              u"%g,%g",
                               double(mItems[i].mX), double(mItems[i].mY));
     // We ignore OOM, since it's not useful for us to return an error.
     aValue.Append(buf);
     if (i != last) {
       aValue.Append(' ');
     }
   }
 }
--- a/dom/svg/nsSVGAngle.cpp
+++ b/dom/svg/nsSVGAngle.cpp
@@ -81,17 +81,17 @@ GetUnitTypeForString(const nsAString& un
   return SVG_ANGLETYPE_UNKNOWN;
 }
 
 static void
 GetValueString(nsAString &aValueAsString, float aValue, uint16_t aUnitType)
 {
   char16_t buf[24];
   nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
-                            MOZ_UTF16("%g"),
+                            u"%g",
                             (double)aValue);
   aValueAsString.Assign(buf);
 
   nsAutoString unitString;
   GetUnitString(unitString, aUnitType);
   aValueAsString.Append(unitString);
 }
 
--- a/dom/svg/nsSVGLength2.cpp
+++ b/dom/svg/nsSVGLength2.cpp
@@ -84,17 +84,17 @@ GetUnitTypeForString(const nsAString& un
   return nsIDOMSVGLength::SVG_LENGTHTYPE_UNKNOWN;
 }
 
 static void
 GetValueString(nsAString &aValueAsString, float aValue, uint16_t aUnitType)
 {
   char16_t buf[24];
   nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
-                            MOZ_UTF16("%g"),
+                            u"%g",
                             (double)aValue);
   aValueAsString.Assign(buf);
 
   nsAutoString unitString;
   GetUnitString(unitString, aUnitType);
   aValueAsString.Append(unitString);
 }
 
--- a/dom/svg/nsSVGTransform.cpp
+++ b/dom/svg/nsSVGTransform.cpp
@@ -20,51 +20,51 @@ nsSVGTransform::GetValueAsString(nsAStri
 {
   char16_t buf[256];
 
   switch (mType) {
     case SVG_TRANSFORM_TRANSLATE:
       // The spec say that if Y is not provided, it is assumed to be zero.
       if (mMatrix._32 != 0)
         nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
-            MOZ_UTF16("translate(%g, %g)"),
+            u"translate(%g, %g)",
             mMatrix._31, mMatrix._32);
       else
         nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
-            MOZ_UTF16("translate(%g)"),
+            u"translate(%g)",
             mMatrix._31);
       break;
     case SVG_TRANSFORM_ROTATE:
       if (mOriginX != 0.0f || mOriginY != 0.0f)
         nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
-            MOZ_UTF16("rotate(%g, %g, %g)"),
+            u"rotate(%g, %g, %g)",
             mAngle, mOriginX, mOriginY);
       else
         nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
-            MOZ_UTF16("rotate(%g)"), mAngle);
+            u"rotate(%g)", mAngle);
       break;
     case SVG_TRANSFORM_SCALE:
       if (mMatrix._11 != mMatrix._22)
         nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
-            MOZ_UTF16("scale(%g, %g)"), mMatrix._11, mMatrix._22);
+            u"scale(%g, %g)", mMatrix._11, mMatrix._22);
       else
         nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
-            MOZ_UTF16("scale(%g)"), mMatrix._11);
+            u"scale(%g)", mMatrix._11);
       break;
     case SVG_TRANSFORM_SKEWX:
       nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
-                                MOZ_UTF16("skewX(%g)"), mAngle);
+                                u"skewX(%g)", mAngle);
       break;
     case SVG_TRANSFORM_SKEWY:
       nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
-                                MOZ_UTF16("skewY(%g)"), mAngle);
+                                u"skewY(%g)", mAngle);
       break;
     case SVG_TRANSFORM_MATRIX:
       nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
-          MOZ_UTF16("matrix(%g, %g, %g, %g, %g, %g)"),
+          u"matrix(%g, %g, %g, %g, %g, %g)",
                             mMatrix._11, mMatrix._12,
                             mMatrix._21, mMatrix._22,
                             mMatrix._31, mMatrix._32);
       break;
     default:
       buf[0] = '\0';
       NS_ERROR("unknown transformation type");
       break;
--- a/dom/svg/nsSVGViewBox.cpp
+++ b/dom/svg/nsSVGViewBox.cpp
@@ -202,17 +202,17 @@ void
 nsSVGViewBox::GetBaseValueString(nsAString& aValue) const
 {
   if (mBaseVal.none) {
     aValue.AssignLiteral("none");
     return;
   }
   char16_t buf[200];
   nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
-                            MOZ_UTF16("%g %g %g %g"),
+                            u"%g %g %g %g",
                             (double)mBaseVal.x, (double)mBaseVal.y,
                             (double)mBaseVal.width, (double)mBaseVal.height);
   aValue.Assign(buf);
 }
 
 
 already_AddRefed<dom::SVGAnimatedRect>
 nsSVGViewBox::ToSVGAnimatedRect(nsSVGElement* aSVGElement)
--- a/dom/system/gonk/AudioManager.cpp
+++ b/dom/system/gonk/AudioManager.cpp
@@ -51,20 +51,20 @@ using namespace mozilla::dom;
 using namespace mozilla::dom::gonk;
 using namespace android;
 using namespace mozilla;
 using namespace mozilla::dom::bluetooth;
 
 #undef LOG
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "AudioManager" , ## args)
 
-#define HEADPHONES_STATUS_HEADSET     MOZ_UTF16("headset")
-#define HEADPHONES_STATUS_HEADPHONE   MOZ_UTF16("headphone")
-#define HEADPHONES_STATUS_OFF         MOZ_UTF16("off")
-#define HEADPHONES_STATUS_UNKNOWN     MOZ_UTF16("unknown")
+#define HEADPHONES_STATUS_HEADSET     u"headset"
+#define HEADPHONES_STATUS_HEADPHONE   u"headphone"
+#define HEADPHONES_STATUS_OFF         u"off"
+#define HEADPHONES_STATUS_UNKNOWN     u"unknown"
 #define HEADPHONES_STATUS_CHANGED     "headphones-status-changed"
 #define MOZ_SETTINGS_CHANGE_ID        "mozsettings-changed"
 #define AUDIO_CHANNEL_PROCESS_CHANGED "audio-channel-process-changed"
 #define AUDIO_POLICY_SERVICE_NAME     "media.audio_policy"
 #define SETTINGS_SERVICE              "@mozilla.org/settingsService;1"
 
 // Refer AudioService.java from Android
 static const uint32_t sMaxStreamVolumeTbl[AUDIO_STREAM_CNT] = {
--- a/dom/system/gonk/GonkGPSGeolocationProvider.cpp
+++ b/dom/system/gonk/GonkGPSGeolocationProvider.cpp
@@ -173,39 +173,37 @@ public:
     nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
     obsService->NotifyObservers(provider, "geolocation-device-events", mData);
     return NS_OK;
   }
 private:
   const char16_t* mData;
 };
 
-
-
 void
 GonkGPSGeolocationProvider::StatusCallback(GpsStatus* status)
 {
   const char* msgStream=0;
   switch (status->status) {
     case GPS_STATUS_NONE:
       msgStream = "geo: GPS_STATUS_NONE\n";
       break;
     case GPS_STATUS_SESSION_BEGIN:
       msgStream = "geo: GPS_STATUS_SESSION_BEGIN\n";
       break;
     case GPS_STATUS_SESSION_END:
       msgStream = "geo: GPS_STATUS_SESSION_END\n";
       break;
     case GPS_STATUS_ENGINE_ON:
       msgStream = "geo: GPS_STATUS_ENGINE_ON\n";
-      NS_DispatchToMainThread(new NotifyObserversGPSTask( MOZ_UTF16("GPSStarting")));
+      NS_DispatchToMainThread(new NotifyObserversGPSTask(u"GPSStarting"));
       break;
     case GPS_STATUS_ENGINE_OFF:
       msgStream = "geo: GPS_STATUS_ENGINE_OFF\n";
-      NS_DispatchToMainThread(new NotifyObserversGPSTask( MOZ_UTF16("GPSShutdown")));
+      NS_DispatchToMainThread(new NotifyObserversGPSTask(u"GPSShutdown"));
       break;
     default:
       msgStream = "geo: Unknown GPS status\n";
       break;
   }
   if (gDebug_isLoggingEnabled){
     DBG("%s", msgStream);
   }
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -67,16 +67,17 @@ var ecmaGlobals =
     {name: "Intl", android: false},
     "Iterator",
     "JSON",
     "Map",
     "Math",
     {name: "NaN", xbl: false},
     "Number",
     "Object",
+    "Promise",
     "Proxy",
     "RangeError",
     "ReferenceError",
     "Reflect",
     "RegExp",
     "Set",
     {name: "SharedArrayBuffer", nightly: true},
     {name: "SIMD", nightly: true},
@@ -957,18 +958,16 @@ var interfaceNamesInGlobalScope =
     {name: "PresentationSession", disabled: true, permission: ["presentation"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "PresentationSessionConnectEvent", disabled: true, permission: ["presentation"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "ProcessingInstruction",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "ProgressEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    "Promise",
-// IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "PushManager", b2g: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "PushSubscription", b2g: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "PushSubscriptionOptions", b2g: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "RadioNodeList",
 // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/dom/tests/mochitest/general/test_offsets.html
+++ b/dom/tests/mochitest/general/test_offsets.html
@@ -7,22 +7,18 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
 
 <style>
   input {
     box-sizing: content-box;
   }
 </style>
 </head>
-
-<!-- We set a transform on the body element so that it creates a reference frame.
-     This makes sure that snapping of scrolled areas for the contained elements
-     is not influenced by offsets outside of this document. -->
 <body id="body" onload="setTimeout(testElements, 0, 'testelements', SimpleTest.finish);"
-      style="margin: 1px; border: 2px solid black; padding: 4px; transform: translateY(1px);">
+      style="margin: 1px; border: 2px solid black; padding: 4px;">
 
 <div id="testelements" style="margin: 0; border: 0; padding: 0;">
   <div id="div1" style="margin: 0; margin-left: 6px; margin-top: 2px; border: 1px solid green; padding: 6px; width: 50px; height: 20px"
          _offsetLeft="13" _offsetTop="9" _offsetWidth="64" _offsetHeight="34"
          _scrollWidth="62" _scrollHeight="32"
          _clientLeft="1" _clientTop="1" _clientWidth="62" _clientHeight="32"></div>
   <div id="noscroll" style="margin: 2px; border: 1px solid blue; padding: 3px;"
        _offsetLeft="10" _offsetTop="12" _offsetWidth="64" _offsetHeight="34"
@@ -54,17 +50,17 @@
   <div id="fixed" style="position: fixed; margin: 2px; border: 1px solid orange; padding: 7px; left: 87px; top: 12px;">
     This is some fixed positioned text.
     <div id="fixed-block" _offsetParent="fixed">
       <div id="fixed-replaced" _offsetParent="fixed" style="margin: 1px; border: 0; padding: 3px;"></div>
     </div>
   </div>
 
   <div id="scrollbox"
-       style="overflow: scroll; padding-left: 0px; margin: 3px; border: 4px solid green; max-width: 80px; max-height: 70px"
+       style="overflow: scroll; padding-left: 0px; margin: 3px; border: 4px solid green; max-width: 80px; max-height: 70px;"
        _scrollWidth="62" _scrollHeight="32"
        _clientLeft="1" _clientTop="1" _clientWidth="62" _clientHeight="32"><p id="p1" style="margin: 0; padding: 0;">One</p>
     <p id="p2">Two</p>
     <p id="scrollchild">Three</p>
     <p id="lastlinebox" style="margin: 0; padding: 0;"><input id="lastline" type="button"
                                style="margin: 0px; border: 2px solid red;"
                                value="This button is much longer than the others">
   </p></div>
--- a/dom/tests/mochitest/general/test_offsets.js
+++ b/dom/tests/mochitest/general/test_offsets.js
@@ -176,35 +176,22 @@ function checkClientState(element, left,
 }
 
 function checkCoord(element, type, val, testname)
 {
   if (val != -10000)
     is(element[type], Math.round(val), testname + " " + type);
 }
 
-function checkCoordFuzzy(element, type, val, fuzz, testname)
-{
-  if (val != -10000)
-    ok(Math.abs(element[type] - Math.round(val)) <= fuzz, testname + " " + type);
-}
-
 function checkCoords(element, type, left, top, width, height, testname)
 {
   checkCoord(element, type + "Left", left, testname);
   checkCoord(element, type + "Top", top, testname);
-
-  if (type == "scroll") {
-    // scrollWidth and scrollHeight can deviate by 1 pixel due to snapping.
-    checkCoordFuzzy(element, type + "Width", width, 1, testname);
-    checkCoordFuzzy(element, type + "Height", height, 1, testname);
-  } else {
-    checkCoord(element, type + "Width", width, testname);
-    checkCoord(element, type + "Height", height, testname);
-  }
+  checkCoord(element, type + "Width", width, testname);
+  checkCoord(element, type + "Height", height, testname);
 
   if (element instanceof SVGElement)
     return;
 
   if (element.id == "outerpopup" && !element.parentNode.open) // closed popup
     return;
 
   if (element.id == "div-displaynone" || element.id == "nonappended") // hidden elements
--- a/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js
+++ b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js
@@ -42,16 +42,17 @@ var ecmaGlobals =
     {name: "Intl", android: false},
     "Iterator",
     "JSON",
     "Map",
     "Math",
     "NaN",
     "Number",
     "Object",
+    "Promise",
     "Proxy",
     "RangeError",
     "ReferenceError",
     "Reflect",
     "RegExp",
     "Set",
     {name: "SharedArrayBuffer", nightly: true},
     {name: "SIMD", nightly: true},
@@ -170,18 +171,16 @@ var interfaceNamesInGlobalScope =
     "PerformanceMark",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "PerformanceMeasure",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     { name: "PerformanceObserver", nightly: true },
 // IMPORTANT: Do not change this list without review from a DOM peer!
     { name: "PerformanceObserverEntryList", nightly: true },
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    "Promise",
-// IMPORTANT: Do not change this list without review from a DOM peer!
     { name: "PushEvent", b2g: false },
 // IMPORTANT: Do not change this list without review from a DOM peer!
     { name: "PushManager", b2g: false },
 // IMPORTANT: Do not change this list without review from a DOM peer!
     { name: "PushMessageData", b2g: false },
 // IMPORTANT: Do not change this list without review from a DOM peer!
     { name: "PushSubscription", b2g: false },
 // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/dom/workers/test/test_worker_interfaces.js
+++ b/dom/workers/test/test_worker_interfaces.js
@@ -42,16 +42,17 @@ var ecmaGlobals =
     {name: "Intl", android: false},
     "Iterator",
     "JSON",
     "Map",
     "Math",
     "NaN",
     "Number",
     "Object",
+    "Promise",
     "Proxy",
     "RangeError",
     "ReferenceError",
     "Reflect",
     "RegExp",
     "Set",
     {name: "SharedArrayBuffer", nightly: true},
     {name: "SIMD", nightly: true},
@@ -162,18 +163,16 @@ var interfaceNamesInGlobalScope =
     "PerformanceMark",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "PerformanceMeasure",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     { name: "PerformanceObserver", nightly: true },
 // IMPORTANT: Do not change this list without review from a DOM peer!
     { name: "PerformanceObserverEntryList", nightly: true },
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    "Promise",
-// IMPORTANT: Do not change this list without review from a DOM peer!
     { name: "PushManager", b2g: false },
 // IMPORTANT: Do not change this list without review from a DOM peer!
     { name: "PushSubscription", b2g: false },
 // IMPORTANT: Do not change this list without review from a DOM peer!
     { name: "PushSubscriptionOptions", b2g: false },
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Request",
 // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/dom/xbl/nsXBLDocumentInfo.cpp
+++ b/dom/xbl/nsXBLDocumentInfo.cpp
@@ -241,17 +241,17 @@ nsXBLDocumentInfo::ReadPrototypeBindings
       break;
 
     rv = nsXBLPrototypeBinding::ReadNewBinding(stream, docInfo, doc, flags);
     if (NS_FAILED(rv)) {
       return rv;
     }
   }
 
-  docInfo.swap(*aDocInfo);
+  docInfo.forget(aDocInfo);
   return NS_OK;
 }
 
 nsresult
 nsXBLDocumentInfo::WritePrototypeBindings()
 {
   // Only write out bindings with the system principal
   if (!nsContentUtils::IsSystemPrincipal(mDocument->NodePrincipal()))
--- a/dom/xbl/nsXBLProtoImplField.cpp
+++ b/dom/xbl/nsXBLProtoImplField.cpp
@@ -489,10 +489,10 @@ nsXBLProtoImplField::Write(nsIObjectOutp
 
   nsresult rv = aStream->Write8(type);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = aStream->WriteWStringZ(mName);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = aStream->Write32(mLineNumber);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  return aStream->WriteWStringZ(mFieldText ? mFieldText : MOZ_UTF16(""));
+  return aStream->WriteWStringZ(mFieldText ? mFieldText : u"");
 }
--- a/dom/xbl/nsXBLPrototypeHandler.cpp
+++ b/dom/xbl/nsXBLPrototypeHandler.cpp
@@ -997,10 +997,10 @@ nsXBLPrototypeHandler::Write(nsIObjectOu
   rv = aStream->Write32(mDetail);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = aStream->WriteWStringZ(nsDependentAtomString(mEventName).get());
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = aStream->Write32(mLineNumber);
   NS_ENSURE_SUCCESS(rv, rv);
-  return aStream->WriteWStringZ(mHandlerText ? mHandlerText : MOZ_UTF16(""));
+  return aStream->WriteWStringZ(mHandlerText ? mHandlerText : u"");
 }
--- a/dom/xbl/nsXBLService.cpp
+++ b/dom/xbl/nsXBLService.cpp
@@ -493,17 +493,17 @@ nsXBLService::LoadBindings(nsIContent* a
 
     // Set up our properties
     rv = newBinding->InstallImplementation();
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Figure out if we have any scoped sheets.  If so, we do a second resolve.
     *aResolveStyle = newBinding->HasStyleSheets();
 
-    newBinding.swap(*aBinding);
+    newBinding.forget(aBinding);
   }
 
   return NS_OK;
 }
 
 nsresult
 nsXBLService::FlushStyleBindings(nsIContent* aContent)
 {
--- a/dom/xml/nsXMLContentSink.cpp
+++ b/dom/xml/nsXMLContentSink.cpp
@@ -1324,18 +1324,18 @@ nsXMLContentSink::ReportError(const char
     mXSLTProcessor->CancelLoads();
     mXSLTProcessor = nullptr;
   }
 
   // release the nodes on stack
   mContentStack.Clear();
   mNotifyLevel = 0;
 
-  rv = HandleProcessingInstruction(MOZ_UTF16("xml-stylesheet"),
-                                   MOZ_UTF16("href=\"chrome://global/locale/intl.css\" type=\"text/css\""));
+  rv = HandleProcessingInstruction(u"xml-stylesheet",
+                                   u"href=\"chrome://global/locale/intl.css\" type=\"text/css\"");
   NS_ENSURE_SUCCESS(rv, rv);
 
   const char16_t* noAtts[] = { 0, 0 };
 
   NS_NAMED_LITERAL_STRING(errorNs,
                           "http://www.mozilla.org/newlayout/xml/parsererror.xml");
 
   nsAutoString parsererror(errorNs);
--- a/dom/xslt/xpath/txPathExpr.cpp
+++ b/dom/xslt/xpath/txPathExpr.cpp
@@ -58,17 +58,17 @@ PathExpr::evaluate(txIEvalContext* aCont
 
     NS_ENSURE_TRUE(res->getResultType() == txAExprResult::NODESET,
                    NS_ERROR_XSLT_NODESET_EXPECTED);
 
     RefPtr<txNodeSet> nodes = static_cast<txNodeSet*>
                                            (static_cast<txAExprResult*>
                                                        (res));
     if (nodes->isEmpty()) {
-        res.swap(*aResult);
+        res.forget(aResult);
 
         return NS_OK;
     }
     res = nullptr; // To allow recycling
 
     // Evaluate remaining steps
     uint32_t i, len = mItems.Length();
     for (i = 1; i < len; ++i) {
--- a/dom/xslt/xslt/txEXSLTFunctions.cpp
+++ b/dom/xslt/xslt/txEXSLTFunctions.cpp
@@ -253,17 +253,17 @@ txEXSLTFunctionCall::evaluate(txIEvalCon
     switch (mType) {
         case NODE_SET:
         {
             RefPtr<txAExprResult> exprResult;
             rv = mParams[0]->evaluate(aContext, getter_AddRefs(exprResult));
             NS_ENSURE_SUCCESS(rv, rv);
 
             if (exprResult->getResultType() == txAExprResult::NODESET) {
-                exprResult.swap(*aResult);
+                exprResult.forget(aResult);
             }
             else {
                 RefPtr<txNodeSet> resultSet;
                 rv = aContext->recycler()->
                     getNodeSet(getter_AddRefs(resultSet));
                 NS_ENSURE_SUCCESS(rv, rv);
 
                 if (exprResult->getResultType() ==
--- a/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp
+++ b/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp
@@ -350,19 +350,17 @@ txStylesheetSink::GetInterface(const nsI
         nsCOMPtr<nsIWindowWatcher> wwatcher =
             do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
         NS_ENSURE_SUCCESS(rv, rv);
 
         nsCOMPtr<nsIAuthPrompt> prompt;
         rv = wwatcher->GetNewAuthPrompter(nullptr, getter_AddRefs(prompt));
         NS_ENSURE_SUCCESS(rv, rv);
 
-        nsIAuthPrompt* rawPtr = nullptr;
-        prompt.swap(rawPtr);
-        *aResult = rawPtr;
+        prompt.forget(aResult);
 
         return NS_OK;
     }
 
     return NS_ERROR_NO_INTERFACE;
 }
 
 class txCompileObserver final : public txACompileObserver
--- a/dom/xslt/xslt/txMozillaXSLTProcessor.cpp
+++ b/dom/xslt/xslt/txMozillaXSLTProcessor.cpp
@@ -1089,22 +1089,22 @@ txMozillaXSLTProcessor::reportError(nsre
 
             nsXPIDLString errorMessage;
             nsCOMPtr<nsIStringBundle> bundle;
             sbs->CreateBundle(XSLT_MSGS_URL, getter_AddRefs(bundle));
 
             if (bundle) {
                 const char16_t* error[] = { errorText.get() };
                 if (mStylesheet) {
-                    bundle->FormatStringFromName(MOZ_UTF16("TransformError"),
+                    bundle->FormatStringFromName(u"TransformError",
                                                  error, 1,
                                                  getter_Copies(errorMessage));
                 }
                 else {
-                    bundle->FormatStringFromName(MOZ_UTF16("LoadingError"),
+                    bundle->FormatStringFromName(u"LoadingError",
                                                  error, 1,
                                                  getter_Copies(errorMessage));
                 }
             }
             mErrorText.Assign(errorMessage);
         }
     }
 
--- a/dom/xul/XULDocument.cpp
+++ b/dom/xul/XULDocument.cpp
@@ -1429,17 +1429,17 @@ XULDocument::GetPopupNode(nsIDOMNode** a
         nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
         if (pm) {
             node = pm->GetLastTriggerPopupNode(this);
         }
     }
 
     if (node && nsContentUtils::CanCallerAccess(node)
         && GetScopeObjectOfNode(node)) {
-        node.swap(*aNode);
+        node.forget(aNode);
     }
 
     return NS_OK;
 }
 
 already_AddRefed<nsINode>
 XULDocument::GetPopupNode()
 {
@@ -1541,17 +1541,17 @@ NS_IMETHODIMP
 XULDocument::GetTooltipNode(nsIDOMNode** aNode)
 {
     *aNode = nullptr;
 
     nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
     if (pm) {
         nsCOMPtr<nsIDOMNode> node = pm->GetLastTriggerTooltipNode(this);
         if (node && nsContentUtils::CanCallerAccess(node))
-            node.swap(*aNode);
+            node.forget(aNode);
     }
 
     return NS_OK;
 }
 
 already_AddRefed<nsINode>
 XULDocument::GetTooltipNode()
 {
@@ -3574,17 +3574,17 @@ XULDocument::CreateElementFromPrototype(
                            NOT_FROM_PARSER);
         if (NS_FAILED(rv))
             return rv;
 
         rv = AddAttributes(aPrototype, result);
         if (NS_FAILED(rv)) return rv;
     }
 
-    result.swap(*aResult);
+    result.forget(aResult);
 
     return NS_OK;
 }
 
 nsresult
 XULDocument::CreateOverlayElement(nsXULPrototypeElement* aPrototype,
                                   Element** aResult)
 {
--- a/dom/xul/nsXULPopupListener.cpp
+++ b/dom/xul/nsXULPopupListener.cpp
@@ -22,17 +22,17 @@
 #include "nsIScriptContext.h"
 #include "nsIDOMWindow.h"
 #include "nsIDOMXULDocument.h"
 #include "nsIDocument.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsLayoutUtils.h"
-#include "nsHTMLReflowState.h"
+#include "mozilla/ReflowInput.h"
 #include "nsIObjectLoadingContent.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent()
 #include "mozilla/dom/EventTarget.h"
 #include "mozilla/dom/FragmentOrElement.h"
 
--- a/dom/xul/templates/nsXULContentUtils.cpp
+++ b/dom/xul/templates/nsXULContentUtils.cpp
@@ -89,17 +89,17 @@ nsXULContentUtils::Init()
 #define XUL_RESOURCE(ident, uri)                              \
   PR_BEGIN_MACRO                                              \
    rv = gRDF->GetResource(NS_LITERAL_CSTRING(uri), &(ident)); \
    if (NS_FAILED(rv)) return rv;                              \
   PR_END_MACRO
 
 #define XUL_LITERAL(ident, val)                                   \
   PR_BEGIN_MACRO                                                  \
-   rv = gRDF->GetLiteral(MOZ_UTF16(val), &(ident));               \
+   rv = gRDF->GetLiteral(val, &(ident));                          \
    if (NS_FAILED(rv)) return rv;                                  \
   PR_END_MACRO
 
 #include "nsXULResourceList.h"
 #undef XUL_RESOURCE
 #undef XUL_LITERAL
 
     gFormat = nsIDateTimeFormat::Create().take();
--- a/dom/xul/templates/nsXULResourceList.h
+++ b/dom/xul/templates/nsXULResourceList.h
@@ -5,9 +5,9 @@
 
 // N.B., no include guard! We'll include this multiple times in some
 // files.
 
 XUL_RESOURCE(NC_child,  NC_NAMESPACE_URI "child");
 XUL_RESOURCE(NC_Folder, NC_NAMESPACE_URI "Folder");
 XUL_RESOURCE(NC_open,   NC_NAMESPACE_URI "open");
 
-XUL_LITERAL(true_, "true");
+XUL_LITERAL(true_, u"true");
--- a/editor/composer/nsEditingSession.cpp
+++ b/editor/composer/nsEditingSession.cpp
@@ -1028,17 +1028,17 @@ nsEditingSession::EndDocumentLoad(nsIWeb
 void
 nsEditingSession::TimerCallback(nsITimer* aTimer, void* aClosure)
 {
   nsCOMPtr<nsIDocShell> docShell = do_QueryReferent(static_cast<nsIWeakReference*> (aClosure));
   if (docShell)
   {
     nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(docShell));
     if (webNav)
-      webNav->LoadURI(MOZ_UTF16("about:blank"),
+      webNav->LoadURI(u"about:blank",
                       0, nullptr, nullptr, nullptr);
   }
 }
 
 /*---------------------------------------------------------------------------
 
   StartPageLoad
 
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -1207,22 +1207,20 @@ HTMLEditor::ReplaceHeadContentsWithHTML(
   NS_ENSURE_TRUE(headNode, NS_ERROR_NULL_POINTER);
 
   // First, make sure there are no return chars in the source.  Bad things
   // happen if you insert returns (instead of dom newlines, \n) into an editor
   // document.
   nsAutoString inputString (aSourceToInsert);  // hope this does copy-on-write
 
   // Windows linebreaks: Map CRLF to LF:
-  inputString.ReplaceSubstring(MOZ_UTF16("\r\n"),
-                               MOZ_UTF16("\n"));
+  inputString.ReplaceSubstring(u"\r\n", u"\n");
 
   // Mac linebreaks: Map any remaining CR to LF:
-  inputString.ReplaceSubstring(MOZ_UTF16("\r"),
-                               MOZ_UTF16("\n"));
+  inputString.ReplaceSubstring(u"\r", u"\n");
 
   AutoEditBatch beginBatching(this);
 
   // Get the first range in the selection, for context:
   RefPtr<nsRange> range = selection->GetRangeAt(0);
   NS_ENSURE_TRUE(range, NS_ERROR_NULL_POINTER);
 
   ErrorResult err;
--- a/embedding/browser/nsDocShellTreeOwner.cpp
+++ b/embedding/browser/nsDocShellTreeOwner.cpp
@@ -6,17 +6,17 @@
 
 // Local Includes
 #include "nsDocShellTreeOwner.h"
 #include "nsWebBrowser.h"
 
 // Helper Classes
 #include "nsStyleCoord.h"
 #include "nsSize.h"
-#include "nsHTMLReflowState.h"
+#include "mozilla/ReflowInput.h"
 #include "nsIServiceManager.h"
 #include "nsComponentManagerUtils.h"
 #include "nsXPIDLString.h"
 #include "nsIAtom.h"
 #include "nsReadableUtils.h"
 #include "nsUnicharUtils.h"
 #include "nsISimpleEnumerator.h"
 #include "mozilla/LookAndFeel.h"
--- a/embedding/components/commandhandler/nsCommandManager.cpp
+++ b/embedding/components/commandhandler/nsCommandManager.cpp
@@ -77,17 +77,17 @@ nsCommandManager::CommandStatusChanged(c
   if (commandObservers) {
     // XXX Should we worry about observers removing themselves from Observe()?
     int32_t i, numItems = commandObservers->Length();
     for (i = 0; i < numItems; ++i) {
       nsCOMPtr<nsIObserver> observer = commandObservers->ElementAt(i);
       // should we get the command state to pass here? This might be expensive.
       observer->Observe(NS_ISUPPORTS_CAST(nsICommandManager*, this),
                         aCommandName,
-                        MOZ_UTF16("command_status_changed"));
+                        u"command_status_changed");
     }
   }
 
   return NS_OK;
 }
 
 #if 0
 #pragma mark -
--- a/extensions/cookie/nsPermissionManager.cpp
+++ b/extensions/cookie/nsPermissionManager.cpp
@@ -1690,17 +1690,17 @@ nsPermissionManager::AddInternal(nsIPrin
       }
 
       if (aNotifyOperation == eNotify) {
         NotifyObserversWithPermission(aPrincipal,
                                       mTypeArray[typeIndex],
                                       aPermission,
                                       aExpireType,
                                       aExpireTime,
-                                      MOZ_UTF16("added"));
+                                      u"added");
       }
 
       break;
     }
 
   case eOperationRemoving:
     {
       PermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
@@ -1714,17 +1714,17 @@ nsPermissionManager::AddInternal(nsIPrin
                  nsIPermissionManager::EXPIRE_NEVER, 0, 0);
 
       if (aNotifyOperation == eNotify) {
         NotifyObserversWithPermission(aPrincipal,
                                       mTypeArray[typeIndex],
                                       oldPermissionEntry.mPermission,
                                       oldPermissionEntry.mExpireType,
                                       oldPermissionEntry.mExpireTime,
-                                      MOZ_UTF16("deleted"));
+                                      u"deleted");
       }
 
       // If there are no more permissions stored for that entry, clear it.
       if (entry->GetPermissions().IsEmpty()) {
         mPermissionTable.RemoveEntry(entry);
       }
 
       break;
@@ -1760,17 +1760,17 @@ nsPermissionManager::AddInternal(nsIPrin
                  aPermission, aExpireType, aExpireTime, aModificationTime);
 
       if (aNotifyOperation == eNotify) {
         NotifyObserversWithPermission(aPrincipal,
                                       mTypeArray[typeIndex],
                                       aPermission,
                                       aExpireType,
                                       aExpireTime,
-                                      MOZ_UTF16("changed"));
+                                      u"changed");
       }
 
       break;
     }
   case eOperationReplacingDefault:
     {
       // this is handling the case when we have an existing permission
       // entry that was created as a "default" (and thus isn't in the DB) with
@@ -1808,17 +1808,17 @@ nsPermissionManager::AddInternal(nsIPrin
       }
 
       if (aNotifyOperation == eNotify) {
         NotifyObserversWithPermission(aPrincipal,
                                       mTypeArray[typeIndex],
                                       aPermission,
                                       aExpireType,
                                       aExpireTime,
-                                      MOZ_UTF16("changed"));
+                                      u"changed");
       }
 
     }
     break;
   }
 
   return NS_OK;
 }
@@ -1919,17 +1919,17 @@ nsPermissionManager::RemoveAllInternal(b
   // database is authoritative, we do not need confirmation from the
   // on-disk database to notify observers.
   RemoveAllFromMemory();
 
   // Re-import the defaults
   ImportDefaults();
 
   if (aNotifyObservers) {
-    NotifyObservers(nullptr, MOZ_UTF16("cleared"));
+    NotifyObservers(nullptr, u"cleared");
   }
 
   // clear the db
   if (mDBConn) {
     nsCOMPtr<mozIStorageAsyncStatement> removeStmt;
     nsresult rv = mDBConn->
       CreateAsyncStatement(NS_LITERAL_CSTRING(
          "DELETE FROM moz_perms"
@@ -2448,32 +2448,32 @@ nsPermissionManager::RemoveExpiredPermis
 
         entry->GetPermissions().RemoveElementAt(i);
 
         NotifyObserversWithPermission(principal,
                                       mTypeArray.ElementAt(oldPermEntry.mType),
                                       oldPermEntry.mPermission,
                                       oldPermEntry.mExpireType,
                                       oldPermEntry.mExpireTime,
-                                      MOZ_UTF16("deleted"));
+                                      u"deleted");
 
         --i;
         continue;
       }
 
       permEntry.mPermission = permEntry.mNonSessionPermission;
       permEntry.mExpireType = permEntry.mNonSessionExpireType;
       permEntry.mExpireTime = permEntry.mNonSessionExpireTime;
 
       NotifyObserversWithPermission(principal,
                                     mTypeArray.ElementAt(permEntry.mType),
                                     permEntry.mPermission,
                                     permEntry.mExpireType,
                                     permEntry.mExpireTime,
-                                    MOZ_UTF16("changed"));
+                                    u"changed");
     }
   }
 
   return NS_OK;
 }
 
 //*****************************************************************************
 //*** nsPermissionManager private methods
--- a/extensions/gio/nsGIOProtocolHandler.cpp
+++ b/extensions/gio/nsGIOProtocolHandler.cpp
@@ -821,27 +821,27 @@ mount_operation_ask_password (GMountOper
     return;
   }
   nsAutoString nsmessage;
 
   if (flags & G_ASK_PASSWORD_NEED_PASSWORD) {
     if (flags & G_ASK_PASSWORD_NEED_USERNAME) {
       if (!realm.IsEmpty()) {
         const char16_t *strings[] = { realm.get(), dispHost.get() };
-        bundle->FormatStringFromName(MOZ_UTF16("EnterLoginForRealm2"),
+        bundle->FormatStringFromName(u"EnterLoginForRealm2",
                                      strings, 2, getter_Copies(nsmessage));
       } else {
         const char16_t *strings[] = { dispHost.get() };
-        bundle->FormatStringFromName(MOZ_UTF16("EnterUserPasswordFor2"),
+        bundle->FormatStringFromName(u"EnterUserPasswordFor2",
                                      strings, 1, getter_Copies(nsmessage));
       }
     } else {
       NS_ConvertUTF8toUTF16 userName(default_user);
       const char16_t *strings[] = { userName.get(), dispHost.get() };
-      bundle->FormatStringFromName(MOZ_UTF16("EnterPasswordFor"),
+      bundle->FormatStringFromName(u"EnterPasswordFor",
                                    strings, 2, getter_Copies(nsmessage));
     }
   } else {
     g_warning("Unknown mount operation request (flags: %x)", flags);
   }
 
   if (nsmessage.IsEmpty()) {
     g_mount_operation_reply(mount_op, G_MOUNT_OPERATION_ABORTED);
--- a/extensions/pref/autoconfig/src/nsAutoConfig.cpp
+++ b/extensions/pref/autoconfig/src/nsAutoConfig.cpp
@@ -509,21 +509,21 @@ nsresult nsAutoConfig::PromptForEMailAdd
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIStringBundle> bundle;
     rv = bundleService->CreateBundle("chrome://autoconfig/locale/autoconfig.properties",
                                 getter_AddRefs(bundle));
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsXPIDLString title;
-    rv = bundle->GetStringFromName(MOZ_UTF16("emailPromptTitle"), getter_Copies(title));
+    rv = bundle->GetStringFromName(u"emailPromptTitle", getter_Copies(title));
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsXPIDLString err;
-    rv = bundle->GetStringFromName(MOZ_UTF16("emailPromptMsg"), getter_Copies(err));
+    rv = bundle->GetStringFromName(u"emailPromptMsg", getter_Copies(err));
     NS_ENSURE_SUCCESS(rv, rv);
     bool check = false;
     nsXPIDLString emailResult;
     bool success;
     rv = promptService->Prompt(nullptr, title.get(), err.get(), getter_Copies(emailResult), nullptr, &check, &success);
     if (!success)
       return NS_ERROR_FAILURE;
     NS_ENSURE_SUCCESS(rv, rv);
--- a/extensions/pref/autoconfig/src/nsReadConfig.cpp
+++ b/extensions/pref/autoconfig/src/nsReadConfig.cpp
@@ -51,22 +51,22 @@ static void DisplayError(void)
 
     nsCOMPtr<nsIStringBundle> bundle;
     bundleService->CreateBundle("chrome://autoconfig/locale/autoconfig.properties",
                                 getter_AddRefs(bundle));
     if (!bundle)
         return;
 
     nsXPIDLString title;
-    rv = bundle->GetStringFromName(MOZ_UTF16("readConfigTitle"), getter_Copies(title));
+    rv = bundle->GetStringFromName(u"readConfigTitle", getter_Copies(title));
     if (NS_FAILED(rv))
         return;
 
     nsXPIDLString err;
-    rv = bundle->GetStringFromName(MOZ_UTF16("readConfigMsg"), getter_Copies(err));
+    rv = bundle->GetStringFromName(u"readConfigMsg", getter_Copies(err));
     if (NS_FAILED(rv))
         return;
 
     promptService->Alert(nullptr, title.get(), err.get());
 }
 
 // nsISupports Implementation
 
--- a/gfx/2d/DrawTargetSkia.cpp
+++ b/gfx/2d/DrawTargetSkia.cpp
@@ -221,22 +221,21 @@ SetPaintPattern(SkPaint& aPaint, const P
     case PatternType::COLOR: {
       Color color = static_cast<const ColorPattern&>(aPattern).mColor;
       aPaint.setColor(ColorToSkColor(color, aAlpha));
       break;
     }
     case PatternType::LINEAR_GRADIENT: {
       const LinearGradientPattern& pat = static_cast<const LinearGradientPattern&>(aPattern);
       GradientStopsSkia *stops = static_cast<GradientStopsSkia*>(pat.mStops.get());
-      SkShader::TileMode mode = ExtendModeToTileMode(stops->mExtendMode, Axis::BOTH);
-
-      if (stops->mCount < 2 ||
+      if (!stops || stops->mCount < 2 ||
           !pat.mBegin.IsFinite() || !pat.mEnd.IsFinite()) {
         aPaint.setColor(SK_ColorTRANSPARENT);
       } else {
+        SkShader::TileMode mode = ExtendModeToTileMode(stops->mExtendMode, Axis::BOTH);
         SkPoint points[2];
         points[0] = SkPoint::Make(SkFloatToScalar(pat.mBegin.x), SkFloatToScalar(pat.mBegin.y));
         points[1] = SkPoint::Make(SkFloatToScalar(pat.mEnd.x), SkFloatToScalar(pat.mEnd.y));
 
         SkMatrix mat;
         GfxMatrixToSkiaMatrix(pat.mMatrix, mat);
         sk_sp<SkShader> shader = SkGradientShader::MakeLinear(points,
                                                               &stops->mColors.front(),
@@ -245,23 +244,22 @@ SetPaintPattern(SkPaint& aPaint, const P
                                                               mode, 0, &mat);
         aPaint.setShader(shader);
       }
       break;
     }
     case PatternType::RADIAL_GRADIENT: {
       const RadialGradientPattern& pat = static_cast<const RadialGradientPattern&>(aPattern);
       GradientStopsSkia *stops = static_cast<GradientStopsSkia*>(pat.mStops.get());
-      SkShader::TileMode mode = ExtendModeToTileMode(stops->mExtendMode, Axis::BOTH);
-
-      if (stops->mCount < 2 ||
+      if (!stops || stops->mCount < 2 ||
           !pat.mCenter1.IsFinite() || !IsFinite(pat.mRadius1) ||
           !pat.mCenter2.IsFinite() || !IsFinite(pat.mRadius2)) {
         aPaint.setColor(SK_ColorTRANSPARENT);
       } else {
+        SkShader::TileMode mode = ExtendModeToTileMode(stops->mExtendMode, Axis::BOTH);
         SkPoint points[2];
         points[0] = SkPoint::Make(SkFloatToScalar(pat.mCenter1.x), SkFloatToScalar(pat.mCenter1.y));
         points[1] = SkPoint::Make(SkFloatToScalar(pat.mCenter2.x), SkFloatToScalar(pat.mCenter2.y));
 
         SkMatrix mat;
         GfxMatrixToSkiaMatrix(pat.mMatrix, mat);
         sk_sp<SkShader> shader = SkGradientShader::MakeTwoPointConical(points[0],
                                                                        SkFloatToScalar(pat.mRadius1),
--- a/gfx/2d/SFNTNameTable.cpp
+++ b/gfx/2d/SFNTNameTable.cpp
@@ -211,17 +211,17 @@ SFNTNameTable::GetU16FullName(mozilla::u
   }
 
   mozilla::u16string styleName;
   if (!ReadU16Name(StyleMatchers(), styleName)) {
     return false;
   }
 
   aU16FullName.assign(Move(familyName));
-  aU16FullName.append(MOZ_UTF16(" "));
+  aU16FullName.append(u" ");
   aU16FullName.append(styleName);
   return true;
 }
 
 bool
 SFNTNameTable::ReadU16Name(const NameRecordMatchers& aMatchers,
                            mozilla::u16string& aU16Name)
 {
--- a/gfx/ipc/GPUParent.cpp
+++ b/gfx/ipc/GPUParent.cpp
@@ -3,22 +3,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/. */
 #include "GPUParent.h"
 #include "gfxConfig.h"
 #include "gfxPlatform.h"
 #include "gfxPrefs.h"
 #include "GPUProcessHost.h"
-#include "VsyncBridgeParent.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/ipc/ProcessChild.h"
 #include "mozilla/layers/CompositorBridgeParent.h"
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/ImageBridgeParent.h"
+#include "VRManager.h"
+#include "VRManagerParent.h"
+#include "VsyncBridgeParent.h"
 
 namespace mozilla {
 namespace gfx {
 
 using namespace ipc;
 using namespace layers;
 
 GPUParent::GPUParent()
@@ -36,25 +38,27 @@ GPUParent::Init(base::ProcessId aParentP
 {
   if (NS_WARN_IF(!Open(aChannel, aParentPid, aIOLoop))) {
     return false;
   }
 
   // Ensure gfxPrefs are initialized.
   gfxPrefs::GetSingleton();
   CompositorThreadHolder::Start();
+  VRManager::ManagerInit();
   gfxPlatform::InitNullMetadata();
   return true;
 }
 
 bool
 GPUParent::RecvInit(nsTArray<GfxPrefSetting>&& prefs)
 {
-  for (auto setting : prefs) {
-    gfxPrefs::Pref* pref = gfxPrefs::all()[setting.index()];
+  const nsTArray<gfxPrefs::Pref*>& globalPrefs = gfxPrefs::all();
+  for (auto& setting : prefs) {
+    gfxPrefs::Pref* pref = globalPrefs[setting.index()];
     pref->SetCachedValue(setting.value());
   }
   return true;
 }
 
 bool
 GPUParent::RecvInitVsyncBridge(Endpoint<PVsyncBridgeParent>&& aVsyncEndpoint)
 {
@@ -65,16 +69,23 @@ GPUParent::RecvInitVsyncBridge(Endpoint<
 bool
 GPUParent::RecvInitImageBridge(Endpoint<PImageBridgeParent>&& aEndpoint)
 {
   ImageBridgeParent::CreateForGPUProcess(Move(aEndpoint));
   return true;
 }
 
 bool
+GPUParent::RecvInitVRManager(Endpoint<PVRManagerParent>&& aEndpoint)
+{
+  VRManagerParent::CreateForGPUProcess(Move(aEndpoint));
+  return true;
+}
+
+bool
 GPUParent::RecvUpdatePref(const GfxPrefSetting& setting)
 {
   gfxPrefs::Pref* pref = gfxPrefs::all()[setting.index()];
   pref->SetCachedValue(setting.value());
   return true;
 }
 
 static void
@@ -107,16 +118,22 @@ GPUParent::RecvNewContentCompositorBridg
 }
 
 bool
 GPUParent::RecvNewContentImageBridge(Endpoint<PImageBridgeParent>&& aEndpoint)
 {
   return ImageBridgeParent::CreateForContent(Move(aEndpoint));
 }
 
+bool
+GPUParent::RecvNewContentVRManager(Endpoint<PVRManagerParent>&& aEndpoint)
+{
+  return VRManagerParent::CreateForContent(Move(aEndpoint));
+}
+
 void
 GPUParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   if (AbnormalShutdown == aWhy) {
     NS_WARNING("Shutting down GPU process early due to a crash!");
     ProcessChild::QuickExit();
   }
 
--- a/gfx/ipc/GPUParent.h
+++ b/gfx/ipc/GPUParent.h
@@ -22,24 +22,26 @@ public:
 
   bool Init(base::ProcessId aParentPid,
             MessageLoop* aIOLoop,
             IPC::Channel* aChannel);
 
   bool RecvInit(nsTArray<GfxPrefSetting>&& prefs) override;
   bool RecvInitVsyncBridge(Endpoint<PVsyncBridgeParent>&& aVsyncEndpoint) override;
   bool RecvInitImageBridge(Endpoint<PImageBridgeParent>&& aEndpoint) override;
+  bool RecvInitVRManager(Endpoint<PVRManagerParent>&& aEndpoint) override;
   bool RecvUpdatePref(const GfxPrefSetting& pref) override;
   bool RecvNewWidgetCompositor(
     Endpoint<PCompositorBridgeParent>&& aEndpoint,
     const CSSToLayoutDeviceScale& aScale,
     const bool& aUseExternalSurface,
     const IntSize& aSurfaceSize) override;
   bool RecvNewContentCompositorBridge(Endpoint<PCompositorBridgeParent>&& aEndpoint) override;
   bool RecvNewContentImageBridge(Endpoint<PImageBridgeParent>&& aEndpoint) override;
+  bool RecvNewContentVRManager(Endpoint<PVRManagerParent>&& aEndpoint) override;
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
 
 private:
   RefPtr<VsyncBridgeParent> mVsyncBridge;
 };
 
 } // namespace gfx
--- a/gfx/ipc/GPUProcessManager.cpp
+++ b/gfx/ipc/GPUProcessManager.cpp
@@ -12,16 +12,18 @@
 #include "mozilla/layers/InProcessCompositorSession.h"
 #include "mozilla/layers/RemoteCompositorSession.h"
 #include "mozilla/widget/PlatformWidgetTypes.h"
 #ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING
 # include "mozilla/widget/CompositorWidgetChild.h"
 #endif
 #include "nsBaseWidget.h"
 #include "nsContentUtils.h"
+#include "VRManagerChild.h"
+#include "VRManagerParent.h"
 #include "VsyncBridgeChild.h"
 #include "VsyncIOThreadHolder.h"
 
 namespace mozilla {
 namespace gfx {
 
 using namespace mozilla::layers;
 
@@ -157,16 +159,44 @@ GPUProcessManager::EnsureImageBridgeChil
     return;
   }
 
   mGPUChild->SendInitImageBridge(Move(parentPipe));
   ImageBridgeChild::InitWithGPUProcess(Move(childPipe));
 }
 
 void
+GPUProcessManager::EnsureVRManager()
+{
+  if (VRManagerChild::IsCreated()) {
+    return;
+  }
+
+  if (!mGPUChild) {
+    VRManagerChild::InitSameProcess();
+    return;
+  }
+
+  ipc::Endpoint<PVRManagerParent> parentPipe;
+  ipc::Endpoint<PVRManagerChild> childPipe;
+  nsresult rv = PVRManager::CreateEndpoints(
+    mGPUChild->OtherPid(),
+    base::GetCurrentProcId(),
+    &parentPipe,
+    &childPipe);
+  if (NS_FAILED(rv)) {
+    DisableGPUProcess("Failed to create PVRManager endpoints");
+    return;
+  }
+
+  mGPUChild->SendInitVRManager(Move(parentPipe));
+  VRManagerChild::InitWithGPUProcess(Move(childPipe));
+}
+
+void
 GPUProcessManager::OnProcessLaunchComplete(GPUProcessHost* aHost)
 {
   MOZ_ASSERT(mProcess && mProcess == aHost);
 
   if (!mProcess->IsConnected()) {
     DisableGPUProcess("Failed to launch GPU process");
     return;
   }
@@ -253,16 +283,17 @@ GPUProcessManager::CreateTopLevelComposi
                                             CSSToLayoutDeviceScale aScale,
                                             bool aUseAPZ,
                                             bool aUseExternalSurfaceSize,
                                             const gfx::IntSize& aSurfaceSize)
 {
   uint64_t layerTreeId = AllocateLayerTreeId();
 
   EnsureImageBridgeChild();
+  EnsureVRManager();
 
   if (mGPUChild) {
     RefPtr<CompositorSession> session = CreateRemoteSession(
       aWidget,
       aLayerManager,
       layerTreeId,
       aScale,
       aUseAPZ,
@@ -413,16 +444,50 @@ GPUProcessManager::CreateContentImageBri
       return false;
     }
   }
 
   *aOutEndpoint = Move(childPipe);
   return true;
 }
 
+bool
+GPUProcessManager::CreateContentVRManager(base::ProcessId aOtherProcess,
+                                          ipc::Endpoint<PVRManagerChild>* aOutEndpoint)
+{
+  EnsureVRManager();
+
+  base::ProcessId gpuPid = mGPUChild
+                           ? mGPUChild->OtherPid()
+                           : base::GetCurrentProcId();
+
+  ipc::Endpoint<PVRManagerParent> parentPipe;
+  ipc::Endpoint<PVRManagerChild> childPipe;
+  nsresult rv = PVRManager::CreateEndpoints(
+    gpuPid,
+    aOtherProcess,
+    &parentPipe,
+    &childPipe);
+  if (NS_FAILED(rv)) {
+    gfxCriticalNote << "Could not create content compositor bridge: " << hexa(int(rv));
+    return false;
+  }
+
+  if (mGPUChild) {
+    mGPUChild->SendNewContentVRManager(Move(parentPipe));
+  } else {
+    if (!VRManagerParent::CreateForContent(Move(parentPipe))) {
+      return false;
+    }
+  }
+
+  *aOutEndpoint = Move(childPipe);
+  return true;
+}
+
 already_AddRefed<APZCTreeManager>
 GPUProcessManager::GetAPZCTreeManagerForLayers(uint64_t aLayersId)
 {
   return CompositorBridgeParent::GetAPZCTreeManager(aLayersId);
 }
 
 uint64_t
 GPUProcessManager::AllocateLayerTreeId()
--- a/gfx/ipc/GPUProcessManager.h
+++ b/gfx/ipc/GPUProcessManager.h
@@ -40,16 +40,17 @@ class TabParent;
 namespace ipc {
 class GeckoChildProcessHost;
 } // namespace ipc
 namespace gfx {
 
 class GPUChild;
 class VsyncBridgeChild;
 class VsyncIOThreadHolder;
+class PVRManagerChild;
 
 // The GPUProcessManager is a singleton responsible for creating GPU-bound
 // objects that may live in another process. Currently, it provides access
 // to the compositor via CompositorBridgeParent.
 class GPUProcessManager final : public GPUProcessHost::Listener
 {
   typedef layers::APZCTreeManager APZCTreeManager;
   typedef layers::ClientLayerManager ClientLayerManager;
@@ -78,19 +79,20 @@ public:
     ClientLayerManager* aLayerManager,
     CSSToLayoutDeviceScale aScale,
     bool aUseAPZ,
     bool aUseExternalSurfaceSize,
     const gfx::IntSize& aSurfaceSize);
 
   bool CreateContentCompositorBridge(base::ProcessId aOtherProcess,
                                      ipc::Endpoint<PCompositorBridgeChild>* aOutEndpoint);
-
   bool CreateContentImageBridge(base::ProcessId aOtherProcess,
                                 ipc::Endpoint<PImageBridgeChild>* aOutEndpoint);
+  bool CreateContentVRManager(base::ProcessId aOtherProcess,
+                              ipc::Endpoint<PVRManagerChild>* aOutEndpoint);
 
   // This returns a reference to the APZCTreeManager to which
   // pan/zoom-related events can be sent.
   already_AddRefed<APZCTreeManager> GetAPZCTreeManagerForLayers(uint64_t aLayersId);
 
   // Allocate an ID that can be used to refer to a layer tree and
   // associated resources that live only on the compositor thread.
   //
@@ -143,16 +145,17 @@ private:
   // Shutdown the GPU process.
   void CleanShutdown();
   void DestroyProcess();
 
   void EnsureVsyncIOThread();
   void ShutdownVsyncIOThread();
 
   void EnsureImageBridgeChild();
+  void EnsureVRManager();
 
   RefPtr<CompositorSession> CreateRemoteSession(
     nsBaseWidget* aWidget,
     ClientLayerManager* aLayerManager,
     const uint64_t& aRootLayerTreeId,
     CSSToLayoutDeviceScale aScale,
     bool aUseAPZ,
     bool aUseExternalSurfaceSize,
--- a/gfx/ipc/PGPU.ipdl
+++ b/gfx/ipc/PGPU.ipdl
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; 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 protocol PCompositorBridge;
 include protocol PImageBridge;
+include protocol PVRManager;
 include protocol PVsyncBridge;
 
 using mozilla::CSSToLayoutDeviceScale from "Units.h";
 using mozilla::gfx::IntSize from "mozilla/gfx/2D.h";
 
 namespace mozilla {
 namespace gfx {
 
@@ -28,25 +29,27 @@ struct GfxPrefSetting {
 sync protocol PGPU
 {
 parent:
   // Sent by the UI process to initiate core settings.
   async Init(GfxPrefSetting[] prefs);
 
   async InitVsyncBridge(Endpoint<PVsyncBridgeParent> endpoint);
   async InitImageBridge(Endpoint<PImageBridgeParent> endpoint);
+  async InitVRManager(Endpoint<PVRManagerParent> endpoint);
 
   // Called to update a gfx preference.
   async UpdatePref(GfxPrefSetting pref);
 
   // Create a new top-level compositor.
   async NewWidgetCompositor(Endpoint<PCompositorBridgeParent> endpoint,
                             CSSToLayoutDeviceScale scale,
                             bool useExternalSurface,
                             IntSize surfaceSize);
 
   // Create a new content-process compositor bridge.
   async NewContentCompositorBridge(Endpoint<PCompositorBridgeParent> endpoint);
   async NewContentImageBridge(Endpoint<PImageBridgeParent> endpoint);
+  async NewContentVRManager(Endpoint<PVRManagerParent> endpoint);
 };
 
 } // namespace gfx
 } // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_touch_action_complex.html
@@ -0,0 +1,143 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <meta name="viewport" content="width=device-width; initial-scale=1.0">
+  <title>Complex touch-action test</title>
+  <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+  <script type="application/javascript" src="apz_test_utils.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+  <script type="application/javascript">
+
+function checkScroll(target, x, y, desc) {
+  is(target.scrollLeft, x, desc + " - x axis");
+  is(target.scrollTop, y, desc + " - y axis");
+}
+
+function resetConfiguration(config, testDriver) {
+  // Cycle through all the configuration_X elements, setting them to display:none
+  // except for when X == config, in which case set it to display:block
+  var i = 0;
+  while (true) {
+    i++;
+    var element = document.getElementById('configuration_' + i);
+    if (element == null) {
+      if (i <= config) {
+        ok(false, "The configuration requested was not encountered!");
+      }
+      break;
+    }
+
+    if (i == config) {
+      element.style.display = 'block';
+    } else {
+      element.style.display = 'none';
+    }
+  }
+
+  // Also reset the scroll position on the scrollframe
+  var s = document.getElementById('scrollframe');
+  s.scrollLeft = 0;
+  s.scrollTop = 0;
+
+  return waitForAllPaints(function() {
+    flushApzRepaints(testDriver);
+  });
+}
+
+function* test(testDriver) {
+  var scrollframe = document.getElementById('scrollframe');
+
+  document.body.addEventListener('touchend', testDriver, { passive: true });
+
+  // Helper function for the tests below.
+  // Touch-pan configuration |configuration| towards scroll offset (dx, dy) with
+  // the pan touching down at (x, y). Check that the final scroll offset is
+  // (ex, ey). |desc| is some description string.
+  function* scrollAndCheck(configuration, x, y, dx, dy, ex, ey, desc) {
+    // Start with a clean slate
+    yield resetConfiguration(configuration, testDriver);
+    // Figure out the panning deltas
+    if (dx != 0) {
+      dx = -(dx + TOUCH_SLOP);
+    }
+    if (dy != 0) {
+      dy = -(dy + TOUCH_SLOP);
+    }
+    // Do the pan
+    yield ok(synthesizeNativeTouchDrag(scrollframe, x, y, dx, dy),
+        "Synthesized drag of (" + dx + ", " + dy + ") on configuration " + configuration);
+    yield waitForAllPaints(function() {
+      flushApzRepaints(testDriver);
+    });
+    // Check for expected scroll position
+    checkScroll(scrollframe, ex, ey, 'configuration ' + configuration + ' ' + desc);
+  }
+
+  // Test configuration_1, which contains two sibling elements that are
+  // overlapping. The touch-action from the second sibling (which is on top)
+  // should be used for the overlapping area.
+  yield* scrollAndCheck(1,  25,  75, 20,  0, 20,  0, "first element horizontal scroll");
+  yield* scrollAndCheck(1,  25,  75,  0, 50,  0,  0, "first element vertical scroll");
+  yield* scrollAndCheck(1,  75,  75, 50,  0,  0,  0, "overlap horizontal scroll");
+  yield* scrollAndCheck(1,  75,  75,  0, 50,  0, 50, "overlap vertical scroll");
+  yield* scrollAndCheck(1, 125,  75, 20,  0,  0,  0, "second element horizontal scroll");
+  yield* scrollAndCheck(1, 125,  75,  0, 50,  0, 50, "second element vertical scroll");
+
+  // Test configuration_2, which contains two overlapping elements with a
+  // parent/child relationship. The parent has pan-x and the child has pan-y,
+  // which means that panning on the parent should work horizontally only, and
+  // on the child no panning should occur at all.
+  yield* scrollAndCheck(2, 125, 125, 50, 50,  0,  0, "child scroll");
+  yield* scrollAndCheck(2,  75,  75, 50, 50,  0,  0, "overlap scroll");
+  yield* scrollAndCheck(2,  25,  75,  0, 50,  0,  0, "parent vertical scroll");
+  yield* scrollAndCheck(2,  75,  25, 50,  0, 50,  0, "parent horizontal scroll");
+
+  // Test configuration_3, which is the same as configuration_2, except the child
+  // has a rotation transform applied. This forces the event regions on the two
+  // elements to be built separately and then get merged.
+  yield* scrollAndCheck(3, 125, 125, 50, 50,  0,  0, "child scroll");
+  yield* scrollAndCheck(3,  75,  75, 50, 50,  0,  0, "overlap scroll");
+  yield* scrollAndCheck(3,  25,  75,  0, 50,  0,  0, "parent vertical scroll");
+  yield* scrollAndCheck(3,  75,  25, 50,  0, 50,  0, "parent horizontal scroll");
+
+  // Test configuration_4 has two elements, one above the other, not overlapping,
+  // and the second element is a child of the first. The parent has pan-x, the
+  // child has pan-y, but that means panning horizontally on the parent should
+  // work and panning in any direction on the child should not do anything.
+  yield* scrollAndCheck(4,  75,  75, 50, 50,  50, 0, "parent diagonal scroll");
+  yield* scrollAndCheck(4,  75, 150, 50, 50,   0, 0, "child diagonal scroll");
+}
+
+waitUntilApzStable()
+.then(runContinuation(test))
+.then(subtestDone);
+
+  </script>
+</head>
+<body>
+ <div id="scrollframe" style="width: 300px; height: 300px; overflow:scroll">
+  <div id="scrolled_content" style="width: 1000px; height: 1000px; background-color: green">
+  </div>
+  <div id="configuration_1" style="display:none; position: relative; top: -1000px">
+   <div style="touch-action: pan-x; width: 100px; height: 100px; background-color: blue"></div>
+   <div style="touch-action: pan-y; width: 100px; height: 100px; position: relative; top: -100px; left: 50px; background-color: yellow"></div>
+  </div>
+  <div id="configuration_2" style="display:none; position: relative; top: -1000px">
+   <div style="touch-action: pan-x; width: 100px; height: 100px; background-color: blue">
+    <div style="touch-action: pan-y; width: 100px; height: 100px; position: relative; top: 50px; left: 50px; background-color: yellow"></div>
+   </div>
+  </div>
+  <div id="configuration_3" style="display:none; position: relative; top: -1000px">
+   <div style="touch-action: pan-x; width: 100px; height: 100px; background-color: blue">
+    <div style="touch-action: pan-y; width: 100px; height: 100px; position: relative; top: 50px; left: 50px; background-color: yellow; transform: rotate(90deg)"></div>
+   </div>
+  </div>
+  <div id="configuration_4" style="display:none; position: relative; top: -1000px">
+   <div style="touch-action: pan-x; width: 100px; height: 100px; background-color: blue">
+    <div style="touch-action: pan-y; width: 100px; height: 100px; position: relative; top: 125px; background-color: yellow"></div>
+   </div>
+  </div>
+ </div>
+</body>
+</html>
--- a/gfx/layers/apz/test/mochitest/mochitest.ini
+++ b/gfx/layers/apz/test/mochitest/mochitest.ini
@@ -20,16 +20,17 @@ support-files =
   helper_bug1271432.html
   helper_touch_action.html
   helper_touch_action_regions.html
   helper_scroll_inactive_perspective.html
   helper_scroll_inactive_zindex.html
   helper_bug1280013.html
   helper_tall.html
   helper_drag_scroll.html
+  helper_touch_action_complex.html
 tags = apz
 [test_bug982141.html]
 [test_bug1151663.html]
 [test_bug1277814.html]
 skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel events not supported on mobile; see bug 1164274 for mulet
 [test_wheel_scroll.html]
 skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel events not supported on mobile; see bug 1164274 for mulet
 [test_wheel_transactions.html]
--- a/gfx/layers/apz/test/mochitest/test_group_touchevents.html
+++ b/gfx/layers/apz/test/mochitest/test_group_touchevents.html
@@ -1,14 +1,15 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>Various touch tests that spawn in new windows</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
   <script type="application/javascript" src="apz_test_utils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
 
 var basic_pan_prefs = [
   // Dropping the touch slop to 0 makes the tests easier to write because
   // we can just do a one-pixel drag to get over the pan threshold rather
   // than having to hard-code some larger value.
@@ -56,22 +57,29 @@ var subtests = [
   // For the following test, we want to make sure APZ doesn't wait for a content
   // response that is never going to arrive. To detect this we set the content response
   // timeout to a day, so that the entire test times out and fails if APZ does
   // end up waiting.
   {'file': 'helper_tap_passive.html', 'prefs': [["apz.content_response_timeout", 24 * 60 * 60 * 1000]]},
 
   // Simple test to exercise touch-action CSS property
   {'file': 'helper_touch_action.html', 'prefs': touch_action_prefs},
+  // More complex touch-action tests, with overlapping regions and such
+  {'file': 'helper_touch_action_complex.html', 'prefs': touch_action_prefs},
   // Tests that touch-action CSS properties are handled in APZ without waiting
   // on the main-thread, when possible
   {'file': 'helper_touch_action_regions.html', 'prefs': touch_action_prefs},
 ];
 
 if (isApzEnabled()) {
+  if (getPlatform() == "android") {
+    // This has a lot of subtests, and Android emulators are slow.
+    SimpleTest.requestLongerTimeout(2);
+  }
+
   SimpleTest.waitForExplicitFinish();
   window.onload = function() {
     runSubtestsSeriallyInFreshWindows(subtests)
     .then(SimpleTest.finish);
   };
 }
 
   </script>
--- a/gfx/layers/client/TextureClientPool.cpp
+++ b/gfx/layers/client/TextureClientPool.cpp
@@ -14,40 +14,35 @@
 #include "nsComponentManagerUtils.h"
 
 #define TCP_LOG(...)
 //#define TCP_LOG(...) printf_stderr(__VA_ARGS__);
 
 namespace mozilla {
 namespace layers {
 
-static void
-ShrinkCallback(nsITimer *aTimer, void *aClosure)
-{
-  static_cast<TextureClientPool*>(aClosure)->ShrinkToMinimumSize();
-}
 
 TextureClientPool::TextureClientPool(LayersBackend aLayersBackend,
                                      gfx::SurfaceFormat aFormat,
                                      gfx::IntSize aSize,
                                      TextureFlags aFlags,
-                                     uint32_t aMaxTextureClients,
-                                     uint32_t aShrinkTimeoutMsec,
+                                     uint32_t aInitialPoolSize,
+                                     uint32_t aPoolIncrementSize,
                                      TextureForwarder* aAllocator)
   : mBackend(aLayersBackend)
   , mFormat(aFormat)
   , mSize(aSize)
   , mFlags(aFlags)
-  , mMaxTextureClients(aMaxTextureClients)
-  , mShrinkTimeoutMsec(aShrinkTimeoutMsec)
+  , mInitialPoolSize(aInitialPoolSize)
+  , mPoolIncrementSize(aPoolIncrementSize)
   , mOutstandingClients(0)
   , mSurfaceAllocator(aAllocator)
 {
-  TCP_LOG("TexturePool %p created with max size %u and timeout %u\n",
-      this, mMaxTextureClients, aShrinkTimeoutMsec);
+  TCP_LOG("TexturePool %p created with maximum unused texture clients %u\n",
+      this, mInitialPoolSize);
   mTimer = do_CreateInstance("@mozilla.org/timer;1");
   if (aFormat == gfx::SurfaceFormat::UNKNOWN) {
     gfxWarning() << "Creating texture pool for SurfaceFormat::UNKNOWN format";
   }
 }
 
 TextureClientPool::~TextureClientPool()
 {
@@ -87,56 +82,76 @@ static bool TestClientPool(const char* w
 }
 #endif
 
 already_AddRefed<TextureClient>
 TextureClientPool::GetTextureClient()
 {
   // Try to fetch a client from the pool
   RefPtr<TextureClient> textureClient;
-  if (mTextureClients.size()) {
-    mOutstandingClients++;
-    textureClient = mTextureClients.top();
-    mTextureClients.pop();
-#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
-    DebugOnly<bool> ok = TestClientPool("fetch", textureClient, this);
-    MOZ_ASSERT(ok);
-#endif
-    TCP_LOG("TexturePool %p giving %p from pool; size %u outstanding %u\n",
-        this, textureClient.get(), mTextureClients.size(), mOutstandingClients);
-    return textureClient.forget();
+
+  // We initially allocate mInitialPoolSize for our pool. If we run
+  // out of TextureClients, we allocate additional TextureClients to try and keep around
+  // mPoolIncrementSize
+  if (!mTextureClients.size()) {
+    size_t size = mOutstandingClients ? mPoolIncrementSize : mInitialPoolSize;
+    AllocateTextureClients(size);
   }
 
-  // We're increasing the number of outstanding TextureClients without reusing a
-  // client, we may need to free a deferred-return TextureClient.
-  ShrinkToMaximumSize();
-
-  // No unused clients in the pool, create one
-  if (gfxPrefs::ForceShmemTiles()) {
-    // gfx::BackendType::NONE means use the content backend
-    textureClient = TextureClient::CreateForRawBufferAccess(mSurfaceAllocator,
-      mFormat, mSize, gfx::BackendType::NONE,
-      mFlags, ALLOC_DEFAULT);
-  } else {
-    textureClient = TextureClient::CreateForDrawing(mSurfaceAllocator,
-      mFormat, mSize, mBackend, BackendSelector::Content, mFlags);
+  if (!mTextureClients.size()) {
+    // All our allocations failed, return nullptr
+    return nullptr;
   }
 
   mOutstandingClients++;
+  textureClient = mTextureClients.top();
+  mTextureClients.pop();
 #ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
   if (textureClient) {
     textureClient->mPoolTracker = this;
   }
+  DebugOnly<bool> ok = TestClientPool("fetch", textureClient, this);
+  MOZ_ASSERT(ok);
 #endif
-  TCP_LOG("TexturePool %p giving new %p; size %u outstanding %u\n",
+  TCP_LOG("TexturePool %p giving %p from pool; size %u outstanding %u\n",
       this, textureClient.get(), mTextureClients.size(), mOutstandingClients);
+
   return textureClient.forget();
 }
 
 void
+TextureClientPool::AllocateTextureClients(size_t aSize)
+{
+  TCP_LOG("TexturePool %p allocating %u clients, outstanding %u\n",
+      this, aSize, mOutstandingClients);
+
+  RefPtr<TextureClient> newClient;
+  while (mTextureClients.size() < aSize) {
+    if (gfxPrefs::ForceShmemTiles()) {
+      // gfx::BackendType::NONE means use the content backend
+      newClient =
+        TextureClient::CreateForRawBufferAccess(mSurfaceAllocator,
+                                                mFormat, mSize,
+                                                gfx::BackendType::NONE,
+                                                mFlags, ALLOC_DEFAULT);
+    } else {
+      newClient =
+        TextureClient::CreateForDrawing(mSurfaceAllocator,
+                                        mFormat, mSize,
+                                        mBackend,
+                                        BackendSelector::Content,
+                                        mFlags);
+    }
+    if (newClient) {
+      mTextureClients.push(newClient);
+    }
+  }
+}
+
+void
 TextureClientPool::ReturnTextureClient(TextureClient *aClient)
 {
   if (!aClient) {
     return;
   }
 #ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
   DebugOnly<bool> ok = TestClientPool("return", aClient, this);
   MOZ_ASSERT(ok);
@@ -145,24 +160,16 @@ TextureClientPool::ReturnTextureClient(T
   MOZ_ASSERT(mOutstandingClients > mTextureClientsDeferred.size());
   mOutstandingClients--;
   mTextureClients.push(aClient);
   TCP_LOG("TexturePool %p had client %p returned; size %u outstanding %u\n",
       this, aClient, mTextureClients.size(), mOutstandingClients);
 
   // Shrink down if we're beyond our maximum size
   ShrinkToMaximumSize();
-
-  // Kick off the pool shrinking timer if there are still more unused texture
-  // clients than our desired minimum cache size.
-  if (mTextureClients.size() > sMinCacheSize) {
-    TCP_LOG("TexturePool %p scheduling a shrink-to-min-size\n", this);
-    mTimer->InitWithFuncCallback(ShrinkCallback, this, mShrinkTimeoutMsec,
-                                 nsITimer::TYPE_ONE_SHOT);
-  }
 }
 
 void
 TextureClientPool::ReturnTextureClientDeferred(TextureClient* aClient)
 {
   if (!aClient) {
     return;
   }
@@ -175,90 +182,64 @@ TextureClientPool::ReturnTextureClientDe
   TCP_LOG("TexturePool %p had client %p defer-returned, size %u outstanding %u\n",
       this, aClient, mTextureClientsDeferred.size(), mOutstandingClients);
   ShrinkToMaximumSize();
 }
 
 void
 TextureClientPool::ShrinkToMaximumSize()
 {
-  uint32_t totalClientsOutstanding = mTextureClients.size() + mOutstandingClients;
-  TCP_LOG("TexturePool %p shrinking to max size %u; total outstanding %u\n",
-      this, mMaxTextureClients, totalClientsOutstanding);
-
   // We're over our desired maximum size, immediately shrink down to the
-  // maximum, or zero if we have too many outstanding texture clients.
+  // maximum.
+  //
   // We cull from the deferred TextureClients first, as we can't reuse those
   // until they get returned.
-  while (totalClientsOutstanding > mMaxTextureClients) {
+  uint32_t totalUnusedTextureClients = mTextureClients.size() + mTextureClientsDeferred.size();
+
+  // If we have > mInitialPoolSize outstanding, then we want to keep around
+  // mPoolIncrementSize at a maximum. If we have fewer than mInitialPoolSize
+  // outstanding, then keep around the entire initial pool size.
+  uint32_t targetUnusedClients;
+  if (mOutstandingClients > mInitialPoolSize) {
+    targetUnusedClients = mPoolIncrementSize;
+  } else {
+    targetUnusedClients = mInitialPoolSize;
+  }
+
+  TCP_LOG("TexturePool %p shrinking to maximum unused size %u; total outstanding %u\n",
+      this, targetUnusedClients, mOutstandingClients);
+
+  while (totalUnusedTextureClients > targetUnusedClients) {
     if (mTextureClientsDeferred.size()) {
-      MOZ_ASSERT(mOutstandingClients > 0);
       mOutstandingClients--;
       TCP_LOG("TexturePool %p dropped deferred client %p; %u remaining\n",
           this, mTextureClientsDeferred.front().get(),
           mTextureClientsDeferred.size() - 1);
       mTextureClientsDeferred.pop_front();
     } else {
-      if (!mTextureClients.size()) {
-        // Getting here means we're over our desired number of TextureClients
-        // with none in the pool. This can happen during shutdown, or for
-        // pathological cases, or it could mean that mMaxTextureClients needs
-        // adjusting for whatever device we're running on.
-        TCP_LOG("TexturePool %p encountering pathological case!\n", this);
-        break;
-      }
       TCP_LOG("TexturePool %p dropped non-deferred client %p; %u remaining\n",
           this, mTextureClients.top().get(), mTextureClients.size() - 1);
       mTextureClients.pop();
     }
-    totalClientsOutstanding--;
-  }
-}
-
-void
-TextureClientPool::ShrinkToMinimumSize()
-{
-  ReturnUnlockedClients();
-
-  while (!mTextureClientsDeferred.empty()) {
-    MOZ_ASSERT(mOutstandingClients > 0);
-    mOutstandingClients--;
-    TCP_LOG("TexturePool %p releasing deferred client %p\n",
-        this, mTextureClientsDeferred.front().get());
-    mTextureClientsDeferred.pop_front();
-  }
-
-  TCP_LOG("TexturePool %p shrinking to minimum size %u\n", this, sMinCacheSize);
-  while (mTextureClients.size() > sMinCacheSize) {
-    TCP_LOG("TexturePool %p popped %p; shrunk to %u\n",
-        this, mTextureClients.top().get(), mTextureClients.size() - 1);
-    mTextureClients.pop();
+    totalUnusedTextureClients--;
   }
 }
 
 void
 TextureClientPool::ReturnDeferredClients()
 {
-  TCP_LOG("TexturePool %p returning %u deferred clients to pool\n",
-      this, mTextureClientsDeferred.size());
-
   if (mTextureClientsDeferred.empty()) {
     return;
   }
 
+  TCP_LOG("TexturePool %p returning %u deferred clients to pool\n",
+      this, mTextureClientsDeferred.size());
+
   ReturnUnlockedClients();
   ShrinkToMaximumSize();
-
-  // Kick off the pool shrinking timer if there are still more unused texture
-  // clients than our desired minimum cache size.
-  if (mTextureClients.size() > sMinCacheSize) {
-    TCP_LOG("TexturePool %p kicking off shrink-to-min timer\n", this);
-    mTimer->InitWithFuncCallback(ShrinkCallback, this, mShrinkTimeoutMsec,
-                                 nsITimer::TYPE_ONE_SHOT);
-  }
 }
 
 void
 TextureClientPool::ReturnUnlockedClients()
 {
   for (auto it = mTextureClientsDeferred.begin(); it != mTextureClientsDeferred.end();) {
     MOZ_ASSERT((*it)->GetReadLock()->GetReadCount() >= 1);
     // Last count is held by the lock itself.
@@ -299,13 +280,14 @@ TextureClientPool::Clear()
         this, mTextureClientsDeferred.front().get());
     mTextureClientsDeferred.pop_front();
   }
 }
 
 void TextureClientPool::Destroy()
 {
   Clear();
-  mMaxTextureClients = 0;
+  mInitialPoolSize = 0;
+  mPoolIncrementSize = 0;
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/client/TextureClientPool.h
+++ b/gfx/layers/client/TextureClientPool.h
@@ -43,18 +43,18 @@ class TextureClientPool final : public T
 {
   ~TextureClientPool();
 
 public:
   TextureClientPool(LayersBackend aBackend,
                     gfx::SurfaceFormat aFormat,
                     gfx::IntSize aSize,
                     TextureFlags aFlags,
-                    uint32_t aMaxTextureClients,
-                    uint32_t aShrinkTimeoutMsec,
+                    uint32_t aInitialPoolSize,
+                    uint32_t aPoolIncrementSize,
                     TextureForwarder* aAllocator);
 
   /**
    * Gets an allocated TextureClient of size and format that are determined
    * by the initialisation parameters given to the pool. This will either be
    * a cached client that was returned to the pool, or a newly allocated
    * client if one isn't available.
    *
@@ -71,34 +71,28 @@ public:
 
   /**
    * Return a TextureClient that is not yet ready to be reused, but will be
    * imminently.
    */
   void ReturnTextureClientDeferred(TextureClient *aClient) override;
 
   /**
-   * Attempt to shrink the pool so that there are no more than
-   * mMaxTextureClients clients outstanding.
-   */
-  void ShrinkToMaximumSize();
-
-  /**
-   * Attempt to shrink the pool so that there are no more than sMinCacheSize
-   * unused clients.
-   */
-  void ShrinkToMinimumSize();
-
-  /**
    * Return any clients to the pool that were previously returned in
    * ReturnTextureClientDeferred.
    */
   void ReturnDeferredClients();
 
   /**
+   * Attempt to shrink the pool so that there are no more than
+   * mInitialPoolSize outstanding.
+   */
+  void ShrinkToMaximumSize();
+
+  /**
    * Report that a client retrieved via GetTextureClient() has become
    * unusable, so that it will no longer be tracked.
    */
   virtual void ReportClientLost() override;
 
   /**
    * Calling this will cause the pool to attempt to relinquish any unused
    * clients.
@@ -112,39 +106,41 @@ public:
   /**
    * Clear the pool and put it in a state where it won't recycle any new texture.
    */
   void Destroy();
 
 private:
   void ReturnUnlockedClients();
 
-  // The minimum size of the pool (the number of tiles that will be kept after
-  // shrinking).
-  static const uint32_t sMinCacheSize = 0;
+  /// We maintain a number of unused texture clients for immediate return from
+  /// GetTextureClient(). This will normally be called if there are no
+  /// TextureClients available in the pool, which ideally should only ever
+  /// be at startup.
+  void AllocateTextureClients(size_t aSize);
 
   /// Backend passed to the TextureClient for buffer creation.
   LayersBackend mBackend;
 
   /// Format is passed to the TextureClient for buffer creation.
   gfx::SurfaceFormat mFormat;
 
   /// The width and height of the tiles to be used.
   gfx::IntSize mSize;
 
   /// Flags passed to the TextureClient for buffer creation.
   const TextureFlags mFlags;
 
-  // The maximum number of texture clients managed by this pool that we want
-  // to remain active.
-  uint32_t mMaxTextureClients;
+  // The initial number of unused texture clients to seed the pool with
+  // on construction
+  uint32_t mInitialPoolSize;
 
-  // The time in milliseconds before the pool will be shrunk to the minimum
-  // size after returning a client.
-  uint32_t mShrinkTimeoutMsec;
+  // How many unused texture clients to try and keep around if we go over
+  // the initial allocation
+  uint32_t mPoolIncrementSize;
 
   /// This is a total number of clients in the wild and in the stack of
   /// deferred clients (see below).  So, the total number of clients in
   /// existence is always mOutstandingClients + the size of mTextureClients.
   uint32_t mOutstandingClients;
 
   // On b2g gonk, std::queue might be a better choice.
   // On ICS, fence wait happens implicitly before drawing.
--- a/gfx/layers/ipc/CompositorBridgeChild.cpp
+++ b/gfx/layers/ipc/CompositorBridgeChild.cpp
@@ -924,28 +924,28 @@ CompositorBridgeChild::GetTexturePool(La
     }
   }
 
   mTexturePools.AppendElement(
       new TextureClientPool(aBackend, aFormat,
                             IntSize(gfxPlatform::GetPlatform()->GetTileWidth(),
                                     gfxPlatform::GetPlatform()->GetTileHeight()),
                             aFlags,
-                            gfxPrefs::LayersTileMaxPoolSize(),
-                            gfxPrefs::LayersTileShrinkPoolTimeout(),
+                            gfxPrefs::LayersTileInitialPoolSize(),
+                            gfxPrefs::LayersTilePoolIncrementSize(),
                             this));
 
   return mTexturePools.LastElement();
 }
 
 void
 CompositorBridgeChild::HandleMemoryPressure()
 {
   for (size_t i = 0; i < mTexturePools.Length(); i++) {
-    mTexturePools[i]->ShrinkToMinimumSize();
+    mTexturePools[i]->Clear();
   }
 }
 
 void
 CompositorBridgeChild::ClearTexturePool()
 {
   for (size_t i = 0; i < mTexturePools.Length(); i++) {
     mTexturePools[i]->Clear();
--- a/gfx/skia/skia/src/effects/SkDashPathEffect.cpp
+++ b/gfx/skia/skia/src/effects/SkDashPathEffect.cpp
@@ -243,17 +243,24 @@ bool SkDashPathEffect::asPoints(PointDat
                 len2 -= fIntervals[1];  // also skip first space
                 if (len2 < 0) {
                     len2 = 0;
                 }
             } else {
                 len2 -= clampedInitialDashLength; // skip initial partial empty
             }
         }
-        int numMidPoints = SkScalarFloorToInt(len2 / fIntervalLength);
+        // Too many midpoints can cause results->fNumPoints to overflow or
+        // otherwise cause the results->fPoints allocation below to OOM.
+        // Cap it to a sane value.
+        SkScalar numIntervals = len2 / fIntervalLength;
+        if (!SkScalarIsFinite(numIntervals) || numIntervals > SkDashPath::kMaxDashCount) {
+            return false;
+        }
+        int numMidPoints = SkScalarFloorToInt(numIntervals);
         results->fNumPoints += numMidPoints;
         len2 -= numMidPoints * fIntervalLength;
         bool partialLast = false;
         if (len2 > 0) {
             if (len2 < fIntervals[0]) {
                 partialLast = true;
             } else {
                 ++numMidPoints;
--- a/gfx/skia/skia/src/gpu/GrDrawContext.cpp
+++ b/gfx/skia/skia/src/gpu/GrDrawContext.cpp
@@ -985,17 +985,22 @@ void GrDrawContext::internalDrawPath(con
 
     if (nullptr == pr) {
         if (!GrPathRenderer::IsStrokeHairlineOrEquivalent(*strokeInfoPtr, viewMatrix, nullptr) &&
             !strokeInfoPtr->isFillStyle()) {
             // It didn't work above, so try again with stroke converted to a fill.
             if (!tmpPath.isValid()) {
                 tmpPath.init();
             }
-            dashlessStrokeInfo.setResScale(SkScalarAbs(viewMatrix.getMaxScale()));
+            SkScalar scale = SkScalarAbs(viewMatrix.getMaxScale());
+            if (!SkScalarIsFinite(scale)) {
+                SkDebugf("View matrix scale is not finite.\n");
+                return;
+            }
+            dashlessStrokeInfo.setResScale(scale);
             if (!dashlessStrokeInfo.applyToPath(tmpPath.get(), *pathPtr)) {
                 return;
             }
             pathPtr = tmpPath.get();
             if (pathPtr->isEmpty()) {
                 return;
             }
             dashlessStrokeInfo.setFillStyle();
--- a/gfx/skia/skia/src/utils/SkDashPath.cpp
+++ b/gfx/skia/skia/src/utils/SkDashPath.cpp
@@ -171,16 +171,17 @@ public:
 
         // now estimate how many quads will be added to the path
         //     resulting segments = pathLen * intervalCount / intervalLen
         //     resulting points = 4 * segments
 
         SkScalar ptCount = SkScalarMulDiv(pathLength,
                                           SkIntToScalar(intervalCount),
                                           intervalLength);
+        ptCount = SkTMin(ptCount, SkDashPath::kMaxDashCount);
         int n = SkScalarCeilToInt(ptCount) << 2;
         dst->incReserve(n);
 
         // we will take care of the stroking
         rec->setFillStyle();
         return true;
     }
 
@@ -247,17 +248,16 @@ bool SkDashPath::InternalFilter(SkPath* 
         // Since the path length / dash length ratio may be arbitrarily large, we can exert
         // significant memory pressure while attempting to build the filtered path. To avoid this,
         // we simply give up dashing beyond a certain threshold.
         //
         // The original bug report (http://crbug.com/165432) is based on a path yielding more than
         // 90 million dash segments and crashing the memory allocator. A limit of 1 million
         // segments seems reasonable: at 2 verbs per segment * 9 bytes per verb, this caps the
         // maximum dash memory overhead at roughly 17MB per path.
-        static const SkScalar kMaxDashCount = 1000000;
         dashCount += length * (count >> 1) / intervalLength;
         if (dashCount > kMaxDashCount) {
             dst->reset();
             return false;
         }
 
         // Using double precision to avoid looping indefinitely due to single precision rounding
         // (for extreme path_length/dash_length ratios). See test_infinite_dash() unittest.
--- a/gfx/skia/skia/src/utils/SkDashPathPriv.h
+++ b/gfx/skia/skia/src/utils/SkDashPathPriv.h
@@ -21,16 +21,18 @@ namespace SkDashPath {
      */
     void CalcDashParameters(SkScalar phase, const SkScalar intervals[], int32_t count,
                             SkScalar* initialDashLength, int32_t* initialDashIndex,
                             SkScalar* intervalLength, SkScalar* adjustedPhase = nullptr);
 
     bool FilterDashPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*,
                         const SkPathEffect::DashInfo& info);
 
+    const SkScalar kMaxDashCount = 1000000;
+
     /*
      * Caller should have already used ValidDashPath to exclude invalid data.
      */
     bool InternalFilter(SkPath* dst, const SkPath& src, SkStrokeRec* rec,
                         const SkRect* cullRect, const SkScalar aIntervals[],
                         int32_t count, SkScalar initialDashLength, int32_t initialDashIndex,
                         SkScalar intervalLength);
 
--- a/gfx/src/nsRect.cpp
+++ b/gfx/src/nsRect.cpp
@@ -1,44 +1,30 @@
 /* -*- 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 "nsRect.h"
 #include "mozilla/gfx/Types.h"          // for NS_SIDE_BOTTOM, etc
-#include "mozilla/CheckedInt.h"         // for CheckedInt
 #include "nsDeviceContext.h"            // for nsDeviceContext
 #include "nsString.h"               // for nsAutoString, etc
 #include "nsMargin.h"                   // for nsMargin
 
 static_assert((int(NS_SIDE_TOP) == 0) &&
               (int(NS_SIDE_RIGHT) == 1) &&
               (int(NS_SIDE_BOTTOM) == 2) &&
               (int(NS_SIDE_LEFT) == 3),
               "The mozilla::css::Side sequence must match the nsMargin nscoord sequence");
 
 const mozilla::gfx::IntRect& GetMaxSizedIntRect() {
   static const mozilla::gfx::IntRect r(0, 0, INT32_MAX, INT32_MAX);
   return r;
 }
 
-
-bool nsRect::Overflows() const {
-#ifdef NS_COORD_IS_FLOAT
-  return false;
-#else
-  mozilla::CheckedInt<int32_t> xMost = this->x;
-  xMost += this->width;
-  mozilla::CheckedInt<int32_t> yMost = this->y;
-  yMost += this->height;
-  return !xMost.isValid() || !yMost.isValid();
-#endif
-}
-
 #ifdef DEBUG
 // Diagnostics
 
 FILE* operator<<(FILE* out, const nsRect& rect)
 {
   nsAutoString tmp;
 
   // Output the coordinates in fractional pixels so they're easier to read
--- a/gfx/src/nsRect.h
+++ b/gfx/src/nsRect.h
@@ -124,19 +124,16 @@ struct nsRect :
   {
     *this = aRect1.SaturatingUnion(aRect2);
   }
   void SaturatingUnionRectEdges(const nsRect& aRect1, const nsRect& aRect2)
   {
     *this = aRect1.SaturatingUnionEdges(aRect2);
   }
 
-  // Return whether this rect's right or bottom edge overflow int32.
-  bool Overflows() const;
-
   /**
    * Return this rect scaled to a different appunits per pixel (APP) ratio.
    * In the RoundOut version we make the rect the smallest rect containing the
    * unrounded result. In the RoundIn version we make the rect the largest rect
    * contained in the unrounded result.
    * @param aFromAPP the APP to scale from
    * @param aToAPP the APP to scale to
    * @note this can turn an empty rectangle into a non-empty rectangle
--- a/gfx/thebes/gfxContext.h
+++ b/gfx/thebes/gfxContext.h
@@ -14,17 +14,16 @@
 #include "gfxMatrix.h"
 #include "gfxPattern.h"
 #include "nsTArray.h"
 
 #include "mozilla/gfx/2D.h"
 
 typedef struct _cairo cairo_t;
 class GlyphBufferAzure;
-template <typename T> class FallibleTArray;
 
 namespace mozilla {
 namespace gfx {
 struct RectCornerRadii;
 } // namespace gfx
 } // namespace mozilla
 
 class ClipExporter;
@@ -208,30 +207,30 @@ public:
      * resulting rectangle is the minimum device-space rectangle that
      * encloses the user-space rectangle given.
      */
     gfxRect UserToDevice(const gfxRect& rect) const;
 
     /**
      * Takes the given rect and tries to align it to device pixels.  If
      * this succeeds, the method will return true, and the rect will
-     * be in device coordinates (already transformed by the CTM).  If it 
+     * be in device coordinates (already transformed by the CTM).  If it
      * fails, the method will return false, and the rect will not be
      * changed.
      *
      * If ignoreScale is true, then snapping will take place even if
      * the CTM has a scale applied.  Snapping never takes place if
      * there is a rotation in the CTM.
      */
     bool UserToDevicePixelSnapped(gfxRect& rect, bool ignoreScale = false) const;
 
     /**
      * Takes the given point and tries to align it to device pixels.  If
      * this succeeds, the method will return true, and the point will
-     * be in device coordinates (already transformed by the CTM).  If it 
+     * be in device coordinates (already transformed by the CTM).  If it
      * fails, the method will return false, and the point will not be
      * changed.
      *
      * If ignoreScale is true, then snapping will take place even if
      * the CTM has a scale applied.  Snapping never takes place if
      * there is a rotation in the CTM.
      */
     bool UserToDevicePixelSnapped(gfxPoint& pt, bool ignoreScale = false) const;
@@ -385,18 +384,18 @@ public:
     gfxRect GetClipExtents();
 
     /**
      * Whether the current clip is not a simple rectangle.
      */
     bool HasComplexClip() const;
 
     /**
-     * Returns true if the given rectangle is fully contained in the current clip. 
-     * This is conservative; it may return false even when the given rectangle is 
+     * Returns true if the given rectangle is fully contained in the current clip.
+     * This is conservative; it may return false even when the given rectangle is
      * fully contained by the current clip.
      */
     bool ClipContainsRect(const gfxRect& aRect);
 
      /**
       * Exports the current clip using the provided exporter.
       */
     bool ExportClip(ClipExporter& aExporter);
@@ -466,17 +465,17 @@ private:
 
   typedef mozilla::gfx::Matrix Matrix;
   typedef mozilla::gfx::DrawTarget DrawTarget;
   typedef mozilla::gfx::Color Color;
   typedef mozilla::gfx::StrokeOptions StrokeOptions;
   typedef mozilla::gfx::Float Float;
   typedef mozilla::gfx::PathBuilder PathBuilder;
   typedef mozilla::gfx::SourceSurface SourceSurface;
-  
+
   struct AzureState {
     AzureState()
       : op(mozilla::gfx::CompositionOp::OP_OVER)
       , color(0, 0, 0, 1.0f)
       , aaMode(mozilla::gfx::AntialiasMode::SUBPIXEL)
       , patternTransformChanged(false)
       , mBlendOpacity(0.0f)
     {}
@@ -557,17 +556,17 @@ public:
 
   ~gfxContextAutoSaveRestore() {
     Restore();
   }
 
   void SetContext(gfxContext *aContext) {
     NS_ASSERTION(!mContext, "Not going to call Restore() on some context!!!");
     mContext = aContext;
-    mContext->Save();    
+    mContext->Save();
   }
 
   void EnsureSaved(gfxContext *aContext) {
     MOZ_ASSERT(!mContext || mContext == aContext, "wrong context");
     if (!mContext) {
         mContext = aContext;
         mContext->Save();
     }
--- a/gfx/thebes/gfxFcPlatformFontList.cpp
+++ b/gfx/thebes/gfxFcPlatformFontList.cpp
@@ -27,16 +27,20 @@
 
 #include <fontconfig/fcfreetype.h>
 
 #ifdef MOZ_WIDGET_GTK
 #include <gdk/gdk.h>
 #include "gfxPlatformGtk.h"
 #endif
 
+#ifdef MOZ_X11
+#include "mozilla/X11Util.h"
+#endif
+
 using namespace mozilla;
 using namespace mozilla::unicode;
 
 #ifndef FC_POSTSCRIPT_NAME
 #define FC_POSTSCRIPT_NAME  "postscriptname"      /* String */
 #endif
 
 #define PRINTING_FC_PROPERTY "gfx.printing"
@@ -712,16 +716,39 @@ gfxFontconfigFontEntry::CreateScaledFont
     return scaledFont;
 }
 
 #ifdef MOZ_WIDGET_GTK
 // defintion included below
 static void ApplyGdkScreenFontOptions(FcPattern *aPattern);
 #endif
 
+#ifdef MOZ_X11
+static bool
+GetXftInt(Display* aDisplay, const char* aName, int* aResult)
+{
+    if (!aDisplay) {
+        return false;
+    }
+    char* value = XGetDefault(aDisplay, "Xft", aName);
+    if (!value) {
+        return false;
+    }
+    if (FcNameConstant(const_cast<FcChar8*>(ToFcChar8Ptr(value)), aResult)) {
+        return true;
+    }
+    char* end;
+    *aResult = strtol(value, &end, 0);
+    if (end != value) {
+        return true;
+    }
+    return false;
+}
+#endif
+
 static void
 PreparePattern(FcPattern* aPattern, bool aIsPrinterFont)
 {
     FcConfigSubstitute(nullptr, aPattern, FcMatchPattern);
 
     // This gets cairo_font_options_t for the Screen.  We should have
     // different font options for printing (no hinting) but we are not told
     // what we are measuring for.
@@ -740,16 +767,26 @@ PreparePattern(FcPattern* aPattern, bool
        cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY);
        cairo_ft_font_options_substitute(options, aPattern);
        cairo_font_options_destroy(options);
        FcPatternAddBool(aPattern, PRINTING_FC_PROPERTY, FcTrue);
     } else {
 #ifdef MOZ_WIDGET_GTK
        ApplyGdkScreenFontOptions(aPattern);
 #endif
+
+#ifdef MOZ_X11
+        FcValue value;
+        int lcdfilter;
+        if (FcPatternGet(aPattern, FC_LCD_FILTER, 0, &value)
+                == FcResultNoMatch &&
+            GetXftInt(DefaultXDisplay(), "lcdfilter", &lcdfilter)) {
+            FcPatternAddInteger(aPattern, FC_LCD_FILTER, lcdfilter);
+        }
+#endif
     }
 
     FcDefaultSubstitute(aPattern);
 }
 
 gfxFont*
 gfxFontconfigFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle,
                                            bool aNeedsBold)
--- a/gfx/thebes/gfxFontUtils.cpp
+++ b/gfx/thebes/gfxFontUtils.cpp
@@ -881,17 +881,17 @@ nsresult gfxFontUtils::MakeUniqueUserFon
 
     // all b64 characters except for '/' are allowed in Postscript names, so convert / ==> -
     char *p;
     for (p = guidB64; *p; p++) {
         if (*p == '/')
             *p = '-';
     }
 
-    aName.AssignLiteral(MOZ_UTF16("uf"));
+    aName.AssignLiteral(u"uf");
     aName.AppendASCII(guidB64);
     return NS_OK;
 }
 
 
 // TrueType/OpenType table handling code
 
 // need byte aligned structs
--- a/gfx/thebes/gfxMacPlatformFontList.h
+++ b/gfx/thebes/gfxMacPlatformFontList.h
@@ -24,17 +24,18 @@ class gfxMacPlatformFontList;
 
 // a single member of a font family (i.e. a single face, such as Times Italic)
 class MacOSFontEntry : public gfxFontEntry
 {
 public:
     friend class gfxMacPlatformFontList;
 
     MacOSFontEntry(const nsAString& aPostscriptName, int32_t aWeight,
-                   bool aIsStandardFace = false);
+                   bool aIsStandardFace = false,
+                   double aSizeHint = 0.0);
 
     // for use with data fonts
     MacOSFontEntry(const nsAString& aPostscriptName, CGFontRef aFontRef,
                    uint16_t aWeight, uint16_t aStretch, uint8_t aStyle,
                    bool aIsDataUserFont, bool aIsLocal);
 
     virtual ~MacOSFontEntry() {
         ::CGFontRelease(mFontRef);
@@ -59,16 +60,18 @@ protected:
     virtual gfxFont* CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold) override;
 
     virtual bool HasFontTable(uint32_t aTableTag) override;
 
     static void DestroyBlobFunc(void* aUserData);
 
     CGFontRef mFontRef; // owning reference to the CGFont, released on destruction
 
+    double mSizeHint;
+
     bool mFontRefInitialized;
     bool mRequiresAAT;
     bool mIsCFF;
     bool mIsCFFInitialized;
     nsTHashtable<nsUint32HashKey> mAvailableTables;
 };
 
 class gfxMacPlatformFontList : public gfxPlatformFontList {
@@ -115,17 +118,17 @@ private:
 
     // initialize font lists
     nsresult InitFontList() override;
 
     // special case font faces treated as font families (set via prefs)
     void InitSingleFaceList();
 
     // initialize system fonts
-    void InitSystemFonts();
+    void InitSystemFontNames();
 
     // helper function to lookup in both hidden system fonts and normal fonts
     gfxFontFamily* FindSystemFontFamily(const nsAString& aFamily);
 
     static void RegisteredFontsChangedNotificationCallback(CFNotificationCenterRef center,
                                                            void *observer,
                                                            CFStringRef name,
                                                            const void *object,
@@ -164,13 +167,13 @@ private:
     // for different locales (e.g. .Helvetica Neue UI, .SF NS Text)
     FontFamilyTable mSystemFontFamilies;
 
     // font families that -apple-system maps to
     // Pre-10.11 this was always a single font family, such as Lucida Grande
     // or Helvetica Neue. For OSX 10.11, Apple uses pair of families
     // for the UI, one for text sizes and another for display sizes
     bool mUseSizeSensitiveSystemFont;
-    RefPtr<gfxFontFamily> mSystemTextFontFamily;
-    RefPtr<gfxFontFamily> mSystemDisplayFontFamily; // only used on OSX 10.11
+    nsString mSystemTextFontFamilyName;
+    nsString mSystemDisplayFontFamilyName; // only used on OSX 10.11
 };
 
 #endif /* gfxMacPlatformFontList_H_ */
--- a/gfx/thebes/gfxMacPlatformFontList.mm
+++ b/gfx/thebes/gfxMacPlatformFontList.mm
@@ -250,35 +250,38 @@ MacOSFontEntry::IsCFF()
         mIsCFF = HasFontTable(TRUETYPE_TAG('C','F','F',' '));
     }
 
     return mIsCFF;
 }
 
 MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName,
                                int32_t aWeight,
-                               bool aIsStandardFace)
+                               bool aIsStandardFace,
+                               double aSizeHint)
     : gfxFontEntry(aPostscriptName, aIsStandardFace),
       mFontRef(NULL),
+      mSizeHint(aSizeHint),
       mFontRefInitialized(false),
       mRequiresAAT(false),
       mIsCFF(false),
       mIsCFFInitialized(false)
 {
     mWeight = aWeight;
 }
 
 MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName,
                                CGFontRef aFontRef,
                                uint16_t aWeight, uint16_t aStretch,
                                uint8_t aStyle,
                                bool aIsDataUserFont,
                                bool aIsLocalUserFont)
     : gfxFontEntry(aPostscriptName, false),
       mFontRef(NULL),
+      mSizeHint(0.0),
       mFontRefInitialized(false),
       mRequiresAAT(false),
       mIsCFF(false),
       mIsCFFInitialized(false)
 {
     mFontRef = aFontRef;
     mFontRefInitialized = true;
     ::CFRetain(mFontRef);
@@ -296,16 +299,29 @@ MacOSFontEntry::MacOSFontEntry(const nsA
 
 CGFontRef
 MacOSFontEntry::GetFontRef()
 {
     if (!mFontRefInitialized) {
         mFontRefInitialized = true;
         NSString *psname = GetNSStringForString(mName);
         mFontRef = ::CGFontCreateWithFontName(CFStringRef(psname));
+        if (!mFontRef) {
+            // This happens on macOS 10.12 for font entry names that start with
+            // .AppleSystemUIFont. For those fonts, we need to go through NSFont
+            // to get the correct CGFontRef.
+            // Both the Text and the Display variant of the display font use
+            // .AppleSystemUIFontSomethingSomething as their member names.
+            // That's why we're carrying along mSizeHint to this place so that
+            // we get the variant that we want for this family.
+            NSFont* font = [NSFont fontWithName:psname size:mSizeHint];
+            if (font) {
+                mFontRef = CTFontCopyGraphicsFont((CTFontRef)font, nullptr);
+            }
+        }
     }
     return mFontRef;
 }
 
 // For a logging build, we wrap the CFDataRef in a FontTableRec so that we can
 // use the MOZ_COUNT_[CD]TOR macros in it. A release build without logging
 // does not get this overhead.
 class FontTableRec {
@@ -394,25 +410,29 @@ MacOSFontEntry::AddSizeOfIncludingThis(M
 }
 
 /* gfxMacFontFamily */
 #pragma mark-
 
 class gfxMacFontFamily : public gfxFontFamily
 {
 public:
-    explicit gfxMacFontFamily(nsAString& aName) :
-        gfxFontFamily(aName)
+    explicit gfxMacFontFamily(nsAString& aName, double aSizeHint) :
+        gfxFontFamily(aName),
+        mSizeHint(aSizeHint)
     {}
 
     virtual ~gfxMacFontFamily() {}
 
     virtual void LocalizedName(nsAString& aLocalizedName);
 
     virtual void FindStyleVariations(FontInfoData *aFontInfoData = nullptr);
+
+protected:
+    double mSizeHint;
 };
 
 void
 gfxMacFontFamily::LocalizedName(nsAString& aLocalizedName)
 {
     nsAutoreleasePool localPool;
 
     if (!HasOtherFamilyNames()) {
@@ -502,17 +522,17 @@ gfxMacFontFamily::FindStyleVariations(Fo
             [facename isEqualToString:@"Bold Italic"] ||
             [facename isEqualToString:@"Bold Oblique"])
         {
             isStandardFace = true;
         }
 
         // create a font entry
         MacOSFontEntry *fontEntry =
-            new MacOSFontEntry(postscriptFontName, cssWeight, isStandardFace);
+            new MacOSFontEntry(postscriptFontName, cssWeight, isStandardFace, mSizeHint);
         if (!fontEntry) {
             break;
         }
 
         // set additional properties based on the traits reported by Cocoa
         if (macTraits & (NSCondensedFontMask | NSNarrowFontMask | NSCompressedFontMask)) {
             fontEntry->mStretch = NS_FONT_STRETCH_CONDENSED;
         } else if (macTraits & NSExpandedFontMask) {
@@ -668,20 +688,26 @@ gfxMacPlatformFontList::AddFamily(CFStri
     bool hiddenSystemFont = [family hasPrefix:@"."];
 
     FontFamilyTable& table =
         hiddenSystemFont ? mSystemFontFamilies : mFontFamilies;
 
     nsAutoString familyName;
     nsCocoaUtils::GetStringForNSString(family, familyName);
 
+    double sizeHint = 0.0;
+    if (hiddenSystemFont && mUseSizeSensitiveSystemFont &&
+        mSystemDisplayFontFamilyName.Equals(familyName)) {
+        sizeHint = 128.0;
+    }
+
     nsAutoString key;
     ToLowerCase(familyName, key);
 
-    gfxFontFamily* familyEntry = new gfxMacFontFamily(familyName);
+    gfxFontFamily* familyEntry = new gfxMacFontFamily(familyName, sizeHint);
     table.Put(key, familyEntry);
 
     // check the bad underline blacklist
     if (mBadUnderlineFamilyNames.Contains(key)) {
         familyEntry->SetBadUnderlineFamily();
     }
 }
 
@@ -693,28 +719,28 @@ gfxMacPlatformFontList::InitFontList()
     Telemetry::AutoTimer<Telemetry::MAC_INITFONTLIST_TOTAL> timer;
 
     // reset font lists
     gfxPlatformFontList::InitFontList();
     mSystemFontFamilies.Clear();
     
     // iterate over available families
 
+    InitSystemFontNames();
+
     CFArrayRef familyNames = CTFontManagerCopyAvailableFontFamilyNames();
 
     for (NSString* familyName in (NSArray*)familyNames) {
         AddFamily((CFStringRef)familyName);
     }
 
     CFRelease(familyNames);
 
     InitSingleFaceList();
 
-    InitSystemFonts();
-
     // to avoid full search of font name tables, seed the other names table with localized names from
     // some of the prefs fonts which are accessed via their localized names.  changes in the pref fonts will only cause
     // a font lookup miss earlier. this is a simple optimization, it's not required for correctness
     PreloadNamesList();
 
     // start the delayed cmap loader
     GetPrefsAndStartLoader();
 
@@ -787,43 +813,40 @@ static NSString* GetRealFamilyName(NSFon
 // for text sizes and another for larger, display sizes. Each has a
 // different number of weights. There aren't efficient API's for looking
 // this information up, so hard code the logic here but confirm via
 // debug assertions that the logic is correct.
 
 const CGFloat kTextDisplayCrossover = 20.0; // use text family below this size
 
 void
-gfxMacPlatformFontList::InitSystemFonts()
+gfxMacPlatformFontList::InitSystemFontNames()
 {
     // system font under 10.11 are two distinct families for text/display sizes
     if (nsCocoaFeatures::OnElCapitanOrLater()) {
         mUseSizeSensitiveSystemFont = true;
     }
 
     // text font family
     NSFont* sys = [NSFont systemFontOfSize: 0.0];
     NSString* textFamilyName = GetRealFamilyName(sys);
     nsAutoString familyName;
     nsCocoaUtils::GetStringForNSString(textFamilyName, familyName);
-    mSystemTextFontFamily = FindSystemFontFamily(familyName);
-    NS_ASSERTION(mSystemTextFontFamily, "null system display font family");
+    mSystemTextFontFamilyName = familyName;
 
     // display font family, if on OSX 10.11
     if (mUseSizeSensitiveSystemFont) {
         NSFont* displaySys = [NSFont systemFontOfSize: 128.0];
         NSString* displayFamilyName = GetRealFamilyName(displaySys);
         nsCocoaUtils::GetStringForNSString(displayFamilyName, familyName);
-        mSystemDisplayFontFamily = FindSystemFontFamily(familyName);
-        NS_ASSERTION(mSystemDisplayFontFamily, "null system display font family");
+        mSystemDisplayFontFamilyName = familyName;
 
 #if DEBUG
         // confirm that the optical size switch is at 20.0
-        NS_ASSERTION(mSystemTextFontFamily && mSystemDisplayFontFamily &&
-                     [textFamilyName compare:displayFamilyName] != NSOrderedSame,
+        NS_ASSERTION([textFamilyName compare:displayFamilyName] != NSOrderedSame,
                      "system text/display fonts are the same!");
         NSString* fam19 = GetRealFamilyName([NSFont systemFontOfSize:
                                              (kTextDisplayCrossover - 1.0)]);
         NSString* fam20 = GetRealFamilyName([NSFont systemFontOfSize:
                                              kTextDisplayCrossover]);
         NS_ASSERTION(fam19 && fam20 && [fam19 compare:fam20] != NSOrderedSame,
                      "system text/display font size switch point is not as expected!");
 #endif
@@ -1105,20 +1128,20 @@ gfxMacPlatformFontList::FindAndAddFamili
                                            nsTArray<gfxFontFamily*>* aOutput,
                                            gfxFontStyle* aStyle,
                                            gfxFloat aDevToCssSize)
 {
     // search for special system font name, -apple-system
     if (aFamily.EqualsLiteral(kSystemFont_system)) {
         if (mUseSizeSensitiveSystemFont &&
             aStyle && (aStyle->size * aDevToCssSize) >= kTextDisplayCrossover) {
-            aOutput->AppendElement(mSystemDisplayFontFamily);
+            aOutput->AppendElement(FindSystemFontFamily(mSystemDisplayFontFamilyName));
             return true;
         }
-        aOutput->AppendElement(mSystemTextFontFamily);
+        aOutput->AppendElement(FindSystemFontFamily(mSystemTextFontFamilyName));
         return true;
     }
 
     return gfxPlatformFontList::FindAndAddFamilies(aFamily, aOutput, aStyle,
                                                    aDevToCssSize);
 }
 
 void
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -410,17 +410,17 @@ NS_IMPL_ISUPPORTS(SRGBOverrideObserver, 
 #define GFX_PREF_CMS_FORCE_SRGB "gfx.color_management.force_srgb"
 
 NS_IMETHODIMP
 SRGBOverrideObserver::Observe(nsISupports *aSubject,
                               const char *aTopic,
                               const char16_t* someData)
 {
     NS_ASSERTION(NS_strcmp(someData,
-                           MOZ_UTF16(GFX_PREF_CMS_FORCE_SRGB)) == 0,
+                           (u"" GFX_PREF_CMS_FORCE_SRGB)) == 0,
                  "Restarting CMS on wrong pref!");
     ShutdownCMS();
     // Update current cms profile.
     gfxPlatform::CreateCMSOutputProfile();
     return NS_OK;
 }
 
 static const char* kObservedPrefs[] = {
@@ -883,17 +883,16 @@ gfxPlatform::InitLayersIPC()
     sLayersIPCIsUp = true;
 
     if (XRE_IsParentProcess())
     {
         layers::CompositorThreadHolder::Start();
 #ifdef MOZ_WIDGET_GONK
         SharedBufferManagerChild::StartUp();
 #endif
-        gfx::VRManagerChild::StartUpSameProcess();
     }
 }
 
 /* static */ void
 gfxPlatform::ShutdownLayersIPC()
 {
     if (!sLayersIPCIsUp) {
       return;
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -65,17 +65,16 @@
 #define DECL_GFX_PREF(Update, Prefname, Name, Type, Default)                  \
 public:                                                                       \
 static Type Name() { MOZ_ASSERT(SingletonExists()); return GetSingleton().mPref##Name.mValue; } \
 static void Set##Name(Type aVal) { MOZ_ASSERT(SingletonExists());             \
     GetSingleton().mPref##Name.Set(UpdatePolicy::Update, Get##Name##PrefName(), aVal); } \
 static const char* Get##Name##PrefName() { return Prefname; }                 \
 static Type Get##Name##PrefDefault() { return Default; }                      \
 private:                                                                      \
-static Pref* Get##Name##PrefPtr() { return &GetSingleton().mPref##Name; }     \
 PrefTemplate<UpdatePolicy::Update, Type, Get##Name##PrefDefault, Get##Name##PrefName> mPref##Name
 
 namespace mozilla {
 namespace gfx {
 class GfxPrefValue;   // defined in PGPU.ipdl
 } // namespace gfx
 } // namespace mozilla
 
@@ -411,29 +410,28 @@ private:
   DECL_GFX_PREF(Once, "image.mem.surfacecache.discard_factor", ImageMemSurfaceCacheDiscardFactor, uint32_t, 1);
   DECL_GFX_PREF(Once, "image.mem.surfacecache.max_size_kb",    ImageMemSurfaceCacheMaxSizeKB, uint32_t, 100 * 1024);
   DECL_GFX_PREF(Once, "image.mem.surfacecache.min_expiration_ms", ImageMemSurfaceCacheMinExpirationMS, uint32_t, 60*1000);
   DECL_GFX_PREF(Once, "image.mem.surfacecache.size_factor",    ImageMemSurfaceCacheSizeFactor, uint32_t, 64);
   DECL_GFX_PREF(Live, "image.mozsamplesize.enabled",           ImageMozSampleSizeEnabled, bool, false);
   DECL_GFX_PREF(Once, "image.multithreaded_decoding.limit",    ImageMTDecodingLimit, int32_t, -1);
   DECL_GFX_PREF(Live, "image.single-color-optimization.enabled", ImageSingleColorOptimizationEnabled, bool, true);
 
-  DECL_GFX_PREF(Live, "layers.child-process-shutdown",         ChildProcessShutdown, bool, true);
   DECL_GFX_PREF(Once, "layers.acceleration.disabled",          LayersAccelerationDisabledDoNotUseDirectly, bool, false);
   DECL_GFX_PREF(Live, "layers.acceleration.draw-fps",          LayersDrawFPS, bool, false);
   DECL_GFX_PREF(Live, "layers.acceleration.draw-fps.print-histogram",  FPSPrintHistogram, bool, false);
   DECL_GFX_PREF(Live, "layers.acceleration.draw-fps.write-to-file", WriteFPSToFile, bool, false);
   DECL_GFX_PREF(Once, "layers.acceleration.force-enabled",     LayersAccelerationForceEnabledDoNotUseDirectly, bool, false);
   DECL_GFX_PREF(Once, "layers.allow-d3d9-fallback",            LayersAllowD3D9Fallback, bool, false);
   DECL_GFX_PREF(Once, "layers.amd-switchable-gfx.enabled",     LayersAMDSwitchableGfxEnabled, bool, false);
   DECL_GFX_PREF(Once, "layers.async-pan-zoom.enabled",         AsyncPanZoomEnabledDoNotUseDirectly, bool, true);
   DECL_GFX_PREF(Once, "layers.async-pan-zoom.separate-event-thread", AsyncPanZoomSeparateEventThread, bool, false);
   DECL_GFX_PREF(Live, "layers.bench.enabled",                  LayersBenchEnabled, bool, false);
   DECL_GFX_PREF(Once, "layers.bufferrotation.enabled",         BufferRotationEnabled, bool, true);
-  DECL_GFX_PREF(Live, "layers.shared-buffer-provider.enabled", PersistentBufferProviderSharedEnabled, bool, false);
+  DECL_GFX_PREF(Live, "layers.child-process-shutdown",         ChildProcessShutdown, bool, true);
 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
   // If MOZ_GFX_OPTIMIZE_MOBILE is defined, we force component alpha off
   // and ignore the preference.
   DECL_GFX_PREF(Skip, "layers.componentalpha.enabled",         ComponentAlphaEnabled, bool, false);
 #else
   // If MOZ_GFX_OPTIMIZE_MOBILE is not defined, we actually take the
   // preference value, defaulting to true.
   DECL_GFX_PREF(Once, "layers.componentalpha.enabled",         ComponentAlphaEnabled, bool, true);
@@ -472,33 +470,34 @@ private:
   DECL_GFX_PREF(Live, "layers.max-active",                     MaxActiveLayers, int32_t, -1);
   DECL_GFX_PREF(Once, "layers.offmainthreadcomposition.force-disabled", LayersOffMainThreadCompositionForceDisabled, bool, false);
   DECL_GFX_PREF(Live, "layers.offmainthreadcomposition.frame-rate", LayersCompositionFrameRate, int32_t,-1);
   DECL_GFX_PREF(Live, "layers.orientation.sync.timeout",       OrientationSyncMillis, uint32_t, (uint32_t)0);
   DECL_GFX_PREF(Once, "layers.overzealous-gralloc-unlocking",  OverzealousGrallocUnlocking, bool, false);
   DECL_GFX_PREF(Once, "layers.prefer-d3d9",                    LayersPreferD3D9, bool, false);
   DECL_GFX_PREF(Once, "layers.prefer-opengl",                  LayersPreferOpenGL, bool, false);
   DECL_GFX_PREF(Live, "layers.progressive-paint",              ProgressivePaintDoNotUseDirectly, bool, false);
+  DECL_GFX_PREF(Live, "layers.shared-buffer-provider.enabled", PersistentBufferProviderSharedEnabled, bool, false);
+  DECL_GFX_PREF(Live, "layers.single-tile.enabled",            LayersSingleTileEnabled, bool, true);
   DECL_GFX_PREF(Once, "layers.stereo-video.enabled",           StereoVideoEnabled, bool, false);
 
   // We allow for configurable and rectangular tile size to avoid wasting memory on devices whose
   // screen size does not align nicely to the default tile size. Although layers can be any size,
   // they are often the same size as the screen, especially for width.
   DECL_GFX_PREF(Once, "layers.tile-width",                     LayersTileWidth, int32_t, 256);
   DECL_GFX_PREF(Once, "layers.tile-height",                    LayersTileHeight, int32_t, 256);
-  DECL_GFX_PREF(Once, "layers.tile-max-pool-size",             LayersTileMaxPoolSize, uint32_t, (uint32_t)50);
-  DECL_GFX_PREF(Once, "layers.tile-shrink-pool-timeout",       LayersTileShrinkPoolTimeout, uint32_t, (uint32_t)1000);
+  DECL_GFX_PREF(Once, "layers.tile-initial-pool-size",         LayersTileInitialPoolSize, uint32_t, (uint32_t)50);
+  DECL_GFX_PREF(Once, "layers.tile-pool-increment-size",       LayersTilePoolIncrementSize, uint32_t, (uint32_t)10);
   DECL_GFX_PREF(Once, "layers.tiles.adjust",                   LayersTilesAdjust, bool, true);
   DECL_GFX_PREF(Once, "layers.tiles.edge-padding",             TileEdgePaddingEnabled, bool, true);
   DECL_GFX_PREF(Live, "layers.tiles.fade-in.enabled",          LayerTileFadeInEnabled, bool, false);
   DECL_GFX_PREF(Live, "layers.tiles.fade-in.duration-ms",      LayerTileFadeInDuration, uint32_t, 250);
   DECL_GFX_PREF(Live, "layers.transaction.warning-ms",         LayerTransactionWarning, uint32_t, 200);
   DECL_GFX_PREF(Once, "layers.uniformity-info",                UniformityInfo, bool, false);
   DECL_GFX_PREF(Once, "layers.use-image-offscreen-surfaces",   UseImageOffscreenSurfaces, bool, true);
-  DECL_GFX_PREF(Live, "layers.single-tile.enabled",            LayersSingleTileEnabled, bool, true);
 
   DECL_GFX_PREF(Live, "layout.css.scroll-behavior.damping-ratio", ScrollBehaviorDampingRatio, float, 1.0f);
   DECL_GFX_PREF(Live, "layout.css.scroll-behavior.enabled",    ScrollBehaviorEnabled, bool, false);
   DECL_GFX_PREF(Live, "layout.css.scroll-behavior.spring-constant", ScrollBehaviorSpringConstant, float, 250.0f);
   DECL_GFX_PREF(Live, "layout.css.scroll-snap.prediction-max-velocity", ScrollSnapPredictionMaxVelocity, int32_t, 2000);
   DECL_GFX_PREF(Live, "layout.css.scroll-snap.prediction-sensitivity", ScrollSnapPredictionSensitivity, float, 0.750f);
   DECL_GFX_PREF(Live, "layout.css.scroll-snap.proximity-threshold", ScrollSnapProximityThreshold, int32_t, 200);
   DECL_GFX_PREF(Live, "layout.css.touch_action.enabled",       TouchActionEnabled, bool, false);
@@ -506,16 +505,19 @@ private:
   DECL_GFX_PREF(Live, "layout.display-list.dump-content",      LayoutDumpDisplayListContent, bool, false);
   DECL_GFX_PREF(Live, "layout.event-regions.enabled",          LayoutEventRegionsEnabledDoNotUseDirectly, bool, false);
   DECL_GFX_PREF(Once, "layout.frame_rate",                     LayoutFrameRate, int32_t, -1);
   DECL_GFX_PREF(Once, "layout.paint_rects_separately",         LayoutPaintRectsSeparately, bool, true);
 
   // This and code dependent on it should be removed once containerless scrolling looks stable.
   DECL_GFX_PREF(Once, "layout.scroll.root-frame-containers",   LayoutUseContainersForRootFrames, bool, true);
 
+  DECL_GFX_PREF(Once, "media.hardware-video-decoding.force-enabled",
+                                                               HardwareVideoDecodingForceEnabled, bool, false);
+
   // These affect how line scrolls from wheel events will be accelerated.
   DECL_GFX_PREF(Live, "mousewheel.acceleration.factor",        MouseWheelAccelerationFactor, int32_t, -1);
   DECL_GFX_PREF(Live, "mousewheel.acceleration.start",         MouseWheelAccelerationStart, int32_t, -1);
 
   // This affects whether events will be routed through APZ or not.
   DECL_GFX_PREF(Live, "mousewheel.system_scroll_override_on_root_content.enabled",
                                                                MouseWheelHasRootScrollDeltaOverride, bool, false);
   DECL_GFX_PREF(Live, "mousewheel.transaction.ignoremovedelay",MouseWheelIgnoreMoveDelayMs, int32_t, (int32_t)100);
@@ -559,19 +561,16 @@ private:
   DECL_GFX_PREF(Live, "webgl.min_capability_mode",             WebGLMinCapabilityMode, bool, false);
   DECL_GFX_PREF(Live, "webgl.msaa-force",                      WebGLForceMSAA, bool, false);
   DECL_GFX_PREF(Live, "webgl.prefer-16bpp",                    WebGLPrefer16bpp, bool, false);
   DECL_GFX_PREF(Live, "webgl.restore-context-when-visible",    WebGLRestoreWhenVisible, bool, true);
   DECL_GFX_PREF(Live, "webgl.allow-immediate-queries",         WebGLImmediateQueries, bool, false);
 
   DECL_GFX_PREF(Live, "webgl.webgl2-compat-mode",              WebGL2CompatMode, bool, false);
 
-  DECL_GFX_PREF(Once, "media.hardware-video-decoding.force-enabled",
-                                                               HardwareVideoDecodingForceEnabled, bool, false);
-
   // WARNING:
   // Please make sure that you've added your new preference to the list above in alphabetical order.
   // Please do not just append it to the end of the list.
 
 public:
   // Manage the singleton:
   static gfxPrefs& GetSingleton()
   {
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -1087,17 +1087,17 @@ gfxWindowsPlatform::UseClearTypeAlways()
     return mUseClearTypeAlways;
 }
 
 void 
 gfxWindowsPlatform::GetDLLVersion(char16ptr_t aDLLPath, nsAString& aVersion)
 {
     DWORD versInfoSize, vers[4] = {0};
     // version info not available case
-    aVersion.AssignLiteral(MOZ_UTF16("0.0.0.0"));
+    aVersion.AssignLiteral(u"0.0.0.0");
     versInfoSize = GetFileVersionInfoSizeW(aDLLPath, nullptr);
     AutoTArray<BYTE,512> versionInfo;
     
     if (versInfoSize == 0 ||
         !versionInfo.AppendElements(uint32_t(versInfoSize)))
     {
         return;
     }
--- a/gfx/vr/ipc/VRManagerChild.cpp
+++ b/gfx/vr/ipc/VRManagerChild.cpp
@@ -38,45 +38,60 @@ VRManagerChild::~VRManagerChild()
 
 /*static*/ VRManagerChild*
 VRManagerChild::Get()
 {
   MOZ_ASSERT(sVRManagerChildSingleton);
   return sVRManagerChildSingleton;
 }
 
-/*static*/ VRManagerChild*
-VRManagerChild::StartUpInChildProcess(Transport* aTransport, ProcessId aOtherPid)
+/* static */ bool
+VRManagerChild::IsCreated()
+{
+  return !!sVRManagerChildSingleton;
+}
+
+/* static */ bool
+VRManagerChild::InitForContent(Endpoint<PVRManagerChild>&& aEndpoint)
 {
   MOZ_ASSERT(NS_IsMainThread());
-
-  // There's only one VRManager per child process.
   MOZ_ASSERT(!sVRManagerChildSingleton);
 
   RefPtr<VRManagerChild> child(new VRManagerChild());
-  if (!child->Open(aTransport, aOtherPid, XRE_GetIOMessageLoop(), ipc::ChildSide)) {
+  if (!aEndpoint.Bind(child, nullptr)) {
     NS_RUNTIMEABORT("Couldn't Open() Compositor channel.");
-    return nullptr;
+    return false;
   }
-
   sVRManagerChildSingleton = child;
-
-  return sVRManagerChildSingleton;
+  return true;
 }
 
 /*static*/ void
-VRManagerChild::StartUpSameProcess()
+VRManagerChild::InitSameProcess()
 {
-  NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!");
-  if (sVRManagerChildSingleton == nullptr) {
-    sVRManagerChildSingleton = new VRManagerChild();
-    sVRManagerParentSingleton = VRManagerParent::CreateSameProcess();
-    sVRManagerChildSingleton->Open(sVRManagerParentSingleton->GetIPCChannel(),
-                                   mozilla::layers::CompositorThreadHolder::Loop(),
-                                   mozilla::ipc::ChildSide);
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!sVRManagerChildSingleton);
+
+  sVRManagerChildSingleton = new VRManagerChild();
+  sVRManagerParentSingleton = VRManagerParent::CreateSameProcess();
+  sVRManagerChildSingleton->Open(sVRManagerParentSingleton->GetIPCChannel(),
+                                 mozilla::layers::CompositorThreadHolder::Loop(),
+                                 mozilla::ipc::ChildSide);
+}
+
+/* static */ void
+VRManagerChild::InitWithGPUProcess(Endpoint<PVRManagerChild>&& aEndpoint)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!sVRManagerChildSingleton);
+
+  sVRManagerChildSingleton = new VRManagerChild();
+  if (!aEndpoint.Bind(sVRManagerChildSingleton, nullptr)) {
+    NS_RUNTIMEABORT("Couldn't Open() Compositor channel.");
+    return;
   }
 }
 
 /*static*/ void
 VRManagerChild::ShutDown()
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (sVRManagerChildSingleton) {
--- a/gfx/vr/ipc/VRManagerChild.h
+++ b/gfx/vr/ipc/VRManagerChild.h
@@ -23,22 +23,23 @@ class VRDeviceProxy;
 class VRManagerChild : public PVRManagerChild
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(VRManagerChild)
 
   int GetInputFrameID();
   bool GetVRDevices(nsTArray<RefPtr<VRDeviceProxy> >& aDevices);
   bool RefreshVRDevicesWithCallback(dom::Navigator* aNavigator);
-  static VRManagerChild* StartUpInChildProcess(Transport* aTransport,
-                                               ProcessId aOtherProcess);
 
-  static void StartUpSameProcess();
+  static void InitSameProcess();
+  static void InitWithGPUProcess(Endpoint<PVRManagerChild>&& aEndpoint);
+  static bool InitForContent(Endpoint<PVRManagerChild>&& aEndpoint);
   static void ShutDown();
 
+  static bool IsCreated();
 
   static VRManagerChild* Get();
 
 protected:
   explicit VRManagerChild();
   ~VRManagerChild();
   void Destroy();
   static void DeferredDestroy(RefPtr<VRManagerChild> aVRManagerChild);
--- a/gfx/vr/ipc/VRManagerParent.cpp
+++ b/gfx/vr/ipc/VRManagerParent.cpp
@@ -12,19 +12,17 @@
 #include "mozilla/TimeStamp.h"               // for TimeStamp
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/unused.h"
 #include "VRManager.h"
 
 namespace mozilla {
 namespace gfx {
 
-VRManagerParent::VRManagerParent(MessageLoop* aLoop,
-                                 Transport* aTransport,
-                                 ProcessId aChildProcessId)
+VRManagerParent::VRManagerParent(ProcessId aChildProcessId)
 {
   MOZ_COUNT_CTOR(VRManagerParent);
   MOZ_ASSERT(NS_IsMainThread());
 
   SetOtherProcessId(aChildProcessId);
 }
 
 VRManagerParent::~VRManagerParent()
@@ -45,53 +43,68 @@ void VRManagerParent::RegisterWithManage
 
 void VRManagerParent::UnregisterFromManager()
 {
   VRManager* vm = VRManager::Get();
   vm->RemoveVRManagerParent(this);
   mVRManagerHolder = nullptr;
 }
 
-/*static*/ void
-VRManagerParent::ConnectVRManagerInParentProcess(VRManagerParent* aVRManager,
-                                ipc::Transport* aTransport,
-                                base::ProcessId aOtherPid)
+/* static */ bool
+VRManagerParent::CreateForContent(Endpoint<PVRManagerParent>&& aEndpoint)
 {
-  aVRManager->Open(aTransport, aOtherPid, XRE_GetIOMessageLoop(), ipc::ParentSide);
-  aVRManager->RegisterWithManager();
+  MessageLoop* loop = layers::CompositorThreadHolder::Loop();
+
+  RefPtr<VRManagerParent> vmp = new VRManagerParent(aEndpoint.OtherPid());
+  loop->PostTask(NewRunnableMethod<Endpoint<PVRManagerParent>&&>(
+    vmp, &VRManagerParent::Bind, Move(aEndpoint)));
+
+  return true;
 }
 
-/*static*/ VRManagerParent*
-VRManagerParent::CreateCrossProcess(Transport* aTransport, ProcessId aChildProcessId)
+void
+VRManagerParent::Bind(Endpoint<PVRManagerParent>&& aEndpoint)
 {
-  MessageLoop* loop = mozilla::layers::CompositorThreadHolder::Loop();
-  RefPtr<VRManagerParent> vmp = new VRManagerParent(loop, aTransport, aChildProcessId);
-  vmp->mSelfRef = vmp;
-  loop->PostTask(NewRunnableFunction(ConnectVRManagerInParentProcess,
-                                     vmp.get(), aTransport, aChildProcessId));
-  return vmp.get();
+  if (!aEndpoint.Bind(this, nullptr)) {
+    return;
+  }
+  mSelfRef = this;
+
+  RegisterWithManager();
 }
 
 /*static*/ void
 VRManagerParent::RegisterVRManagerInCompositorThread(VRManagerParent* aVRManager)
 {
   aVRManager->RegisterWithManager();
 }
 
 /*static*/ VRManagerParent*
 VRManagerParent::CreateSameProcess()
 {
   MessageLoop* loop = mozilla::layers::CompositorThreadHolder::Loop();
-  RefPtr<VRManagerParent> vmp = new VRManagerParent(loop, nullptr, base::GetCurrentProcId());
+  RefPtr<VRManagerParent> vmp = new VRManagerParent(base::GetCurrentProcId());
   vmp->mCompositorThreadHolder = layers::CompositorThreadHolder::GetSingleton();
   vmp->mSelfRef = vmp;
   loop->PostTask(NewRunnableFunction(RegisterVRManagerInCompositorThread, vmp.get()));
   return vmp.get();
 }
 
+bool
+VRManagerParent::CreateForGPUProcess(Endpoint<PVRManagerParent>&& aEndpoint)
+{
+  MessageLoop* loop = mozilla::layers::CompositorThreadHolder::Loop();
+
+  RefPtr<VRManagerParent> vmp = new VRManagerParent(aEndpoint.OtherPid());
+  vmp->mCompositorThreadHolder = layers::CompositorThreadHolder::GetSingleton();
+  loop->PostTask(NewRunnableMethod<Endpoint<PVRManagerParent>&&>(
+    vmp, &VRManagerParent::Bind, Move(aEndpoint)));
+  return true;
+}
+
 void
 VRManagerParent::DeferredDestroy()
 {
   mCompositorThreadHolder = nullptr;
   mSelfRef = nullptr;
 }
 
 void
@@ -101,29 +114,17 @@ VRManagerParent::ActorDestroy(ActorDestr
   MessageLoop::current()->PostTask(NewRunnableMethod(this, &VRManagerParent::DeferredDestroy));
 }
 
 mozilla::ipc::IToplevelProtocol*
 VRManagerParent::CloneToplevel(const InfallibleTArray<mozilla::ipc::ProtocolFdMapping>& aFds,
                                base::ProcessHandle aPeerProcess,
                                mozilla::ipc::ProtocolCloneContext* aCtx)
 {
-  for (unsigned int i = 0; i < aFds.Length(); i++) {
-    if (aFds[i].protocolId() == unsigned(GetProtocolId())) {
-      UniquePtr<Transport> transport =
-        OpenDescriptor(aFds[i].fd(), Transport::MODE_SERVER);
-      PVRManagerParent* vm = CreateCrossProcess(transport.get(), base::GetProcId(aPeerProcess));
-      vm->CloneManagees(this, aCtx);
-      vm->IToplevelProtocol::SetTransport(Move(transport));
-      // The reference to the compositor thread is held in OnChannelConnected().
-      // We need to do this for cloned actors, too.
-      vm->OnChannelConnected(base::GetProcId(aPeerProcess));
-      return vm;
-    }
-  }
+  MOZ_ASSERT_UNREACHABLE("Not supported");
   return nullptr;
 }
 
 void
 VRManagerParent::OnChannelConnected(int32_t aPid)
 {
   mCompositorThreadHolder = layers::CompositorThreadHolder::GetSingleton();
 }
--- a/gfx/vr/ipc/VRManagerParent.h
+++ b/gfx/vr/ipc/VRManagerParent.h
@@ -18,22 +18,21 @@ namespace mozilla {
 namespace gfx {
 
 class VRManager;
 
 class VRManagerParent final : public PVRManagerParent
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(VRManagerParent)
 public:
-  VRManagerParent(MessageLoop* aLoop, Transport* aTransport, ProcessId aChildProcessId);
+  explicit VRManagerParent(ProcessId aChildProcessId);
 
-  static VRManagerParent* CreateCrossProcess(Transport* aTransport,
-                                              ProcessId aOtherProcess);
   static VRManagerParent* CreateSameProcess();
-
+  static bool CreateForGPUProcess(Endpoint<PVRManagerParent>&& aEndpoint);
+  static bool CreateForContent(Endpoint<PVRManagerParent>&& aEndpoint);
 
   // Overriden from IToplevelProtocol
   ipc::IToplevelProtocol*
   CloneToplevel(const InfallibleTArray<ipc::ProtocolFdMapping>& aFds,
                 base::ProcessHandle aPeerProcess,
                 mozilla::ipc::ProtocolCloneContext* aCtx) override;
 
 protected:
@@ -47,24 +46,22 @@ protected:
   virtual bool RecvKeepSensorTracking(const uint32_t& aDeviceID) override;
   virtual bool RecvSetFOV(const uint32_t& aDeviceID,
                           const VRFieldOfView& aFOVLeft,
                           const VRFieldOfView& aFOVRight,
                           const double& zNear,
                           const double& zFar) override;
 
 private:
-
   void RegisterWithManager();
   void UnregisterFromManager();
 
+  void Bind(Endpoint<PVRManagerParent>&& aEndpoint);
+
   static void RegisterVRManagerInCompositorThread(VRManagerParent* aVRManager);
-  static void ConnectVRManagerInParentProcess(VRManagerParent* aVRManager,
-                                              ipc::Transport* aTransport,
-                                              base::ProcessId aOtherPid);
 
   void DeferredDestroy();
 
   // This keeps us alive until ActorDestroy(), at which point we do a
   // deferred destruction of ourselves.
   RefPtr<VRManagerParent> mSelfRef;
 
   // Keep the compositor thread alive, until we have destroyed ourselves.
--- a/image/Decoder.cpp
+++ b/image/Decoder.cpp
@@ -47,16 +47,17 @@ private:
 Decoder::Decoder(RasterImage* aImage)
   : mImageData(nullptr)
   , mImageDataLength(0)
   , mColormap(nullptr)
   , mColormapSize(0)
   , mImage(aImage)
   , mProgress(NoProgress)
   , mFrameCount(0)
+  , mLoopLength(FrameTimeout::Zero())
   , mDecoderFlags(DefaultDecoderFlags())
   , mSurfaceFlags(DefaultSurfaceFlags())
   , mInitialized(false)
   , mMetadataDecode(false)
   , mInFrame(false)
   , mReachedTerminalState(false)
   , mDecodeDone(false)
   , mError(false)
@@ -336,45 +337,43 @@ Decoder::AllocateFrameInternal(uint32_t 
       // Another decoder beat us to decoding this frame. We abort this decoder
       // rather than treat this as a real error.
       mDecodeAborted = true;
       ref->Abort();
       return RawAccessFrameRef();
     }
   }
 
-  nsIntRect refreshArea;
-
   if (aFrameNum == 1) {
     MOZ_ASSERT(aPreviousFrame, "Must provide a previous frame when animated");
     aPreviousFrame->SetRawAccessOnly();
 
     // If we dispose of the first frame by clearing it, then the first frame's
     // refresh area is all of itself.
     // RESTORE_PREVIOUS is invalid (assumed to be DISPOSE_CLEAR).
     AnimationData previousFrameData = aPreviousFrame->GetAnimationData();
     if (previousFrameData.mDisposalMethod == DisposalMethod::CLEAR ||
         previousFrameData.mDisposalMethod == DisposalMethod::CLEAR_ALL ||
         previousFrameData.mDisposalMethod == DisposalMethod::RESTORE_PREVIOUS) {
-      refreshArea = previousFrameData.mRect;
+      mFirstFrameRefreshArea = previousFrameData.mRect;
     }
   }
 
   if (aFrameNum > 0) {
     ref->SetRawAccessOnly();
 
     // Some GIFs are huge but only have a small area that they animate. We only
     // need to refresh that small area when frame 0 comes around again.
-    refreshArea.UnionRect(refreshArea, frame->GetRect());
+    mFirstFrameRefreshArea.UnionRect(mFirstFrameRefreshArea, frame->GetRect());
   }
 
   mFrameCount++;
 
   if (mImage) {
-    mImage->OnAddedFrame(mFrameCount, refreshArea);
+    mImage->OnAddedFrame(mFrameCount);
   }
 
   return ref;
 }
 
 /*
  * Hook stubs. Override these as necessary in decoder implementations.
  */
@@ -406,45 +405,47 @@ Decoder::PostSize(int32_t aWidth,
 
 void
 Decoder::PostHasTransparency()
 {
   mProgress |= FLAG_HAS_TRANSPARENCY;
 }
 
 void
-Decoder::PostIsAnimated(int32_t aFirstFrameTimeout)
+Decoder::PostIsAnimated(FrameTimeout aFirstFrameTimeout)
 {
   mProgress |= FLAG_IS_ANIMATED;
   mImageMetadata.SetHasAnimation();
   mImageMetadata.SetFirstFrameTimeout(aFirstFrameTimeout);
 }
 
 void
 Decoder::PostFrameStop(Opacity aFrameOpacity
                          /* = Opacity::SOME_TRANSPARENCY */,
                        DisposalMethod aDisposalMethod
                          /* = DisposalMethod::KEEP */,
-                       int32_t aTimeout         /* = 0 */,
+                       FrameTimeout aTimeout /* = FrameTimeout::Forever() */,
                        BlendMethod aBlendMethod /* = BlendMethod::OVER */,
                        const Maybe<nsIntRect>& aBlendRect /* = Nothing() */)
 {
   // We should be mid-frame
   MOZ_ASSERT(!IsMetadataDecode(), "Stopping frame during metadata decode");
   MOZ_ASSERT(mInFrame, "Stopping frame when we didn't start one");
   MOZ_ASSERT(mCurrentFrame, "Stopping frame when we don't have one");
 
   // Update our state
   mInFrame = false;
 
   mCurrentFrame->Finish(aFrameOpacity, aDisposalMethod, aTimeout,
                         aBlendMethod, aBlendRect);
 
   mProgress |= FLAG_FRAME_COMPLETE;
 
+  mLoopLength += aTimeout;
+
   // If we're not sending partial invalidations, then we send an invalidation
   // here when the first frame is complete.
   if (!ShouldSendPartialInvalidations() && mFrameCount == 1) {
     mInvalidRect.UnionRect(mInvalidRect,
                            gfx::IntRect(gfx::IntPoint(0, 0), GetSize()));
   }
 }
 
@@ -470,16 +471,25 @@ Decoder::PostDecodeDone(int32_t aLoopCou
 {
   MOZ_ASSERT(!IsMetadataDecode(), "Done with decoding in metadata decode");
   MOZ_ASSERT(!mInFrame, "Can't be done decoding if we're mid-frame!");
   MOZ_ASSERT(!mDecodeDone, "Decode already done!");
   mDecodeDone = true;
 
   mImageMetadata.SetLoopCount(aLoopCount);
 
+  // Some metadata that we track should take into account every frame in the
+  // image. If this is a first-frame-only decode, our accumulated loop length
+  // and first frame refresh area only includes the first frame, so it's not
+  // correct and we don't record it.
+  if (!IsFirstFrameDecode()) {
+    mImageMetadata.SetLoopLength(mLoopLength);
+    mImageMetadata.SetFirstFrameRefreshArea(mFirstFrameRefreshArea);
+  }
+
   mProgress |= FLAG_DECODE_COMPLETE;
 }
 
 void
 Decoder::PostError()
 {
   mError = true;
 
--- a/image/Decoder.h
+++ b/image/Decoder.h
@@ -329,26 +329,26 @@ protected:
   // until the entire frame has been decoded, decoders may take into account the
   // actual contents of the frame and give a more accurate result.
   void PostHasTransparency();
 
   // Called by decoders if they determine that the image is animated.
   //
   // @param aTimeout The time for which the first frame should be shown before
   //                 we advance to the next frame.
-  void PostIsAnimated(int32_t aFirstFrameTimeout);
+  void PostIsAnimated(FrameTimeout aFirstFrameTimeout);
 
   // Called by decoders when they end a frame. Informs the image, sends
   // notifications, and does internal book-keeping.
   // Specify whether this frame is opaque as an optimization.
   // For animated images, specify the disposal, blend method and timeout for
   // this frame.
   void PostFrameStop(Opacity aFrameOpacity = Opacity::SOME_TRANSPARENCY,
                      DisposalMethod aDisposalMethod = DisposalMethod::KEEP,
-                     int32_t aTimeout = 0,
+                     FrameTimeout aTimeout = FrameTimeout::Forever(),
                      BlendMethod aBlendMethod = BlendMethod::OVER,
                      const Maybe<nsIntRect>& aBlendRect = Nothing());
 
   /**
    * Called by the decoders when they have a region to invalidate. We may not
    * actually pass these invalidations on right away.
    *
    * @param aRect The invalidation rect in the coordinate system of the unscaled
@@ -422,16 +422,19 @@ private:
   RefPtr<RasterImage> mImage;
   Maybe<SourceBufferIterator> mIterator;
   RawAccessFrameRef mCurrentFrame;
   ImageMetadata mImageMetadata;
   nsIntRect mInvalidRect; // Tracks an invalidation region in the current frame.
   Progress mProgress;
 
   uint32_t mFrameCount; // Number of frames, including anything in-progress
+  FrameTimeout mLoopLength;  // Length of a single loop of this image.
+  gfx::IntRect mFirstFrameRefreshArea;  // The area of the image that needs to
+                                        // be invalidated when the animation loops.
 
   // Telemetry data for this decoder.
   TimeDuration mDecodeTime;
 
   DecoderFlags mDecoderFlags;
   SurfaceFlags mSurfaceFlags;
 
   bool mInitialized : 1;
--- a/image/FrameAnimator.cpp
+++ b/image/FrameAnimator.cpp
@@ -15,285 +15,270 @@
 #include "pixman.h"
 
 namespace mozilla {
 
 using namespace gfx;
 
 namespace image {
 
-int32_t
-FrameAnimator::GetSingleLoopTime() const
+///////////////////////////////////////////////////////////////////////////////
+// AnimationState implementation.
+///////////////////////////////////////////////////////////////////////////////
+
+void
+AnimationState::SetDoneDecoding(bool aDone)
+{
+  mDoneDecoding = aDone;
+}
+
+void
+AnimationState::ResetAnimation()
+{
+  mCurrentAnimationFrameIndex = 0;
+}
+
+void
+AnimationState::SetAnimationMode(uint16_t aAnimationMode)
+{
+  mAnimationMode = aAnimationMode;
+}
+
+void
+AnimationState::SetFirstFrameRefreshArea(const IntRect& aRefreshArea)
 {
-  // If we aren't done decoding, we don't know the image's full play time.
-  if (!mDoneDecoding) {
-    return -1;
+  mFirstFrameRefreshArea = aRefreshArea;
+}
+
+void
+AnimationState::InitAnimationFrameTimeIfNecessary()
+{
+  if (mCurrentAnimationFrameTime.IsNull()) {
+    mCurrentAnimationFrameTime = TimeStamp::Now();
   }
+}
 
-  // If we're not looping, a single loop time has no meaning
-  if (mAnimationMode != imgIContainer::kNormalAnimMode) {
-    return -1;
+void
+AnimationState::SetAnimationFrameTime(const TimeStamp& aTime)
+{
+  mCurrentAnimationFrameTime = aTime;
+}
+
+uint32_t
+AnimationState::GetCurrentAnimationFrameIndex() const
+{
+  return mCurrentAnimationFrameIndex;
+}
+
+FrameTimeout
+AnimationState::LoopLength() const
+{
+  // If we don't know the loop length yet, we have to treat it as infinite.
+  if (!mLoopLength) {
+    return FrameTimeout::Forever();
   }
 
-  int32_t looptime = 0;
-  for (uint32_t i = 0; i < mImage->GetNumFrames(); ++i) {
-    int32_t timeout = GetTimeoutForFrame(i);
-    if (timeout >= 0) {
-      looptime += static_cast<uint32_t>(timeout);
-    } else {
-      // If we have a frame that never times out, we're probably in an error
-      // case, but let's handle it more gracefully.
-      NS_WARNING("Negative frame timeout - how did this happen?");
-      return -1;
-    }
+  MOZ_ASSERT(mDoneDecoding, "We know the loop length but decoding isn't done?");
+
+  // If we're not looping, a single loop time has no meaning.
+  if (mAnimationMode != imgIContainer::kNormalAnimMode) {
+    return FrameTimeout::Forever();
   }
 
-  return looptime;
+  return *mLoopLength;
 }
 
+
+///////////////////////////////////////////////////////////////////////////////
+// FrameAnimator implementation.
+///////////////////////////////////////////////////////////////////////////////
+
 TimeStamp
-FrameAnimator::GetCurrentImgFrameEndTime() const
+FrameAnimator::GetCurrentImgFrameEndTime(AnimationState& aState) const
 {
-  TimeStamp currentFrameTime = mCurrentAnimationFrameTime;
-  int32_t timeout =
-    GetTimeoutForFrame(mCurrentAnimationFrameIndex);
+  TimeStamp currentFrameTime = aState.mCurrentAnimationFrameTime;
+  FrameTimeout timeout = GetTimeoutForFrame(aState.mCurrentAnimationFrameIndex);
 
-  if (timeout < 0) {
+  if (timeout == FrameTimeout::Forever()) {
     // We need to return a sentinel value in this case, because our logic
-    // doesn't work correctly if we have a negative timeout value. We use
-    // one year in the future as the sentinel because it works with the loop
-    // in RequestRefresh() below.
+    // doesn't work correctly if we have an infinitely long timeout. We use one
+    // year in the future as the sentinel because it works with the loop in
+    // RequestRefresh() below.
     // XXX(seth): It'd be preferable to make our logic work correctly with
-    // negative timeouts.
+    // infinitely long timeouts.
     return TimeStamp::NowLoRes() +
            TimeDuration::FromMilliseconds(31536000.0);
   }
 
   TimeDuration durationOfTimeout =
-    TimeDuration::FromMilliseconds(static_cast<double>(timeout));
+    TimeDuration::FromMilliseconds(double(timeout.AsMilliseconds()));
   TimeStamp currentFrameEndTime = currentFrameTime + durationOfTimeout;
 
   return currentFrameEndTime;
 }
 
-FrameAnimator::RefreshResult
-FrameAnimator::AdvanceFrame(TimeStamp aTime)
+RefreshResult
+FrameAnimator::AdvanceFrame(AnimationState& aState, TimeStamp aTime)
 {
   NS_ASSERTION(aTime <= TimeStamp::Now(),
                "Given time appears to be in the future");
   PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS);
 
   RefreshResult ret;
 
   // Determine what the next frame is, taking into account looping.
-  uint32_t currentFrameIndex = mCurrentAnimationFrameIndex;
+  uint32_t currentFrameIndex = aState.mCurrentAnimationFrameIndex;
   uint32_t nextFrameIndex = currentFrameIndex + 1;
 
   if (mImage->GetNumFrames() == nextFrameIndex) {
     // We can only accurately determine if we are at the end of the loop if we are
     // done decoding, otherwise we don't know how many frames there will be.
-    if (!mDoneDecoding) {
+    if (!aState.mDoneDecoding) {
       // We've already advanced to the last decoded frame, nothing more we can do.
       // We're blocked by network/decoding from displaying the animation at the
       // rate specified, so that means the frame we are displaying (the latest
       // available) is the frame we want to be displaying at this time. So we
       // update the current animation time. If we didn't update the current
       // animation time then it could lag behind, which would indicate that we
       // are behind in the animation and should try to catch up. When we are
       // done decoding (and thus can loop around back to the start of the
       // animation) we would then jump to a random point in the animation to
       // try to catch up. But we were never behind in the animation.
-      mCurrentAnimationFrameTime = aTime;
+      aState.mCurrentAnimationFrameTime = aTime;
       return ret;
     }
 
     // End of an animation loop...
 
     // If we are not looping forever, initialize the loop counter
-    if (mLoopRemainingCount < 0 && LoopCount() >= 0) {
-      mLoopRemainingCount = LoopCount();
+    if (aState.mLoopRemainingCount < 0 && aState.LoopCount() >= 0) {
+      aState.mLoopRemainingCount = aState.LoopCount();
     }
 
     // If animation mode is "loop once", or we're at end of loop counter,
     // it's time to stop animating.
-    if (mAnimationMode == imgIContainer::kLoopOnceAnimMode ||
-        mLoopRemainingCount == 0) {
-      ret.animationFinished = true;
+    if (aState.mAnimationMode == imgIContainer::kLoopOnceAnimMode ||
+        aState.mLoopRemainingCount == 0) {
+      ret.mAnimationFinished = true;
     }
 
     nextFrameIndex = 0;
 
-    if (mLoopRemainingCount > 0) {
-      mLoopRemainingCount--;
+    if (aState.mLoopRemainingCount > 0) {
+      aState.mLoopRemainingCount--;
     }
 
     // If we're done, exit early.
-    if (ret.animationFinished) {
+    if (ret.mAnimationFinished) {
       return ret;
     }
   }
 
   // There can be frames in the surface cache with index >= mImage->GetNumFrames()
   // that GetRawFrame can access because the decoding thread has decoded them, but
   // RasterImage hasn't acknowledged those frames yet. We don't want to go past
   // what RasterImage knows about so that we stay in sync with RasterImage. The code
   // above should obey this, the MOZ_ASSERT records this invariant.
   MOZ_ASSERT(nextFrameIndex < mImage->GetNumFrames());
   RawAccessFrameRef nextFrame = GetRawFrame(nextFrameIndex);
 
   // If we're done decoding, we know we've got everything we're going to get.
   // If we aren't, we only display fully-downloaded frames; everything else
   // gets delayed.
-  bool canDisplay = mDoneDecoding ||
+  bool canDisplay = aState.mDoneDecoding ||
                     (nextFrame && nextFrame->IsFinished());
 
   if (!canDisplay) {
     // Uh oh, the frame we want to show is currently being decoded (partial)
     // Wait until the next refresh driver tick and try again
     return ret;
   }
 
-  // Bad data
-  if (GetTimeoutForFrame(nextFrameIndex) < 0) {
-    ret.animationFinished = true;
-    ret.error = true;
+  if (GetTimeoutForFrame(nextFrameIndex) == FrameTimeout::Forever()) {
+    ret.mAnimationFinished = true;
   }
 
   if (nextFrameIndex == 0) {
-    ret.dirtyRect = mFirstFrameRefreshArea;
+    ret.mDirtyRect = aState.FirstFrameRefreshArea();
   } else {
     MOZ_ASSERT(nextFrameIndex == currentFrameIndex + 1);
 
     // Change frame
-    if (!DoBlend(&ret.dirtyRect, currentFrameIndex, nextFrameIndex)) {
+    if (!DoBlend(&ret.mDirtyRect, currentFrameIndex, nextFrameIndex)) {
       // something went wrong, move on to next
       NS_WARNING("FrameAnimator::AdvanceFrame(): Compositing of frame failed");
       nextFrame->SetCompositingFailed(true);
-      mCurrentAnimationFrameTime = GetCurrentImgFrameEndTime();
-      mCurrentAnimationFrameIndex = nextFrameIndex;
+      aState.mCurrentAnimationFrameTime = GetCurrentImgFrameEndTime(aState);
+      aState.mCurrentAnimationFrameIndex = nextFrameIndex;
 
-      ret.error = true;
       return ret;
     }
 
     nextFrame->SetCompositingFailed(false);
   }
 
-  mCurrentAnimationFrameTime = GetCurrentImgFrameEndTime();
+  aState.mCurrentAnimationFrameTime = GetCurrentImgFrameEndTime(aState);
 
   // If we can get closer to the current time by a multiple of the image's loop
-  // time, we should. We need to be done decoding in order to know the full loop
-  // time though!
-  int32_t loopTime = GetSingleLoopTime();
-  if (loopTime > 0) {
-    // We shouldn't be advancing by a whole loop unless we are decoded and know
-    // what a full loop actually is. GetSingleLoopTime should return -1 so this
-    // never happens.
-    MOZ_ASSERT(mDoneDecoding);
-    TimeDuration delay = aTime - mCurrentAnimationFrameTime;
-    if (delay.ToMilliseconds() > loopTime) {
+  // time, we should. We can only do this if we're done decoding; otherwise, we
+  // don't know the full loop length, and LoopLength() will have to return
+  // FrameTimeout::Forever().
+  FrameTimeout loopTime = aState.LoopLength();
+  if (loopTime != FrameTimeout::Forever()) {
+    TimeDuration delay = aTime - aState.mCurrentAnimationFrameTime;
+    if (delay.ToMilliseconds() > loopTime.AsMilliseconds()) {
       // Explicitly use integer division to get the floor of the number of
       // loops.
-      uint64_t loops = static_cast<uint64_t>(delay.ToMilliseconds()) / loopTime;
-      mCurrentAnimationFrameTime +=
-        TimeDuration::FromMilliseconds(loops * loopTime);
+      uint64_t loops = static_cast<uint64_t>(delay.ToMilliseconds())
+                     / loopTime.AsMilliseconds();
+      aState.mCurrentAnimationFrameTime +=
+        TimeDuration::FromMilliseconds(loops * loopTime.AsMilliseconds());
     }
   }
 
   // Set currentAnimationFrameIndex at the last possible moment
-  mCurrentAnimationFrameIndex = nextFrameIndex;
+  aState.mCurrentAnimationFrameIndex = nextFrameIndex;
 
   // If we're here, we successfully advanced the frame.
-  ret.frameAdvanced = true;
+  ret.mFrameAdvanced = true;
 
   return ret;
 }
 
-FrameAnimator::RefreshResult
-FrameAnimator::RequestRefresh(const TimeStamp& aTime)
+RefreshResult
+FrameAnimator::RequestRefresh(AnimationState& aState, const TimeStamp& aTime)
 {
   // only advance the frame if the current time is greater than or
   // equal to the current frame's end time.
-  TimeStamp currentFrameEndTime = GetCurrentImgFrameEndTime();
+  TimeStamp currentFrameEndTime = GetCurrentImgFrameEndTime(aState);
 
   // By default, an empty RefreshResult.
   RefreshResult ret;
 
   while (currentFrameEndTime <= aTime) {
     TimeStamp oldFrameEndTime = currentFrameEndTime;
 
-    RefreshResult frameRes = AdvanceFrame(aTime);
+    RefreshResult frameRes = AdvanceFrame(aState, aTime);
 
     // Accumulate our result for returning to callers.
     ret.Accumulate(frameRes);
 
-    currentFrameEndTime = GetCurrentImgFrameEndTime();
+    currentFrameEndTime = GetCurrentImgFrameEndTime(aState);
 
-    // if we didn't advance a frame, and our frame end time didn't change,
+    // If we didn't advance a frame, and our frame end time didn't change,
     // then we need to break out of this loop & wait for the frame(s)
-    // to finish downloading
-    if (!frameRes.frameAdvanced && (currentFrameEndTime == oldFrameEndTime)) {
+    // to finish downloading.
+    if (!frameRes.mFrameAdvanced && (currentFrameEndTime == oldFrameEndTime)) {
       break;
     }
   }
 
   return ret;
 }
 
-void
-FrameAnimator::ResetAnimation()
-{
-  mCurrentAnimationFrameIndex = 0;
-  mLastCompositedFrameIndex = -1;
-}
-
-void
-FrameAnimator::SetDoneDecoding(bool aDone)
-{
-  mDoneDecoding = aDone;
-}
-
-void
-FrameAnimator::SetAnimationMode(uint16_t aAnimationMode)
-{
-  mAnimationMode = aAnimationMode;
-}
-
-void
-FrameAnimator::InitAnimationFrameTimeIfNecessary()
-{
-  if (mCurrentAnimationFrameTime.IsNull()) {
-    mCurrentAnimationFrameTime = TimeStamp::Now();
-  }
-}
-
-void
-FrameAnimator::SetAnimationFrameTime(const TimeStamp& aTime)
-{
-  mCurrentAnimationFrameTime = aTime;
-}
-
-void
-FrameAnimator::UnionFirstFrameRefreshArea(const nsIntRect& aRect)
-{
-  mFirstFrameRefreshArea.UnionRect(mFirstFrameRefreshArea, aRect);
-}
-
-uint32_t
-FrameAnimator::GetCurrentAnimationFrameIndex() const
-{
-  return mCurrentAnimationFrameIndex;
-}
-
-nsIntRect
-FrameAnimator::GetFirstFrameRefreshArea() const
-{
-  return mFirstFrameRefreshArea;
-}
-
 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)) {
     return LookupResult(mCompositingFrame->DrawableRef(), MatchType::EXACT);
@@ -306,50 +291,27 @@ FrameAnimator::GetCompositedFrame(uint32
                          RasterSurfaceKey(mSize,
                                           DefaultSurfaceFlags(),
                                           aFrameNum));
   MOZ_ASSERT(!result || !result.DrawableRef()->GetIsPaletted(),
              "About to return a paletted frame");
   return result;
 }
 
-int32_t
+FrameTimeout
 FrameAnimator::GetTimeoutForFrame(uint32_t aFrameNum) const
 {
-  int32_t rawTimeout = 0;
-
   RawAccessFrameRef frame = GetRawFrame(aFrameNum);
   if (frame) {
     AnimationData data = frame->GetAnimationData();
-    rawTimeout = data.mRawTimeout;
-  } else if (aFrameNum == 0) {
-    rawTimeout = mFirstFrameTimeout;
-  } else {
-    NS_WARNING("No frame; called GetTimeoutForFrame too early?");
-    return 100;
+    return data.mTimeout;
   }
 
-  // Ensure a minimal time between updates so we don't throttle the UI thread.
-  // consider 0 == unspecified and make it fast but not too fast.  Unless we
-  // have a single loop GIF. See bug 890743, bug 125137, bug 139677, and bug
-  // 207059. The behavior of recent IE and Opera versions seems to be:
-  // IE 6/Win:
-  //   10 - 50ms go 100ms
-  //   >50ms go correct speed
-  // Opera 7 final/Win:
-  //   10ms goes 100ms
-  //   >10ms go correct speed
-  // It seems that there are broken tools out there that set a 0ms or 10ms
-  // timeout when they really want a "default" one.  So munge values in that
-  // range.
-  if (rawTimeout >= 0 && rawTimeout <= 10) {
-    return 100;
-  }
-
-  return rawTimeout;
+  NS_WARNING("No frame; called GetTimeoutForFrame too early?");
+  return FrameTimeout::FromRawMilliseconds(100);
 }
 
 static void
 DoCollectSizeOfCompositingSurfaces(const RawAccessFrameRef& aSurface,
                                    SurfaceMemoryCounterType aType,
                                    nsTArray<SurfaceMemoryCounter>& aCounters,
                                    MallocSizeOf aMallocSizeOf)
 {
@@ -402,17 +364,17 @@ FrameAnimator::GetRawFrame(uint32_t aFra
   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(nsIntRect* aDirtyRect,
+FrameAnimator::DoBlend(IntRect* aDirtyRect,
                        uint32_t aPrevFrameIndex,
                        uint32_t aNextFrameIndex)
 {
   RawAccessFrameRef prevFrame = GetRawFrame(aPrevFrameIndex);
   RawAccessFrameRef nextFrame = GetRawFrame(aNextFrameIndex);
 
   MOZ_ASSERT(prevFrame && nextFrame, "Should have frames here");
 
@@ -683,73 +645,73 @@ FrameAnimator::DoBlend(nsIntRect* aDirty
   mLastCompositedFrameIndex = int32_t(aNextFrameIndex);
 
   return true;
 }
 
 //******************************************************************************
 // Fill aFrame with black. Does also clears the mask.
 void
-FrameAnimator::ClearFrame(uint8_t* aFrameData, const nsIntRect& aFrameRect)
+FrameAnimator::ClearFrame(uint8_t* aFrameData, const IntRect& aFrameRect)
 {
   if (!aFrameData) {
     return;
   }
 
   memset(aFrameData, 0, aFrameRect.width * aFrameRect.height * 4);
 }
 
 //******************************************************************************
 void
-FrameAnimator::ClearFrame(uint8_t* aFrameData, const nsIntRect& aFrameRect,
-                          const nsIntRect& aRectToClear)
+FrameAnimator::ClearFrame(uint8_t* aFrameData, const IntRect& aFrameRect,
+                          const IntRect& aRectToClear)
 {
   if (!aFrameData || aFrameRect.width <= 0 || aFrameRect.height <= 0 ||
       aRectToClear.width <= 0 || aRectToClear.height <= 0) {
     return;
   }
 
-  nsIntRect toClear = aFrameRect.Intersect(aRectToClear);
+  IntRect toClear = aFrameRect.Intersect(aRectToClear);
   if (toClear.IsEmpty()) {
     return;
   }
 
   uint32_t bytesPerRow = aFrameRect.width * 4;
   for (int row = toClear.y; row < toClear.y + toClear.height; ++row) {
     memset(aFrameData + toClear.x * 4 + row * bytesPerRow, 0,
            toClear.width * 4);
   }
 }
 
 //******************************************************************************
 // Whether we succeed or fail will not cause a crash, and there's not much
 // we can do about a failure, so there we don't return a nsresult
 bool
 FrameAnimator::CopyFrameImage(const uint8_t* aDataSrc,
-                              const nsIntRect& aRectSrc,
+                              const IntRect& aRectSrc,
                               uint8_t* aDataDest,
-                              const nsIntRect& aRectDest)
+                              const IntRect& aRectDest)
 {
   uint32_t dataLengthSrc = aRectSrc.width * aRectSrc.height * 4;
   uint32_t dataLengthDest = aRectDest.width * aRectDest.height * 4;
 
   if (!aDataDest || !aDataSrc || dataLengthSrc != dataLengthDest) {
     return false;
   }
 
   memcpy(aDataDest, aDataSrc, dataLengthDest);
 
   return true;
 }
 
 nsresult
-FrameAnimator::DrawFrameTo(const uint8_t* aSrcData, const nsIntRect& aSrcRect,
+FrameAnimator::DrawFrameTo(const uint8_t* aSrcData, const IntRect& aSrcRect,
                            uint32_t aSrcPaletteLength, bool aSrcHasAlpha,
-                           uint8_t* aDstPixels, const nsIntRect& aDstRect,
-                           BlendMethod aBlendMethod, const Maybe<nsIntRect>& aBlendRect)
+                           uint8_t* aDstPixels, const IntRect& aDstRect,
+                           BlendMethod aBlendMethod, const Maybe<IntRect>& aBlendRect)
 {
   NS_ENSURE_ARG_POINTER(aSrcData);
   NS_ENSURE_ARG_POINTER(aDstPixels);
 
   // According to both AGIF and APNG specs, offsets are unsigned
   if (aSrcRect.x < 0 || aSrcRect.y < 0) {
     NS_WARNING("FrameAnimator::DrawFrameTo: negative offsets not allowed");
     return NS_ERROR_FAILURE;
--- a/image/FrameAnimator.h
+++ b/image/FrameAnimator.h
@@ -5,94 +5,38 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_image_FrameAnimator_h
 #define mozilla_image_FrameAnimator_h
 
 #include "mozilla/Maybe.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/TimeStamp.h"
-#include "gfx2DGlue.h"
 #include "gfxTypes.h"
 #include "imgFrame.h"
 #include "nsCOMPtr.h"
 #include "nsRect.h"
 #include "SurfaceCache.h"
 
 namespace mozilla {
 namespace image {
 
 class RasterImage;
 
-class FrameAnimator
+class AnimationState
 {
 public:
-  FrameAnimator(RasterImage* aImage,
-                gfx::IntSize aSize,
-                uint16_t aAnimationMode)
-    : mImage(aImage)
-    , mSize(aSize)
-    , mCurrentAnimationFrameIndex(0)
+  explicit AnimationState(uint16_t aAnimationMode)
+    : mCurrentAnimationFrameIndex(0)
     , mLoopRemainingCount(-1)
-    , mLastCompositedFrameIndex(-1)
     , mLoopCount(-1)
-    , mFirstFrameTimeout(0)
+    , mFirstFrameTimeout(FrameTimeout::FromRawMilliseconds(0))
     , mAnimationMode(aAnimationMode)
     , mDoneDecoding(false)
-  {
-     MOZ_COUNT_CTOR(FrameAnimator);
-  }
-
-  ~FrameAnimator()
-  {
-    MOZ_COUNT_DTOR(FrameAnimator);
-  }
-
-  /**
-   * Return value from RequestRefresh. Tells callers what happened in that call
-   * to RequestRefresh.
-   */
-  struct RefreshResult
-  {
-    // The dirty rectangle to be re-drawn after this RequestRefresh().
-    nsIntRect dirtyRect;
-
-    // Whether any frame changed, and hence the dirty rect was set.
-    bool frameAdvanced : 1;
-
-    // Whether the animation has finished playing.
-    bool animationFinished : 1;
-
-    // Whether an error has occurred when trying to advance a frame. Note that
-    // errors do not, on their own, end the animation.
-    bool error : 1;
-
-    RefreshResult()
-      : frameAdvanced(false)
-      , animationFinished(false)
-      , error(false)
-    { }
-
-    void Accumulate(const RefreshResult& other)
-    {
-      frameAdvanced = frameAdvanced || other.frameAdvanced;
-      animationFinished = animationFinished || other.animationFinished;
-      error = error || other.error;
-      dirtyRect = dirtyRect.Union(other.dirtyRect);
-    }
-  };
-
-  /**
-   * Re-evaluate what frame we're supposed to be on, and do whatever blending
-   * is necessary to get us to that frame.
-   *
-   * Returns the result of that blending, including whether the current frame
-   * changed and what the resulting dirty rectangle is.
-   */
-  RefreshResult RequestRefresh(const TimeStamp& aTime);
+  { }
 
   /**
    * Call when this image is finished decoding so we know that there aren't any
    * more frames coming.
    */
   void SetDoneDecoding(bool aDone);
 
   /**
@@ -104,20 +48,21 @@ public:
   /**
    * The animation mode of the image.
    *
    * Constants defined in imgIContainer.idl.
    */
   void SetAnimationMode(uint16_t aAnimationMode);
 
   /**
-   * Union the area to refresh when we loop around to the first frame with this
-   * rect.
+   * Get or set the area of the image to invalidate when we loop around to the
+   * first frame.
    */
-  void UnionFirstFrameRefreshArea(const nsIntRect& aRect);
+  void SetFirstFrameRefreshArea(const gfx::IntRect& aRefreshArea);
+  gfx::IntRect FirstFrameRefreshArea() const { return mFirstFrameRefreshArea; }
 
   /**
    * If the animation frame time has not yet been set, set it to
    * TimeStamp::Now().
    */
   void InitAnimationFrameTimeIfNecessary();
 
   /**
@@ -125,112 +70,191 @@ public:
    */
   void SetAnimationFrameTime(const TimeStamp& aTime);
 
   /**
    * The current frame we're on, from 0 to (numFrames - 1).
    */
   uint32_t GetCurrentAnimationFrameIndex() const;
 
+  /*
+   * Set number of times to loop the image.
+   * @note -1 means loop forever.
+   */
+  void SetLoopCount(int32_t aLoopCount) { mLoopCount = aLoopCount; }
+  int32_t LoopCount() const { return mLoopCount; }
+
+  /// Set the @aLength of a single loop through this image.
+  void SetLoopLength(FrameTimeout aLength) { mLoopLength = Some(aLength); }
+
   /**
-   * Get the area we refresh when we loop around to the first frame.
+   * @return the length of a single loop of this image. If this image is not
+   * finished decoding, is not animated, or it is animated but does not loop,
+   * returns FrameTimeout::Forever().
+   */
+  FrameTimeout LoopLength() const;
+
+  /*
+   * Get or set the timeout for the first frame. This is used to allow animation
+   * scheduling even before a full decode runs for this image.
    */
-  nsIntRect GetFirstFrameRefreshArea() const;
+  void SetFirstFrameTimeout(FrameTimeout aTimeout) { mFirstFrameTimeout = aTimeout; }
+  FrameTimeout FirstFrameTimeout() const { return mFirstFrameTimeout; }
+
+private:
+  friend class FrameAnimator;
+
+  //! Area of the first frame that needs to be redrawn on subsequent loops.
+  gfx::IntRect mFirstFrameRefreshArea;
+
+  //! the time that the animation advanced to the current frame
+  TimeStamp mCurrentAnimationFrameTime;
+
+  //! The current frame index we're on. 0 to (numFrames - 1).
+  uint32_t mCurrentAnimationFrameIndex;
+
+  //! number of loops remaining before animation stops (-1 no stop)
+  int32_t mLoopRemainingCount;
+
+  //! The total number of loops for the image.
+  int32_t mLoopCount;
+
+  //! The length of a single loop through this image.
+  Maybe<FrameTimeout> mLoopLength;
+
+  //! The timeout for the first frame of this image.
+  FrameTimeout mFirstFrameTimeout;
+
+  //! The animation mode of this image. Constants defined in imgIContainer.
+  uint16_t mAnimationMode;
+
+  //! Whether this image is done being decoded.
+  bool mDoneDecoding;
+};
+
+/**
+ * RefreshResult is used to let callers know how the state of the animation
+ * changed during a call to FrameAnimator::RequestRefresh().
+ */
+struct RefreshResult
+{
+  RefreshResult()
+    : mFrameAdvanced(false)
+    , mAnimationFinished(false)
+  { }
+
+  /// Merges another RefreshResult's changes into this RefreshResult.
+  void Accumulate(const RefreshResult& aOther)
+  {
+    mFrameAdvanced = mFrameAdvanced || aOther.mFrameAdvanced;
+    mAnimationFinished = mAnimationFinished || aOther.mAnimationFinished;
+    mDirtyRect = mDirtyRect.Union(aOther.mDirtyRect);
+  }
+
+  // The region of the image that has changed.
+  gfx::IntRect mDirtyRect;
+
+  // If true, we changed frames at least once. Note that, due to looping, we
+  // could still have ended up on the same frame!
+  bool mFrameAdvanced : 1;
+
+  // Whether the animation has finished playing.
+  bool mAnimationFinished : 1;
+};
+
+class FrameAnimator
+{
+public:
+  FrameAnimator(RasterImage* aImage, const gfx::IntSize& aSize)
+    : mImage(aImage)
+    , mSize(aSize)
+    , mLastCompositedFrameIndex(-1)
+  {
+     MOZ_COUNT_CTOR(FrameAnimator);
+  }
+
+  ~FrameAnimator()
+  {
+    MOZ_COUNT_DTOR(FrameAnimator);
+  }
+
+  /**
+   * Re-evaluate what frame we're supposed to be on, and do whatever blending
+   * is necessary to get us to that frame.
+   *
+   * Returns the result of that blending, including whether the current frame
+   * changed and what the resulting dirty rectangle is.
+   */
+  RefreshResult RequestRefresh(AnimationState& aState, const TimeStamp& aTime);
 
   /**
    * If we have a composited frame for @aFrameNum, returns it. Otherwise,
    * returns an empty LookupResult. It is an error to call this method with
    * aFrameNum == 0, because the first frame is never composited.
    */
   LookupResult GetCompositedFrame(uint32_t aFrameNum);
 
-  /*
-   * Returns the frame's adjusted timeout. If the animation loops and the
-   * timeout falls in between a certain range then the timeout is adjusted so
-   * that it's never 0. If the animation does not loop then no adjustments are
-   * made.
-   */
-  int32_t GetTimeoutForFrame(uint32_t aFrameNum) const;
-
-  /*
-   * Set number of times to loop the image.
-   * @note -1 means loop forever.
-   */
-  void SetLoopCount(int32_t aLoopCount) { mLoopCount = aLoopCount; }
-  int32_t LoopCount() const { return mLoopCount; }
-
-  /*
-   * Set the timeout for the first frame. This is used to allow animation
-   * scheduling even before a full decode runs for this image.
-   */
-  void SetFirstFrameTimeout(int32_t aTimeout) { mFirstFrameTimeout = aTimeout; }
-
   /**
    * Collect an accounting of the memory occupied by the compositing surfaces we
    * use during animation playback. All of the actual animation frames are
    * stored in the SurfaceCache, so we don't need to report them here.
    */
   void CollectSizeOfCompositingSurfaces(nsTArray<SurfaceMemoryCounter>& aCounters,
                                         MallocSizeOf aMallocSizeOf) const;
 
 private: // methods
   /**
-   * Gets the length of a single loop of this image, in milliseconds.
-   *
-   * If this image is not finished decoding, is not animated, or it is animated
-   * but does not loop, returns -1. Can return 0 in the case of an animated
-   * image that has a 0ms delay between its frames and does not loop.
-   */
-  int32_t GetSingleLoopTime() const;
-
-  /**
    * Advances the animation. Typically, this will advance a single frame, but it
    * may advance multiple frames. This may happen if we have infrequently
    * "ticking" refresh drivers (e.g. in background tabs), or extremely short-
    * lived animation frames.
    *
    * @param aTime the time that the animation should advance to. This will
    *              typically be <= TimeStamp::Now().
    *
    * @returns a RefreshResult that shows whether the frame was successfully
    *          advanced, and its resulting dirty rect.
    */
-  RefreshResult AdvanceFrame(TimeStamp aTime);
+  RefreshResult AdvanceFrame(AnimationState& aState, TimeStamp aTime);
+
+  /**
+   * Get the @aIndex-th frame in the frame index, ignoring results of blending.
+   */
+  RawAccessFrameRef GetRawFrame(uint32_t aFrameNum) const;
+
+  /// @return the given frame's timeout.
+  FrameTimeout GetTimeoutForFrame(uint32_t aFrameNum) const;
 
   /**
    * Get the time the frame we're currently displaying is supposed to end.
    *
    * In the error case, returns an "infinity" timestamp.
    */
-  TimeStamp GetCurrentImgFrameEndTime() const;
-
-  bool DoBlend(nsIntRect* aDirtyRect, uint32_t aPrevFrameIndex,
-               uint32_t aNextFrameIndex);
+  TimeStamp GetCurrentImgFrameEndTime(AnimationState& aState) const;
 
-  /**
-   * Get the @aIndex-th frame in the frame index, ignoring results of blending.
-   */
-  RawAccessFrameRef GetRawFrame(uint32_t aFrameNum) const;
+  bool DoBlend(gfx::IntRect* aDirtyRect,
+               uint32_t aPrevFrameIndex,
+               uint32_t aNextFrameIndex);
 
   /** Clears an area of <aFrame> with transparent black.
    *
    * @param aFrameData Target Frame data
    * @param aFrameRect The rectangle of the data pointed ot by aFrameData
    *
    * @note Does also clears the transparency mask
    */
-  static void ClearFrame(uint8_t* aFrameData, const nsIntRect& aFrameRect);
+  static void ClearFrame(uint8_t* aFrameData, const gfx::IntRect& aFrameRect);
 
   //! @overload
-  static void ClearFrame(uint8_t* aFrameData, const nsIntRect& aFrameRect,
-                         const nsIntRect& aRectToClear);
+  static void ClearFrame(uint8_t* aFrameData, const gfx::IntRect& aFrameRect,
+                         const gfx::IntRect& aRectToClear);
 
   //! Copy one frame's image and mask into another
-  static bool CopyFrameImage(const uint8_t* aDataSrc, const nsIntRect& aRectSrc,
-                             uint8_t* aDataDest, const nsIntRect& aRectDest);
+  static bool CopyFrameImage(const uint8_t* aDataSrc, const gfx::IntRect& aRectSrc,
+                             uint8_t* aDataDest, const gfx::IntRect& aRectDest);
 
   /**
    * Draws one frame's image to into another, at the position specified by
    * aSrcRect.
    *
    * @aSrcData the raw data of the current frame being drawn
    * @aSrcRect the size of the source frame, and the position of that frame in
    *           the composition frame
@@ -239,21 +263,21 @@ private: // methods
    * @aSrcHasAlpha whether the source data represents an image with alpha
    * @aDstPixels the raw data of the composition frame where the current frame
    *             is drawn into (32-bit ARGB)
    * @aDstRect the size of the composition frame
    * @aBlendMethod the blend method for how to blend src on the composition
    * frame.
    */
   static nsresult DrawFrameTo(const uint8_t* aSrcData,
-                              const nsIntRect& aSrcRect,
+                              const gfx::IntRect& aSrcRect,
                               uint32_t aSrcPaletteLength, bool aSrcHasAlpha,
-                              uint8_t* aDstPixels, const nsIntRect& aDstRect,
+                              uint8_t* aDstPixels, const gfx::IntRect& aDstRect,
                               BlendMethod aBlendMethod,
-                              const Maybe<nsIntRect>& aBlendRect);
+                              const Maybe<gfx::IntRect>& aBlendRect);
 
 private: // data
   //! A weak pointer to our owning image.
   RasterImage* mImage;
 
   //! The intrinsic size of the image.
   gfx::IntSize mSize;
 
@@ -270,40 +294,16 @@ private: // data
   /** the previous composited frame, for DISPOSE_RESTORE_PREVIOUS
    *
    * The Previous Frame (all frames composited up to the current) needs to be
    * stored in cases where the image specifies it wants the last frame back
    * when it's done with the current frame.
    */
   RawAccessFrameRef mCompositingPrevFrame;
 
-  //! Area of the first frame that needs to be redrawn on subsequent loops.
-  nsIntRect mFirstFrameRefreshArea;
-
-  //! the time that the animation advanced to the current frame
-  TimeStamp mCurrentAnimationFrameTime;
-
-  //! The current frame index we're on. 0 to (numFrames - 1).
-  uint32_t mCurrentAnimationFrameIndex;
-
-  //! number of loops remaining before animation stops (-1 no stop)
-  int32_t mLoopRemainingCount;
-
   //! Track the last composited frame for Optimizations (See DoComposite code)
   int32_t mLastCompositedFrameIndex;
-
-  //! The total number of loops for the image.
-  int32_t mLoopCount;
-
-  //! The timeout for the first frame of this image.
-  int32_t mFirstFrameTimeout;
-
-  //! The animation mode of this image. Constants defined in imgIContainer.
-  uint16_t mAnimationMode;
-
-  //! Whether this image is done being decoded.
-  bool mDoneDecoding;
 };
 
 } // namespace image
 } // namespace mozilla
 
 #endif // mozilla_image_FrameAnimator_h
--- a/image/ImageMetadata.h
+++ b/image/ImageMetadata.h
@@ -18,35 +18,46 @@ namespace image {
 class RasterImage;
 
 // The metadata about an image that decoders accumulate as they decode.
 class ImageMetadata
 {
 public:
   ImageMetadata()
     : mLoopCount(-1)
-    , mFirstFrameTimeout(0)
+    , mFirstFrameTimeout(FrameTimeout::Forever())
     , mHasAnimation(false)
   { }
 
   void SetHotspot(uint16_t aHotspotX, uint16_t aHotspotY)
   {
     mHotspot = Some(gfx::IntPoint(aHotspotX, aHotspotY));
   }
   gfx::IntPoint GetHotspot() const { return *mHotspot; }
   bool HasHotspot() const { return mHotspot.isSome(); }
 
   void SetLoopCount(int32_t loopcount)
   {
     mLoopCount = loopcount;
   }
   int32_t GetLoopCount() const { return mLoopCount; }
 
-  void SetFirstFrameTimeout(int32_t aTimeout) { mFirstFrameTimeout = aTimeout; }
-  int32_t GetFirstFrameTimeout() const { return mFirstFrameTimeout; }
+  void SetLoopLength(FrameTimeout aLength) { mLoopLength = Some(aLength); }
+  FrameTimeout GetLoopLength() const { return *mLoopLength; }
+  bool HasLoopLength() const { return mLoopLength.isSome(); }
+
+  void SetFirstFrameTimeout(FrameTimeout aTimeout) { mFirstFrameTimeout = aTimeout; }
+  FrameTimeout GetFirstFrameTimeout() const { return mFirstFrameTimeout; }
+
+  void SetFirstFrameRefreshArea(const gfx::IntRect& aRefreshArea)
+  {
+    mFirstFrameRefreshArea = Some(aRefreshArea);
+  }
+  gfx::IntRect GetFirstFrameRefreshArea() const { return *mFirstFrameRefreshArea; }
+  bool HasFirstFrameRefreshArea() const { return mFirstFrameRefreshArea.isSome(); }
 
   void SetSize(int32_t width, int32_t height, Orientation orientation)
   {
     if (!HasSize()) {
       mSize.emplace(nsIntSize(width, height));
       mOrientation.emplace(orientation);
     }
   }
@@ -60,18 +71,25 @@ public:
 
 private:
   /// The hotspot found on cursors, if present.
   Maybe<gfx::IntPoint> mHotspot;
 
   /// The loop count for animated images, or -1 for infinite loop.
   int32_t mLoopCount;
 
+  // The total length of a single loop through an animated image.
+  Maybe<FrameTimeout> mLoopLength;
+
   /// The timeout of an animated image's first frame.
-  int32_t mFirstFrameTimeout;
+  FrameTimeout mFirstFrameTimeout;
+
+  // The area of the image that needs to be invalidated when the animation
+  // loops.
+  Maybe<gfx::IntRect> mFirstFrameRefreshArea;
 
   Maybe<nsIntSiz