Merge mozilla-central to fx-team
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 21 Jul 2016 16:27:04 +0200
changeset 346173 f5154aaeaec4546dd04c66ac61a0d0ee69f4dfdf
parent 346172 d8a5ea157504793ff2bb2b5161103154a31f08e3 (current diff)
parent 346117 6b180266ac16e3226be33319ff710ddfa85f5836 (diff)
child 346174 8c5a29a4fc59ed860f2ba6eac9971867e6ce4659
push id6389
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:38:22 +0000
treeherdermozilla-beta@01d67bfe6c81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to fx-team
dom/media/MediaCallbackID.cpp
dom/media/MediaCallbackID.h
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
toolkit/components/microformats/Microformats.js
toolkit/components/microformats/tests/.eslintrc
toolkit/components/microformats/tests/geo.html
toolkit/components/microformats/tests/mochitest.ini
toolkit/components/microformats/tests/test_Microformats.html
toolkit/components/microformats/tests/test_Microformats_add.html
toolkit/components/microformats/tests/test_Microformats_adr.html
toolkit/components/microformats/tests/test_Microformats_count.html
toolkit/components/microformats/tests/test_Microformats_geo.html
toolkit/components/microformats/tests/test_Microformats_getters.html
toolkit/components/microformats/tests/test_Microformats_hCalendar.html
toolkit/components/microformats/tests/test_Microformats_hCard.html
toolkit/components/microformats/tests/test_Microformats_negative.html
toolkit/components/microformats/tests/test_framerecursion.html
--- 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/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -275,16 +275,17 @@ tags = mcb
 skip-if = os == 'win'
 [browser_bug1064280_changeUrlInPinnedTab.js]
 [browser_accesskeys.js]
 [browser_clipboard.js]
 subsuite = clipboard
 [browser_clipboard_pastefile.js]
 [browser_contentAreaClick.js]
 [browser_contextmenu.js]
+subsuite = clipboard
 tags = fullscreen
 skip-if = toolkit == "gtk2" || toolkit == "gtk3" # disabled on Linux due to bug 513558
 [browser_contextmenu_input.js]
 skip-if = toolkit == "gtk2" || toolkit == "gtk3" # disabled on Linux due to bug 513558
 [browser_ctrlTab.js]
 [browser_datachoices_notification.js]
 skip-if = !datareporting
 [browser_decoderDoctor.js]
--- 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/extensions/test/browser/browser_ext_currentWindow.js
+++ b/browser/components/extensions/test/browser/browser_ext_currentWindow.js
@@ -1,18 +1,18 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
 function genericChecker() {
   let kind = "background";
   let path = window.location.pathname;
-  if (path.indexOf("popup") != -1) {
+  if (path.includes("/popup.html")) {
     kind = "popup";
-  } else if (path.indexOf("page") != -1) {
+  } else if (path.includes("/page.html")) {
     kind = "page";
   }
 
   browser.test.onMessage.addListener((msg, ...args) => {
     if (msg == kind + "-check-current1") {
       browser.tabs.query({
         currentWindow: true,
       }, function(tabs) {
@@ -29,17 +29,17 @@ function genericChecker() {
         browser.test.sendMessage("result", window.id);
       });
     } else if (msg == kind + "-open-page") {
       browser.tabs.create({windowId: args[0], url: browser.runtime.getURL("page.html")});
     } else if (msg == kind + "-close-page") {
       browser.tabs.query({
         windowId: args[0],
       }, tabs => {
-        let tab = tabs.find(tab => tab.url.indexOf("page.html") != -1);
+        let tab = tabs.find(tab => tab.url.includes("/page.html"));
         browser.tabs.remove(tab.id, () => {
           browser.test.sendMessage("closed");
         });
       });
     }
   });
   browser.test.sendMessage(kind + "-ready");
 }
--- 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]
@@ -143,16 +143,17 @@
 #endif
 
 ; [Components]
 @RESPATH@/browser/components/components.manifest
 @RESPATH@/components/alerts.xpt
 #ifdef ACCESSIBILITY
 #ifdef XP_WIN32
 @BINPATH@/AccessibleMarshal.dll
+@BINPATH@/IA2Marshal.dll
 #endif
 @RESPATH@/components/accessibility.xpt
 #endif
 @RESPATH@/components/appshell.xpt
 @RESPATH@/components/appstartup.xpt
 @RESPATH@/components/autocomplete.xpt
 @RESPATH@/components/autoconfig.xpt
 @RESPATH@/components/browser-element.xpt
--- a/browser/installer/windows/nsis/shared.nsh
+++ b/browser/installer/windows/nsis/shared.nsh
@@ -1365,16 +1365,17 @@
 ; directory.
 !macro PushFilesToCheck
   ; The first string to be pushed onto the stack MUST be "end" to indicate
   ; that there are no more files to check in $INSTDIR and the last string
   ; should be ${FileMainEXE} so if it is in use the CheckForFilesInUse macro
   ; returns after the first check.
   Push "end"
   Push "AccessibleMarshal.dll"
+  Push "IA2Marshal.dll"
   Push "freebl3.dll"
   Push "nssckbi.dll"
   Push "nspr4.dll"
   Push "nssdbm3.dll"
   Push "mozsqlite3.dll"
   Push "xpcom.dll"
   Push "crashreporter.exe"
   Push "updater.exe"
--- 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/nsIAddonPolicyService.idl
+++ b/caps/nsIAddonPolicyService.idl
@@ -29,16 +29,23 @@ interface nsIAddonPolicyService : nsISup
   /**
    * Returns the content security policy which applies to documents belonging
    * to the extension with the given ID. This may be either a custom policy,
    * if one was supplied, or the default policy if one was not.
    */
   AString getAddonCSP(in AString aAddonId);
 
   /**
+   * Returns the generated background page as a data-URI, if any. If the addon
+   * does not have an auto-generated background page, an empty string is
+   * returned.
+   */
+  ACString getGeneratedBackgroundPageUrl(in ACString aAddonId);
+
+  /**
    * Returns true if unprivileged code associated with the given addon may load
    * data from |aURI|.
    */
   boolean addonMayLoadURI(in AString aAddonId, in nsIURI aURI);
 
   /**
    * Returns true if a given extension:// URI is web-accessible.
    */
--- 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/client/inspector/rules/test/browser_rules_pseudo-element_01.js
+++ b/devtools/client/inspector/rules/test/browser_rules_pseudo-element_01.js
@@ -5,39 +5,39 @@
 "use strict";
 
 // Test that pseudoelements are displayed correctly in the rule view
 
 const TEST_URI = URL_ROOT + "doc_pseudoelement.html";
 const PSEUDO_PREF = "devtools.inspector.show_pseudo_elements";
 
 add_task(function* () {
-  Services.prefs.setBoolPref(PSEUDO_PREF, true);
+  yield pushPref(PSEUDO_PREF, true);
 
   yield addTab(TEST_URI);
   let {inspector, view} = yield openRuleView();
 
   yield testTopLeft(inspector, view);
   yield testTopRight(inspector, view);
   yield testBottomRight(inspector, view);
   yield testBottomLeft(inspector, view);
   yield testParagraph(inspector, view);
   yield testBody(inspector, view);
-
-  Services.prefs.clearUserPref(PSEUDO_PREF);
 });
 
 function* testTopLeft(inspector, view) {
   let id = "#topleft";
   let rules = yield assertPseudoElementRulesNumbers(id,
     inspector, view, {
       elementRulesNb: 4,
       firstLineRulesNb: 2,
       firstLetterRulesNb: 1,
-      selectionRulesNb: 0
+      selectionRulesNb: 0,
+      afterRulesNb: 1,
+      beforeRulesNb: 2
     }
   );
 
   let gutters = assertGutters(view);
 
   info("Make sure that clicking on the twisty hides pseudo elements");
   let expander = gutters[0].querySelector(".ruleview-expander");
   ok(!view.element.children[1].hidden, "Pseudo Elements are expanded");
@@ -117,17 +117,19 @@ function* testTopLeft(inspector, view) {
      "rgb(0, 255, 0)", "Added prop does not apply to pseudo");
 }
 
 function* testTopRight(inspector, view) {
   yield assertPseudoElementRulesNumbers("#topright", inspector, view, {
     elementRulesNb: 4,
     firstLineRulesNb: 1,
     firstLetterRulesNb: 1,
-    selectionRulesNb: 0
+    selectionRulesNb: 0,
+    beforeRulesNb: 2,
+    afterRulesNb: 1
   });
 
   let gutters = assertGutters(view);
 
   let expander = gutters[0].querySelector(".ruleview-expander");
   ok(!view.element.firstChild.classList.contains("show-expandable-container"),
      "Pseudo Elements remain collapsed after switching element");
 
@@ -137,36 +139,42 @@ function* testTopRight(inspector, view) 
     "Pseudo Elements are shown again after clicking twisty");
 }
 
 function* testBottomRight(inspector, view) {
   yield assertPseudoElementRulesNumbers("#bottomright", inspector, view, {
     elementRulesNb: 4,
     firstLineRulesNb: 1,
     firstLetterRulesNb: 1,
-    selectionRulesNb: 0
+    selectionRulesNb: 0,
+    beforeRulesNb: 3,
+    afterRulesNb: 1
   });
 }
 
 function* testBottomLeft(inspector, view) {
   yield assertPseudoElementRulesNumbers("#bottomleft", inspector, view, {
     elementRulesNb: 4,
     firstLineRulesNb: 1,
     firstLetterRulesNb: 1,
-    selectionRulesNb: 0
+    selectionRulesNb: 0,
+    beforeRulesNb: 2,
+    afterRulesNb: 1
   });
 }
 
 function* testParagraph(inspector, view) {
   let rules =
     yield assertPseudoElementRulesNumbers("#bottomleft p", inspector, view, {
       elementRulesNb: 3,
       firstLineRulesNb: 1,
       firstLetterRulesNb: 1,
-      selectionRulesNb: 1
+      selectionRulesNb: 1,
+      beforeRulesNb: 0,
+      afterRulesNb: 0
     });
 
   assertGutters(view);
 
   let elementFirstLineRule = rules.firstLineRules[0];
   is(convertTextPropsToString(elementFirstLineRule.textProps),
      "background: blue",
      "Paragraph first-line properties are correct");
@@ -204,27 +212,35 @@ function* assertPseudoElementRulesNumber
 
   let rules = {
     elementRules: elementStyle.rules.filter(rule => !rule.pseudoElement),
     firstLineRules: elementStyle.rules.filter(rule =>
       rule.pseudoElement === ":first-line"),
     firstLetterRules: elementStyle.rules.filter(rule =>
       rule.pseudoElement === ":first-letter"),
     selectionRules: elementStyle.rules.filter(rule =>
-      rule.pseudoElement === ":-moz-selection")
+      rule.pseudoElement === ":-moz-selection"),
+    beforeRules: elementStyle.rules.filter(rule =>
+      rule.pseudoElement === ":before"),
+    afterRules: elementStyle.rules.filter(rule =>
+      rule.pseudoElement === ":after"),
   };
 
   is(rules.elementRules.length, ruleNbs.elementRulesNb,
      selector + " has the correct number of non pseudo element rules");
   is(rules.firstLineRules.length, ruleNbs.firstLineRulesNb,
      selector + " has the correct number of :first-line rules");
   is(rules.firstLetterRules.length, ruleNbs.firstLetterRulesNb,
      selector + " has the correct number of :first-letter rules");
   is(rules.selectionRules.length, ruleNbs.selectionRulesNb,
      selector + " has the correct number of :selection rules");
+  is(rules.beforeRules.length, ruleNbs.beforeRulesNb,
+     selector + " has the correct number of :before rules");
+  is(rules.afterRules.length, ruleNbs.afterRulesNb,
+     selector + " has the correct number of :after rules");
 
   return rules;
 }
 
 function getGutters(view) {
   return view.element.querySelectorAll(".theme-gutter");
 }
 
--- a/devtools/client/inspector/rules/test/browser_rules_pseudo-element_02.js
+++ b/devtools/client/inspector/rules/test/browser_rules_pseudo-element_02.js
@@ -1,15 +1,15 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-// Test that pseudoelements are displayed correctly in the rule view
+// Test that pseudoelements are displayed correctly in the markup view.
 
 const TEST_URI = URL_ROOT + "doc_pseudoelement.html";
 
 add_task(function* () {
   yield addTab(TEST_URI);
   let {inspector} = yield openRuleView();
 
   let node = yield getNodeFront("#topleft", inspector);
--- a/devtools/client/shared/components/tabs/tabbar.css
+++ b/devtools/client/shared/components/tabs/tabbar.css
@@ -43,12 +43,12 @@
   flex-grow: 1;
 }
 
 .tabs .tabs-navigation .tabs-menu-item a {
   text-align: center;
 }
 
 /* Firebug theme doesn't stretch the tabs. */
-.theme-firebug .tabs .tabs-navigation .tabs-menu {
-  display: block;
+.theme-firebug .tabs .tabs-navigation .tabs-menu-item {
+  flex-grow: 0;
 }
 
--- a/devtools/client/shared/components/tabs/tabs.css
+++ b/devtools/client/shared/components/tabs/tabs.css
@@ -12,17 +12,17 @@
 .tabs .tabs-menu {
   display: table;
   list-style: none;
   padding: 0;
   margin: 0;
 }
 
 .tabs .tabs-menu-item {
-  float: left;
+  float: inline-start;
 }
 
 .tabs .tabs-menu-item a {
   display: block;
   color: #A9A9A9;
   padding: 4px 8px;
   border: 1px solid transparent;
   text-decoration: none;
--- a/devtools/client/shared/test/unit/test_parseDeclarations.js
+++ b/devtools/client/shared/test/unit/test_parseDeclarations.js
@@ -341,17 +341,23 @@ const TEST_DATA = [
   },
 
   // Parsing our special comments skips the name-check heuristic.
   {
     parseComments: true,
     input: "/*! walrus: zebra; */",
     expected: [{name: "walrus", value: "zebra", priority: "",
                 offsets: [4, 18], commentOffsets: [0, 21]}]
-  }
+  },
+
+  // Regression test for bug 1287620.
+  {
+    input: "color: blue \\9 no\\_need",
+    expected: [{name: "color", value: "blue \\9 no_need", priority: "", offsets: [0, 23]}]
+  },
 ];
 
 function run_test() {
   run_basic_tests();
   run_comment_tests();
 }
 
 // Test parseDeclarations.
--- 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/server/actors/styles.js
+++ b/devtools/server/actors/styles.js
@@ -18,22 +18,18 @@ const events = require("sdk/event/core")
 const {UPDATE_PRESERVING_RULES, UPDATE_GENERAL} = require("devtools/server/actors/stylesheets");
 const {pageStyleSpec, styleRuleSpec, ELEMENT_STYLE} = require("devtools/shared/specs/styles");
 
 loader.lazyRequireGetter(this, "CSS", "CSS");
 loader.lazyGetter(this, "CssLogic", () => require("devtools/server/css-logic").CssLogic);
 loader.lazyGetter(this, "SharedCssLogic", () => require("devtools/shared/inspector/css-logic"));
 loader.lazyGetter(this, "DOMUtils", () => Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils));
 
-// When gathering rules to read for pseudo elements, we will skip
-// :before and :after, which are handled as a special case.
-loader.lazyGetter(this, "PSEUDO_ELEMENTS_TO_READ", () => {
-  return DOMUtils.getCSSPseudoElementNames().filter(pseudo => {
-    return pseudo !== ":before" && pseudo !== ":after";
-  });
+loader.lazyGetter(this, "PSEUDO_ELEMENTS", () => {
+  return DOMUtils.getCSSPseudoElementNames();
 });
 
 const XHTML_NS = "http://www.w3.org/1999/xhtml";
 const FONT_PREVIEW_TEXT = "Abc";
 const FONT_PREVIEW_FONT_SIZE = 40;
 const FONT_PREVIEW_FILLSTYLE = "black";
 const NORMAL_FONT_WEIGHT = 400;
 const BOLD_FONT_WEIGHT = 700;
@@ -536,20 +532,19 @@ var PageStyleActor = protocol.ActorClass
           // The only case when there would be a pseudo here is
           // ::before/::after, and in this case we want to tell the
           // view that it belongs to the element (which is a
           // _moz_generated_content native anonymous element).
           oneRule.pseudoElement = null;
           rules.push(oneRule);
         });
 
-    // Now any pseudos (except for ::before / ::after, which was handled as
-    // a 'normal rule' above.
+    // Now any pseudos.
     if (showElementStyles) {
-      for (let readPseudo of PSEUDO_ELEMENTS_TO_READ) {
+      for (let readPseudo of PSEUDO_ELEMENTS) {
         this._getElementRules(bindingElement, readPseudo, inherited, options)
             .forEach(oneRule => {
               rules.push(oneRule);
             });
       }
     }
 
     return rules;
--- a/devtools/shared/css-parsing-utils.js
+++ b/devtools/shared/css-parsing-utils.js
@@ -342,17 +342,19 @@ function parseDeclarationsInternal(isCss
     } else if (token.tokenType === "ident") {
       if (token.text === "important" && hasBang) {
         lastProp.priority = "important";
         hasBang = false;
       } else {
         if (hasBang) {
           current += "!";
         }
-        current += token.text;
+        // Re-escape the token to avoid dequoting problems.
+        // See bug 1287620.
+        current += CSS.escape(token.text);
       }
     } else if (token.tokenType === "symbol" && token.text === "!") {
       hasBang = true;
     } else if (token.tokenType === "whitespace") {
       if (current !== "") {
         current += " ";
       }
     } else if (token.tokenType === "comment") {
--- 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/nsContentCreatorFunctions.h
+++ b/dom/base/nsContentCreatorFunctions.h
@@ -25,26 +25,28 @@ namespace dom {
 class Element;
 class NodeInfo;
 } // namespace dom
 } // namespace mozilla
 
 nsresult
 NS_NewElement(mozilla::dom::Element** aResult,
               already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
-              mozilla::dom::FromParser aFromParser);
+              mozilla::dom::FromParser aFromParser,
+              nsAString* aIs = nullptr);
 
 nsresult
 NS_NewXMLElement(mozilla::dom::Element** aResult,
                  already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 nsresult
 NS_NewHTMLElement(mozilla::dom::Element** aResult,
                   already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
-                  mozilla::dom::FromParser aFromParser);
+                  mozilla::dom::FromParser aFromParser,
+                  nsAString* aIs = nullptr);
 
 // First argument should be nsHTMLTag, but that adds dependency to parser
 // for a bunch of files.
 already_AddRefed<nsGenericHTMLElement>
 CreateHTMLElement(uint32_t aNodeType,
                   already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
                   mozilla::dom::FromParser aFromParser);
 
--- 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);
@@ -5585,73 +5585,108 @@ nsDocument::GetDocumentElement(nsIDOMEle
 }
 
 NS_IMETHODIMP
 nsDocument::CreateElement(const nsAString& aTagName,
                           nsIDOMElement** aReturn)
 {
   *aReturn = nullptr;
   ErrorResult rv;
-  nsCOMPtr<Element> element = nsIDocument::CreateElement(aTagName, rv);
+  ElementCreationOptions options;
+  nsCOMPtr<Element> element = CreateElement(aTagName, options, rv);
   NS_ENSURE_FALSE(rv.Failed(), rv.StealNSResult());
   return CallQueryInterface(element, aReturn);
 }
 
 bool IsLowercaseASCII(const nsAString& aValue)
 {
   int32_t len = aValue.Length();
   for (int32_t i = 0; i < len; ++i) {
     char16_t c = aValue[i];
     if (!(0x0061 <= (c) && ((c) <= 0x007a))) {
       return false;
     }
   }
   return true;
 }
 
+CustomElementDefinition*
+nsDocument::LookupCustomElementDefinition(const nsAString& aLocalName,
+                                          uint32_t aNameSpaceID,
+                                          const nsAString* aIs)
+{
+  if (!mRegistry || aNameSpaceID != kNameSpaceID_XHTML) {
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIAtom> localNameAtom = NS_Atomize(aLocalName);
+  nsCOMPtr<nsIAtom> typeAtom = aIs ? NS_Atomize(*aIs) : localNameAtom;
+
+  CustomElementDefinition* data;
+  CustomElementHashKey key(aNameSpaceID, typeAtom);
+  if (mRegistry->mCustomDefinitions.Get(&key, &data) &&
+      data->mLocalName == localNameAtom) {
+    return data;
+  }
+
+  return nullptr;
+}
+
 already_AddRefed<Element>
-nsIDocument::CreateElement(const nsAString& aTagName, ErrorResult& rv)
+nsDocument::CreateElement(const nsAString& aTagName,
+                          const ElementCreationOptions& aOptions,
+                          ErrorResult& rv)
 {
   rv = nsContentUtils::CheckQName(aTagName, false);
   if (rv.Failed()) {
     return nullptr;
   }
 
   bool needsLowercase = IsHTMLDocument() && !IsLowercaseASCII(aTagName);
   nsAutoString lcTagName;
   if (needsLowercase) {
     nsContentUtils::ASCIIToLower(aTagName, lcTagName);
   }
 
-  return CreateElem(needsLowercase ? lcTagName : aTagName, nullptr,
-                    mDefaultElementType);
+  // Throw NotFoundError if 'is' is not-null and definition is null
+  nsString* is = CheckCustomElementName(
+    aOptions, needsLowercase ? lcTagName : aTagName, mDefaultElementType, rv);
+  if (rv.Failed()) {
+    return nullptr;
+  }
+
+  RefPtr<Element> elem = CreateElem(
+    needsLowercase ? lcTagName : aTagName, nullptr, mDefaultElementType, is);
+
+  return elem.forget();
 }
 
 void
 nsDocument::SetupCustomElement(Element* aElement,
                                uint32_t aNamespaceID,
                                const nsAString* aTypeExtension)
 {
-  if (!mRegistry) {
+  if (!mRegistry || aNamespaceID != kNameSpaceID_XHTML) {
     return;
   }
 
   nsCOMPtr<nsIAtom> tagAtom = aElement->NodeInfo()->NameAtom();
   nsCOMPtr<nsIAtom> typeAtom = aTypeExtension ?
     NS_Atomize(*aTypeExtension) : tagAtom;
 
   if (aTypeExtension && !aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::is)) {
     // Custom element setup in the parser happens after the "is"
     // attribute is added.
     aElement->SetAttr(kNameSpaceID_None, nsGkAtoms::is, *aTypeExtension, true);
   }
 
-  CustomElementDefinition* data;
-  CustomElementHashKey key(aNamespaceID, typeAtom);
-  if (!mRegistry->mCustomDefinitions.Get(&key, &data)) {
+  CustomElementDefinition* data = LookupCustomElementDefinition(
+    aElement->NodeInfo()->LocalName(), aNamespaceID, aTypeExtension);
+
+  if (!data) {
     // The type extension doesn't exist in the registry,
     // thus we don't need to enqueue callback or adjust
     // the "is" attribute, but it is possibly an upgrade candidate.
     RegisterUnresolvedElement(aElement, typeAtom);
     return;
   }
 
   if (data->mLocalName != tagAtom) {
@@ -5661,105 +5696,61 @@ nsDocument::SetupCustomElement(Element* 
     return;
   }
 
   // Enqueuing the created callback will set the CustomElementData on the
   // element, causing prototype swizzling to occur in Element::WrapObject.
   EnqueueLifecycleCallback(nsIDocument::eCreated, aElement, nullptr, data);
 }
 
-already_AddRefed<Element>
-nsDocument::CreateElement(const nsAString& aTagName,
-                          const nsAString& aTypeExtension,
-                          ErrorResult& rv)
-{
-  RefPtr<Element> elem = nsIDocument::CreateElement(aTagName, rv);
-  if (rv.Failed()) {
-    return nullptr;
-  }
-
-  if (!aTypeExtension.IsVoid() &&
-      !aTagName.Equals(aTypeExtension)) {
-    // do not process 'is' if it is null or the extended type is the same as
-    // the localName
-    SetupCustomElement(elem, GetDefaultNamespaceID(), &aTypeExtension);
-  }
-
-  return elem.forget();
-}
-
 NS_IMETHODIMP
 nsDocument::CreateElementNS(const nsAString& aNamespaceURI,
                             const nsAString& aQualifiedName,
                             nsIDOMElement** aReturn)
 {
   *aReturn = nullptr;
+  ElementCreationOptions options;
   ErrorResult rv;
   nsCOMPtr<Element> element =
-    nsIDocument::CreateElementNS(aNamespaceURI, aQualifiedName, rv);
+    CreateElementNS(aNamespaceURI, aQualifiedName, options, rv);
   NS_ENSURE_FALSE(rv.Failed(), rv.StealNSResult());
   return CallQueryInterface(element, aReturn);
 }
 
 already_AddRefed<Element>
-nsIDocument::CreateElementNS(const nsAString& aNamespaceURI,
-                             const nsAString& aQualifiedName,
-                             ErrorResult& rv)
+nsDocument::CreateElementNS(const nsAString& aNamespaceURI,
+                            const nsAString& aQualifiedName,
+                            const ElementCreationOptions& aOptions,
+                            ErrorResult& rv)
 {
   RefPtr<mozilla::dom::NodeInfo> nodeInfo;
   rv = nsContentUtils::GetNodeInfoFromQName(aNamespaceURI,
                                             aQualifiedName,
                                             mNodeInfoManager,
                                             nsIDOMNode::ELEMENT_NODE,
                                             getter_AddRefs(nodeInfo));
   if (rv.Failed()) {
     return nullptr;
   }
 
-  nsCOMPtr<Element> element;
-  rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(),
-                     NOT_FROM_PARSER);
-  if (rv.Failed()) {
-    return nullptr;
-  }
-  return element.forget();
-}
-
-already_AddRefed<Element>
-nsDocument::CreateElementNS(const nsAString& aNamespaceURI,
-                            const nsAString& aQualifiedName,
-                            const nsAString& aTypeExtension,
-                            ErrorResult& rv)
-{
-  RefPtr<Element> elem = nsIDocument::CreateElementNS(aNamespaceURI,
-                                                        aQualifiedName,
-                                                        rv);
+  // Throw NotFoundError if 'is' is not-null and definition is null
+  nsString* is = CheckCustomElementName(
+    aOptions, aQualifiedName, nodeInfo->NamespaceID(), rv);
   if (rv.Failed()) {
     return nullptr;
   }
 
-  if (aTypeExtension.IsVoid() ||
-      aQualifiedName.Equals(aTypeExtension)) {
-    // do not process 'is' if it is null or the extended type is the same as
-    // the localName
-    return elem.forget();
-  }
-
-  int32_t nameSpaceId = kNameSpaceID_Wildcard;
-  if (!aNamespaceURI.EqualsLiteral("*")) {
-    rv = nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI,
-                                                               nameSpaceId);
-    if (rv.Failed()) {
-      return nullptr;
-    }
-  }
-
-  SetupCustomElement(elem, nameSpaceId, &aTypeExtension);
-
-  return elem.forget();
+  nsCOMPtr<Element> element;
+  rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(),
+                     NOT_FROM_PARSER, is);
+  if (rv.Failed()) {
+    return nullptr;
+  }
+
+  return element.forget();
 }
 
 NS_IMETHODIMP
 nsDocument::CreateTextNode(const nsAString& aData, nsIDOMText** aReturn)
 {
   *aReturn = nsIDocument::CreateTextNode(aData).take();
   return NS_OK;
 }
@@ -8779,17 +8770,17 @@ nsDocument::RetrieveRelevantHeaders(nsIC
   mLastModified.Truncate();
   if (modDate != 0) {
     GetFormattedTimeString(modDate, mLastModified);
   }
 }
 
 already_AddRefed<Element>
 nsDocument::CreateElem(const nsAString& aName, nsIAtom *aPrefix,
-                       int32_t aNamespaceID)
+                       int32_t aNamespaceID, nsAString* aIs)
 {
 #ifdef DEBUG
   nsAutoString qName;
   if (aPrefix) {
     aPrefix->ToString(qName);
     qName.Append(':');
   }
   qName.Append(aName);
@@ -8806,17 +8797,17 @@ nsDocument::CreateElem(const nsAString& 
   RefPtr<mozilla::dom::NodeInfo> nodeInfo;
   mNodeInfoManager->GetNodeInfo(aName, aPrefix, aNamespaceID,
                                 nsIDOMNode::ELEMENT_NODE,
                                 getter_AddRefs(nodeInfo));
   NS_ENSURE_TRUE(nodeInfo, nullptr);
 
   nsCOMPtr<Element> element;
   nsresult rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(),
-                              NOT_FROM_PARSER);
+                              NOT_FROM_PARSER, aIs);
   return NS_SUCCEEDED(rv) ? element.forget() : nullptr;
 }
 
 bool
 nsDocument::IsSafeToFlush() const
 {
   nsIPresShell* shell = GetShell();
   if (!shell)
@@ -9747,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;
@@ -13492,8 +13483,31 @@ nsIDocument::UpdateStyleBackendType()
     nsLayoutUtils::SupportsServoStyleBackend(this) &&
     mDocumentContainer ?
       StyleBackendType::Servo :
       StyleBackendType::Gecko;
 #else
   mStyleBackendType = StyleBackendType::Gecko;
 #endif
 }
+
+nsString*
+nsDocument::CheckCustomElementName(const ElementCreationOptions& aOptions,
+                                   const nsAString& aLocalName,
+                                   uint32_t aNamespaceID,
+                                   ErrorResult& rv)
+{
+  // only check aOptions if 'is' is passed and the webcomponents preference
+  // is enabled
+  if (!aOptions.mIs.WasPassed() ||
+      !Preferences::GetBool("dom.webcomponents.enabled")) {
+      return nullptr;
+  }
+
+  nsString* is = const_cast<nsString*>(&(aOptions.mIs.Value()));
+
+  // Throw NotFoundError if 'is' is not-null and definition is null
+  if (!LookupCustomElementDefinition(aLocalName, aNamespaceID, is)) {
+      rv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
+  }
+
+  return is;
+}
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -999,17 +999,18 @@ public:
   NS_DECL_NSIOBSERVER
 
   NS_DECL_NSIDOMXPATHEVALUATOR
 
   virtual nsresult Init();
 
   virtual already_AddRefed<Element> CreateElem(const nsAString& aName,
                                                nsIAtom* aPrefix,
-                                               int32_t aNamespaceID) override;
+                                               int32_t aNamespaceID,
+                                               nsAString* aIs = nullptr) override;
 
   virtual void Sanitize() override;
 
   virtual void EnumerateSubDocuments(nsSubDocEnumFunc aCallback,
                                                  void *aData) override;
 
   virtual bool CanSavePresentation(nsIRequest *aNewRequest) override;
   virtual void Destroy() override;
@@ -1327,24 +1328,22 @@ public:
                     const mozilla::dom::ElementRegistrationOptions& aOptions,
                     JS::MutableHandle<JSObject*> aRetval,
                     mozilla::ErrorResult& rv) override;
   virtual mozilla::dom::StyleSheetList* StyleSheets() override;
   virtual void SetSelectedStyleSheetSet(const nsAString& aSheetSet) override;
   virtual void GetLastStyleSheetSet(nsString& aSheetSet) override;
   virtual mozilla::dom::DOMStringList* StyleSheetSets() override;
   virtual void EnableStyleSheetsForSet(const nsAString& aSheetSet) override;
-  using nsIDocument::CreateElement;
-  using nsIDocument::CreateElementNS;
   virtual already_AddRefed<Element> CreateElement(const nsAString& aTagName,
-                                                  const nsAString& aTypeExtension,
-                                                  mozilla::ErrorResult& rv) override;
+                                                  const mozilla::dom::ElementCreationOptions& aOptions,
+                                                  ErrorResult& rv) override;
   virtual already_AddRefed<Element> CreateElementNS(const nsAString& aNamespaceURI,
                                                     const nsAString& aQualifiedName,
-                                                    const nsAString& aTypeExtension,
+                                                    const mozilla::dom::ElementCreationOptions& aOptions,
                                                     mozilla::ErrorResult& rv) override;
   virtual void UseRegistryFromDocument(nsIDocument* aDocument) override;
 
   virtual nsIDocument* MasterDocument() override
   {
     return mMasterDocument ? mMasterDocument.get()
                            : this;
   }
@@ -1581,16 +1580,37 @@ private:
   // element queues. Each queue is represented by a sequence of
   // CustomElementData in this array, separated by nullptr that
   // represent the boundaries of the items in the stack. The first
   // queue in the stack is the base element queue.
   static mozilla::Maybe<nsTArray<RefPtr<mozilla::dom::CustomElementData>>> sProcessingStack;
 
   static bool CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp);
 
+  /**
+   * Looking up a custom element definition.
+   * https://html.spec.whatwg.org/#look-up-a-custom-element-definition
+   */
+  mozilla::dom::CustomElementDefinition* LookupCustomElementDefinition(
+    const nsAString& aLocalName, uint32_t aNameSpaceID, const nsAString* aIs);
+
+  /**
+   * Check if the passed custom element name, aOptions.mIs, is a registered
+   * custom element type or not, then return the custom element name for future
+   * usage.
+   *
+   * If there is no existing custom element definition for this name, throw a
+   * NotFoundError.
+   */
+  nsString* CheckCustomElementName(
+    const mozilla::dom::ElementCreationOptions& aOptions,
+    const nsAString& aLocalName,
+    uint32_t aNamespaceID,
+    ErrorResult& rv);
+
 public:
   // Enqueue created callback or register upgrade candidate for
   // newly created custom elements, possibly extending an existing type.
   // ex. <x-button>, <button is="x-button> (type extension)
   virtual void SetupCustomElement(Element* aElement,
                                   uint32_t aNamespaceID,
                                   const nsAString* aTypeExtension) override;
 
--- 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/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -113,16 +113,17 @@ class CDATASection;
 class Comment;
 struct CustomElementDefinition;
 class DocumentFragment;
 class DocumentTimeline;
 class DocumentType;
 class DOMImplementation;
 class DOMStringList;
 class Element;
+struct ElementCreationOptions;
 struct ElementRegistrationOptions;
 class Event;
 class EventTarget;
 class FontFaceSet;
 class FrameRequestCallback;
 struct FullscreenRequest;
 class ImportManager;
 class HTMLBodyElement;
@@ -1503,17 +1504,18 @@ public:
   virtual bool IsScriptEnabled() = 0;
 
   /**
    * Create an element with the specified name, prefix and namespace ID.
    * Returns null if element name parsing failed.
    */
   virtual already_AddRefed<Element> CreateElem(const nsAString& aName,
                                                nsIAtom* aPrefix,
-                                               int32_t aNamespaceID) = 0;
+                                               int32_t aNamespaceID,
+                                               nsAString* aIs = nullptr) = 0;
 
   /**
    * Get the security info (i.e. SSL state etc) that the document got
    * from the channel/document that created the content of the
    * document.
    *
    * @see nsIChannel
    */
@@ -2513,28 +2515,25 @@ public:
   }
   already_AddRefed<nsContentList>
     GetElementsByTagNameNS(const nsAString& aNamespaceURI,
                            const nsAString& aLocalName,
                            mozilla::ErrorResult& aResult);
   already_AddRefed<nsContentList>
     GetElementsByClassName(const nsAString& aClasses);
   // GetElementById defined above
-  already_AddRefed<Element> CreateElement(const nsAString& aTagName,
-                                          mozilla::ErrorResult& rv);
-  already_AddRefed<Element> CreateElementNS(const nsAString& aNamespaceURI,
-                                            const nsAString& aQualifiedName,
-                                            mozilla::ErrorResult& rv);
-  virtual already_AddRefed<Element> CreateElement(const nsAString& aTagName,
-                                                  const nsAString& aTypeExtension,
-                                                  mozilla::ErrorResult& rv) = 0;
-  virtual already_AddRefed<Element> CreateElementNS(const nsAString& aNamespaceURI,
-                                                    const nsAString& aQualifiedName,
-                                                    const nsAString& aTypeExtension,
-                                                    mozilla::ErrorResult& rv) = 0;
+  virtual already_AddRefed<Element>
+    CreateElement(const nsAString& aTagName,
+                  const mozilla::dom::ElementCreationOptions& aOptions,
+                  mozilla::ErrorResult& rv) = 0;
+  virtual already_AddRefed<Element>
+    CreateElementNS(const nsAString& aNamespaceURI,
+                    const nsAString& aQualifiedName,
+                    const mozilla::dom::ElementCreationOptions& aOptions,
+                    mozilla::ErrorResult& rv) = 0;
   already_AddRefed<mozilla::dom::DocumentFragment>
     CreateDocumentFragment() const;
   already_AddRefed<nsTextNode> CreateTextNode(const nsAString& aData) const;
   already_AddRefed<mozilla::dom::Comment>
     CreateComment(const nsAString& aData) const;
   already_AddRefed<mozilla::dom::ProcessingInstruction>
     CreateProcessingInstruction(const nsAString& target, const nsAString& data,
                                 mozilla::ErrorResult& rv) const;
--- 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/nsNameSpaceManager.cpp
+++ b/dom/base/nsNameSpaceManager.cpp
@@ -134,22 +134,23 @@ nsNameSpaceManager::GetNameSpaceID(nsIAt
   }
 
   return kNameSpaceID_Unknown;
 }
 
 nsresult
 NS_NewElement(Element** aResult,
               already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
-              FromParser aFromParser)
+              FromParser aFromParser,
+              nsAString* aIs)
 {
   RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
   int32_t ns = ni->NamespaceID();
   if (ns == kNameSpaceID_XHTML) {
-    return NS_NewHTMLElement(aResult, ni.forget(), aFromParser);
+    return NS_NewHTMLElement(aResult, ni.forget(), aFromParser, aIs);
   }
 #ifdef MOZ_XUL
   if (ns == kNameSpaceID_XUL) {
     return NS_NewXULElement(aResult, ni.forget());
   }
 #endif
   if (ns == kNameSpaceID_MathML) {
     return NS_NewMathMLElement(aResult, ni.forget());
--- 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/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -3085,21 +3085,61 @@ HTMLMediaElement::ReportTelemetry()
       uint32_t percentage = 100 * droppedFrames / totalFrames;
       LOG(LogLevel::Debug,
           ("Reporting telemetry DROPPED_FRAMES_IN_VIDEO_PLAYBACK"));
       Telemetry::Accumulate(Telemetry::VIDEO_DROPPED_FRAMES_PROPORTION,
                             percentage);
     }
   }
 
-  Telemetry::Accumulate(Telemetry::VIDEO_PLAY_TIME_MS, SECONDS_TO_MS(mPlayTime.Total()));
-  LOG(LogLevel::Debug, ("%p VIDEO_PLAY_TIME_MS = %f", this, mPlayTime.Total()));
-
-  Telemetry::Accumulate(Telemetry::VIDEO_HIDDEN_PLAY_TIME_MS, SECONDS_TO_MS(mHiddenPlayTime.Total()));
-  LOG(LogLevel::Debug, ("%p VIDEO_HIDDEN_PLAY_TIME_MS = %f", this, mHiddenPlayTime.Total()));
+  double playTime = mPlayTime.Total();
+  double hiddenPlayTime = mHiddenPlayTime.Total();
+
+  Telemetry::Accumulate(Telemetry::VIDEO_PLAY_TIME_MS, SECONDS_TO_MS(playTime));
+  LOG(LogLevel::Debug, ("%p VIDEO_PLAY_TIME_MS = %f", this, playTime));
+
+  Telemetry::Accumulate(Telemetry::VIDEO_HIDDEN_PLAY_TIME_MS, SECONDS_TO_MS(hiddenPlayTime));
+  LOG(LogLevel::Debug, ("%p VIDEO_HIDDEN_PLAY_TIME_MS = %f", this, hiddenPlayTime));
+
+  if (playTime > 0.0 &&
+      mMediaInfo.HasVideo() &&
+      mMediaInfo.mVideo.mImage.height > 0) {
+    // We have actually played some valid video -> Report hidden/total ratio.
+    uint32_t hiddenPercentage = uint32_t(hiddenPlayTime / playTime * 100.0 + 0.5);
+
+    // Keyed by audio+video or video alone, and by a resolution range.
+    nsCString key(mMediaInfo.HasAudio() ? "AV," : "V,");
+    static const struct { int32_t mH; const char* mRes; } sResolutions[] = {
+      {  240, "0<h<=240" },
+      {  480, "240<h<=480" },
+      {  576, "480<h<=576" },
+      {  720, "576<h<=720" },
+      { 1080, "720<h<=1080" },
+      { 2160, "1080<h<=2160" }
+    };
+    const char* resolution = "h>2160";
+    int32_t height = mMediaInfo.mVideo.mImage.height;
+    for (const auto& res : sResolutions) {
+      if (height <= res.mH) {
+        resolution = res.mRes;
+        break;
+      }
+    }
+    key.AppendASCII(resolution);
+
+    Telemetry::Accumulate(Telemetry::VIDEO_HIDDEN_PLAY_TIME_PERCENTAGE,
+                          key,
+                          hiddenPercentage);
+    // Also accumulate all percentages in an "All" key.
+    Telemetry::Accumulate(Telemetry::VIDEO_HIDDEN_PLAY_TIME_PERCENTAGE,
+                          NS_LITERAL_CSTRING("All"),
+                          hiddenPercentage);
+    LOG(LogLevel::Debug, ("%p VIDEO_HIDDEN_PLAY_TIME_PERCENTAGE = %u, keys: '%s' and 'All'",
+                          this, hiddenPercentage, key.get()));
+  }
 }
 
 void HTMLMediaElement::UnbindFromTree(bool aDeep,
                                       bool aNullParent)
 {
   if (!mPaused && mNetworkState != nsIDOMHTMLMediaElement::NETWORK_EMPTY) {
     Pause();
   }
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -1570,44 +1570,57 @@ protected:
     // tree before.
     ELEMENT_NOT_INTREE_HAD_INTREE
   };
 
   ElementInTreeState mElementInTreeState;
 
 public:
   // Helper class to measure times for MSE telemetry stats
-  class TimeDurationAccumulator {
+  class TimeDurationAccumulator
+  {
   public:
     TimeDurationAccumulator()
       : mCount(0)
+    {}
+    void Start()
     {
-    }
-    void Start() {
       if (IsStarted()) {
         return;
       }
       mStartTime = TimeStamp::Now();
     }
-    void Pause() {
+    void Pause()
+    {
       if (!IsStarted()) {
         return;
       }
       mSum += (TimeStamp::Now() - mStartTime);
       mCount++;
       mStartTime = TimeStamp();
     }
-    bool IsStarted() const {
+    bool IsStarted() const
+    {
       return !mStartTime.IsNull();
     }
-    double Total() const {
-      return mSum.ToSeconds();
+    double Total() const
+    {
+      if (!IsStarted()) {
+        return mSum.ToSeconds();
+      }
+      // Add current running time until now, but keep it running.
+      return (mSum + (TimeStamp::Now() - mStartTime)).ToSeconds();
     }
-    uint32_t Count() const {
-      return mCount;
+    uint32_t Count() const
+    {
+      if (!IsStarted()) {
+        return mCount;
+      }
+      // Count current run in this report, without increasing the stored count.
+      return mCount + 1;
     }
   private:
     TimeStamp mStartTime;
     TimeDuration mSum;
     uint32_t mCount;
   };
 private:
   // Total time a video has spent playing.
--- 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;
@@ -233,44 +231,45 @@ public:
 
   Node* mStack;
   int32_t mStackSize;
   int32_t mStackPos;
 };
 
 nsresult
 NS_NewHTMLElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
-                  FromParser aFromParser)
+                  FromParser aFromParser, nsAString* aIs)
 {
   *aResult = nullptr;
 
   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)) {
+  if ((tag == eHTMLTag_userdefined &&
+      nsContentUtils::IsCustomElementName(name)) ||
+      aIs) {
     nsIDocument* doc = nodeInfo->GetDocument();
 
     NS_IF_ADDREF(*aResult = NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
     if (!*aResult) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
-    doc->SetupCustomElement(*aResult, kNameSpaceID_XHTML);
+    doc->SetupCustomElement(*aResult, kNameSpaceID_XHTML, aIs);
 
     return NS_OK;
   }
 
   *aResult = CreateHTMLElement(tag,
                                nodeInfo.forget(), aFromParser).take();
   return *aResult ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
 }
@@ -668,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)
@@ -715,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);
@@ -833,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
@@ -894,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;
@@ -1090,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/ContentProcess.h
+++ b/dom/ipc/ContentProcess.h
@@ -6,16 +6,20 @@
 
 #ifndef dom_tabs_ContentThread_h
 #define dom_tabs_ContentThread_h 1
 
 #include "mozilla/ipc/ProcessChild.h"
 #include "mozilla/ipc/ScopedXREEmbed.h"
 #include "ContentChild.h"
 
+#if defined(XP_WIN)
+#include "mozilla/mscom/MainThreadRuntime.h"
+#endif
+
 namespace mozilla {
 namespace dom {
 
 /**
  * ContentProcess is a singleton on the content process which represents
  * the main thread where tab instances live.
  */
 class ContentProcess : public mozilla::ipc::ProcessChild
@@ -33,16 +37,20 @@ public:
   virtual bool Init() override;
   virtual void CleanUp() override;
 
   void SetAppDir(const nsACString& aPath);
 
 private:
   ContentChild mContent;
   mozilla::ipc::ScopedXREEmbed mXREEmbed;
+#if defined(XP_WIN)
+  // This object initializes and configures COM.
+  mozilla::mscom::MainThreadRuntime mCOMRuntime;
+#endif
 
   DISALLOW_EVIL_CONSTRUCTORS(ContentProcess);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif  // ifndef dom_tabs_ContentThread_h
--- 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/AccurateSeekTask.h
+++ b/dom/media/AccurateSeekTask.h
@@ -3,17 +3,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef ACCURATE_SEEK_TASK_H
 #define ACCURATE_SEEK_TASK_H
 
 #include "SeekTask.h"
-#include "MediaCallbackID.h"
 #include "MediaDecoderReader.h"
 #include "SeekJob.h"
 
 namespace mozilla {
 
 class AccurateSeekTask final : public SeekTask {
 public:
   AccurateSeekTask(const void* aDecoderID,
--- 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];
deleted file mode 100644
--- a/dom/media/MediaCallbackID.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-#include "MediaCallbackID.h"
-
-namespace mozilla {
-
-char const* CallbackID::INVALID_TAG = "INVALID_TAG";
-int32_t const CallbackID::INVALID_ID = -1;
-
-CallbackID::CallbackID()
-  : mTag(INVALID_TAG), mID(INVALID_ID)
-{
-}
-
-CallbackID::CallbackID(char const* aTag, int32_t aID /* = 0*/)
-  : mTag(aTag), mID(aID)
-{
-}
-
-CallbackID&
-CallbackID::operator++()
-{
-  ++mID;
-  return *this;
-}
-
-CallbackID
-CallbackID::operator++(int)
-{
-  CallbackID ret = *this;
-  ++(*this); // call prefix++
-  return ret;
-}
-
-bool
-CallbackID::operator==(const CallbackID& rhs) const
-{
-  return (strcmp(mTag, rhs.mTag) == 0) && (mID == rhs.mID);
-}
-
-bool
-CallbackID::operator!=(const CallbackID& rhs) const
-{
-  return !(*this == rhs);
-}
-
-CallbackID::operator int() const
-{
-  return mID;
-}
-
-} // namespace mozilla
deleted file mode 100644
--- a/dom/media/MediaCallbackID.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef MediaCallbackID_h_
-#define MediaCallbackID_h_
-
-namespace mozilla {
-
-struct CallbackID
-{
-  static char const* INVALID_TAG;
-  static int32_t const INVALID_ID;
-
-  CallbackID();
-
-  explicit CallbackID(char const* aTag, int32_t aID = 0);
-
-  CallbackID& operator++();   // prefix++
-
-  CallbackID operator++(int); // postfix++
-
-  bool operator==(const CallbackID& rhs) const;
-
-  bool operator!=(const CallbackID& rhs) const;
-
-  operator int() const;
-
-private:
-  char const* mTag;
-  int32_t mID;
-};
-
-} // namespace mozilla
-
-#endif // MediaCallbackID_h_
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -201,25 +201,21 @@ static void InitVideoQueuePrefs() {
       "media.video-queue.hw-accel-size", HW_VIDEO_QUEUE_SIZE);
     sVideoQueueSendToCompositorSize = Preferences::GetUint(
       "media.video-queue.send-to-compositor-size", VIDEO_QUEUE_SEND_TO_COMPOSITOR_SIZE);
   }
 }
 
 // Delay, in milliseconds, that tabs needs to be in background before video
 // decoding is suspended.
-static TimeDuration sSuspendBackgroundVideoDelay;
-
-static void
-InitSuspendBackgroundPref()
+static TimeDuration
+SuspendBackgroundVideoDelay()
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
-
-  sSuspendBackgroundVideoDelay = TimeDuration::FromMilliseconds(
-      MediaPrefs::MDSMSuspendBackgroundVideoDelay());
+  return TimeDuration::FromMilliseconds(
+    MediaPrefs::MDSMSuspendBackgroundVideoDelay());
 }
 
 #define INIT_WATCHABLE(name, val) \
   name(val, "MediaDecoderStateMachine::" #name)
 #define INIT_MIRROR(name, val) \
   name(mTaskQueue, val, "MediaDecoderStateMachine::" #name " (Mirror)")
 #define INIT_CANONICAL(name, val) \
   name(mTaskQueue, val, "MediaDecoderStateMachine::" #name " (Canonical)")
@@ -289,17 +285,16 @@ MediaDecoderStateMachine::MediaDecoderSt
   INIT_CANONICAL(mCurrentPosition, 0),
   INIT_CANONICAL(mPlaybackOffset, 0),
   INIT_CANONICAL(mIsAudioDataAudible, false)
 {
   MOZ_COUNT_CTOR(MediaDecoderStateMachine);
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
 
   InitVideoQueuePrefs();
-  InitSuspendBackgroundPref();
 
   mBufferingWait = IsRealTime() ? 0 : 15;
   mLowDataThresholdUsecs = IsRealTime() ? 0 : detail::LOW_DATA_THRESHOLD_USECS;
 
 #ifdef XP_WIN
   // Ensure high precision timers are enabled on Windows, otherwise the state
   // machine isn't woken up at reliable intervals to set the next frame,
   // and we drop frames while painting. Note that multiple calls to this
@@ -758,17 +753,16 @@ MediaDecoderStateMachine::MaybeFinishDec
     return false;
   }
   FinishDecodeFirstFrame();
   if (!mQueuedSeek.Exists()) {
     return false;
   }
 
   // We can now complete the pending seek.
-  SetState(DECODER_STATE_SEEKING);
   InitiateSeek(Move(mQueuedSeek));
   return true;
 }
 
 void
 MediaDecoderStateMachine::OnVideoDecoded(MediaData* aVideoSample,
                                          TimeStamp aDecodeStartTime)
 {
@@ -1266,17 +1260,16 @@ void MediaDecoderStateMachine::StartDeco
       // the first samples in order to determine the media start time,
       // we have the start time from last time we loaded.
       // FinishDecodeFirstFrame will be launched upon completion of the seek when
       // we have data ready to play.
       MOZ_ASSERT(mQueuedSeek.Exists() && mSentFirstFrameLoadedEvent,
                  "Return from dormant must have queued seek");
     }
     if (mQueuedSeek.Exists()) {
-      SetState(DECODER_STATE_SEEKING);
       InitiateSeek(Move(mQueuedSeek));
       return;
     }
   }
 
   mDecodeStartTime = TimeStamp::Now();
 
   CheckIfDecodeComplete();
@@ -1348,17 +1341,17 @@ void MediaDecoderStateMachine::Visibilit
 
   // If not playing then there's nothing to do.
   if (mPlayState != MediaDecoder::PLAY_STATE_PLAYING) {
     return;
   }
 
   // Start timer to trigger suspended decoding state when going invisible.
   if (!mIsVisible) {
-    TimeStamp target = TimeStamp::Now() + sSuspendBackgroundVideoDelay;
+    TimeStamp target = TimeStamp::Now() + SuspendBackgroundVideoDelay();
 
     RefPtr<MediaDecoderStateMachine> self = this;
     mVideoDecodeSuspendTimer.Ensure(target,
                                     [=]() { self->OnSuspendTimerResolved(); },
                                     [=]() { self->OnSuspendTimerRejected(); });
     return;
   }
 
@@ -1519,22 +1512,20 @@ MediaDecoderStateMachine::Seek(SeekTarge
     DECODER_LOG("Seek() Not Enough Data to continue at this stage, queuing seek");
     mQueuedSeek.RejectIfExists(__func__);
     mQueuedSeek.mTarget = aTarget;
     return mQueuedSeek.mPromise.Ensure(__func__);
   }
   mQueuedSeek.RejectIfExists(__func__);
 
   DECODER_LOG("Changed state to SEEKING (to %lld)", aTarget.GetTime().ToMicroseconds());
-  SetState(DECODER_STATE_SEEKING);
 
   SeekJob seekJob;
   seekJob.mTarget = aTarget;
-  InitiateSeek(Move(seekJob));
-  return mCurrentSeek.mPromise.Ensure(__func__);
+  return InitiateSeek(Move(seekJob));
 }
 
 RefPtr<MediaDecoder::SeekPromise>
 MediaDecoderStateMachine::InvokeSeek(SeekTarget aTarget)
 {
   return InvokeAsync(OwnerThread(), this, __func__,
                      &MediaDecoderStateMachine::Seek, aTarget);
 }
@@ -1604,65 +1595,69 @@ MediaDecoderStateMachine::DispatchDecode
   if (needIdle) {
     DECODER_LOG("Dispatching SetIdle() audioQueue=%lld videoQueue=%lld",
                 GetDecodedAudioDuration(),
                 VideoQueue().Duration());
     mReader->SetIdle();
   }
 }
 
-void
+RefPtr<MediaDecoder::SeekPromise>
 MediaDecoderStateMachine::InitiateSeek(SeekJob aSeekJob)
 {
   MOZ_ASSERT(OnTaskQueue());
 
+  SetState(DECODER_STATE_SEEKING);
+
   // Discard the existing seek task.
   DiscardSeekTaskIfExist();
 
   mSeekTaskRequest.DisconnectIfExists();
 
   // SeekTask will register its callbacks to MediaDecoderReaderWrapper.
   CancelMediaDecoderReaderWrapperCallback();
 
   // Create a new SeekTask instance for the incoming seek task.
-  MOZ_ASSERT(!mCurrentSeek.Exists());
-  mCurrentSeek = Move(aSeekJob);
-  if (mCurrentSeek.mTarget.IsAccurate() || mCurrentSeek.mTarget.IsFast()) {
+  if (aSeekJob.mTarget.IsAccurate() || aSeekJob.mTarget.IsFast()) {
     mSeekTask = new AccurateSeekTask(mDecoderID, OwnerThread(),
-                                     mReader.get(), mCurrentSeek.mTarget,
+                                     mReader.get(), aSeekJob.mTarget,
                                      mInfo, Duration(), GetMediaTime());
-  } else if (mCurrentSeek.mTarget.IsNextFrame()) {
+  } else if (aSeekJob.mTarget.IsNextFrame()) {
     mSeekTask = new NextFrameSeekTask(mDecoderID, OwnerThread(), mReader.get(),
-                                      mCurrentSeek.mTarget, mInfo, Duration(),
+                                      aSeekJob.mTarget, mInfo, Duration(),
                                       GetMediaTime(), AudioQueue(), VideoQueue());
   } else {
     MOZ_DIAGNOSTIC_ASSERT(false, "Cannot handle this seek task.");
   }
 
   // Stop playback now to ensure that while we're outside the monitor
   // dispatching SeekingStarted, playback doesn't advance and mess with
   // mCurrentPosition that we've setting to seekTime here.
   StopPlayback();
 
-  // mCurrentSeek.mTarget.mTime might be different from
+  // aSeekJob.mTarget.mTime might be different from
   // mSeekTask->GetSeekTarget().mTime because the seek task might clamp the seek
   // target to [0, duration]. We want to update the playback position to the
   // clamped value.
   UpdatePlaybackPositionInternal(mSeekTask->GetSeekTarget().GetTime().ToMicroseconds());
 
-  mOnSeekingStart.Notify(mCurrentSeek.mTarget.mEventVisibility);
+  mOnSeekingStart.Notify(aSeekJob.mTarget.mEventVisibility);
 
   // Reset our state machine and decoding pipeline before seeking.
   if (mSeekTask->NeedToResetMDSM()) { Reset(); }
 
   // Do the seek.
   mSeekTaskRequest.Begin(mSeekTask->Seek(Duration())
     ->Then(OwnerThread(), __func__, this,
            &MediaDecoderStateMachine::OnSeekTaskResolved,
            &MediaDecoderStateMachine::OnSeekTaskRejected));
+
+  MOZ_ASSERT(!mCurrentSeek.Exists());
+  mCurrentSeek = Move(aSeekJob);
+  return mCurrentSeek.mPromise.Ensure(__func__);
 }
 
 void
 MediaDecoderStateMachine::OnSeekTaskResolved(SeekTaskResolveValue aValue)
 {
   MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(mState == DECODER_STATE_SEEKING);
 
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -83,17 +83,16 @@ hardware (via AudioStream).
 #define MediaDecoderStateMachine_h__
 
 #include "mozilla/Attributes.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/StateMirroring.h"
 
 #include "nsAutoPtr.h"
 #include "nsThreadUtils.h"
-#include "MediaCallbackID.h"
 #include "MediaDecoder.h"
 #include "MediaDecoderReader.h"
 #include "MediaDecoderOwner.h"
 #include "MediaEventSource.h"
 #include "MediaMetadataManager.h"
 #include "MediaStatistics.h"
 #include "MediaTimer.h"
 #include "ImageContainer.h"
@@ -504,18 +503,17 @@ protected:
   // Dispatches a LoadedMetadataEvent.
   // This is threadsafe and can be called on any thread.
   // The decoder monitor must be held.
   void EnqueueLoadedMetadataEvent();
 
   void EnqueueFirstFrameLoadedEvent();
 
   // Clears any previous seeking state and initiates a new seek on the decoder.
-  // The decoder monitor must be held.
-  void InitiateSeek(SeekJob aSeekJob);
+  RefPtr<MediaDecoder::SeekPromise> InitiateSeek(SeekJob aSeekJob);
 
   // Clears any previous seeking state and initiates a seek on the decoder to
   // resync the video and audio positions, when recovering from video decoding
   // being suspended in background or from audio and video decoding being
   // suspended due to the decoder limit.
   void InitiateDecodeRecoverySeek(TrackSet aTracks);
 
   nsresult DispatchAudioDecodeTaskIfNeeded();
--- 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/MediaPrefs.cpp
+++ b/dom/media/MediaPrefs.cpp
@@ -64,9 +64,16 @@ void MediaPrefs::PrefAddVarCache(uint32_
 
 void MediaPrefs::PrefAddVarCache(float* aVariable,
                                  const char* aPref,
                                  float aDefault)
 {
   Preferences::AddFloatVarCache(aVariable, aPref, aDefault);
 }
 
+void MediaPrefs::PrefAddVarCache(AtomicUint32* aVariable,
+                                 const char* aPref,
+                                 uint32_t aDefault)
+{
+  Preferences::AddAtomicUintVarCache(aVariable, aPref, aDefault);
+}
+
 } // namespace mozilla
--- a/dom/media/MediaPrefs.h
+++ b/dom/media/MediaPrefs.h
@@ -5,16 +5,18 @@
 
 #ifndef MEDIA_PREFS_H
 #define MEDIA_PREFS_H
 
 #ifdef MOZ_WIDGET_ANDROID
 #include "AndroidBridge.h"
 #endif
 
+#include "mozilla/Atomics.h"
+
 // First time MediaPrefs::GetSingleton() needs to be called on the main thread,
 // before any of the methods accessing the values are used, but after
 // the Preferences system has been initialized.
 
 // The static methods to access the preference value are safe to call
 // from any thread after that first call.
 
 // To register a preference, you need to add a line in this file using
@@ -28,34 +30,48 @@
 // "media.resampling.enabled".  If the value is not set, the default would be
 // false.
 
 #define DECL_MEDIA_PREF(Pref, Name, Type, Default)                            \
 public:                                                                       \
 static const Type& Name() { MOZ_ASSERT(SingletonExists()); return GetSingleton().mPref##Name.mValue; } \
 private:                                                                      \
 static const char* Get##Name##PrefName() { return Pref; }                     \
-static Type Get##Name##PrefDefault() { return Default; }                      \
+static StripAtomic<Type> Get##Name##PrefDefault() { return Default; }         \
 PrefTemplate<Type, Get##Name##PrefDefault, Get##Name##PrefName> mPref##Name
 
 // Custom Definitions.
 #define GMP_DEFAULT_ASYNC_SHUTDOWN_TIMEOUT 3000
 #define SUSPEND_BACKGROUND_VIDEO_DELAY_MS 10000
 #define TEST_PREFERENCE_FAKE_RECOGNITION_SERVICE "media.webspeech.test.fake_recognition_service"
 
 namespace mozilla {
 
 template<class T> class StaticAutoPtr;
 
 class MediaPrefs final
 {
+  typedef Atomic<uint32_t, Relaxed> AtomicUint32;
+
+  template <typename T>
+  struct StripAtomicImpl {
+    typedef T Type;
+  };
+
+  template <typename T, MemoryOrdering Order>
+  struct StripAtomicImpl<Atomic<T, Order>> {
+    typedef T Type;
+  };
+
+  template <typename T>
+  using StripAtomic = typename StripAtomicImpl<T>::Type;
 
 private:
   // Since we cannot use const char*, use a function that returns it.
-  template <class T, T Default(), const char* Pref()>
+  template <class T, StripAtomic<T> Default(), const char* Pref()>
   class PrefTemplate
   {
   public:
     PrefTemplate()
     : mValue(Default())
     {
       Register(Pref());
     }
@@ -109,17 +125,17 @@ private:
   DECL_MEDIA_PREF("media.decoder.fuzzing.video-output-minimum-interval-ms", PDMFuzzingInterval, uint32_t, 0);
   DECL_MEDIA_PREF("media.decoder.fuzzing.dont-delay-inputexhausted", PDMFuzzingDelayInputExhausted, bool, true);
   DECL_MEDIA_PREF("media.gmp.decoder.enabled",                PDMGMPEnabled, bool, true);
   DECL_MEDIA_PREF("media.gmp.decoder.aac",                    GMPAACPreferred, uint32_t, 0);
   DECL_MEDIA_PREF("media.gmp.decoder.h264",                   GMPH264Preferred, uint32_t, 0);
 
   // MediaDecoderStateMachine
   DECL_MEDIA_PREF("media.suspend-bkgnd-video.enabled",        MDSMSuspendBackgroundVideoEnabled, bool, false);
-  DECL_MEDIA_PREF("media.suspend-bkgnd-video.delay-ms",       MDSMSuspendBackgroundVideoDelay, uint32_t, SUSPEND_BACKGROUND_VIDEO_DELAY_MS);
+  DECL_MEDIA_PREF("media.suspend-bkgnd-video.delay-ms",       MDSMSuspendBackgroundVideoDelay, AtomicUint32, SUSPEND_BACKGROUND_VIDEO_DELAY_MS);
 
   // WebSpeech
   DECL_MEDIA_PREF("media.webspeech.synth.force_global_queue", WebSpeechForceGlobal, bool, false);
   DECL_MEDIA_PREF("media.webspeech.test.enable",              WebSpeechTestEnabled, bool, false);
   DECL_MEDIA_PREF("media.webspeech.test.fake_fsm_events",     WebSpeechFakeFSMEvents, bool, false);
   DECL_MEDIA_PREF(TEST_PREFERENCE_FAKE_RECOGNITION_SERVICE,   WebSpeechFakeRecognitionService, bool, false);
   DECL_MEDIA_PREF("media.webspeech.recognition.enable",       WebSpeechRecognitionEnabled, bool, false);
   DECL_MEDIA_PREF("media.webspeech.recognition.force_enable", WebSpeechRecognitionForceEnabled, bool, false);
@@ -151,16 +167,17 @@ private:
     return -1;
   }
 
   // Creating these to avoid having to include Preferences.h in the .h
   static void PrefAddVarCache(bool*, const char*, bool);
   static void PrefAddVarCache(int32_t*, const char*, int32_t);
   static void PrefAddVarCache(uint32_t*, const char*, uint32_t);
   static void PrefAddVarCache(float*, const char*, float);
+  static void PrefAddVarCache(AtomicUint32*, const char*, uint32_t);
 
   static void AssertMainThread();
 
   MediaPrefs();
   MediaPrefs(const MediaPrefs&) = delete;
   MediaPrefs& operator=(const MediaPrefs&) = delete;
 };
 
--- a/dom/media/SeekTask.h
+++ b/dom/media/SeekTask.h
@@ -3,17 +3,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef SEEK_TASK_H
 #define SEEK_TASK_H
 
 #include "mozilla/MozPromise.h"
-#include "MediaCallbackID.h"
 #include "SeekTarget.h"
 
 namespace mozilla {
 
 class AbstractThread;
 class MediaData;
 class MediaDecoderReaderWrapper;
 
--- 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/gmp/GMPProcessParent.cpp
+++ b/dom/media/gmp/GMPProcessParent.cpp
@@ -58,17 +58,30 @@ GMPProcessParent::Launch(int32_t aTimeou
   // The sandbox doesn't allow file system rules where the paths contain
   // symbolic links or junction points. Sometimes the Users folder has been
   // moved to another drive using a junction point, so allow for this specific
   // case. See bug 1236680 for details.
   if (!widget::WinUtils::ResolveMovedUsersFolder(wGMPPath)) {
     NS_WARNING("ResolveMovedUsersFolder failed for GMP path.");
     return false;
   }
-  mAllowedFilesRead.push_back(wGMPPath + L"\\*");
+
+  // If the GMP path is a network path that is not mapped to a drive letter,
+  // then we need to fix the path format for the sandbox rule.
+  wchar_t volPath[MAX_PATH];
+  if (::GetVolumePathNameW(wGMPPath.c_str(), volPath, MAX_PATH) &&
+      ::GetDriveTypeW(volPath) == DRIVE_REMOTE &&
+      wGMPPath.compare(0, 2, L"\\\\") == 0) {
+    std::wstring sandboxGMPPath(wGMPPath);
+    sandboxGMPPath.insert(1, L"??\\UNC");
+    mAllowedFilesRead.push_back(sandboxGMPPath + L"\\*");
+  } else {
+    mAllowedFilesRead.push_back(wGMPPath + L"\\*");
+  }
+
   args.push_back(WideToUTF8(wGMPPath));
 #else
   args.push_back(mGMPPath);
 #endif
 
   args.push_back(string(voucherPath.BeginReading(), voucherPath.EndReading()));
 
   return SyncLaunch(args, aTimeoutMs, base::GetCurrentProcessArchitecture());
--- 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/moz.build
+++ b/dom/media/moz.build
@@ -103,17 +103,16 @@ EXPORTS += [
     'DecoderTraits.h',
     'DOMMediaStream.h',
     'EncodedBufferCache.h',
     'FileBlockCache.h',
     'FrameStatistics.h',
     'Intervals.h',
     'Latency.h',
     'MediaCache.h',
-    'MediaCallbackID.h',
     'MediaData.h',
     'MediaDataDemuxer.h',
     'MediaDecoder.h',
     'MediaDecoderOwner.h',
     'MediaDecoderReader.h',
     'MediaDecoderStateMachine.h',
     'MediaEventSource.h',
     'MediaFormatReader.h',
@@ -217,17 +216,16 @@ UNIFIED_SOURCES += [
     'DecoderDoctorDiagnostics.cpp',
     'DOMMediaStream.cpp',
     'EncodedBufferCache.cpp',
     'FileBlockCache.cpp',
     'GetUserMediaRequest.cpp',
     'GraphDriver.cpp',
     'Latency.cpp',
     'MediaCache.cpp',
-    'MediaCallbackID.cpp',
     'MediaData.cpp',
     'MediaDecoder.cpp',
     'MediaDecoderReader.cpp',
     'MediaDecoderReaderWrapper.cpp',
     'MediaDecoderStateMachine.cpp',
     'MediaDeviceInfo.cpp',
     'MediaDevices.cpp',
     'MediaFormatReader.cpp',
--- 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/plugins/ipc/PluginProcessChild.cpp
+++ b/dom/plugins/ipc/PluginProcessChild.cpp
@@ -16,17 +16,16 @@
 #if defined(XP_MACOSX)
 #include "nsCocoaFeatures.h"
 // An undocumented CoreGraphics framework method, present in the same form
 // since at least OS X 10.5.
 extern "C" CGError CGSSetDebugOptions(int options);
 #endif
 
 #ifdef XP_WIN
-#include <objbase.h>
 bool ShouldProtectPluginCurrentDirectory(char16ptr_t pluginFilePath);
 #if defined(MOZ_SANDBOX)
 #define TARGET_SANDBOX_EXPORTS
 #include "mozilla/sandboxTarget.h"
 #endif
 #endif
 
 using mozilla::ipc::IOThreadChild;
@@ -76,22 +75,16 @@ PluginProcessChild::Init()
             }
             // Values passed to PR_SetEnv() must be seperately allocated.
             char* setInterposePtr = strdup(setInterpose.get());
             PR_SetEnv(setInterposePtr);
         }
     }
 #endif
 
-#ifdef XP_WIN
-    // Drag-and-drop needs OleInitialize to be called, and Silverlight depends
-    // on the host calling CoInitialize (which is called by OleInitialize).
-    ::OleInitialize(nullptr);
-#endif
-
     // Certain plugins, such as flash, steal the unhandled exception filter
     // thus we never get crash reports when they fault. This call fixes it.
     message_loop()->set_exception_restoration(true);
 
     std::string pluginFilename;
 
 #if defined(OS_POSIX)
     // NB: need to be very careful in ensuring that the first arg
@@ -144,16 +137,13 @@ PluginProcessChild::Init()
     }
 #endif
     return retval;
 }
 
 void
 PluginProcessChild::CleanUp()
 {
-#ifdef XP_WIN
-    ::OleUninitialize();
-#endif
     nsRegion::ShutdownStatic();
 }
 
 } // namespace plugins
 } // namespace mozilla
--- a/dom/plugins/ipc/PluginProcessChild.h
+++ b/dom/plugins/ipc/PluginProcessChild.h
@@ -5,16 +5,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef dom_plugins_PluginProcessChild_h
 #define dom_plugins_PluginProcessChild_h 1
 
 #include "mozilla/ipc/ProcessChild.h"
 #include "mozilla/plugins/PluginModuleChild.h"
 
+#if defined(XP_WIN)
+#include "mozilla/mscom/MainThreadRuntime.h"
+#endif
+
 namespace mozilla {
 namespace plugins {
 //-----------------------------------------------------------------------------
 
 class PluginProcessChild : public mozilla::ipc::ProcessChild {
 protected:
     typedef mozilla::ipc::ProcessChild ProcessChild;
 
@@ -30,16 +34,21 @@ public:
     virtual void CleanUp() override;
 
 protected:
     static PluginProcessChild* current() {
         return static_cast<PluginProcessChild*>(ProcessChild::current());
     }
 
 private:
+#if defined(XP_WIN)
+    /* Drag-and-drop and Silverlight depend on the host initializing COM.
+     * This object initializes and configures COM. */
+    mozilla::mscom::MainThreadRuntime mCOMRuntime;
+#endif
     PluginModuleChild mPlugin;
 
     DISALLOW_EVIL_CONSTRUCTORS(PluginProcessChild);
 };
 
 } // namespace plugins
 } // namespace mozilla
 
--- 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/tests/mochitest/webcomponents/test_custom_element_clone_callbacks_extended.html
+++ b/dom/tests/mochitest/webcomponents/test_custom_element_clone_callbacks_extended.html
@@ -9,48 +9,32 @@ https://bugzilla.mozilla.org/show_bug.cg
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1081039">Bug 1081039</a>
 <script>
 
 SimpleTest.waitForExplicitFinish();
 
-// Test to make sure created callback is called on clones that are upgraded and clones
-// created after registering the custom element.
-
-var callbackCalledOnUpgrade = false;
-var callbackCalledOnClone = false;
+// Test to make sure created callback is called on clones created after
+// registering the custom element.
 
-var foo = document.createElement("button", "x-foo");
-is(foo.getAttribute("is"), "x-foo");
-
-var fooClone = foo.cloneNode(true);
-
+var count = 0;
 var p = Object.create(HTMLButtonElement.prototype);
-p.createdCallback = function() {
+p.createdCallback = function() { // should be called by createElement and cloneNode
   is(this.__proto__, p, "Correct prototype should be set on custom elements.");
 
-  if (this == fooClone) {
-    // Callback called for the element created before registering the custom element.
-    // Should be called on element upgrade.
-    is(callbackCalledOnUpgrade, false, "Upgrade should only be called once per clone.");
-    callbackCalledOnUpgrade = true;
-  } else if (this != foo) {
-    // Callback called for the element created after registering the custom element.
-    is(callbackCalledOnClone, false, "Upgrade should only be called once per clone.");
-    callbackCalledOnClone = true;
-  }
-
-  if (callbackCalledOnUpgrade && callbackCalledOnClone) {
+  if (++count == 2) {
     SimpleTest.finish();
   }
 };
 
 document.registerElement("x-foo", { prototype: p, extends: "button" });
+var foo = document.createElement("button", {is: "x-foo"});
+is(foo.getAttribute("is"), "x-foo");
 
-var anotherFooClone = foo.cloneNode(true);
+var fooClone = foo.cloneNode(true);
 
 SimpleTest.waitForExplicitFinish();
 
 </script>
 </body>
 </html>
--- a/dom/tests/mochitest/webcomponents/test_document_register.html
+++ b/dom/tests/mochitest/webcomponents/test_document_register.html
@@ -111,60 +111,66 @@ function startTest() {
   testRegisterSimple("X-SVG-DUPE-ME", Object.create(HTMLElement.prototype), true);
   testRegisterSimple("x-svg-dupe-me", null, true);
   testRegisterExtend("x-svg-dupe-me", "span", Object.create(HTMLElement.prototype), true);
   testRegisterExtend("x-svg-dupe-me", "shape", Object.create(SVGElement.prototype), true);
 
   // document.createElement with extended type.
   var extendedProto = Object.create(HTMLButtonElement.prototype);
   var buttonConstructor = document.registerElement("x-extended-button", { prototype: extendedProto, extends: "button" });
-  var extendedButton = document.createElement("button", "x-extended-button");
+  var extendedButton = document.createElement("button", {is: "x-extended-button"});
   is(extendedButton.tagName, "BUTTON", "Created element should have local name of BUTTON");
   is(extendedButton.__proto__, extendedProto, "Created element should have the prototype of the extended type.");
   is(extendedButton.getAttribute("is"), "x-extended-button", "The |is| attribute of the created element should be the extended type.");
 
   // document.createElementNS with different namespace than definition.
-  var svgButton = document.createElementNS("http://www.w3.org/2000/svg", "button", "x-extended-button");
-  isnot(svgButton.__proto__, extendedProto, "Definition for element is in html namespace, registration should not apply for SVG elements.");
+  try {
+    var svgButton = document.createElementNS("http://www.w3.org/2000/svg", "button", {is: "x-extended-button"});
+    ok(false, "An exception should've been thrown");
+  } catch(err) {
+    is(err.name, "NotFoundError", "A NotFoundError exception should've been thrown");
+  }
 
   // document.createElementNS with no namespace.
-  var noNamespaceButton = document.createElementNS("", "button", "x-extended-button");
-  isnot(noNamespaceButton.__proto__, extendedProto, "Definition for element is in html namespace, registration should not apply for elements with no namespace.");
+  try {
+    var noNamespaceButton = document.createElementNS("", "button", {is: "x-extended-button"});
+    ok(false, "An exception should've been thrown");
+  } catch(err) {
+    is(err.name, "NotFoundError", "A NotFoundError exception should've been thrown");
+  }
 
   // document.createElement with non-existant extended type.
-  var normalButton = document.createElement("button", "x-non-existant");
-  is(normalButton.__proto__, HTMLButtonElement.prototype, "When the extended type doesn't exist, prototype should not change.");
+  try {
+    var normalButton = document.createElement("button", {is: "x-non-existant"});
+    ok(false, "An exception should've been thrown");
+  } catch(err) {
+    is(err.name, "NotFoundError", "A NotFoundError exception should've been thrown");
+  }
 
   // document.createElement with exteneded type that does not match with local name of element.
-  var normalDiv = document.createElement("div", "x-extended-button");
-  is(normalDiv.__proto__, HTMLDivElement.prototype, "Prototype should not change when local name of extended type defintion does not match.");
+  try {
+    var normalDiv = document.createElement("div", {is: "x-extended-button"});
+    ok(false, "An exception should've been thrown");
+  } catch(err) {
+    is(err.name, "NotFoundError", "A NotFoundError exception should've been thrown");
+  }
 
   // Custom element constructor.
   var constructedButton = new buttonConstructor();
   is(constructedButton.tagName, "BUTTON", "Created element should have local name of BUTTON");
   is(constructedButton.__proto__, extendedProto, "Created element should have the prototype of the extended type.");
   is(constructedButton.getAttribute("is"), "x-extended-button", "The |is| attribute of the created element should be the extended type.");
 
-  // document.createElementNS with extended type.
-  var svgExtendedProto = Object.create(SVGTextElement.prototype);
-  var svgConstructor = document.registerElement("x-extended-text", { prototype: svgExtendedProto, extends: "text"});
-  var extendedText = document.createElementNS("http://www.w3.org/2000/svg", "text", "x-extended-text");
-  is(extendedText.tagName, "text", "Created element should have a local name of |text|.");
-  is(extendedText.__proto__, svgExtendedProto, "Created element have the registered prototype.");
-  is(extendedText.getAttribute("is"), "x-extended-text", "The |is| attribute of the created element should be the extended type.");
-
   // document.createElement with different namespace than definition for extended element.
-  var htmlText = document.createElement("text", "x-extended-text");
-  isnot(htmlText.__proto__, svgExtendedProto, "Definition for element in SVG namespace should not apply to HTML elements.");
-
-  // Custom element constructor for a SVG element.
-  var constructedText = new svgConstructor();
-  is(constructedText.tagName, "text", "Created element should have a local name of |text|.");
-  is(constructedText.__proto__, svgExtendedProto, "Created element have the registered prototype.");
-  is(constructedText.getAttribute("is"), "x-extended-text", "The |is| attribute of the created element should be the extended type.");
+  try {
+    var htmlText = document.createElement("text", {is: "x-extended-text"});
+    ok(false, "An exception should've been thrown");
+  } catch(err) {
+    is(err.name, "NotFoundError", "A NotFoundError exception should've been thrown");
+  }
 
   // Try creating an element with a custom element name, but not in the html namespace.
   var htmlNamespaceProto = Object.create(HTMLElement.prototype);
   document.registerElement("x-in-html-namespace", { prototype: htmlNamespaceProto });
   var wrongNamespaceElem = document.createElementNS("http://www.w3.org/2000/svg", "x-in-html-namespace");
   isnot(wrongNamespaceElem.__proto__, htmlNamespaceProto, "Definition for element in html namespace should not apply to SVG elements.");
 }
 
--- a/dom/tests/mochitest/webcomponents/test_document_register_lifecycle.html
+++ b/dom/tests/mochitest/webcomponents/test_document_register_lifecycle.html
@@ -295,17 +295,17 @@ function testAttributeChangedExtended() 
   p.attributeChangedCallback = function(name, oldValue, newValue) {
     is(callbackCalled, false, "Callback should only be called once in this test.");
     callbackCalled = true;
     runNextTest();
   };
 
   document.registerElement("x-extended-attribute-change", { prototype: p, extends: "button" });
 
-  var elem = document.createElement("button", "x-extended-attribute-change");
+  var elem = document.createElement("button", {is: "x-extended-attribute-change"});
   elem.setAttribute("foo", "bar");
 }
 
 // Creates a custom element that is an upgrade candidate (no registration)
 // and mutate the element in ways that would call callbacks for registered
 // elements.
 function testUpgradeCandidate() {
   var createdElement = document.createElement("x-upgrade-candidate");
--- a/dom/tv/TVTuner.cpp
+++ b/dom/tv/TVTuner.cpp
@@ -230,17 +230,18 @@ TVTuner::CreateSimulatedMediaStream()
   }
 
   nsIDocument* doc = domWin->GetExtantDoc();
   if (NS_WARN_IF(!doc)) {
     return nullptr;
   }
 
   ErrorResult error;
-  RefPtr<Element> element = doc->CreateElement(VIDEO_TAG, error);
+  ElementCreationOptions options;
+  RefPtr<Element> element = doc->CreateElement(VIDEO_TAG, options, error);
   if (NS_WARN_IF(error.Failed())) {
     return nullptr;
   }
 
   nsCOMPtr<nsIContent> content(do_QueryInterface(element));
   if (NS_WARN_IF(!content)) {
     return nullptr;
   }
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -9,16 +9,21 @@
 interface WindowProxy;
 interface nsISupports;
 interface URI;
 interface nsIDocShell;
 interface nsILoadGroup;
 
 enum VisibilityState { "hidden", "visible", "prerender" };
 
+/* https://dom.spec.whatwg.org/#dictdef-elementcreationoptions */
+dictionary ElementCreationOptions {
+  DOMString is;
+};
+
 /* http://dom.spec.whatwg.org/#interface-document */
 [Constructor]
 interface Document : Node {
   [Throws]
   readonly attribute DOMImplementation implementation;
   [Pure]
   readonly attribute DOMString URL;
   [Pure]
@@ -43,19 +48,19 @@ interface Document : Node {
   [Pure, Throws]
   HTMLCollection getElementsByTagNameNS(DOMString? namespace, DOMString localName);
   [Pure]
   HTMLCollection getElementsByClassName(DOMString classNames);
   [Pure]
   Element? getElementById(DOMString elementId);
 
   [NewObject, Throws]
-  Element createElement(DOMString localName);
+  Element createElement(DOMString localName, optional ElementCreationOptions options);
   [NewObject, Throws]
-  Element createElementNS(DOMString? namespace, DOMString qualifiedName);
+  Element createElementNS(DOMString? namespace, DOMString qualifiedName, optional ElementCreationOptions options);
   [NewObject]
   DocumentFragment createDocumentFragment();
   [NewObject]
   Text createTextNode(DOMString data);
   [NewObject]
   Comment createComment(DOMString data);
   [NewObject, Throws]
   ProcessingInstruction createProcessingInstruction(DOMString target, DOMString data);
@@ -255,24 +260,16 @@ partial interface Document {
 };
 
 //http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/index.html#dfn-document-register
 partial interface Document {
     [Throws, Func="nsDocument::IsWebComponentsEnabled"]
     object registerElement(DOMString name, optional ElementRegistrationOptions options);
 };
 
-//http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/index.html#dfn-document-register
-partial interface Document {
-    [NewObject, Throws]
-    Element createElement(DOMString localName, DOMString? typeExtension);
-    [NewObject, Throws]
-    Element createElementNS(DOMString? namespace, DOMString qualifiedName, DOMString? typeExtension);
-};
-
 // http://dvcs.w3.org/hg/webperf/raw-file/tip/specs/PageVisibility/Overview.html#sec-document-interface
 partial interface Document {
   readonly attribute boolean hidden;
   readonly attribute boolean mozHidden;
   readonly attribute VisibilityState visibilityState;
   readonly attribute VisibilityState mozVisibilityState;
 };
 
--- 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/Logging.h
+++ b/gfx/2d/Logging.h
@@ -496,19 +496,24 @@ private:
       }
       mMessage << "]: ";
     }
   }
 
   void WriteLog(const std::string &aString) {
     if (MOZ_UNLIKELY(LogIt())) {
       Logger::OutputMessage(aString, L, NoNewline());
+      // Assert if required.  We don't have a three parameter MOZ_ASSERT
+      // so use the underlying functions instead (see bug 1281702):
+#ifdef DEBUG
       if (mOptions & int(LogOptions::AssertOnCall)) {
-        MOZ_ASSERT(false, "An assert from the graphics logger");
+        MOZ_ReportAssertionFailure(aString.c_str(), __FILE__, __LINE__);
+        MOZ_CRASH("GFX: An assert from the graphics logger");
       }
+#endif
       if ((mOptions & int(LogOptions::CrashAction)) && ValidReason()) {
         Logger::CrashAction(mReason);
       }
     }
   }
 
   std::stringstream mMessage;
   int mOptions;
--- 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;
+    }