Merge mozilla-central to autoland
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 21 Jul 2016 16:27:58 +0200
changeset 346125 8ad7dc5cba0ca3c18cdc32beaa0b0808488cabd3
parent 346124 4ff8b2aeedf817730e30e378e39d036d0cb52f6e (current diff)
parent 346117 6b180266ac16e3226be33319ff710ddfa85f5836 (diff)
child 346126 2e3390571fdb3a1ff3d2f7f828adf67dbc237bc8
child 346127 115209d4f543a66994fa68ee9a187490cc60c17d
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 autoland
browser/themes/linux/customizableui/panelUIOverlay.css
browser/themes/osx/customizableui/panelUIOverlay.css
browser/themes/shared/customizableui/panelUIOverlay.inc.css
browser/themes/windows/customizableui/panelUIOverlay.css
devtools/client/framework/source-location.js
devtools/client/framework/test/browser_source-location-01.js
devtools/client/framework/test/browser_source-location-02.js
devtools/server/actors/utils/ScriptStore.js
devtools/server/tests/unit/test_ScriptStore.js
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/telemetry/Histograms.json
widget/gonk/nsScreenManagerGonk.cpp
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 911216 - followup from the backout to fix bustage
+Bug 911216 - clobber needed
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -2756,46 +2756,46 @@ KeyBinding::ToPlatformFormat(nsAString& 
     stringBundleService->CreateBundle(
       "chrome://global-platform/locale/platformKeys.properties",
       getter_AddRefs(keyStringBundle));
 
   if (!keyStringBundle)
     return;
 
   nsAutoString separator;
-  keyStringBundle->GetStringFromName(MOZ_UTF16("MODIFIER_SEPARATOR"),
+  keyStringBundle->GetStringFromName(u"MODIFIER_SEPARATOR",
                                      getter_Copies(separator));
 
   nsAutoString modifierName;
   if (mModifierMask & kControl) {
-    keyStringBundle->GetStringFromName(MOZ_UTF16("VK_CONTROL"),
+    keyStringBundle->GetStringFromName(u"VK_CONTROL",
                                        getter_Copies(modifierName));
 
     aValue.Append(modifierName);
     aValue.Append(separator);
   }
 
   if (mModifierMask & kAlt) {
-    keyStringBundle->GetStringFromName(MOZ_UTF16("VK_ALT"),
+    keyStringBundle->GetStringFromName(u"VK_ALT",
                                        getter_Copies(modifierName));
 
     aValue.Append(modifierName);
     aValue.Append(separator);
   }
 
   if (mModifierMask & kShift) {
-    keyStringBundle->GetStringFromName(MOZ_UTF16("VK_SHIFT"),
+    keyStringBundle->GetStringFromName(u"VK_SHIFT",
                                        getter_Copies(modifierName));
 
     aValue.Append(modifierName);
     aValue.Append(separator);
   }
 
   if (mModifierMask & kMeta) {
-    keyStringBundle->GetStringFromName(MOZ_UTF16("VK_META"),
+    keyStringBundle->GetStringFromName(u"VK_META",
                                        getter_Copies(modifierName));
 
     aValue.Append(modifierName);
     aValue.Append(separator);
   }
 
   aValue.Append(mKey);
 }
--- a/accessible/generic/ApplicationAccessible.cpp
+++ b/accessible/generic/ApplicationAccessible.cpp
@@ -49,17 +49,17 @@ ApplicationAccessible::Name(nsString& aN
 
   nsCOMPtr<nsIStringBundle> bundle;
   nsresult rv = bundleService->CreateBundle("chrome://branding/locale/brand.properties",
                                             getter_AddRefs(bundle));
   if (NS_FAILED(rv))
     return eNameOK;
 
   nsXPIDLString appName;
-  rv = bundle->GetStringFromName(MOZ_UTF16("brandShortName"),
+  rv = bundle->GetStringFromName(u"brandShortName",
                                  getter_Copies(appName));
   if (NS_FAILED(rv) || appName.IsEmpty()) {
     NS_WARNING("brandShortName not found, using default app name");
     appName.AssignLiteral("Gecko based application");
   }
 
   aName.Assign(appName);
   return eNameOK;
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -1125,20 +1125,36 @@ DocAccessible::CharacterDataChanged(nsID
 
 void
 DocAccessible::ContentInserted(nsIDocument* aDocument, nsIContent* aContainer,
                                nsIContent* aChild, int32_t /* unused */)
 {
 }
 
 void
-DocAccessible::ContentRemoved(nsIDocument* aDocument, nsIContent* aContainer,
-                              nsIContent* aChild, int32_t /* unused */,
-                              nsIContent* aPreviousSibling)
+DocAccessible::ContentRemoved(nsIDocument* aDocument,
+                              nsIContent* aContainerNode,
+                              nsIContent* aChildNode, int32_t /* unused */,
+                              nsIContent* aPreviousSiblingNode)
 {
+#ifdef A11Y_LOG
+  if (logging::IsEnabled(logging::eTree)) {
+    logging::MsgBegin("TREE", "DOM content removed; doc: %p", this);
+    logging::Node("container node", aContainerNode);
+    logging::Node("content node", aChildNode);
+    logging::MsgEnd();
+  }
+#endif
+  // This one and content removal notification from layout may result in
+  // double processing of same subtrees. If it pops up in profiling, then
+  // consider reusing a document node cache to reject these notifications early.
+  Accessible* container = GetAccessibleOrContainer(aContainerNode);
+  if (container) {
+    UpdateTreeOnRemoval(container, aChildNode);
+  }
 }
 
 void
 DocAccessible::ParentChainChanged(nsIContent* aContent)
 {
 }
 
 
--- a/accessible/tests/mochitest/treeupdate/a11y.ini
+++ b/accessible/tests/mochitest/treeupdate/a11y.ini
@@ -9,16 +9,17 @@ support-files =
 [test_bug852150.xhtml]
 [test_bug883708.xhtml]
 [test_bug884251.xhtml]
 [test_bug895082.html]
 [test_bug1040735.html]
 [test_bug1100602.html]
 [test_bug1175913.html]
 [test_bug1189277.html]
+[test_bug1276857.html]
 [test_canvas.html]
 [test_colorpicker.xul]
 [test_contextmenu.xul]
 [test_cssoverflow.html]
 [test_deck.xul]
 [test_doc.html]
 [test_gencontent.html]
 [test_general.html]
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_bug1276857.html
@@ -0,0 +1,143 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>DOM mutations test</title>
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="../role.js"></script>
+  <script type="application/javascript"
+          src="../events.js"></script>
+
+  <script type="application/javascript">
+    function runTest()
+    {
+      // children change will recreate the table
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, getNode('c1'))
+      ];
+
+      this.invoke = function runTest_invoke() {
+        var tree = {
+          SECTION: [ // c1
+            { TEXT_LEAF: [] }, // Some text
+            { TEXT_CONTAINER: [
+              { TEXT_LEAF: [] } // something with ..
+            ] },
+            { TEXT_LEAF: [] } // More text
+          ]
+        };
+        testAccessibleTree('c1', tree);
+
+        getNode('c1_t').querySelector('span').remove();
+      };
+
+      this.finalCheck = function runTest_finalCheck() {
+        var tree = {
+          SECTION: [ // c1
+            { TEXT_LEAF: [] }, // Some text
+            { TEXT_LEAF: [] } // More text
+          ]
+        };
+        testAccessibleTree('c1', tree);
+      };
+
+      this.getID = function runTest_getID()
+      {
+        return 'child DOM node is removed before the layout notifies the a11y about parent removal/show';
+      };
+    }
+
+    function runShadowTest()
+    {
+      // children change will recreate the table
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, 'c2')
+      ];
+
+      this.invoke = function runShadowTest_invoke() {
+        var tree = {
+          SECTION: [ // c2
+            { TEXT_LEAF: [] }, // Some text
+            { TEXT_CONTAINER: [
+              { TEXT_LEAF: [] } // something with ..
+            ] },
+            { TEXT_LEAF: [] } // More text
+          ]
+        };
+        testAccessibleTree('c2', tree);
+
+        gShadowRoot.firstElementChild.querySelector('span').remove();
+      };
+
+      this.finalCheck = function runShadowTest_finalCheck() {
+        var tree = {
+          SECTION: [ // c2
+            { TEXT_LEAF: [] }, // Some text
+            { TEXT_LEAF: [] } // More text
+          ]
+        };
+        testAccessibleTree('c2', tree);
+      };
+
+      this.getID = function runShadowTest_getID() {
+        return 'child DOM node is removed before the layout notifies the a11y about parent removal/show in shadow DOM';
+      };
+    }
+
+    //enableLogging("tree");
+    //gA11yEventDumpToConsole = true;
+
+    var gQueue = null;
+    function doTest()
+    {
+      gQueue = new eventQueue();
+      gQueue.push(new runTest());
+      gQueue.push(new runShadowTest());
+      gQueue.invoke(); // will call SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  </script>
+
+</head>
+
+<body>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <div id="c1">
+    <div id="c1_t" style="display: table" role="presentation">
+    Some text
+    <span style="display: table-cell">something with accessibles goes here</span>
+    More text
+    </div>
+  </div>
+
+  <template id="tmpl">
+    <div style="display: table" role="presentation">
+    Some text
+    <span style="display: table-cell">something with accessibles goes here</span>
+    More text
+    </div>
+  </template>
+
+  <div id="c2"><div id="c2_c" role="presentation"></div></div>
+
+  <script>
+    var gShadowRoot = document.getElementById('c2_c').createShadowRoot();
+    var tmpl = document.getElementById('tmpl');
+    gShadowRoot.appendChild(document.importNode(tmpl.content, true));
+  </script>
+</body>
+</html>
--- a/addon-sdk/source/lib/sdk/event/chrome.js
+++ b/addon-sdk/source/lib/sdk/event/chrome.js
@@ -52,14 +52,14 @@ function observe(topic) {
   // observerChannel (since third argument is `true`). There for if it
   // will be GC-ed with all it's event listeners once no other references
   // will be held.
   addObserver(observerChannel, topic, true);
 
   // We need to remove any observer added once the add-on is unloaded;
   // otherwise we'll get a "dead object" exception.
   // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1001833
-  unload(() => removeObserver(observerChannel, topic));
+  unload(() => removeObserver(observerChannel, topic), { weak: true });
 
   return observerChannel;
 }
 
 exports.observe = observe;
--- a/addon-sdk/source/lib/sdk/system/unload.js
+++ b/addon-sdk/source/lib/sdk/system/unload.js
@@ -5,26 +5,43 @@
 // Parts of this module were taken from narwhal:
 //
 // http://narwhaljs.org
 
 module.metadata = {
   "stability": "experimental"
 };
 
+const { Cu } = require('chrome');
 const { on, off } = require('./events');
 const unloadSubject = require('@loader/unload');
 
 const observers = [];
 const unloaders = [];
 
-var when = exports.when = function when(observer) {
-  if (observers.indexOf(observer) != -1)
-    return;
-  observers.unshift(observer);
+function WeakObserver(inner) {
+  this._inner = Cu.getWeakReference(inner);
+}
+
+Object.defineProperty(WeakObserver.prototype, 'value', {
+  get: function() { this._inner.get() }
+});
+
+var when = exports.when = function when(observer, opts) {
+  opts = opts || {};
+  for (var i = 0; i < observers.length; ++i) {
+    if (observers[i] === observer || observers[i].value === observer) {
+      return;
+    }
+  }
+  if (opts.weak) {
+    observers.unshift(new WeakObserver(observer));
+  } else {
+    observers.unshift(observer);
+  }
 };
 
 var ensure = exports.ensure = function ensure(obj, destructorName) {
   if (!destructorName)
     destructorName = "unload";
   if (!(destructorName in obj))
     throw new Error("object has no '" + destructorName + "' property");
 
@@ -50,17 +67,22 @@ var ensure = exports.ensure = function e
   unloaders.push(unloadWrapper);
 
   obj[destructorName] = unloadWrapper;
 };
 
 function unload(reason) {
   observers.forEach(function(observer) {
     try {
-      observer(reason);
+      if (observer instanceof WeakObserver) {
+        observer = observer.value;
+      }
+      if (typeof observer === 'function') {
+        observer(reason);
+      }
     }
     catch (error) {
       console.exception(error);
     }
   });
 }
 
 when(function(reason) {
--- a/addon-sdk/source/test/leak/jetpack-package.ini
+++ b/addon-sdk/source/test/leak/jetpack-package.ini
@@ -1,6 +1,7 @@
 [DEFAULT]
 support-files =
   leak-utils.js
 
 [test-leak-window-events.js]
 [test-leak-event-dom-closed-window.js]
+[test-leak-event-chrome.js]
--- a/addon-sdk/source/test/leak/leak-utils.js
+++ b/addon-sdk/source/test/leak/leak-utils.js
@@ -26,16 +26,17 @@ function gc() {
           resolve();
         }
       }
     }
 
     Cu.schedulePreciseGC(genGCCallback());
   });
 }
+exports.gc = gc;
 
 // Execute the given test function and verify that we did not leak windows
 // in the process.  The test function must return a promise or be a generator.
 // If the promise is resolved, or generator completes, with an sdk loader
 // object then it will be unloaded after the memory measurements.
 exports.asyncWindowLeakTest = function*(assert, asyncTestFunc) {
 
   // SelfSupportBackend periodically tries to open windows.  This can
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/leak/test-leak-event-chrome.js
@@ -0,0 +1,41 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+'use strict';
+
+const { gc } = require("./leak-utils");
+const { Loader } = require("sdk/test/loader");
+const { Cu } = require("chrome");
+const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
+
+exports["test sdk/event/chrome does not leak when not referenced"] = function*(assert) {
+  let loader = Loader(module);
+  let { observe } = loader.require("sdk/event/chrome");
+  let { on } = loader.require("sdk/event/core");
+
+  let gotFooEvent = false;
+  on(observe("test-foo"), "data", function(evt) {
+    gotFooEvent = true;
+  });
+
+  let bar = observe("test-bar");
+  let barPromise = new Promise(resolve => {
+    on(bar, "data", function(evt) {
+      assert.ok(!gotFooEvent, "should not have gotten test-foo event");
+      resolve();
+    });
+  });
+
+  // This should clear the test-foo observer channel because we are not
+  // holding a reference to it above.
+  yield gc();
+
+  Services.obs.notifyObservers(null, "test-foo", null);
+  Services.obs.notifyObservers(null, "test-bar", null);
+
+  yield barPromise;
+
+  loader.unload();
+}
+
+require("sdk/test").run(exports);
--- a/b2g/chrome/content/devtools/hud.js
+++ b/b2g/chrome/content/devtools/hud.js
@@ -20,17 +20,17 @@ XPCOMUtils.defineLazyGetter(this, 'Debug
   return devtools.require('devtools/shared/client/main').DebuggerClient;
 });
 
 XPCOMUtils.defineLazyGetter(this, 'WebConsoleUtils', function() {
   return devtools.require('devtools/shared/webconsole/utils').Utils;
 });
 
 XPCOMUtils.defineLazyGetter(this, 'EventLoopLagFront', function() {
-  return devtools.require('devtools/server/actors/eventlooplag').EventLoopLagFront;
+  return devtools.require('devtools/shared/fronts/eventlooplag').EventLoopLagFront;
 });
 
 XPCOMUtils.defineLazyGetter(this, 'PerformanceEntriesFront', function() {
   return devtools.require('devtools/server/actors/performance-entries').PerformanceEntriesFront;
 });
 
 XPCOMUtils.defineLazyGetter(this, 'MemoryFront', function() {
   return devtools.require('devtools/server/actors/memory').MemoryFront;
--- 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/browser.xul
+++ b/browser/base/content/browser.xul
@@ -6,17 +6,17 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 <?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/content/places/places.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/content/usercontext/usercontext.css" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/skin/devtools-browser.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/skin/controlcenter/panel.css" type="text/css"?>
-<?xml-stylesheet href="chrome://browser/skin/customizableui/panelUIOverlay.css" type="text/css"?>
+<?xml-stylesheet href="chrome://browser/skin/customizableui/panelUI.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/skin/browser-lightweightTheme.css" type="text/css"?>
 
 <?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
 <?xul-overlay href="chrome://browser/content/baseMenuOverlay.xul"?>
 <?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?>
 
 # All DTD information is stored in a separate file so that it can be shared by
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -6119,28 +6119,44 @@
           // the tabbar (half a tab)
           let endScreenY = bo.screenY + 1.5 * bo.height;
           if (eY < endScreenY && eY > window.screenY)
             return;
         }
 
         // screen.availLeft et. al. only check the screen that this window is on,
         // but we want to look at the screen the tab is being dropped onto.
-        var sX = {}, sY = {}, sWidth = {}, sHeight = {};
-        Cc["@mozilla.org/gfx/screenmanager;1"]
-          .getService(Ci.nsIScreenManager)
-          .screenForRect(eX, eY, 1, 1)
-          .GetAvailRect(sX, sY, sWidth, sHeight);
+        var screen = Cc["@mozilla.org/gfx/screenmanager;1"]
+                       .getService(Ci.nsIScreenManager)
+                       .screenForRect(eX, eY, 1, 1);
+        var fullX = {}, fullY = {}, fullWidth = {}, fullHeight = {};
+        var availX = {}, availY = {}, availWidth = {}, availHeight = {};
+        // get full screen rect and available rect, both in desktop pix
+        screen.GetRectDisplayPix(fullX, fullY, fullWidth, fullHeight);
+        screen.GetAvailRectDisplayPix(availX, availY, availWidth, availHeight);
+
+        // scale factor to convert desktop pixels to CSS px
+        var scaleFactor =
+          screen.contentsScaleFactor / screen.defaultCSSScaleFactor;
+        // synchronize CSS-px top-left coordinates with the screen's desktop-px
+        // coordinates, to ensure uniqueness across multiple screens
+        // (compare the equivalent adjustments in nsGlobalWindow::GetScreenXY()
+        // and related methods)
+        availX.value = (availX.value - fullX.value) * scaleFactor + fullX.value;
+        availY.value = (availY.value - fullY.value) * scaleFactor + fullY.value;
+        availWidth.value *= scaleFactor;
+        availHeight.value *= scaleFactor;
+
         // ensure new window entirely within screen
-        var winWidth = Math.min(window.outerWidth, sWidth.value);
-        var winHeight = Math.min(window.outerHeight, sHeight.value);
-        var left = Math.min(Math.max(eX - draggedTab._dragData.offsetX, sX.value),
-                            sX.value + sWidth.value - winWidth);
-        var top = Math.min(Math.max(eY - draggedTab._dragData.offsetY, sY.value),
-                           sY.value + sHeight.value - winHeight);
+        var winWidth = Math.min(window.outerWidth, availWidth.value);
+        var winHeight = Math.min(window.outerHeight, availHeight.value);
+        var left = Math.min(Math.max(eX - draggedTab._dragData.offsetX, availX.value),
+                            availX.value + availWidth.value - winWidth);
+        var top = Math.min(Math.max(eY - draggedTab._dragData.offsetY, availY.value),
+                           availY.value + availHeight.value - winHeight);
 
         delete draggedTab._dragData;
 
         if (this.tabbrowser.tabs.length == 1) {
           // resize _before_ move to ensure the window fits the new screen.  if
           // the window is too large for its screen, the window manager may do
           // automatic repositioning.
           window.resizeTo(winWidth, winHeight);
--- a/browser/components/contextualidentity/test/browser/browser_forgetaboutsite.js
+++ b/browser/components/contextualidentity/test/browser/browser_forgetaboutsite.js
@@ -238,25 +238,25 @@ function* test_storage_cleared() {
   for (let userContextId of Object.keys(USER_CONTEXTS)) {
     // Load the page in 3 different contexts and set the local storage
     // which should only be visible in that context.
     let value = USER_CONTEXTS[userContextId];
 
     // Open our tab in the given user context.
     let tabInfo = yield* openTabInUserContext(TEST_URL+ "file_set_storages.html?" + value, userContextId);
 
-    // Check that the local storage has been set correctly.
-    let win = tabInfo.browser.contentWindow;
-    Assert.equal(win.localStorage.getItem("userContext"), USER_CONTEXTS[userContextId], "Check the local storage value");
+    // Check that the storages has been set correctly.
+    yield ContentTask.spawn(tabInfo.browser, { userContext: USER_CONTEXTS[userContextId] }, function* (arg) {
+      // Check that the local storage has been set correctly.
+      Assert.equal(content.localStorage.getItem("userContext"), arg.userContext, "Check the local storage value");
 
-    // Check that the session storage has been set correctly.
-    Assert.equal(win.sessionStorage.getItem("userContext"), USER_CONTEXTS[userContextId], "Check the session storage value");
+      // Check that the session storage has been set correctly.
+      Assert.equal(content.sessionStorage.getItem("userContext"), arg.userContext, "Check the session storage value");
 
-    // Check that the indexedDB has been set correctly.
-    yield ContentTask.spawn(tabInfo.browser, { userContext: USER_CONTEXTS[userContextId] }, function* (arg) {
+      // Check that the indexedDB has been set correctly.
       let request = content.indexedDB.open("idb", 1);
 
       let db = yield new Promise(done => {
         request.onsuccess = event => {
           done(event.target.result);
         };
       });
 
@@ -280,26 +280,26 @@ function* test_storage_cleared() {
   // Forget the site.
   ForgetAboutSite.removeDataFromDomain(TEST_HOST);
 
   // Open the tab again without setting the localStorage and check that the
   // local storage has been cleared or not.
   for (let userContextId of Object.keys(USER_CONTEXTS)) {
     // Open our tab in the given user context without setting local storage.
     let tabInfo = yield* openTabInUserContext(TEST_URL+ "file_set_storages.html", userContextId);
-    let win = tabInfo.browser.contentWindow;
-
-    // Check that does the local storage be cleared or not.
-    Assert.ok(!win.localStorage.getItem("userContext"), "The local storage has been cleared");
 
-    // Check that does the session storage be cleared or not.
-    Assert.ok(!win.sessionStorage.getItem("userContext"), "The session storage has been cleared");
+    // Check that do storages be cleared or not.
+    yield ContentTask.spawn(tabInfo.browser, null, function* () {
+      // Check that does the local storage be cleared or not.
+      Assert.ok(!content.localStorage.getItem("userContext"), "The local storage has been cleared");
 
-    // Check that does the indexedDB be cleared or not.
-    yield ContentTask.spawn(tabInfo.browser, null, function* () {
+      // Check that does the session storage be cleared or not.
+      Assert.ok(!content.sessionStorage.getItem("userContext"), "The session storage has been cleared");
+
+      // Check that does the indexedDB be cleared or not.
       let request = content.indexedDB.open("idb", 1);
 
       let db = yield new Promise(done => {
         request.onsuccess = event => {
           done(event.target.result);
         };
       });
       try {
--- a/browser/components/extensions/ext-history.js
+++ b/browser/components/extensions/ext-history.js
@@ -94,27 +94,25 @@ var _observer;
 
 function getObserver() {
   if (!_observer) {
     _observer = {
       onDeleteURI: function(uri, guid, reason) {
         this.emit("visitRemoved", {allHistory: false, urls: [uri.spec]});
       },
       onVisit: function(uri, visitId, time, sessionId, referringId, transitionType, guid, hidden, visitCount, typed) {
-        PlacesUtils.promisePlaceInfo(guid).then(placeInfo => {
-          let data = {
-            id: guid,
-            url: uri.spec,
-            title: placeInfo.title,
-            lastVisitTime: time / 1000,  // time from Places is microseconds,
-            visitCount,
-            typedCount: typed,
-          };
-          this.emit("visited", data);
-        });
+        let data = {
+          id: guid,
+          url: uri.spec,
+          title: "",
+          lastVisitTime: time / 1000,  // time from Places is microseconds,
+          visitCount,
+          typedCount: typed,
+        };
+        this.emit("visited", data);
       },
       onBeginUpdateBatch: function() {},
       onEndUpdateBatch: function() {},
       onTitleChanged: function() {},
       onClearHistory: function() {
         this.emit("visitRemoved", {allHistory: true});
       },
       onPageChanged: function() {},
--- a/browser/components/extensions/test/browser/browser_ext_history.js
+++ b/browser/components/extensions/test/browser/browser_ext_history.js
@@ -454,17 +454,19 @@ add_task(function* test_on_visited() {
   yield PlacesUtils.history.insertMany(PAGE_INFOS);
 
   let onVisitedData = yield extension.awaitMessage("on-visited-data");
 
   function checkOnVisitedData(index, expected) {
     let onVisited = onVisitedData[index];
     ok(PlacesUtils.isValidGuid(onVisited.id), "onVisited received a valid id");
     is(onVisited.url, expected.url, "onVisited received the expected url");
-    is(onVisited.title, expected.title, "onVisited received the expected title");
+    // Title will be blank until bug 1287928 lands
+    // https://bugzilla.mozilla.org/show_bug.cgi?id=1287928
+    is(onVisited.title, "", "onVisited received a blank title");
     is(onVisited.lastVisitTime, expected.time, "onVisited received the expected time");
     is(onVisited.visitCount, expected.visitCount, "onVisited received the expected visitCount");
   }
 
   let expected = {
     url: PAGE_INFOS[0].url,
     title: PAGE_INFOS[0].title,
     time: PAGE_INFOS[0].visits[0].date.getTime(),
--- 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]
rename from browser/themes/linux/customizableui/panelUIOverlay.css
rename to browser/themes/linux/customizableui/panelUI.css
--- a/browser/themes/linux/customizableui/panelUIOverlay.css
+++ b/browser/themes/linux/customizableui/panelUI.css
@@ -1,13 +1,13 @@
 /* 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 ../../shared/customizableui/panelUIOverlay.inc.css
+%include ../../shared/customizableui/panelUI.inc.css
 
 .panel-subviews {
   background-color: -moz-dialog;
 }
 
 #BMB_bookmarksPopup > menuitem[type="checkbox"] {
   -moz-appearance: none !important; /* important, to override toolkit rule */
 }
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -46,17 +46,17 @@ browser.jar:
   skin/classic/browser/Toolbar-inverted@2x.png
   skin/classic/browser/Toolbar-small.png
   skin/classic/browser/webRTC-indicator.css
 * skin/classic/browser/controlcenter/panel.css        (controlcenter/panel.css)
   skin/classic/browser/customizableui/background-noise-toolbar.png  (customizableui/background-noise-toolbar.png)
   skin/classic/browser/customizableui/customizeMode-gridTexture.png  (customizableui/customizeMode-gridTexture.png)
   skin/classic/browser/customizableui/customizeMode-separatorHorizontal.png  (customizableui/customizeMode-separatorHorizontal.png)
   skin/classic/browser/customizableui/customizeMode-separatorVertical.png  (customizableui/customizeMode-separatorVertical.png)
-* skin/classic/browser/customizableui/panelUIOverlay.css (customizableui/panelUIOverlay.css)
+* skin/classic/browser/customizableui/panelUI.css (customizableui/panelUI.css)
 * skin/classic/browser/downloads/allDownloadsViewOverlay.css   (downloads/allDownloadsViewOverlay.css)
   skin/classic/browser/downloads/buttons.png          (downloads/buttons.png)
   skin/classic/browser/downloads/download-glow-menuPanel.png (downloads/download-glow-menuPanel.png)
   skin/classic/browser/downloads/download-notification-finish.png (downloads/download-notification-finish.png)
   skin/classic/browser/downloads/download-notification-start.png (downloads/download-notification-start.png)
   skin/classic/browser/downloads/download-summary.png (downloads/download-summary.png)
 * skin/classic/browser/downloads/downloads.css        (downloads/downloads.css)
   skin/classic/browser/feeds/feedIcon.png             (feeds/feedIcon.png)
rename from browser/themes/osx/customizableui/panelUIOverlay.css
rename to browser/themes/osx/customizableui/panelUI.css
--- a/browser/themes/osx/customizableui/panelUIOverlay.css
+++ b/browser/themes/osx/customizableui/panelUI.css
@@ -1,13 +1,13 @@
 /* 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 ../../shared/customizableui/panelUIOverlay.inc.css
+%include ../../shared/customizableui/panelUI.inc.css
 
 .panel-subviews {
   background-color: hsla(0,0%,100%,.97);
 }
 
 .panelUI-grid .toolbarbutton-1 {
   margin-right: 0;
   margin-left: 0;
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -67,17 +67,17 @@ browser.jar:
   skin/classic/browser/webRTC-indicator.css
 * skin/classic/browser/controlcenter/panel.css        (controlcenter/panel.css)
   skin/classic/browser/customizableui/background-noise-toolbar.png  (customizableui/background-noise-toolbar.png)
   skin/classic/browser/customizableui/customize-titleBar-toggle.png  (customizableui/customize-titleBar-toggle.png)
   skin/classic/browser/customizableui/customize-titleBar-toggle@2x.png  (customizableui/customize-titleBar-toggle@2x.png)
   skin/classic/browser/customizableui/customizeMode-gridTexture.png  (customizableui/customizeMode-gridTexture.png)
   skin/classic/browser/customizableui/customizeMode-separatorHorizontal.png  (customizableui/customizeMode-separatorHorizontal.png)
   skin/classic/browser/customizableui/customizeMode-separatorVertical.png  (customizableui/customizeMode-separatorVertical.png)
-* skin/classic/browser/customizableui/panelUIOverlay.css    (customizableui/panelUIOverlay.css)
+* skin/classic/browser/customizableui/panelUI.css    (customizableui/panelUI.css)
 * skin/classic/browser/downloads/allDownloadsViewOverlay.css (downloads/allDownloadsViewOverlay.css)
   skin/classic/browser/downloads/buttons.png                (downloads/buttons.png)
   skin/classic/browser/downloads/buttons@2x.png             (downloads/buttons@2x.png)
   skin/classic/browser/downloads/download-glow-menuPanel.png (downloads/download-glow-menuPanel.png)
   skin/classic/browser/downloads/download-glow-menuPanel@2x.png (downloads/download-glow-menuPanel@2x.png)
   skin/classic/browser/downloads/download-notification-finish.png  (downloads/download-notification-finish.png)
   skin/classic/browser/downloads/download-notification-finish@2x.png  (downloads/download-notification-finish@2x.png)
   skin/classic/browser/downloads/download-notification-start.png  (downloads/download-notification-start.png)
rename from browser/themes/shared/customizableui/panelUIOverlay.inc.css
rename to browser/themes/shared/customizableui/panelUI.inc.css
rename from browser/themes/windows/customizableui/panelUIOverlay.css
rename to browser/themes/windows/customizableui/panelUI.css
--- a/browser/themes/windows/customizableui/panelUIOverlay.css
+++ b/browser/themes/windows/customizableui/panelUI.css
@@ -1,13 +1,13 @@
 /* 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 ../../shared/customizableui/panelUIOverlay.inc.css
+%include ../../shared/customizableui/panelUI.inc.css
 
 .panel-subviews {
   background-color: -moz-field;
 }
 
 #PanelUI-contents #zoom-out-btn {
   padding-left: 12px;
   padding-right: 12px;
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -81,17 +81,17 @@ browser.jar:
 * skin/classic/browser/controlcenter/panel.css                 (controlcenter/panel.css)
   skin/classic/browser/customizableui/background-noise-toolbar.png  (customizableui/background-noise-toolbar.png)
   skin/classic/browser/customizableui/customize-titleBar-toggle.png  (customizableui/customize-titleBar-toggle.png)
   skin/classic/browser/customizableui/customize-titleBar-toggle@2x.png  (customizableui/customize-titleBar-toggle@2x.png)
   skin/classic/browser/customizableui/customizeMode-gridTexture.png  (customizableui/customizeMode-gridTexture.png)
   skin/classic/browser/customizableui/customizeMode-separatorHorizontal.png  (customizableui/customizeMode-separatorHorizontal.png)
   skin/classic/browser/customizableui/customizeMode-separatorVertical.png  (customizableui/customizeMode-separatorVertical.png)
   skin/classic/browser/customizableui/menu-arrow.svg           (customizableui/menu-arrow.svg)
-* skin/classic/browser/customizableui/panelUIOverlay.css       (customizableui/panelUIOverlay.css)
+* skin/classic/browser/customizableui/panelUI.css       (customizableui/panelUI.css)
 * skin/classic/browser/downloads/allDownloadsViewOverlay.css   (downloads/allDownloadsViewOverlay.css)
   skin/classic/browser/downloads/buttons.png                   (downloads/buttons.png)
   skin/classic/browser/downloads/buttons-XP.png                (downloads/buttons-XP.png)
   skin/classic/browser/downloads/download-glow-menuPanel.png   (downloads/download-glow-menuPanel.png)
   skin/classic/browser/downloads/download-glow-menuPanel-XPVista7.png   (downloads/download-glow-menuPanel-XPVista7.png)
   skin/classic/browser/downloads/download-notification-finish.png (downloads/download-notification-finish.png)
   skin/classic/browser/downloads/download-notification-start.png (downloads/download-notification-start.png)
   skin/classic/browser/downloads/download-summary.png          (downloads/download-summary.png)
--- a/build/autoconf/icu.m4
+++ b/build/autoconf/icu.m4
@@ -77,17 +77,20 @@ if test -n "$USE_ICU"; then
 
     # TODO: the l is actually endian-dependent
     # We could make this set as 'l' or 'b' for little or big, respectively,
     # but we'd need to check in a big-endian version of the file.
     ICU_DATA_FILE="icudt${version}l.dat"
 
     dnl We won't build ICU data as a separate file when building
     dnl JS standalone so that embedders don't have to deal with it.
-    if test -z "$JS_STANDALONE" -a -z "$MOZ_SYSTEM_ICU"; then
+    dnl We also don't do it on Windows because sometimes the file goes
+    dnl missing -- possibly due to overzealous antivirus software? --
+    dnl which prevents the browser from starting up :(
+    if test -z "$JS_STANDALONE" -a -z "$MOZ_SYSTEM_ICU" -a "$OS_TARGET" != WINNT; then
         MOZ_ICU_DATA_ARCHIVE=1
     else
         MOZ_ICU_DATA_ARCHIVE=
     fi
 fi
 
 AC_SUBST(MOZ_ICU_VERSION)
 AC_SUBST(ENABLE_INTL_API)
--- a/build/autoconf/sanitize.m4
+++ b/build/autoconf/sanitize.m4
@@ -18,18 +18,18 @@ if test -n "$MOZ_ASAN"; then
         MOZ_CLANG_RT_ASAN_LIB=clang_rt.asan_dynamic-i386.dll
         # We use MOZ_PATH_PROG in order to get a Windows style path.
         MOZ_PATH_PROG(MOZ_CLANG_RT_ASAN_LIB_PATH, $MOZ_CLANG_RT_ASAN_LIB)
         if test -z "$MOZ_CLANG_RT_ASAN_LIB_PATH"; then
             AC_MSG_ERROR([Couldn't find $MOZ_CLANG_RT_ASAN_LIB.  It should be available in the same location as clang-cl.])
         fi
         AC_SUBST(MOZ_CLANG_RT_ASAN_LIB_PATH)
     fi
-    CFLAGS="-fsanitize=address -Dxmalloc=myxmalloc -fPIC $CFLAGS"
-    CXXFLAGS="-fsanitize=address -Dxmalloc=myxmalloc -fPIC $CXXFLAGS"
+    CFLAGS="-fsanitize=address $CFLAGS"
+    CXXFLAGS="-fsanitize=address $CXXFLAGS"
     LDFLAGS="-fsanitize=address $LDFLAGS"
     AC_DEFINE(MOZ_ASAN)
     MOZ_PATH_PROG(LLVM_SYMBOLIZER, llvm-symbolizer)
 fi
 AC_SUBST(MOZ_ASAN)
 
 dnl ========================================================
 dnl = Use Memory Sanitizer
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -208,21 +208,17 @@ def get_compiler_info(compiler, language
     '''
     # Note: MSVC doesn't expose __STDC_VERSION__. It does expose __STDC__,
     # but only when given the -Za option, which disables compiler
     # extensions.
     # Note: We'd normally do a version check for clang, but versions of clang
     # in Xcode have a completely different versioning scheme despite exposing
     # the version with the same defines.
     # So instead, we make things such that the version is missing when the
-    # clang used is below the minimum supported version (currently clang 3.4).
-    # Normally, we'd use __has_feature, but there are unfortunately no C++11
-    # differences in clang 3.4. However, it supports the 2013-08-28 draft of
-    # the ISO WG21 SG10 feature test macro recommendations, and thus exposes
-    # new __cpp_* macros that older clang versions didn't.
+    # clang used is below the minimum supported version (currently clang 3.6).
     # We then only include the version information when the C++ compiler
     # matches the feature check, so that an unsupported version of clang would
     # have no version number.
     check = dedent('''\
         #if defined(_MSC_VER)
         #if defined(__clang__)
         %COMPILER clang-cl
         %VERSION _MSC_FULL_VER
@@ -533,17 +529,17 @@ def compiler(language, host_or_target, c
             raise FatalCheckError(
                 'Only GCC 4.8 or newer is supported (found version %s).'
                 % info.version)
 
         # If you want to bump the version check here search for
         # __cpp_static_assert above, and see the associated comment.
         if info.type == 'clang' and not info.version:
             raise FatalCheckError(
-                'Only clang/llvm 3.4 or newer is supported.')
+                'Only clang/llvm 3.6 or newer is supported.')
 
         if info.type == 'msvc':
             if info.version < '19.00.23918':
                 raise FatalCheckError(
                     'This version (%s) of the MSVC compiler is not '
                     'supported.\n'
                     'You must install Visual C++ 2015 Update 2 or newer in '
                     'order to build.\n'
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -1009,17 +1009,17 @@ nsScriptSecurityManager::CheckLoadURIFla
                              nsIProtocolHandler::URI_LOADABLE_BY_SUBSUMERS,
                              &hasSubsumersFlag);
     NS_ENSURE_SUCCESS(rv, rv);
     if (!hasFlags && !hasSubsumersFlag) {
         nsXPIDLString message;
         NS_ConvertASCIItoUTF16 ucsTargetScheme(targetScheme);
         const char16_t* formatStrings[] = { ucsTargetScheme.get() };
         rv = sStrBundle->
-            FormatStringFromName(MOZ_UTF16("ProtocolFlagError"),
+            FormatStringFromName(u"ProtocolFlagError",
                                  formatStrings,
                                  ArrayLength(formatStrings),
                                  getter_Copies(message));
         if (NS_SUCCEEDED(rv)) {
             nsCOMPtr<nsIConsoleService> console(
               do_GetService("@mozilla.org/consoleservice;1"));
             NS_ENSURE_TRUE(console, NS_ERROR_FAILURE);
 
new file mode 100644
--- /dev/null
+++ b/devtools/client/framework/location-store.js
@@ -0,0 +1,103 @@
+/* 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 SOURCE_TOKEN = "<:>";
+
+function LocationStore (store) {
+  this._store = store || new Map();
+}
+
+/**
+ * Method to get a promised location from the Store.
+ * @param location
+ * @returns Promise<Object>
+ */
+LocationStore.prototype.get = function (location) {
+  this._safeAccessInit(location.url);
+  return this._store.get(location.url).get(location);
+};
+
+/**
+ * Method to set a promised location to the Store
+ * @param location
+ * @param promisedLocation
+ */
+LocationStore.prototype.set = function (location, promisedLocation = null) {
+  this._safeAccessInit(location.url);
+  this._store.get(location.url).set(serialize(location), promisedLocation);
+};
+
+/**
+ * Utility method to verify if key exists in Store before accessing it.
+ * If not, initializing it.
+ * @param url
+ * @private
+ */
+LocationStore.prototype._safeAccessInit = function (url) {
+  if (!this._store.has(url)) {
+    this._store.set(url, new Map());
+  }
+};
+
+/**
+ * Utility proxy method to Map.clear() method
+ */
+LocationStore.prototype.clear = function () {
+  this._store.clear();
+};
+
+/**
+ * Retrieves an object containing all locations to be resolved when `source-updated`
+ * event is triggered.
+ * @param url
+ * @returns {Array<String>}
+ */
+LocationStore.prototype.getByURL = function (url){
+  if (this._store.has(url)) {
+    return [...this._store.get(url).keys()];
+  }
+  return [];
+};
+
+/**
+ * Invalidates the stale location promises from the store when `source-updated`
+ * event is triggered, and when FrameView unsubscribes from a location.
+ * @param url
+ */
+LocationStore.prototype.clearByURL = function (url) {
+  this._safeAccessInit(url);
+  this._store.set(url, new Map());
+};
+
+exports.LocationStore = LocationStore;
+exports.serialize = serialize;
+exports.deserialize = deserialize;
+
+/**
+ * Utility method to serialize the source
+ * @param source
+ * @returns {string}
+ */
+function serialize(source) {
+  let { url, line, column } = source;
+  line = line || 0;
+  column = column || 0;
+  return `${url}${SOURCE_TOKEN}${line}${SOURCE_TOKEN}${column}`;
+};
+
+/**
+ * Utility method to serialize the source
+ * @param source
+ * @returns Object
+ */
+function deserialize(source) {
+  let [ url, line, column ] = source.split(SOURCE_TOKEN);
+  line = parseInt(line);
+  column = parseInt(column);
+  if (column === 0) {
+    return { url, line };
+  }
+  return { url, line, column };
+};
--- a/devtools/client/framework/moz.build
+++ b/devtools/client/framework/moz.build
@@ -11,21 +11,22 @@ TEST_HARNESS_FILES.xpcshell.devtools.cli
 
 DevToolsModules(
     'about-devtools-toolbox.js',
     'attach-thread.js',
     'browser-menus.js',
     'devtools-browser.js',
     'devtools.js',
     'gDevTools.jsm',
+    'location-store.js',
     'menu-item.js',
     'menu.js',
     'selection.js',
     'sidebar.js',
-    'source-location.js',
+    'source-map-service.js',
     'target-from-url.js',
     'target.js',
     'toolbox-highlighter-utils.js',
     'toolbox-hosts.js',
     'toolbox-options.js',
     'toolbox.js',
     'ToolboxProcess.jsm',
 )
rename from devtools/client/framework/source-location.js
rename to devtools/client/framework/source-map-service.js
--- a/devtools/client/framework/source-location.js
+++ b/devtools/client/framework/source-map-service.js
@@ -1,98 +1,171 @@
 /* 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 { Task } = require("devtools/shared/task");
-const { assert } = require("devtools/shared/DevToolsUtils");
+const EventEmitter = require("devtools/shared/event-emitter");
+const { LocationStore, serialize, deserialize } = require("./location-store");
 
 /**
  * A manager class that wraps a TabTarget and listens to source changes
  * from source maps and resolves non-source mapped locations to the source mapped
  * versions and back and forth, and creating smart elements with a location that
  * auto-update when the source changes (from pretty printing, source maps loading, etc)
  *
  * @param {TabTarget} target
  */
-function SourceLocationController(target) {
-  this.target = target;
-  this.locations = new Set();
+
+function SourceMapService(target) {
+  this._target = target;
+  this._locationStore = new LocationStore();
+  this._isInitialResolve = true;
+
+  EventEmitter.decorate(this);
 
   this._onSourceUpdated = this._onSourceUpdated.bind(this);
+  this._resolveLocation = this._resolveLocation.bind(this);
+  this._resolveAndUpdate = this._resolveAndUpdate.bind(this);
+  this.subscribe = this.subscribe.bind(this);
+  this.unsubscribe = this.unsubscribe.bind(this);
   this.reset = this.reset.bind(this);
   this.destroy = this.destroy.bind(this);
 
   target.on("source-updated", this._onSourceUpdated);
   target.on("navigate", this.reset);
   target.on("will-navigate", this.reset);
   target.on("close", this.destroy);
 }
 
-SourceLocationController.prototype.reset = function () {
-  this.locations.clear();
+/**
+ * Clears the store containing the cached resolved locations and promises
+ */
+SourceMapService.prototype.reset = function () {
+  this._isInitialResolve = true;
+  this._locationStore.clear();
+};
+
+SourceMapService.prototype.destroy = function () {
+  this.reset();
+  this._target.off("source-updated", this._onSourceUpdated);
+  this._target.off("navigate", this.reset);
+  this._target.off("will-navigate", this.reset);
+  this._target.off("close", this.destroy);
+  this._isInitialResolve = null;
+  this._target = this._locationStore = null;
 };
 
-SourceLocationController.prototype.destroy = function () {
-  this.locations.clear();
-  this.target.off("source-updated", this._onSourceUpdated);
-  this.target.off("navigate", this.reset);
-  this.target.off("will-navigate", this.reset);
-  this.target.off("close", this.destroy);
-  this.target = this.locations = null;
+/**
+ * Sets up listener for the callback to update the FrameView and tries to resolve location
+ * @param location
+ * @param callback
+ */
+SourceMapService.prototype.subscribe = function (location, callback) {
+  this.on(serialize(location), callback);
+  this._locationStore.set(location);
+  if (this._isInitialResolve) {
+    this._resolveAndUpdate(location);
+    this._isInitialResolve = false;
+  }
+};
+
+/**
+ * Removes the listener for the location and clears cached locations
+ * @param location
+ * @param callback
+ */
+SourceMapService.prototype.unsubscribe = function (location, callback) {
+  this.off(serialize(location), callback);
+  this._locationStore.clearByURL(location.url);
 };
 
 /**
- * Add this `location` to be observed and register a callback
- * whenever the underlying source is updated.
- *
- * @param {Object} location
- *        An object with a {String} url, {Number} line, and optionally
- *        a {Number} column.
- * @param {Function} callback
+ * Tries to resolve the location and if successful,
+ * emits the resolved location and caches it
+ * @param location
+ * @private
  */
-SourceLocationController.prototype.bindLocation = function (location, callback) {
-  assert(location.url, "Location must have a url.");
-  assert(location.line, "Location must have a line.");
-  this.locations.add({ location, callback });
+SourceMapService.prototype._resolveAndUpdate = function (location) {
+  this._resolveLocation(location).then(resolvedLocation => {
+    // We try to source map the first console log to initiate the source-updated event from
+    // target. The isSameLocation check is to make sure we don't update the frame, if the
+    // location is not source-mapped.
+    if (resolvedLocation) {
+      if (this._isInitialResolve) {
+        if (!isSameLocation(location, resolvedLocation)) {
+          this.emit(serialize(location), location, resolvedLocation);
+          return;
+        }
+      }
+      this.emit(serialize(location), location, resolvedLocation);
+    }
+  });
 };
 
 /**
- * Called when a new source occurs (a normal source, source maps) or an updated
- * source (pretty print) occurs.
- *
- * @param {String} eventName
- * @param {Object} sourceEvent
+ * Validates the location model,
+ * checks if there is existing promise to resolve location, if so returns cached promise
+ * if not promised to resolve,
+ * tries to resolve location and returns a promised location
+ * @param location
+ * @return Promise<Object>
+ * @private
  */
-SourceLocationController.prototype._onSourceUpdated = function (_, sourceEvent) {
+SourceMapService.prototype._resolveLocation = Task.async(function* (location) {
+  // Location must have a url and a line
+  if (!location.url || !location.line) {
+    return null;
+  }
+  const cachedLocation = this._locationStore.get(location);
+  if (cachedLocation) {
+    return cachedLocation;
+  } else {
+    const promisedLocation = resolveLocation(this._target, location);
+    if (promisedLocation) {
+      this._locationStore.set(location, promisedLocation);
+      return promisedLocation;
+    }
+  }
+});
+
+/**
+ * Checks if the `source-updated` event is fired from the target.
+ * Checks to see if location store has the source url in its cache,
+ * if so, tries to update each stale location in the store.
+ * @param _
+ * @param sourceEvent
+ * @private
+ */
+SourceMapService.prototype._onSourceUpdated = function (_, sourceEvent) {
   let { type, source } = sourceEvent;
   // If we get a new source, and it's not a source map, abort;
-  // we can ahve no actionable updates as this is just a new normal source.
+  // we can have no actionable updates as this is just a new normal source.
   // Also abort if there's no `url`, which means it's unsourcemappable anyway,
   // like an eval script.
   if (!source.url || type === "newSource" && !source.isSourceMapped) {
     return;
   }
-
-  for (let locationItem of this.locations) {
-    if (isSourceRelated(locationItem.location, source)) {
-      this._updateSource(locationItem);
+  let sourceUrl = null;
+  if (source.generatedUrl && source.isSourceMapped) {
+    sourceUrl = source.generatedUrl;
+  } else if (source.url && source.isPrettyPrinted) {
+    sourceUrl = source.url;
+  }
+  const locationsToResolve = this._locationStore.getByURL(sourceUrl);
+  if (locationsToResolve.length) {
+    this._locationStore.clearByURL(sourceUrl);
+    for (let location of locationsToResolve) {
+      this._resolveAndUpdate(deserialize(location));
     }
   }
 };
 
-SourceLocationController.prototype._updateSource = Task.async(function* (locationItem) {
-  let newLocation = yield resolveLocation(this.target, locationItem.location);
-  if (newLocation) {
-    let previousLocation = Object.assign({}, locationItem.location);
-    Object.assign(locationItem.location, newLocation);
-    locationItem.callback(previousLocation, newLocation);
-  }
-});
+exports.SourceMapService = SourceMapService;
 
 /**
  * Take a TabTarget and a location, containing a `url`, `line`, and `column`, resolve
  * the location to the latest location (so a source mapped location, or if pretty print
  * status has been updated)
  *
  * @param {TabTarget} target
  * @param {Object} location
@@ -100,38 +173,28 @@ SourceLocationController.prototype._upda
  */
 function resolveLocation(target, location) {
   return Task.spawn(function* () {
     let newLocation = yield target.resolveLocation({
       url: location.url,
       line: location.line,
       column: location.column || Infinity
     });
-
     // Source or mapping not found, so don't do anything
     if (newLocation.error) {
       return null;
     }
 
     return newLocation;
   });
 }
 
 /**
- * Takes a serialized SourceActor form and returns a boolean indicating
- * if this source is related to this location, like if a location is a generated source,
- * and the source map is loaded subsequently, the new source mapped SourceActor
- * will be considered related to this location. Same with pretty printing new sources.
- *
- * @param {Object} location
- * @param {Object} source
- * @return {Boolean}
+ * Returns if the original location and resolved location are the same
+ * @param location
+ * @param resolvedLocation
+ * @returns {boolean}
  */
-function isSourceRelated(location, source) {
-         // Mapping location to subsequently loaded source map
-  return source.generatedUrl === location.url ||
-         // Mapping source map loc to source map
-         source.url === location.url;
-}
-
-exports.SourceLocationController = SourceLocationController;
-exports.resolveLocation = resolveLocation;
-exports.isSourceRelated = isSourceRelated;
+function isSameLocation(location, resolvedLocation) {
+  return location.url === resolvedLocation.url &&
+    location.line === resolvedLocation.line &&
+    location.column === resolvedLocation.column;
+};
\ No newline at end of file
--- a/devtools/client/framework/test/browser.ini
+++ b/devtools/client/framework/test/browser.ini
@@ -3,18 +3,22 @@ tags = devtools
 subsuite = devtools
 support-files =
   browser_toolbox_options_disable_js.html
   browser_toolbox_options_disable_js_iframe.html
   browser_toolbox_options_disable_cache.sjs
   browser_toolbox_sidebar_tool.xul
   browser_toolbox_window_title_changes_page.html
   browser_toolbox_window_title_frame_select_page.html
+  code_binary_search.coffee
+  code_binary_search.js
+  code_binary_search.map
   code_math.js
   code_ugly.js
+  doc_empty-tab-01.html
   head.js
   shared-head.js
   shared-redux-head.js
   helper_disable_cache.js
   doc_theme.css
   doc_viewsource.html
   browser_toolbox_options_enable_serviceworkers_testing_frame_script.js
   browser_toolbox_options_enable_serviceworkers_testing.html
@@ -26,18 +30,18 @@ support-files =
 [browser_devtools_api_destroy.js]
 [browser_dynamic_tool_enabling.js]
 [browser_ignore_toolbox_network_requests.js]
 [browser_keybindings_01.js]
 [browser_keybindings_02.js]
 [browser_keybindings_03.js]
 [browser_menu_api.js]
 [browser_new_activation_workflow.js]
-[browser_source-location-01.js]
-[browser_source-location-02.js]
+[browser_source_map-01.js]
+[browser_source_map-02.js]
 [browser_target_from_url.js]
 [browser_target_events.js]
 [browser_target_remote.js]
 [browser_target_support.js]
 [browser_toolbox_custom_host.js]
 [browser_toolbox_dynamic_registration.js]
 [browser_toolbox_getpanelwhenready.js]
 [browser_toolbox_highlight.js]
rename from devtools/client/framework/test/browser_source-location-01.js
rename to devtools/client/framework/test/browser_source_map-01.js
--- a/devtools/client/framework/test/browser_source-location-01.js
+++ b/devtools/client/framework/test/browser_source_map-01.js
@@ -1,66 +1,65 @@
 /* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
+ http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Whitelisting this test.
 // As part of bug 1077403, the leaking uncaught rejections should be fixed.
 thisTestLeaksUncaughtRejectionsAndShouldBeFixed("[object Object]");
 thisTestLeaksUncaughtRejectionsAndShouldBeFixed(
   "TypeError: this.transport is null");
 
 /**
- * Tests the SourceMapController updates generated sources when source maps
+ * Tests the SourceMapService updates generated sources when source maps
  * are subsequently found. Also checks when no column is provided, and
  * when tagging an already source mapped location initially.
  */
 
 const DEBUGGER_ROOT = "http://example.com/browser/devtools/client/debugger/test/mochitest/";
 // Empty page
 const PAGE_URL = `${DEBUGGER_ROOT}doc_empty-tab-01.html`;
-const JS_URL = `${DEBUGGER_ROOT}code_binary_search.js`;
-const COFFEE_URL = `${DEBUGGER_ROOT}code_binary_search.coffee`;
-const { SourceLocationController } = require("devtools/client/framework/source-location");
+const JS_URL = `${URL_ROOT}code_binary_search.js`;
+const COFFEE_URL = `${URL_ROOT}code_binary_search.coffee`;
+const { SourceMapService } = require("devtools/client/framework/source-map-service");
 
 add_task(function* () {
-  let toolbox = yield openNewTabAndToolbox(PAGE_URL, "jsdebugger");
+  const toolbox = yield openNewTabAndToolbox(PAGE_URL, "jsdebugger");
 
-  let controller = new SourceLocationController(toolbox.target);
+  const service = new SourceMapService(toolbox.target);
 
-  let aggregator = [];
+  const aggregator = [];
 
-  function onUpdate(oldLoc, newLoc) {
+  function onUpdate(e, oldLoc, newLoc) {
     if (oldLoc.line === 6) {
       checkLoc1(oldLoc, newLoc);
     } else if (oldLoc.line === 8) {
       checkLoc2(oldLoc, newLoc);
     } else if (oldLoc.line === 2) {
       checkLoc3(oldLoc, newLoc);
     } else {
       throw new Error(`Unexpected location update: ${JSON.stringify(oldLoc)}`);
     }
     aggregator.push(newLoc);
   }
 
   let loc1 = { url: JS_URL, line: 6 };
   let loc2 = { url: JS_URL, line: 8, column: 3 };
-  let loc3 = { url: COFFEE_URL, line: 2, column: 0 };
 
-  controller.bindLocation(loc1, onUpdate);
-  controller.bindLocation(loc2, onUpdate);
-  controller.bindLocation(loc3, onUpdate);
+  service.subscribe(loc1, onUpdate);
+  service.subscribe(loc2, onUpdate);
 
   // Inject JS script
+  let sourceShown = waitForSourceShown(toolbox.getCurrentPanel(), "code_binary_search");
   yield createScript(JS_URL);
+  yield sourceShown;
 
-  yield waitUntil(() => aggregator.length === 3);
+  yield waitUntil(() => aggregator.length === 2);
 
   ok(aggregator.find(i => i.url === COFFEE_URL && i.line === 4), "found first updated location");
   ok(aggregator.find(i => i.url === COFFEE_URL && i.line === 6), "found second updated location");
-  ok(aggregator.find(i => i.url === COFFEE_URL && i.line === 2), "found third updated location");
 
   yield toolbox.destroy();
   gBrowser.removeCurrentTab();
   finish();
 });
 
 function checkLoc1(oldLoc, newLoc) {
   is(oldLoc.line, 6, "Correct line for JS:6");
@@ -75,28 +74,37 @@ function checkLoc2(oldLoc, newLoc) {
   is(oldLoc.line, 8, "Correct line for JS:8:3");
   is(oldLoc.column, 3, "Correct column for JS:8:3");
   is(oldLoc.url, JS_URL, "Correct url for JS:8:3");
   is(newLoc.line, 6, "Correct line for JS:8:3 -> COFFEE");
   is(newLoc.column, 10, "Correct column for JS:8:3 -> COFFEE");
   is(newLoc.url, COFFEE_URL, "Correct url for JS:8:3 -> COFFEE");
 }
 
-function checkLoc3(oldLoc, newLoc) {
-  is(oldLoc.line, 2, "Correct line for COFFEE:2:0");
-  is(oldLoc.column, 0, "Correct column for COFFEE:2:0");
-  is(oldLoc.url, COFFEE_URL, "Correct url for COFFEE:2:0");
-  is(newLoc.line, 2, "Correct line for COFFEE:2:0 -> COFFEE");
-  is(newLoc.column, 0, "Correct column for COFFEE:2:0 -> COFFEE");
-  is(newLoc.url, COFFEE_URL, "Correct url for COFFEE:2:0 -> COFFEE");
-}
-
 function createScript(url) {
   info(`Creating script: ${url}`);
   let mm = getFrameScript();
   let command = `
     let script = document.createElement("script");
     script.setAttribute("src", "${url}");
     document.body.appendChild(script);
     null;
   `;
   return evalInDebuggee(mm, command);
 }
+
+function waitForSourceShown(debuggerPanel, url) {
+  let { panelWin } = debuggerPanel;
+  let deferred = defer();
+
+  info(`Waiting for source ${url} to be shown in the debugger...`);
+  panelWin.on(panelWin.EVENTS.SOURCE_SHOWN, function onSourceShown(_, source) {
+
+    let sourceUrl = source.url || source.generatedUrl;
+    if (sourceUrl.includes(url)) {
+      panelWin.off(panelWin.EVENTS.SOURCE_SHOWN, onSourceShown);
+      info(`Source shown for ${url}`);
+      deferred.resolve(source);
+    }
+  });
+
+  return deferred.promise;
+}
rename from devtools/client/framework/test/browser_source-location-02.js
rename to devtools/client/framework/test/browser_source_map-02.js
--- a/devtools/client/framework/test/browser_source-location-02.js
+++ b/devtools/client/framework/test/browser_source_map-02.js
@@ -1,63 +1,63 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
+ http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
- * Tests the SourceLocationController updates generated sources when pretty printing
+ * Tests the SourceMapService updates generated sources when pretty printing
  * and un pretty printing.
  */
 
 const DEBUGGER_ROOT = "http://example.com/browser/devtools/client/debugger/test/mochitest/";
 // Empty page
 const PAGE_URL = `${DEBUGGER_ROOT}doc_empty-tab-01.html`;
 const JS_URL = `${URL_ROOT}code_ugly.js`;
-const { SourceLocationController } = require("devtools/client/framework/source-location");
+const { SourceMapService } = require("devtools/client/framework/source-map-service");
 
 add_task(function* () {
   let toolbox = yield openNewTabAndToolbox(PAGE_URL, "jsdebugger");
 
-  let controller = new SourceLocationController(toolbox.target);
+  let service = new SourceMapService(toolbox.target);
 
   let checkedPretty = false;
   let checkedUnpretty = false;
 
-  function onUpdate(oldLoc, newLoc) {
+  function onUpdate(e, oldLoc, newLoc) {
     if (oldLoc.line === 3) {
       checkPrettified(oldLoc, newLoc);
       checkedPretty = true;
     } else if (oldLoc.line === 9) {
       checkUnprettified(oldLoc, newLoc);
       checkedUnpretty = true;
     } else {
       throw new Error(`Unexpected location update: ${JSON.stringify(oldLoc)}`);
     }
   }
-
-  controller.bindLocation({ url: JS_URL, line: 3 }, onUpdate);
+  const loc1 = { url: JS_URL, line: 3 };
+  service.subscribe(loc1, onUpdate);
 
   // Inject JS script
   let sourceShown = waitForSourceShown(toolbox.getCurrentPanel(), "code_ugly.js");
   yield createScript(JS_URL);
   yield sourceShown;
 
   let ppButton = toolbox.getCurrentPanel().panelWin.document.getElementById("pretty-print");
   sourceShown = waitForSourceShown(toolbox.getCurrentPanel(), "code_ugly.js");
   ppButton.click();
   yield sourceShown;
   yield waitUntil(() => checkedPretty);
 
   // TODO check unprettified change once bug 1177446 fixed
-  /*
-  sourceShown = waitForSourceShown(toolbox.getCurrentPanel(), "code_ugly.js");
-  ppButton.click();
-  yield sourceShown;
-  yield waitUntil(() => checkedUnpretty);
-  */
+  // info("Testing un-pretty printing.");
+  // sourceShown = waitForSourceShown(toolbox.getCurrentPanel(), "code_ugly.js");
+  // ppButton.click();
+  // yield sourceShown;
+  // yield waitUntil(() => checkedUnpretty);
+
 
   yield toolbox.destroy();
   gBrowser.removeCurrentTab();
   finish();
 });
 
 function checkPrettified(oldLoc, newLoc) {
   is(oldLoc.line, 3, "Correct line for JS:3");
copy from devtools/client/debugger/test/mochitest/code_binary_search.coffee
copy to devtools/client/framework/test/code_binary_search.coffee
copy from devtools/client/debugger/test/mochitest/code_binary_search.js
copy to devtools/client/framework/test/code_binary_search.js
copy from devtools/client/debugger/test/mochitest/code_binary_search.map
copy to devtools/client/framework/test/code_binary_search.map
copy from devtools/client/debugger/test/mochitest/doc_empty-tab-01.html
copy to devtools/client/framework/test/doc_empty-tab-01.html
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -6,16 +6,17 @@
 
 const MAX_ORDINAL = 99;
 const SPLITCONSOLE_ENABLED_PREF = "devtools.toolbox.splitconsoleEnabled";
 const SPLITCONSOLE_HEIGHT_PREF = "devtools.toolbox.splitconsoleHeight";
 const OS_HISTOGRAM = "DEVTOOLS_OS_ENUMERATED_PER_USER";
 const OS_IS_64_BITS = "DEVTOOLS_OS_IS_64_BITS_PER_USER";
 const SCREENSIZE_HISTOGRAM = "DEVTOOLS_SCREEN_RESOLUTION_ENUMERATED_PER_USER";
 const HTML_NS = "http://www.w3.org/1999/xhtml";
+const { SourceMapService } = require("./source-map-service");
 
 var {Cc, Ci, Cu} = require("chrome");
 var promise = require("promise");
 var defer = require("devtools/shared/defer");
 var Services = require("Services");
 var {Task} = require("devtools/shared/task");
 var {gDevTools} = require("devtools/client/framework/devtools");
 var EventEmitter = require("devtools/shared/event-emitter");
@@ -113,16 +114,19 @@ const ToolboxButtons = exports.ToolboxBu
  *        Type of host that will host the toolbox (e.g. sidebar, window)
  * @param {object} hostOptions
  *        Options for host specifically
  */
 function Toolbox(target, selectedTool, hostType, hostOptions) {
   this._target = target;
   this._toolPanels = new Map();
   this._telemetry = new Telemetry();
+  if (Services.prefs.getBoolPref("devtools.sourcemap.locations.enabled")) {
+    this._sourceMapService = new SourceMapService(this._target);
+  }
 
   this._initInspector = null;
   this._inspector = null;
 
   // Map of frames (id => frame-info) and currently selected frame id.
   this.frameMap = new Map();
   this.selectedFrameId = null;
 
@@ -2026,16 +2030,20 @@ Toolbox.prototype = {
     this.off("ready", this._showDevEditionPromo);
 
     gDevTools.off("tool-registered", this._toolRegistered);
     gDevTools.off("tool-unregistered", this._toolUnregistered);
 
     gDevTools.off("pref-changed", this._prefChanged);
 
     this._lastFocusedElement = null;
+    if (this._sourceMapService) {
+      this._sourceMapService.destroy();
+      this._sourceMapService = null;
+    }
 
     if (this.webconsolePanel) {
       this._saveSplitConsoleHeight();
       this.webconsolePanel.removeEventListener("resize",
         this._saveSplitConsoleHeight);
     }
     this.closeButton.removeEventListener("click", this.destroy, true);
     this.textboxContextMenuPopup.removeEventListener("popupshowing",
--- a/devtools/client/inspector/markup/test/browser_markup_dragdrop_autoscroll_01.js
+++ b/devtools/client/inspector/markup/test/browser_markup_dragdrop_autoscroll_01.js
@@ -22,29 +22,29 @@ add_task(function* () {
   markup.isDragging = true;
 
   info("Simulate a mousemove on the view, at the bottom, and expect scrolling");
   let onScrolled = waitForScrollStop(markup.doc);
 
   markup._onMouseMove({
     preventDefault: () => {},
     target: markup.doc.body,
-    pageY: viewHeight
+    pageY: viewHeight + markup.doc.defaultView.scrollY
   });
 
   let bottomScrollPos = yield onScrolled;
   ok(bottomScrollPos > 0, "The view was scrolled down");
 
   info("Simulate a mousemove at the top and expect more scrolling");
   onScrolled = waitForScrollStop(markup.doc);
 
   markup._onMouseMove({
     preventDefault: () => {},
     target: markup.doc.body,
-    pageY: 0
+    pageY: markup.doc.defaultView.scrollY
   });
 
   let topScrollPos = yield onScrolled;
   ok(topScrollPos < bottomScrollPos, "The view was scrolled up");
   is(topScrollPos, 0, "The view was scrolled up to the top");
 
   info("Simulate a mouseup to stop dragging");
   markup._onMouseUp();
--- a/devtools/client/inspector/markup/test/browser_markup_dragdrop_autoscroll_02.js
+++ b/devtools/client/inspector/markup/test/browser_markup_dragdrop_autoscroll_02.js
@@ -21,29 +21,28 @@ add_task(function* () {
   markup.isDragging = true;
 
   info("Simulate a mousemove on the view, at the bottom, and expect scrolling");
   let onScrolled = waitForScrollStop(markup.doc);
 
   markup._onMouseMove({
     preventDefault: () => {},
     target: markup.doc.body,
-    pageY: viewHeight
+    pageY: viewHeight + markup.doc.defaultView.scrollY
   });
 
   let bottomScrollPos = yield onScrolled;
   ok(bottomScrollPos > 0, "The view was scrolled down");
-
   info("Simulate a mousemove at the top and expect more scrolling");
   onScrolled = waitForScrollStop(markup.doc);
 
   markup._onMouseMove({
     preventDefault: () => {},
     target: markup.doc.body,
-    pageY: 0
+    pageY: markup.doc.defaultView.scrollY
   });
 
   let topScrollPos = yield onScrolled;
   ok(topScrollPos < bottomScrollPos, "The view was scrolled up");
   is(topScrollPos, 0, "The view was scrolled up to the top");
 
   info("Simulate a mouseup to stop dragging");
   markup._onMouseUp();
--- a/devtools/client/inspector/markup/test/doc_markup_dragdrop_autoscroll_02.html
+++ b/devtools/client/inspector/markup/test/doc_markup_dragdrop_autoscroll_02.html
@@ -31,33 +31,10 @@ https://bugzilla.mozilla.org/show_bug.cg
   <div></div>
   <div></div>
   <div></div>
   <div></div>
   <div></div>
   <div></div>
   <div></div>
   <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
 </body>
 </html>
--- a/devtools/client/jsonview/components/text-panel.js
+++ b/devtools/client/jsonview/components/text-panel.js
@@ -66,29 +66,29 @@ define(function (require, exports, modul
     onCopy: function (event) {
       this.props.actions.onCopyJson();
     },
 
     render: function () {
       return (
         Toolbar({},
           ToolbarButton({
-            className: "btn prettyprint",
-            onClick: this.onPrettify},
-            Locale.$STR("jsonViewer.PrettyPrint")
-          ),
-          ToolbarButton({
             className: "btn save",
             onClick: this.onSave},
             Locale.$STR("jsonViewer.Save")
           ),
           ToolbarButton({
             className: "btn copy",
             onClick: this.onCopy},
             Locale.$STR("jsonViewer.Copy")
+          ),
+          ToolbarButton({
+            className: "btn prettyprint",
+            onClick: this.onPrettify},
+            Locale.$STR("jsonViewer.PrettyPrint")
           )
         )
       );
     },
   }));
 
   // Exports from this module
   exports.TextPanel = TextPanel;
--- a/devtools/client/preferences/devtools.js
+++ b/devtools/client/preferences/devtools.js
@@ -291,16 +291,19 @@ pref("devtools.webconsole.timestampMessa
 
 // Web Console automatic multiline mode: |true| if you want incomplete statements
 // to automatically trigger multiline editing (equivalent to shift + enter).
 pref("devtools.webconsole.autoMultiline", true);
 
 // Enable the experimental webconsole frontend (work in progress)
 pref("devtools.webconsole.new-frontend-enabled", false);
 
+// Enable the experimental support for source maps in console (work in progress)
+pref("devtools.sourcemap.locations.enabled", false);
+
 // The number of lines that are displayed in the web console.
 pref("devtools.hud.loglimit", 1000);
 
 // The number of lines that are displayed in the web console for the Net,
 // CSS, JS and Web Developer categories. These defaults should be kept in sync
 // with DEFAULT_LOG_LIMIT in the webconsole frontend.
 pref("devtools.hud.loglimit.network", 1000);
 pref("devtools.hud.loglimit.cssparser", 1000);
--- a/devtools/client/shared/components/frame.js
+++ b/devtools/client/shared/components/frame.js
@@ -29,59 +29,136 @@ module.exports = createClass({
     // Option to display a function name even if it's anonymous.
     showAnonymousFunctionName: PropTypes.bool,
     // Option to display a host name after the source link.
     showHost: PropTypes.bool,
     // Option to display a host name if the filename is empty or just '/'
     showEmptyPathAsHost: PropTypes.bool,
     // Option to display a full source instead of just the filename.
     showFullSourceUrl: PropTypes.bool,
+    // Service to enable the source map feature for console.
+    sourceMapService: PropTypes.object,
   },
 
   getDefaultProps() {
     return {
       showFunctionName: false,
       showAnonymousFunctionName: false,
       showHost: false,
       showEmptyPathAsHost: false,
       showFullSourceUrl: false,
     };
   },
 
+  componentWillMount() {
+    const sourceMapService = this.props.sourceMapService;
+    if (sourceMapService) {
+      const source = this.getSource();
+      sourceMapService.subscribe(source, this.onSourceUpdated);
+    }
+  },
+
+  componentWillUnmount() {
+    const sourceMapService = this.props.sourceMapService;
+    if (sourceMapService) {
+      const source = this.getSource();
+      sourceMapService.unsubscribe(source, this.onSourceUpdated);
+    }
+  },
+
+  /**
+   * Component method to update the FrameView when a resolved location is available
+   * @param event
+   * @param location
+   */
+  onSourceUpdated(event, location, resolvedLocation) {
+    const frame = this.getFrame(resolvedLocation);
+    this.setState({
+      frame,
+      isSourceMapped: true,
+    });
+  },
+
+  /**
+   * Utility method to convert the Frame object to the
+   * Source Object model required by SourceMapService
+   * @param frame
+   * @returns {{url: *, line: *, column: *}}
+   */
+  getSource(frame) {
+    frame = frame || this.props.frame;
+    const { source, line, column } = frame;
+    return {
+      url: source,
+      line,
+      column,
+    };
+  },
+
+  /**
+   * Utility method to convert the Source object model to the
+   * Frame object model required by FrameView class.
+   * @param source
+   * @returns {{source: *, line: *, column: *, functionDisplayName: *}}
+   */
+  getFrame(source) {
+    const { url, line, column } = source;
+    return {
+      source: url,
+      line,
+      column,
+      functionDisplayName: this.props.frame.functionDisplayName,
+    };
+  },
+
   render() {
+    let frame, isSourceMapped;
     let {
       onClick,
-      frame,
       showFunctionName,
       showAnonymousFunctionName,
       showHost,
       showEmptyPathAsHost,
       showFullSourceUrl
     } = this.props;
 
+    if (this.state && this.state.isSourceMapped) {
+      frame = this.state.frame;
+      isSourceMapped = this.state.isSourceMapped;
+    } else {
+      frame = this.props.frame;
+    }
+
     let source = frame.source ? String(frame.source) : "";
     let line = frame.line != void 0 ? Number(frame.line) : null;
     let column = frame.column != void 0 ? Number(frame.column) : null;
 
     const { short, long, host } = getSourceNames(source);
     // Reparse the URL to determine if we should link this; `getSourceNames`
     // has already cached this indirectly. We don't want to attempt to
     // link to "self-hosted" and "(unknown)". However, we do want to link
     // to Scratchpad URIs.
-    const isLinkable = !!(isScratchpadScheme(source) || parseURL(source));
+    // Source mapped sources might not necessary linkable, but they
+    // are still valid in the debugger.
+    const isLinkable = !!(isScratchpadScheme(source) || parseURL(source))
+      || isSourceMapped;
     const elements = [];
     const sourceElements = [];
     let sourceEl;
 
     let tooltip = long;
+
+    // If the source is linkable and line > 0
+    const shouldDisplayLine = isLinkable && line;
+
     // Exclude all falsy values, including `0`, as even
     // a number 0 for line doesn't make sense, and should not be displayed.
     // If source isn't linkable, don't attempt to append line and column
     // info, as this probably doesn't make sense.
-    if (isLinkable && line) {
+    if (shouldDisplayLine) {
       tooltip += `:${line}`;
       // Intentionally exclude 0
       if (column) {
         tooltip += `:${column}`;
       }
     }
 
     let attributes = {
@@ -99,26 +176,35 @@ module.exports = createClass({
         elements.push(
           dom.span({ className: "frame-link-function-display-name" },
             functionDisplayName)
         );
       }
     }
 
     let displaySource = showFullSourceUrl ? long : short;
-    if (showEmptyPathAsHost && (displaySource === "" || displaySource === "/")) {
+    // SourceMapped locations might not be parsed properly by parseURL.
+    // Eg: sourcemapped location could be /folder/file.coffee instead of a url
+    // and so the url parser would not parse non-url locations properly
+    // Check for "/" in displaySource. If "/" is in displaySource,
+    // take everything after last "/".
+    if (isSourceMapped) {
+      displaySource = displaySource.lastIndexOf("/") < 0 ?
+        displaySource :
+        displaySource.slice(displaySource.lastIndexOf("/") + 1);
+    } else if (showEmptyPathAsHost && (displaySource === "" || displaySource === "/")) {
       displaySource = host;
     }
 
     sourceElements.push(dom.span({
       className: "frame-link-filename",
     }, displaySource));
 
     // If source is linkable, and we have a line number > 0
-    if (isLinkable && line) {
+    if (shouldDisplayLine) {
       let lineInfo = `:${line}`;
       // Add `data-line` attribute for testing
       attributes["data-line"] = line;
 
       // Intentionally exclude 0
       if (column) {
         lineInfo += `:${column}`;
         // Add `data-column` attribute for testing
@@ -129,17 +215,17 @@ module.exports = createClass({
     }
 
     // If source is not a URL (self-hosted, eval, etc.), don't make
     // it an anchor link, as we can't link to it.
     if (isLinkable) {
       sourceEl = dom.a({
         onClick: e => {
           e.preventDefault();
-          onClick(frame);
+          onClick(this.getSource(frame));
         },
         href: source,
         className: "frame-link-source",
         draggable: false,
         title: l10n.getFormatStr("frame.viewsourceindebugger", tooltip)
       }, sourceElements);
     } else {
       sourceEl = dom.span({
--- a/devtools/client/shared/components/reps/array.js
+++ b/devtools/client/shared/components/reps/array.js
@@ -122,17 +122,18 @@ define(function (require, exports, modul
     },
 
     render: function () {
       let mode = this.props.mode || "short";
       let object = this.props.object;
       let items;
 
       if (mode == "tiny") {
-        items = DOM.span({className: "length"}, object.length);
+        let isEmpty = object.length === 0;
+        items = DOM.span({className: "length"}, isEmpty ? "" : object.length);
       } else {
         let max = (mode == "short") ? 3 : 300;
         items = this.arrayIterator(object, max);
       }
 
       let objectLink = this.props.objectLink || DOM.span;
 
       return (
--- a/devtools/client/shared/components/reps/grip-array.js
+++ b/devtools/client/shared/components/reps/grip-array.js
@@ -103,17 +103,19 @@ define(function (require, exports, modul
 
     render: function () {
       let mode = this.props.mode || "short";
       let object = this.props.object;
 
       let items;
 
       if (mode == "tiny") {
-        items = span({className: "length"}, this.getLength(object));
+        let objectLength = this.getLength(object);
+        let isEmpty = objectLength === 0;
+        items = span({className: "length"}, isEmpty ? "" : objectLength);
       } else {
         let max = (mode == "short") ? 3 : 300;
         items = this.arrayIterator(object, max);
       }
 
       let objectLink = this.props.objectLink || span;
 
       return (
--- a/devtools/client/shared/components/test/mochitest/test_reps_array.html
+++ b/devtools/client/shared/components/test/mochitest/test_reps_array.html
@@ -52,17 +52,17 @@ window.onload = Task.async(function* () 
 
     const modeTests = [
       {
         mode: undefined,
         expectedOutput: defaultOutput,
       },
       {
         mode: "tiny",
-        expectedOutput: `[0]`,
+        expectedOutput: `[]`,
       },
       {
         mode: "short",
         expectedOutput: defaultOutput,
       },
       {
         mode: "long",
         expectedOutput: defaultOutput,
--- a/devtools/client/shared/components/test/mochitest/test_reps_grip-array.html
+++ b/devtools/client/shared/components/test/mochitest/test_reps_grip-array.html
@@ -52,17 +52,17 @@ window.onload = Task.async(function* () 
 
     const modeTests = [
       {
         mode: undefined,
         expectedOutput: defaultOutput,
       },
       {
         mode: "tiny",
-        expectedOutput: `[0]`,
+        expectedOutput: `[]`,
       },
       {
         mode: "short",
         expectedOutput: defaultOutput,
       },
       {
         mode: "long",
         expectedOutput: defaultOutput,
--- a/devtools/client/shared/components/tree/tree-view.css
+++ b/devtools/client/shared/components/tree/tree-view.css
@@ -18,16 +18,21 @@
 /* TreeView Table*/
 
 .treeTable .treeLabelCell {
   padding: 2px 0 2px 0px;
   vertical-align: top;
   white-space: nowrap;
 }
 
+.treeTable .treeLabelCell::after {
+  content: ":";
+  color: var(--object-color);
+}
+
 .treeTable .treeValueCell {
   padding: 2px 0 2px 5px;
   overflow: hidden;
 }
 
 .treeTable .treeLabel {
   cursor: default;
   overflow: hidden;
--- a/devtools/client/shared/widgets/TableWidget.js
+++ b/devtools/client/shared/widgets/TableWidget.js
@@ -47,35 +47,38 @@ const MAX_VISIBLE_STRING_SIZE = 100;
  *
  * @param {nsIDOMNode} node
  *        The container element for the table widget.
  * @param {object} options
  *        - initialColumns: map of key vs display name for initial columns of
  *                          the table. See @setupColumns for more info.
  *        - uniqueId: the column which will be the unique identifier of each
  *                    entry in the table. Default: name.
+ *        - wrapTextInElements: Don't ever use 'value' attribute on labels.
+ *                              Default: false.
  *        - emptyText: text to display when no entries in the table to display.
  *        - highlightUpdated: true to highlight the changed/added row.
  *        - removableColumns: Whether columns are removeable. If set to false,
  *                            the context menu in the headers will not appear.
  *        - firstColumn: key of the first column that should appear.
  *        - cellContextMenuId: ID of a <menupopup> element to be set as a
  *                             context menu of every cell.
  */
 function TableWidget(node, options = {}) {
   EventEmitter.decorate(this);
 
   this.document = node.ownerDocument;
   this.window = this.document.defaultView;
   this._parent = node;
 
   let {initialColumns, emptyText, uniqueId, highlightUpdated, removableColumns,
-       firstColumn, cellContextMenuId} = options;
+       firstColumn, wrapTextInElements, cellContextMenuId} = options;
   this.emptyText = emptyText || "";
   this.uniqueId = uniqueId || "name";
+  this.wrapTextInElements = wrapTextInElements || false;
   this.firstColumn = firstColumn || "";
   this.highlightUpdated = highlightUpdated || false;
   this.removableColumns = removableColumns !== false;
   this.cellContextMenuId = cellContextMenuId;
 
   this.tbody = this.document.createElementNS(XUL_NS, "hbox");
   this.tbody.className = "table-widget-body theme-body";
   this.tbody.setAttribute("flex", "1");
@@ -959,16 +962,17 @@ module.exports.TableWidget = TableWidget
  *        The displayed string on the column's header.
  */
 function Column(table, id, header) {
   this.tbody = table.tbody;
   this.document = table.document;
   this.window = table.window;
   this.id = id;
   this.uniqueId = table.uniqueId;
+  this.wrapTextInElements = table.wrapTextInElements;
   this.table = table;
   this.cells = [];
   this.items = {};
 
   this.highlightUpdated = table.highlightUpdated;
 
   // This wrapping element is required solely so that position:sticky works on
   // the headers of the columns.
@@ -1441,16 +1445,17 @@ Column.prototype = {
  *        can be a DOMNode that is appended or a string value.
  * @param {Cell} nextCell
  *        The cell object which is next to this cell. null if this cell is last
  *        cell of the column
  */
 function Cell(column, item, nextCell) {
   let document = column.document;
 
+  this.wrapTextInElements = column.wrapTextInElements;
   this.label = document.createElementNS(XUL_NS, "label");
   this.label.setAttribute("crop", "end");
   this.label.className = "plain table-widget-cell";
 
   if (nextCell) {
     column.column.insertBefore(this.label, nextCell.label);
   } else {
     column.column.appendChild(this.label);
@@ -1494,16 +1499,22 @@ Cell.prototype = {
 
   set value(value) {
     this._value = value;
     if (value == null) {
       this.label.setAttribute("value", "");
       return;
     }
 
+    if (this.wrapTextInElements && !(value instanceof Ci.nsIDOMNode)) {
+      let span = this.label.ownerDocument.createElementNS(HTML_NS, "span");
+      span.textContent = value;
+      value = span;
+    }
+
     if (!(value instanceof Ci.nsIDOMNode) &&
         value.length > MAX_VISIBLE_STRING_SIZE) {
       value = value .substr(0, MAX_VISIBLE_STRING_SIZE) + "\u2026";
     }
 
     if (value instanceof Ci.nsIDOMNode) {
       this.label.removeAttribute("value");
 
--- a/devtools/client/themes/webconsole.css
+++ b/devtools/client/themes/webconsole.css
@@ -493,16 +493,26 @@ a {
   border: 1px solid var(--theme-splitter-color);
   border-radius: 3px;
 }
 
 .consoletable {
   margin: 5px 0 0 0;
 }
 
+/* Force cells to only show one row of contents.  Getting normal ellipses
+   behavior has proven impossible so far, so this is better than letting
+   rows get out of vertical alignment when one cell has a lot of content. */
+.consoletable .table-widget-cell > span {
+  overflow: hidden;
+  display: flex;
+  height: 1.25em;
+  line-height: 1.25em;
+}
+
 .theme-light .message[severity=error] .stacktrace {
   background-color: rgba(255, 255, 255, 0.5);
 }
 
 .theme-dark .message[severity=error] .stacktrace {
   background-color: rgba(0, 0, 0, 0.5);
 }
 
--- a/devtools/client/webconsole/console-output.js
+++ b/devtools/client/webconsole/console-output.js
@@ -3615,16 +3615,17 @@ Widgets.Table.prototype = extend(Widgets
     if (this.element) {
       return this;
     }
 
     let result = this.element = this.document.createElementNS(XHTML_NS, "div");
     result.className = "consoletable devtools-monospace";
 
     this.table = new TableWidget(result, {
+      wrapTextInElements: true,
       initialColumns: this.columns,
       uniqueId: "_index",
       firstColumn: "_index"
     });
 
     for (let row of this.data) {
       this.table.push(row);
     }
--- a/devtools/client/webconsole/webconsole.js
+++ b/devtools/client/webconsole/webconsole.js
@@ -2526,55 +2526,62 @@ WebConsoleFrame.prototype = {
     locationNode.className = "message-location devtools-monospace";
 
     if (!url) {
       url = "";
     }
 
     let fullURL = url.split(" -> ").pop();
     // Make the location clickable.
-    let onClick = () => {
+    let onClick = ({ url, line }) => {
       let category = locationNode.closest(".message").category;
       let target = null;
 
       if (/^Scratchpad\/\d+$/.test(url)) {
         target = "scratchpad";
       } else if (category === CATEGORY_CSS) {
         target = "styleeditor";
       } else if (category === CATEGORY_JS || category === CATEGORY_WEBDEV) {
         target = "jsdebugger";
-      } else if (/\.js$/.test(fullURL)) {
+      } else if (/\.js$/.test(url)) {
         // If it ends in .js, let's attempt to open in debugger
         // anyway, as this falls back to normal view-source.
         target = "jsdebugger";
+      } else {
+        // Point everything else to debugger, if source not available,
+        // it will fall back to view-source.
+        target = "jsdebugger";
       }
 
       switch (target) {
         case "scratchpad":
           this.owner.viewSourceInScratchpad(url, line);
           return;
         case "jsdebugger":
-          this.owner.viewSourceInDebugger(fullURL, line);
+          this.owner.viewSourceInDebugger(url, line);
           return;
         case "styleeditor":
-          this.owner.viewSourceInStyleEditor(fullURL, line);
+          this.owner.viewSourceInStyleEditor(url, line);
           return;
       }
       // No matching tool found; use old school view-source
-      this.owner.viewSource(fullURL, line);
+      this.owner.viewSource(url, line);
     };
 
+    const toolbox = gDevTools.getToolbox(this.owner.target);
+
     this.ReactDOM.render(this.FrameView({
       frame: {
         source: fullURL,
         line,
         column
       },
       showEmptyPathAsHost: true,
       onClick,
+      sourceMapService: toolbox ? toolbox._sourceMapService : null,
     }), locationNode);
 
     return locationNode;
   },
 
   /**
    * Adjusts the category and severity of the given message.
    *
--- a/devtools/server/actors/eventlooplag.js
+++ b/devtools/server/actors/eventlooplag.js
@@ -8,74 +8,53 @@
  * The eventLoopLag actor emits "event-loop-lag" events when the event
  * loop gets unresponsive. The event comes with a "time" property (the
  * duration of the lag in milliseconds).
  */
 
 const {Ci} = require("chrome");
 const Services = require("Services");
 const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
-const protocol = require("devtools/shared/protocol");
-const {method, Arg, RetVal} = protocol;
+const {Actor, ActorClassWithSpec} = require("devtools/shared/protocol");
 const events = require("sdk/event/core");
-
-var EventLoopLagActor = exports.EventLoopLagActor = protocol.ActorClass({
-
-  typeName: "eventLoopLag",
+const {eventLoopLagSpec} = require("devtools/shared/specs/eventlooplag");
 
+var EventLoopLagActor = exports.EventLoopLagActor = ActorClassWithSpec(eventLoopLagSpec, {
   _observerAdded: false,
 
-  events: {
-    "event-loop-lag" : {
-      type: "event-loop-lag",
-      time: Arg(0, "number") // duration of the lag in milliseconds.
-    }
-  },
-
   /**
    * Start tracking the event loop lags.
    */
-  start: method(function () {
+  start: function () {
     if (!this._observerAdded) {
       Services.obs.addObserver(this, "event-loop-lag", false);
       this._observerAdded = true;
     }
     return Services.appShell.startEventLoopLagTracking();
-  }, {
-    request: {},
-    response: {success: RetVal("number")}
-  }),
+  },
 
   /**
    * Stop tracking the event loop lags.
    */
-  stop: method(function () {
+  stop: function () {
     if (this._observerAdded) {
       Services.obs.removeObserver(this, "event-loop-lag");
       this._observerAdded = false;
     }
     Services.appShell.stopEventLoopLagTracking();
-  }, {request: {}, response: {}}),
+  },
 
   destroy: function () {
     this.stop();
-    protocol.Actor.prototype.destroy.call(this);
+    Actor.prototype.destroy.call(this);
   },
 
   // nsIObserver
 
   observe: function (subject, topic, data) {
     if (topic == "event-loop-lag") {
       // Forward event loop lag event
       events.emit(this, "event-loop-lag", data);
     }
   },
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
 });
-
-exports.EventLoopLagFront = protocol.FrontClass(EventLoopLagActor, {
-  initialize: function (client, form) {
-    protocol.Front.prototype.initialize.call(this, client);
-    this.actorID = form.eventLoopLagActor;
-    this.manage(this);
-  },
-});
--- a/devtools/server/actors/script.js
+++ b/devtools/server/actors/script.js
@@ -14,19 +14,17 @@ 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 ScriptStore = require("./utils/ScriptStore");
 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", () => {
   let Debugger = require("Debugger");
@@ -489,24 +487,16 @@ const ThreadActor = ActorClassWithSpec(t
     if (!this._threadLifetimePool) {
       this._threadLifetimePool = new ActorPool(this.conn);
       this.conn.addActorPool(this._threadLifetimePool);
       this._threadLifetimePool.objectActors = new WeakMap();
     }
     return this._threadLifetimePool;
   },
 
-  get scripts() {
-    if (!this._scripts) {
-      this._scripts = new ScriptStore();
-      this._scripts.addScripts(this.dbg.findScripts());
-    }
-    return this._scripts;
-  },
-
   get sources() {
     return this._parent.sources;
   },
 
   get youngestFrame() {
     if (this.state != "paused") {
       return null;
     }
@@ -640,18 +630,16 @@ const ThreadActor = ActorClassWithSpec(t
     try {
       // Put ourselves in the paused state.
       let packet = this._paused();
       if (!packet) {
         return { error: "notAttached" };
       }
       packet.why = { type: "attached" };
 
-      this._restoreBreakpoints();
-
       // Send the response to the attach request now (rather than
       // returning it), because we're going to start a nested event loop
       // here.
       this.conn.send(packet);
 
       // Start a nested event loop.
       this._pushThreadPause();
 
@@ -1179,17 +1167,18 @@ const ThreadActor = ActorClassWithSpec(t
   _breakOnEnter: function (script) {
     let offsets = script.getAllOffsets();
     for (let line = 0, n = offsets.length; line < n; line++) {
       if (offsets[line]) {
         // N.B. Hidden breakpoints do not have an original location, and are not
         // stored in the breakpoint actor map.
         let actor = new BreakpointActor(this);
         this.threadLifetimePool.addActor(actor);
-        let scripts = this.scripts.getScriptsBySourceAndLine(script.source, line);
+
+        let scripts = this.dbg.findScripts({ source: script.source, line: line });
         let entryPoints = findEntryPointsForLine(scripts, line);
         setBreakpointAtEntryPoints(actor, entryPoints);
         this._hiddenBreakpoints.set(actor.actorID, actor);
         break;
       }
     }
   },
 
@@ -1313,17 +1302,18 @@ const ThreadActor = ActorClassWithSpec(t
   },
 
   /**
    * Get the script and source lists from the debugger.
    */
   _discoverSources: function () {
     // Only get one script per Debugger.Source.
     const sourcesToScripts = new Map();
-    const scripts = this.scripts.getAllScripts();
+    const scripts = this.dbg.findScripts();
+
     for (let i = 0, len = scripts.length; i < len; i++) {
       let s = scripts[i];
       if (s.source) {
         sourcesToScripts.set(s.source, s);
       }
     }
 
     return all([...sourcesToScripts.values()].map(script => {
@@ -1919,46 +1909,27 @@ const ThreadActor = ActorClassWithSpec(t
         from: this.actorID,
         type: name,
         source: source.form()
       });
     }
   },
 
   /**
-   * Restore any pre-existing breakpoints to the sources that we have access to.
-   */
-  _restoreBreakpoints: function () {
-    if (this.breakpointActorMap.size === 0) {
-      return;
-    }
-
-    for (let s of this.scripts.getSources()) {
-      this._addSource(s);
-    }
-  },
-
-  /**
    * Add the provided source to the server cache.
    *
    * @param aSource Debugger.Source
    *        The source that will be stored.
    * @returns true, if the source was added; false otherwise.
    */
   _addSource: function (aSource) {
     if (!this.sources.allowSource(aSource) || this._debuggerSourcesSeen.has(aSource)) {
       return false;
     }
 
-    // The scripts must be added to the ScriptStore before restoring
-    // breakpoints. If we try to add them to the ScriptStore any later, we can
-    // accidentally set a breakpoint in a top level script as a "closest match"
-    // because we wouldn't have added the child scripts to the ScriptStore yet.
-    this.scripts.addScripts(this.dbg.findScripts({ source: aSource }));
-
     let sourceActor = this.sources.createNonSourceMappedActor(aSource);
     let bpActors = [...this.breakpointActorMap.findActors()];
 
     if (this._options.useSourceMaps) {
       let promises = [];
 
       // Go ahead and establish the source actors for this script, which
       // fetches sourcemaps if available and sends onNewSource
--- a/devtools/server/actors/source.js
+++ b/devtools/server/actors/source.js
@@ -180,17 +180,16 @@ let SourceActor = ActorClassWithSpec(sou
 
   get isInlineSource() {
     return this._isInlineSource;
   },
 
   get threadActor() { return this._threadActor; },
   get sources() { return this._threadActor.sources; },
   get dbg() { return this.threadActor.dbg; },
-  get scripts() { return this.threadActor.scripts; },
   get source() { return this._source; },
   get generatedSource() { return this._generatedSource; },
   get breakpointActorMap() { return this.threadActor.breakpointActorMap; },
   get url() {
     if (this.source) {
       return getSourceURL(this.source, this.threadActor._parent.window);
     }
     return this._originalUrl;
@@ -439,17 +438,17 @@ let SourceActor = ActorClassWithSpec(sou
   /**
    * Extract all executable offsets from the given script
    * @param String url - extract offsets of the script with this url
    * @param Boolean onlyLine - will return only the line number
    * @return Set - Executable offsets/lines of the script
    **/
   getExecutableOffsets: function (source, onlyLine) {
     let offsets = new Set();
-    for (let s of this.threadActor.scripts.getScriptsBySource(source)) {
+    for (let s of this.dbg.findScripts({ source })) {
       for (let offset of s.getAllColumnOffsets()) {
         offsets.add(onlyLine ? offset.lineNumber : offset);
       }
     }
 
     return offsets;
   },
 
@@ -713,20 +712,27 @@ let SourceActor = ActorClassWithSpec(sou
     const { originalLocation } = actor;
     const { originalLine, originalSourceActor } = originalLocation;
 
     if (!this.isSourceMapped) {
       if (!this._setBreakpointAtGeneratedLocation(
         actor,
         GeneratedLocation.fromOriginalLocation(originalLocation)
       )) {
-        const scripts = this.scripts.getScriptsBySourceActorAndLine(
-          this,
-          originalLine
-        );
+        const query = { line: originalLine };
+        // For most cases, we have a real source to query for. The
+        // only time we don't is for HTML pages. In that case we want
+        // to query for scripts in an HTML page based on its URL, as
+        // there could be several sources within an HTML page.
+        if (this.source) {
+          query.source = this.source;
+        } else {
+          query.url = this.url;
+        }
+        const scripts = this.dbg.findScripts(query);
 
         // Never do breakpoint sliding for column breakpoints.
         // Additionally, never do breakpoint sliding if no scripts
         // exist on this line.
         //
         // Sliding can go horribly wrong if we always try to find the
         // next line with valid entry points in the entire file.
         // Scripts may be completely GCed and we never knew they
@@ -831,21 +837,25 @@ let SourceActor = ActorClassWithSpec(sou
   _setBreakpointAtGeneratedLocation: function (actor, generatedLocation) {
     let {
       generatedSourceActor,
       generatedLine,
       generatedColumn,
       generatedLastColumn
     } = generatedLocation;
 
-    // Find all scripts that match the given source actor and line number.
-    let scripts = this.scripts.getScriptsBySourceActorAndLine(
-      generatedSourceActor,
-      generatedLine
-    );
+    // Find all scripts that match the given source actor and line
+    // number.
+    const query = { line: generatedLine };
+    if (generatedSourceActor.source) {
+      query.source = generatedSourceActor.source;
+    } else {
+      query.url = generatedSourceActor.url;
+    }
+    let scripts = this.dbg.findScripts(query);
 
     scripts = scripts.filter((script) => !actor.hasScript(script));
 
     // Find all entry points that correspond to the given location.
     let entryPoints = [];
     if (generatedColumn === undefined) {
       // This is a line breakpoint, so we are interested in all offsets
       // that correspond to the given line number.
--- a/devtools/server/actors/stylesheets.js
+++ b/devtools/server/actors/stylesheets.js
@@ -455,17 +455,20 @@ var StyleSheetActor = protocol.ActorClas
       charset: this._getCSSCharset()
     };
 
     // Bug 1282660 - We use the system principal to load the default internal
     // stylesheets instead of the content principal since such stylesheets
     // require system principal to load. At meanwhile, we strip the loadGroup
     // for preventing the assertion of the userContextId mismatching.
     // The default internal stylesheets load from the 'resource:' URL.
-    if (!/^resource:\/\//.test(this.href)) {
+    // Bug 1287607 - The 'chrome:' URL will be also loaded from here, so we do
+    // the same thing for such URLs as well.
+    if (!/^resource:\/\//.test(this.href) &&
+        !/^chrome:\/\//.test(this.href)) {
       options.window = this.window;
       options.principal = this.document.nodePrincipal;
     }
 
     return fetch(this.href, options).then(({ content }) => {
       this.text = content;
       return content;
     });
deleted file mode 100644
--- a/devtools/server/actors/utils/ScriptStore.js
+++ /dev/null
@@ -1,219 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const { noop } = require("devtools/shared/DevToolsUtils");
-
-/**
- * A `ScriptStore` is a cache of `Debugger.Script` instances. It holds strong
- * references to the cached scripts to alleviate the GC-sensitivity issues that
- * plague `Debugger.prototype.findScripts`, but this means that its lifetime
- * must be managed carefully. It is the `ScriptStore` user's responsibility to
- * ensure that the `ScriptStore` stays up to date.
- *
- * Implementation Notes:
- *
- * The ScriptStore's prototype methods are very hot, in general. To help the
- * JIT, they avoid ES6-isms and higher-order iteration functions, for the most
- * part. You might be wondering why we don't maintain indices on, say,
- * Debugger.Source for faster querying, if these methods are so hot. First, the
- * hottest method is actually just getting all scripts; second, populating the
- * store becomes prohibitively expensive. So we fall back to linear queries
- * (which isn't so bad, because Debugger.prototype.findScripts is also linear).
- */
-function ScriptStore() {
-  // Set of every Debugger.Script in the cache.
-  this._scripts = new NoDeleteSet;
-}
-
-module.exports = ScriptStore;
-
-ScriptStore.prototype = {
-  // Populating a ScriptStore.
-
-  /**
-   * Add one script to the cache.
-   *
-   * @param Debugger.Script script
-   */
-  addScript(script) {
-    this._scripts.add(script);
-  },
-
-  /**
-   * Add many scripts to the cache at once.
-   *
-   * @param Array scripts
-   *        The set of Debugger.Scripts to add to the cache.
-   */
-  addScripts(scripts) {
-    for (var i = 0, len = scripts.length; i < len; i++) {
-      this.addScript(scripts[i]);
-    }
-  },
-
-  // Querying a ScriptStore.
-
-  /**
-   * Get all the sources for which we have scripts cached.
-   *
-   * @returns Array of Debugger.Source
-   */
-  getSources() {
-    return [...new Set(this._scripts.items.map(s => s.source))];
-  },
-
-  /**
-   * Get all the scripts in the cache.
-   *
-   * @returns read-only Array of Debugger.Script.
-   *
-   * NB: The ScriptStore retains ownership of the returned array, and the
-   * ScriptStore's consumers MUST NOT MODIFY its contents!
-   */
-  getAllScripts() {
-    return this._scripts.items;
-  },
-
-  getScriptsBySourceActor(sourceActor) {
-    return sourceActor.source ?
-           this.getScriptsBySource(sourceActor.source) :
-           this.getScriptsByURL(sourceActor._originalUrl);
-  },
-
-  getScriptsBySourceActorAndLine(sourceActor, line) {
-    return sourceActor.source ?
-           this.getScriptsBySourceAndLine(sourceActor.source, line) :
-           this.getScriptsByURLAndLine(sourceActor._originalUrl, line);
-  },
-
-  /**
-   * Get all scripts produced from the given source.
-   *
-   * @oaram Debugger.Source source
-   * @returns Array of Debugger.Script
-   */
-  getScriptsBySource(source) {
-    var results = [];
-    var scripts = this._scripts.items;
-    var length = scripts.length;
-    for (var i = 0; i < length; i++) {
-      if (scripts[i].source === source) {
-        results.push(scripts[i]);
-      }
-    }
-    return results;
-  },
-
-  /**
-   * Get all scripts produced from the given source whose source code definition
-   * spans the given line.
-   *
-   * @oaram Debugger.Source source
-   * @param Number line
-   * @returns Array of Debugger.Script
-   */
-  getScriptsBySourceAndLine(source, line) {
-    var results = [];
-    var scripts = this._scripts.items;
-    var length = scripts.length;
-    for (var i = 0; i < length; i++) {
-      var script = scripts[i];
-      if (script.source === source &&
-          script.startLine <= line &&
-          (script.startLine + script.lineCount) > line) {
-        results.push(script);
-      }
-    }
-    return results;
-  },
-
-  /**
-   * Get all scripts defined by a source at the given URL.
-   *
-   * @param String url
-   * @returns Array of Debugger.Script
-   */
-  getScriptsByURL(url) {
-    var results = [];
-    var scripts = this._scripts.items;
-    var length = scripts.length;
-    for (var i = 0; i < length; i++) {
-      if (scripts[i].url === url) {
-        results.push(scripts[i]);
-      }
-    }
-    return results;
-  },
-
-  /**
-   * Get all scripts defined by a source a the given URL and whose source code
-   * definition spans the given line.
-   *
-   * @param String url
-   * @param Number line
-   * @returns Array of Debugger.Script
-   */
-  getScriptsByURLAndLine(url, line) {
-    var results = [];
-    var scripts = this._scripts.items;
-    var length = scripts.length;
-    for (var i = 0; i < length; i++) {
-      var script = scripts[i];
-      if (script.url === url &&
-          script.startLine <= line &&
-          (script.startLine + script.lineCount) > line) {
-        results.push(script);
-      }
-    }
-    return results;
-  },
-};
-
-
-/**
- * A set which can only grow, and does not support the delete operation.
- * Provides faster iteration than the native Set by maintaining an array of all
- * items, in addition to the internal set of all items, which allows direct
- * iteration (without the iteration protocol and calling into C++, which are
- * both expensive).
- */
-function NoDeleteSet() {
-  this._set = new Set();
-  this.items = [];
-}
-
-NoDeleteSet.prototype = {
-  /**
-   * An array containing every item in the set for convenience and faster
-   * iteration. This is public for reading only, and consumers MUST NOT modify
-   * this array!
-   */
-  items: null,
-
-  /**
-   * Add an item to the set.
-   *
-   * @param any item
-   */
-  add(item) {
-    if (!this._set.has(item)) {
-      this._set.add(item);
-      this.items.push(item);
-    }
-  },
-
-  /**
-   * Return true if the item is in the set, false otherwise.
-   *
-   * @param any item
-   * @returns Boolean
-   */
-  has(item) {
-    return this._set.has(item);
-  }
-};
--- a/devtools/server/actors/utils/TabSources.js
+++ b/devtools/server/actors/utils/TabSources.js
@@ -240,17 +240,17 @@ TabSources.prototype = {
         }
       }
 
       if (url in this._sourceMappedSourceActors) {
         return this._sourceMappedSourceActors[url];
       }
     }
 
-    throw new Error("getSourceByURL: could not find source for " + url);
+    throw new Error("getSourceActorByURL: could not find source for " + url);
     return null;
   },
 
   /**
    * Returns true if the URL likely points to a minified resource, false
    * otherwise.
    *
    * @param String aURL
--- a/devtools/server/actors/utils/moz.build
+++ b/devtools/server/actors/utils/moz.build
@@ -5,13 +5,12 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DevToolsModules(
     'actor-registry-utils.js',
     'audionodes.json',
     'automation-timeline.js',
     'make-debugger.js',
     'map-uri-to-addon-id.js',
-    'ScriptStore.js',
     'stack.js',
     'TabSources.js',
     'walker-search.js'
 )
--- a/devtools/server/actors/webbrowser.js
+++ b/devtools/server/actors/webbrowser.js
@@ -2086,51 +2086,45 @@ TabActor.prototype = {
    * @param {String} request.url
    * @param {Number} request.line
    * @param {Number?} request.column
    * @return {Promise<Object>}
    */
   onResolveLocation(request) {
     let { url, line } = request;
     let column = request.column || 0;
-    let actor = this.sources.getSourceActorByURL(url);
-
-    if (actor) {
-      // Get the generated source actor if this is source mapped
-      let generatedActor = actor.generatedSource ?
-        this.sources.createNonSourceMappedActor(actor.generatedSource) :
-        actor;
-      let generatedLocation = new GeneratedLocation(
-        generatedActor, line, column);
+    const scripts = this.threadActor.dbg.findScripts({ url });
 
-      return this.sources.getOriginalLocation(generatedLocation).then(loc => {
-        // If no map found, return this packet
-        if (loc.originalLine == null) {
-          return {
-            from: this.actorID,
-            type: "resolveLocation",
-            error: "MAP_NOT_FOUND"
-          };
-        }
-
-        loc = loc.toJSON();
-        return {
-          from: this.actorID,
-          url: loc.source.url,
-          column: loc.column,
-          line: loc.line
-        };
+    if (!scripts[0] || !scripts[0].source) {
+      return promise.resolve({
+        from: this.actorID,
+        type: "resolveLocation",
+        error: "SOURCE_NOT_FOUND"
       });
     }
+    const source = scripts[0].source;
+    const generatedActor = this.sources.createNonSourceMappedActor(source);
+    let generatedLocation = new GeneratedLocation(
+      generatedActor, line, column);
+    return this.sources.getOriginalLocation(generatedLocation).then(loc => {
+      // If no map found, return this packet
+      if (loc.originalLine == null) {
+        return {
+          type: "resolveLocation",
+          error: "MAP_NOT_FOUND"
+        };
+      }
 
-    // Fall back to this packet when source is not found
-    return promise.resolve({
-      from: this.actorID,
-      type: "resolveLocation",
-      error: "SOURCE_NOT_FOUND"
+      loc = loc.toJSON();
+      return {
+        from: this.actorID,
+        url: loc.source.url,
+        column: loc.column,
+        line: loc.line
+      };
     });
   },
 };
 
 /**
  * The request types this actor can handle.
  */
 TabActor.prototype.requestTypes = {
deleted file mode 100644
--- a/devtools/server/tests/unit/test_ScriptStore.js
+++ /dev/null
@@ -1,168 +0,0 @@
-/* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-// Test the functionality of ScriptStore.
-
-const ScriptStore = require("devtools/server/actors/utils/ScriptStore");
-
-// Fixtures
-
-const firstSource = "firstSource";
-const secondSource = "secondSource";
-const thirdSource = "thirdSource";
-
-const scripts = new Set([
-  {
-    url: "a.js",
-    source: firstSource,
-    startLine: 1,
-    lineCount: 100,
-    global: "g1"
-  },
-  {
-    url: "a.js",
-    source: firstSource,
-    startLine: 1,
-    lineCount: 40,
-    global: "g1"
-  },
-  {
-    url: "a.js",
-    source: firstSource,
-    startLine: 50,
-    lineCount: 100,
-    global: "g1"
-  },
-  {
-    url: "a.js",
-    source: firstSource,
-    startLine: 60,
-    lineCount: 90,
-    global: "g1"
-  },
-  {
-    url: "index.html",
-    source: secondSource,
-    startLine: 150,
-    lineCount: 1,
-    global: "g2"
-  },
-  {
-    url: "index.html",
-    source: thirdSource,
-    startLine: 200,
-    lineCount: 100,
-    global: "g2"
-  },
-  {
-    url: "index.html",
-    source: thirdSource,
-    startLine: 250,
-    lineCount: 10,
-    global: "g2"
-  },
-  {
-    url: "index.html",
-    source: thirdSource,
-    startLine: 275,
-    lineCount: 5,
-    global: "g2"
-  }
-]);
-
-function contains(script, line) {
-  return script.startLine <= line &&
-    line < script.startLine + script.lineCount;
-}
-
-function run_test() {
-  testAddScript();
-  testAddScripts();
-  testGetSources();
-  testGetScriptsBySource();
-  testGetScriptsBySourceAndLine();
-  testGetScriptsByURL();
-  testGetScriptsByURLAndLine();
-}
-
-function testAddScript() {
-  const ss = new ScriptStore();
-
-  for (let s of scripts) {
-    ss.addScript(s);
-  }
-
-  equal(ss.getAllScripts().length, scripts.size);
-
-  for (let s of ss.getAllScripts()) {
-    ok(scripts.has(s));
-  }
-}
-
-function testAddScripts() {
-  const ss = new ScriptStore();
-  ss.addScripts([...scripts]);
-
-  equal(ss.getAllScripts().length, scripts.size);
-
-  for (let s of ss.getAllScripts()) {
-    ok(scripts.has(s));
-  }
-}
-
-function testGetSources() {
-  const ss = new ScriptStore();
-  ss.addScripts([...scripts]);
-
-  const expected = new Set([firstSource, secondSource, thirdSource]);
-  const actual = ss.getSources();
-  equal(expected.size, actual.length);
-
-  for (let s of actual) {
-    ok(expected.has(s));
-    expected.delete(s);
-  }
-}
-
-function testGetScriptsBySource() {
-  const ss = new ScriptStore();
-  ss.addScripts([...scripts]);
-
-  const expected = [...scripts].filter(s => s.source === thirdSource);
-  const actual = ss.getScriptsBySource(thirdSource);
-
-  deepEqual(actual, expected);
-}
-
-function testGetScriptsBySourceAndLine() {
-  const ss = new ScriptStore();
-  ss.addScripts([...scripts]);
-
-  const expected = [...scripts].filter(
-    s => s.source === firstSource && contains(s, 65));
-  const actual = ss.getScriptsBySourceAndLine(firstSource, 65);
-
-  deepEqual(actual, expected);
-}
-
-function testGetScriptsByURL() {
-  const ss = new ScriptStore();
-  ss.addScripts([...scripts]);
-
-  const expected = [...scripts].filter(s => s.url === "index.html");
-  const actual = ss.getScriptsByURL("index.html");
-
-  deepEqual(actual, expected);
-}
-
-function testGetScriptsByURLAndLine() {
-  const ss = new ScriptStore();
-  ss.addScripts([...scripts]);
-
-  const expected = [...scripts].filter(
-    s => s.url === "index.html" && contains(s, 250));
-  const actual = ss.getScriptsByURLAndLine("index.html", 250);
-
-  deepEqual(actual, expected);
-}
--- a/devtools/server/tests/unit/test_eventlooplag_actor.js
+++ b/devtools/server/tests/unit/test_eventlooplag_actor.js
@@ -4,17 +4,17 @@
 /**
  * Test the eventLoopLag actor.
  */
 
 "use strict";
 
 function run_test()
 {
-  let {EventLoopLagFront} = require("devtools/server/actors/eventlooplag");
+  let {EventLoopLagFront} = require("devtools/shared/fronts/eventlooplag");
 
   DebuggerServer.init();
   DebuggerServer.addBrowserActors();
 
   // As seen in EventTracer.cpp
   let threshold = 20;
   let interval = 10;
 
--- a/devtools/server/tests/unit/xpcshell.ini
+++ b/devtools/server/tests/unit/xpcshell.ini
@@ -32,17 +32,16 @@ support-files =
   setBreakpoint-on-line-with-no-offsets-in-gcd-script.js
   addons/web-extension/manifest.json
   addons/web-extension2/manifest.json
 
 [test_addon_reload.js]
 [test_addons_actor.js]
 [test_animation_name.js]
 [test_animation_type.js]
-[test_ScriptStore.js]
 [test_actor-registry-actor.js]
 [test_nesting-01.js]
 [test_nesting-02.js]
 [test_nesting-03.js]
 [test_forwardingprefix.js]
 [test_getyoungestframe.js]
 [test_nsjsinspector.js]
 [test_dbgactor.js]
new file mode 100644
--- /dev/null
+++ b/devtools/shared/fronts/eventlooplag.js
@@ -0,0 +1,15 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+const { Front, FrontClassWithSpec } = require("devtools/shared/protocol");
+const { eventLoopLagSpec } = require("devtools/shared/specs/eventlooplag");
+
+exports.EventLoopLagFront = FrontClassWithSpec(eventLoopLagSpec, {
+  initialize: function (client, form) {
+    Front.prototype.initialize.call(this, client);
+    this.actorID = form.eventLoopLagActor;
+    this.manage(this);
+  },
+});
--- a/devtools/shared/fronts/moz.build
+++ b/devtools/shared/fronts/moz.build
@@ -10,16 +10,17 @@ DevToolsModules(
     'animation.js',
     'call-watcher.js',
     'canvas.js',
     'css-properties.js',
     'csscoverage.js',
     'device.js',
     'director-manager.js',
     'director-registry.js',
+    'eventlooplag.js',
     'framerate.js',
     'gcli.js',
     'highlighters.js',
     'inspector.js',
     'layout.js',
     'memory.js',
     'performance-recording.js',
     'performance.js',
--- 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,
new file mode 100644
--- /dev/null
+++ b/devtools/shared/specs/eventlooplag.js
@@ -0,0 +1,31 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+const { Arg, RetVal, generateActorSpec } = require("devtools/shared/protocol");
+
+const eventLoopLagSpec = generateActorSpec({
+  typeName: "eventLoopLag",
+
+  events: {
+    "event-loop-lag": {
+      type: "event-loop-lag",
+      // duration of the lag in milliseconds.
+      time: Arg(0, "number")
+    }
+  },
+
+  methods: {
+    start: {
+      request: {},
+      response: {success: RetVal("number")}
+    },
+    stop: {
+      request: {},
+      response: {}
+    }
+  }
+});
+
+exports.eventLoopLagSpec = eventLoopLagSpec;
--- a/devtools/shared/specs/moz.build
+++ b/devtools/shared/specs/moz.build
@@ -12,16 +12,17 @@ DevToolsModules(
     'call-watcher.js',
     'canvas.js',
     'css-properties.js',
     'csscoverage.js',
     'device.js',
     'director-manager.js',
     'director-registry.js',
     'environment.js',
+    'eventlooplag.js',
     'frame.js',
     'framerate.js',
     'gcli.js',
     'heap-snapshot-file.js',
     'highlighters.js',
     'inspector.js',
     'layout.js',
     'memory.js',
--- a/devtools/shared/webconsole/test/common.js
+++ b/devtools/shared/webconsole/test/common.js
@@ -92,16 +92,19 @@ function _attachConsole(aListeners, aCal
           aCallback(aState, aResponse);
           return;
         }
         let tab = aResponse.tabs[aResponse.selected];
         aState.dbgClient.attachTab(tab.actor, function (response, tabClient) {
           if (aAttachToWorker) {
             let workerName = "console-test-worker.js#" + new Date().getTime();
             var worker = new Worker(workerName);
+            // Keep a strong reference to the Worker to avoid it being
+            // GCd during the test (bug 1237492).
+            aState._worker_ref = worker;
             worker.addEventListener("message", function listener() {
               worker.removeEventListener("message", listener);
               tabClient.listWorkers(function (response) {
                 let worker = response.workers.filter(w => w.url == workerName)[0];
                 if (!worker) {
                   console.error("listWorkers failed. Unable to find the " +
                                 "worker actor\n");
                   return;
--- a/devtools/shared/worker/loader.js
+++ b/devtools/shared/worker/loader.js
@@ -312,22 +312,16 @@ function WorkerDebuggerLoader(options) {
 }
 
 this.WorkerDebuggerLoader = WorkerDebuggerLoader;
 
 // The following APIs rely on the use of Components, and the worker debugger
 // does not provide alternative definitions for them. Consequently, they are
 // stubbed out both on the main thread and worker threads.
 
-var PromiseDebugging = {
-  getState: function () {
-    throw new Error("PromiseDebugging is not available in workers!");
-  }
-};
-
 var chrome = {
   CC: undefined,
   Cc: undefined,
   ChromeWorker: undefined,
   Cm: undefined,
   Ci: undefined,
   Cu: undefined,
   Cr: undefined,
@@ -491,17 +485,16 @@ this.worker = new WorkerDebuggerLoader({
     "reportError": reportError,
     "rpc": rpc,
     "setImmediate": setImmediate,
     "URL": URL,
   },
   loadSubScript: loadSubScript,
   modules: {
     "Debugger": Debugger,
-    "PromiseDebugging": PromiseDebugging,
     "Services": Object.create(null),
     "chrome": chrome,
     "xpcInspector": xpcInspector
   },
   paths: {
     // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
     "": "resource://gre/modules/commonjs/",
     // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
--- a/docshell/base/nsDSURIContentListener.cpp
+++ b/docshell/base/nsDSURIContentListener.cpp
@@ -443,17 +443,17 @@ nsDSURIContentListener::CheckFrameOption
   while (tokenizer.hasMoreTokens()) {
     const nsSubstring& tok = tokenizer.nextToken();
     if (!CheckOneFrameOptionsPolicy(httpChannel, tok)) {
       // cancel the load and display about:blank
       httpChannel->Cancel(NS_BINDING_ABORTED);
       if (mDocShell) {
         nsCOMPtr<nsIWebNavigation> webNav(do_QueryObject(mDocShell));
         if (webNav) {
-          webNav->LoadURI(MOZ_UTF16("about:blank"),
+          webNav->LoadURI(u"about:blank",
                           0, nullptr, nullptr, nullptr);
         }
       }
       return false;
     }
   }
 
   return true;
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -5068,17 +5068,17 @@ nsDocShell::DisplayLoadError(nsresult aE
       nsCOMPtr<Element> element = do_QueryInterface(handler);
       element->GetAttribute(NS_LITERAL_STRING("crashedPageTitle"), messageStr);
     }
 
     // DisplayLoadError requires a non-empty messageStr to proceed and call
     // LoadErrorPage. If the page doesn't have a title, we will use a blank
     // space which will be trimmed and thus treated as empty by the front-end.
     if (messageStr.IsEmpty()) {
-      messageStr.AssignLiteral(MOZ_UTF16(" "));
+      messageStr.AssignLiteral(u" ");
     }
   } else {
     // Errors requiring simple formatting
     switch (aError) {
       case NS_ERROR_MALFORMED_URI:
         // URI is malformed
         error.AssignLiteral("malformedURI");
         break;
@@ -7683,17 +7683,17 @@ nsDocShell::EndPageLoad(nsIWebProgress* 
       DisplayLoadError(aStatus, url, nullptr, aChannel);
       return NS_OK;
     } else if (aStatus == NS_ERROR_INVALID_SIGNATURE) {
       // NS_ERROR_INVALID_SIGNATURE indicates a content-signature error.
       // This currently only happens in case a remote about page fails.
       // We have to load a fallback in this case.
       // XXX: We always load about blank here, firefox has to overwrite this if
       // it wants to display something else.
-      return LoadURI(MOZ_UTF16("about:blank"),  // URI string
+      return LoadURI(u"about:blank",            // URI string
                      nsIChannel::LOAD_NORMAL,   // Load flags
                      nullptr,                   // Referring URI
                      nullptr,                   // Post data stream
                      nullptr);                  // Headers stream
     }
 
     // Handle iframe document not loading error because source was
     // a tracking URL. We make a note of this iframe node by including
@@ -13006,38 +13006,38 @@ nsDocShell::ConfirmRepost(bool* aRepost)
   rv = stringBundleService->CreateBundle(kBrandBundleURL,
                                          getter_AddRefs(brandBundle));
   NS_ENSURE_SUCCESS(rv, rv);
 
   NS_ASSERTION(prompter && brandBundle && appBundle,
                "Unable to set up repost prompter.");
 
   nsXPIDLString brandName;
-  rv = brandBundle->GetStringFromName(MOZ_UTF16("brandShortName"),
+  rv = brandBundle->GetStringFromName(u"brandShortName",
                                       getter_Copies(brandName));
 
   nsXPIDLString msgString, button0Title;
   if (NS_FAILED(rv)) { // No brand, use the generic version.
-    rv = appBundle->GetStringFromName(MOZ_UTF16("confirmRepostPrompt"),
+    rv = appBundle->GetStringFromName(u"confirmRepostPrompt",
                                       getter_Copies(msgString));
   } else {
     // Brand available - if the app has an override file with formatting, the
     // app name will be included. Without an override, the prompt will look
     // like the generic version.
     const char16_t* formatStrings[] = { brandName.get() };
-    rv = appBundle->FormatStringFromName(MOZ_UTF16("confirmRepostPrompt"),
+    rv = appBundle->FormatStringFromName(u"confirmRepostPrompt",
                                          formatStrings,
                                          ArrayLength(formatStrings),
                                          getter_Copies(msgString));
   }
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  rv = appBundle->GetStringFromName(MOZ_UTF16("resendButton.label"),
+  rv = appBundle->GetStringFromName(u"resendButton.label",
                                     getter_Copies(button0Title));
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   int32_t buttonPressed;
   // The actual value here is irrelevant, but we can't pass an invalid
   // bool through XPConnect.
--- a/docshell/test/browser/browser_timelineMarkers-frame-04.js
+++ b/docshell/test/browser/browser_timelineMarkers-frame-04.js
@@ -45,17 +45,21 @@ if (Services.prefs.getBoolPref("javascri
       markers = markers.filter(m => (m.name == "Javascript" &&
                                      m.causeName == "promise callback"));
       ok(markers.length > 0, "Found a Javascript marker");
 
       let frame = markers[0].stack;
       ok(frame.asyncParent !== null, "Parent frame has async parent");
       is(frame.asyncParent.asyncCause, "promise callback",
          "Async parent has correct cause");
-      is(frame.asyncParent.functionDisplayName, "do_promise",
+      let asyncFrame = frame.asyncParent;
+      // Skip over self-hosted parts of our Promise implementation.
+      while (asyncFrame.source === 'self-hosted')
+        asyncFrame = asyncFrame.parent;
+      is(asyncFrame.functionDisplayName, "do_promise",
          "Async parent has correct function name");
     }
   }, {
     desc: "Async stack trace on Javascript marker with script",
     searchFor: (markers) => {
       return markers.some(m => (m.name == "Javascript" &&
                                 m.causeName == "promise callback"));
     },
@@ -66,15 +70,19 @@ if (Services.prefs.getBoolPref("javascri
       markers = markers.filter(m => (m.name == "Javascript" &&
                                      m.causeName == "promise callback"));
       ok(markers.length > 0, "Found a Javascript marker");
 
       let frame = markers[0].stack;
       ok(frame.asyncParent !== null, "Parent frame has async parent");
       is(frame.asyncParent.asyncCause, "promise callback",
          "Async parent has correct cause");
-      is(frame.asyncParent.functionDisplayName, "do_promise_script",
+      let asyncFrame = frame.asyncParent;
+      // Skip over self-hosted parts of our Promise implementation.
+      while (asyncFrame.source === 'self-hosted')
+        asyncFrame = asyncFrame.parent;
+      is(asyncFrame.functionDisplayName, "do_promise_script",
          "Async parent has correct function name");
     }
   });
 }
 
 timelineContentTest(TESTS);
--- a/docshell/test/browser/browser_timelineMarkers-frame-05.js
+++ b/docshell/test/browser/browser_timelineMarkers-frame-05.js
@@ -86,20 +86,32 @@ if (Services.prefs.getBoolPref("javascri
     searchFor: "ConsoleTime",
     setup: function(docShell) {
       let resolver = makePromise();
       resolvePromise(resolver);
     },
     check: function(markers) {
       markers = markers.filter(m => m.name == "ConsoleTime");
       ok(markers.length > 0, "Promise marker includes stack");
-
+      ok(markers[0].stack.functionDisplayName == "testConsoleTime",
+         "testConsoleTime is on the stack");
       let frame = markers[0].endStack;
-      ok(frame.parent.asyncParent !== null, "Parent frame has async parent");
-      is(frame.parent.asyncParent.asyncCause, "promise callback",
+      ok(frame.functionDisplayName == "testConsoleTimeEnd",
+         "testConsoleTimeEnd is on the stack");
+
+      frame = frame.parent;
+      ok(frame.functionDisplayName == "makePromise/<",
+         "makePromise/< is on the stack");
+      let asyncFrame = frame.asyncParent;
+      ok(asyncFrame !== null, "Frame has async parent");
+      is(asyncFrame.asyncCause, "promise callback",
          "Async parent has correct cause");
-      is(frame.parent.asyncParent.functionDisplayName, "makePromise",
+      // Skip over self-hosted parts of our Promise implementation.
+      while (asyncFrame.source === 'self-hosted') {
+        asyncFrame = asyncFrame.parent;
+      }
+      is(asyncFrame.functionDisplayName, "makePromise",
          "Async parent has correct function name");
     }
   });
 }
 
 timelineContentTest(TESTS);
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -75,18 +75,18 @@ public:
     AudioChannelService::GetAudioChannelString(mAudioChannel, name);
 
     nsAutoCString topic;
     topic.Assign("audiochannel-activity-");
     topic.Append(NS_ConvertUTF16toUTF8(name));
 
     observerService->NotifyObservers(wrapper, topic.get(),
                                      mActive
-                                       ? MOZ_UTF16("active")
-                                       : MOZ_UTF16("inactive"));
+                                       ? u"active"
+                                       : u"inactive");
 
     MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
            ("NotifyChannelActiveRunnable, type = %d, active = %d\n",
             mAudioChannel, mActive));
 
     return NS_OK;
   }
 
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -983,17 +983,17 @@ public:
    * namespace ID must not be kNameSpaceID_Unknown and the name must not be
    * null.  Note that this can only return info on attributes that actually
    * live on this element (and is only virtual to handle XUL prototypes).  That
    * is, this should only be called from methods that only care about attrs
    * that effectively live in mAttrsAndChildren.
    */
   virtual nsAttrInfo GetAttrInfo(int32_t aNamespaceID, nsIAtom* aName) const;
 
-  virtual void NodeInfoChanged(mozilla::dom::NodeInfo* aOldNodeInfo)
+  virtual void NodeInfoChanged()
   {
   }
 
   /**
    * Parse a string into an nsAttrValue for a CORS attribute.  This
    * never fails.  The resulting value is an enumerated value whose
    * GetEnumValue() returns one of the above constants.
    */
--- a/dom/base/EventSource.cpp
+++ b/dom/base/EventSource.cpp
@@ -873,21 +873,21 @@ EventSource::ConsoleError()
   nsresult rv = mSrc->GetSpec(targetSpec);
   NS_ENSURE_SUCCESS(rv, rv);
 
   NS_ConvertUTF8toUTF16 specUTF16(targetSpec);
   const char16_t *formatStrings[] = { specUTF16.get() };
 
   if (mReadyState == CONNECTING) {
     rv = PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
-                             MOZ_UTF16("connectionFailure"),
+                             u"connectionFailure",
                              formatStrings, ArrayLength(formatStrings));
   } else {
     rv = PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
-                             MOZ_UTF16("netInterrupt"),
+                             u"netInterrupt",
                              formatStrings, ArrayLength(formatStrings));
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 nsresult
--- a/dom/base/NodeIterator.cpp
+++ b/dom/base/NodeIterator.cpp
@@ -176,17 +176,18 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
     NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMNodeIterator)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(NodeIterator)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(NodeIterator)
 
 NS_IMETHODIMP NodeIterator::GetRoot(nsIDOMNode * *aRoot)
 {
-    NS_ADDREF(*aRoot = Root()->AsDOMNode());
+    nsCOMPtr<nsIDOMNode> root = Root()->AsDOMNode();
+    root.forget(aRoot);
     return NS_OK;
 }
 
 NS_IMETHODIMP NodeIterator::GetWhatToShow(uint32_t *aWhatToShow)
 {
     *aWhatToShow = WhatToShow();
     return NS_OK;
 }
--- a/dom/base/WebSocket.cpp
+++ b/dom/base/WebSocket.cpp
@@ -545,21 +545,21 @@ WebSocketImpl::ConsoleError()
     }
   }
 
   NS_ConvertUTF8toUTF16 specUTF16(mURI);
   const char16_t* formatStrings[] = { specUTF16.get() };
 
   if (mWebSocket->ReadyState() < WebSocket::OPEN) {
     PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
-                        MOZ_UTF16("connectionFailure"),
+                        u"connectionFailure",
                         formatStrings, ArrayLength(formatStrings));
   } else {
     PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
-                        MOZ_UTF16("netInterrupt"),
+                        u"netInterrupt",
                         formatStrings, ArrayLength(formatStrings));
   }
   /// todo some specific errors - like for message too large
   return NS_OK;
 }
 
 void
 WebSocketImpl::FailConnection(uint16_t aReasonCode,
@@ -1594,18 +1594,18 @@ WebSocketImpl::Init(JSContext* aCx,
 
     // upgrade the request from ws:// to wss:// and mark as secure
     mURI.ReplaceSubstring("ws://", "wss://");
     if (NS_WARN_IF(mURI.Find("wss://") != 0)) {
       return;
     }
     mSecure = true;
 
-    const char16_t* params[] = { reportSpec.get(), MOZ_UTF16("wss") };
-    CSP_LogLocalizedStr(MOZ_UTF16("upgradeInsecureRequest"),
+    const char16_t* params[] = { reportSpec.get(), u"wss" };
+    CSP_LogLocalizedStr(u"upgradeInsecureRequest",
                         params, ArrayLength(params),
                         EmptyString(), // aSourceFile
                         EmptyString(), // aScriptSample
                         0, // aLineNumber
                         0, // aColumnNumber
                         nsIScriptError::warningFlag, "CSP",
                         mInnerWindowID);
   }
@@ -2628,26 +2628,19 @@ public:
     : MainThreadWorkerRunnable(aWorkerPrivate)
     , mImpl(aImpl)
   {
   }
 
   bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
     aWorkerPrivate->AssertIsOnWorkerThread();
-    aWorkerPrivate->ModifyBusyCountFromWorker(true);
     return !NS_FAILED(mImpl->CancelInternal());
   }
 
-  void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
-               bool aRunResult) override
-  {
-    aWorkerPrivate->ModifyBusyCountFromWorker(false);
-  }
-
 private:
   RefPtr<WebSocketImpl> mImpl;
 };
 
 } // namespace
 
 // Window closed, stop/reload button pressed, user navigated away from page, etc.
 NS_IMETHODIMP
@@ -2777,31 +2770,29 @@ public:
     , mWebSocketImpl(aImpl)
     , mEvent(Move(aEvent))
   {
   }
 
   bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
     aWorkerPrivate->AssertIsOnWorkerThread();
-    aWorkerPrivate->ModifyBusyCountFromWorker(true);
 
     // No messages when disconnected.
     if (mWebSocketImpl->mDisconnectingOrDisconnected) {
       NS_WARNING("Dispatching a WebSocket event after the disconnection!");
       return true;
     }
 
     return !NS_FAILED(mEvent->Run());
   }
 
   void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
                bool aRunResult) override
   {
-    aWorkerPrivate->ModifyBusyCountFromWorker(false);
   }
 
   bool
   PreDispatch(WorkerPrivate* aWorkerPrivate) override
   {
     // We don't call WorkerRunnable::PreDispatch because it would assert the
     // wrong thing about which thread we're on.  We're on whichever thread the
     // channel implementation is running on (probably the main thread or socket
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -681,22 +681,22 @@ nsContentUtils::InitializeModifierString
   nsXPIDLString shiftModifier;
   nsXPIDLString metaModifier;
   nsXPIDLString osModifier;
   nsXPIDLString altModifier;
   nsXPIDLString controlModifier;
   nsXPIDLString modifierSeparator;
   if (bundle) {
     //macs use symbols for each modifier key, so fetch each from the bundle, which also covers i18n
-    bundle->GetStringFromName(MOZ_UTF16("VK_SHIFT"), getter_Copies(shiftModifier));
-    bundle->GetStringFromName(MOZ_UTF16("VK_META"), getter_Copies(metaModifier));
-    bundle->GetStringFromName(MOZ_UTF16("VK_WIN"), getter_Copies(osModifier));
-    bundle->GetStringFromName(MOZ_UTF16("VK_ALT"), getter_Copies(altModifier));
-    bundle->GetStringFromName(MOZ_UTF16("VK_CONTROL"), getter_Copies(controlModifier));
-    bundle->GetStringFromName(MOZ_UTF16("MODIFIER_SEPARATOR"), getter_Copies(modifierSeparator));
+    bundle->GetStringFromName(u"VK_SHIFT", getter_Copies(shiftModifier));
+    bundle->GetStringFromName(u"VK_META", getter_Copies(metaModifier));
+    bundle->GetStringFromName(u"VK_WIN", getter_Copies(osModifier));
+    bundle->GetStringFromName(u"VK_ALT", getter_Copies(altModifier));
+    bundle->GetStringFromName(u"VK_CONTROL", getter_Copies(controlModifier));
+    bundle->GetStringFromName(u"MODIFIER_SEPARATOR", getter_Copies(modifierSeparator));
   }
   //if any of these don't exist, we get  an empty string
   sShiftText = new nsString(shiftModifier);
   sMetaText = new nsString(metaModifier);
   sOSText = new nsString(osModifier);
   sAltText = new nsString(altModifier);
   sControlText = new nsString(controlModifier);
   sModifierSeparator = new nsString(modifierSeparator);
@@ -5117,17 +5117,17 @@ nsContentUtils::GetWindowProviderForCont
 already_AddRefed<nsPIDOMWindowOuter>
 nsContentUtils::GetMostRecentNonPBWindow()
 {
   nsCOMPtr<nsIWindowMediator> windowMediator =
     do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
   nsCOMPtr<nsIWindowMediator_44> wm = do_QueryInterface(windowMediator);
 
   nsCOMPtr<mozIDOMWindowProxy> window;
-  wm->GetMostRecentNonPBWindow(MOZ_UTF16("navigator:browser"),
+  wm->GetMostRecentNonPBWindow(u"navigator:browser",
                                getter_AddRefs(window));
   nsCOMPtr<nsPIDOMWindowOuter> pwindow;
   pwindow = do_QueryInterface(window);
 
   return pwindow.forget();
 }
 
 /* static */
@@ -6386,26 +6386,22 @@ nsContentUtils::PlatformToDOMLineBreaks(
   }
 }
 
 bool
 nsContentUtils::PlatformToDOMLineBreaks(nsString& aString, const fallible_t& aFallible)
 {
   if (aString.FindChar(char16_t('\r')) != -1) {
     // Windows linebreaks: Map CRLF to LF:
-    if (!aString.ReplaceSubstring(MOZ_UTF16("\r\n"),
-                                  MOZ_UTF16("\n"),
-                                  aFallible)) {
+    if (!aString.ReplaceSubstring(u"\r\n", u"\n", aFallible)) {
       return false;
     }
 
     // Mac linebreaks: Map any remaining CR to LF:
-    if (!aString.ReplaceSubstring(MOZ_UTF16("\r"),
-                                  MOZ_UTF16("\n"),
-                                  aFallible)) {
+    if (!aString.ReplaceSubstring(u"\r", u"\n", aFallible)) {
       return false;
     }
   }
 
   return true;
 }
 
 void
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -3021,17 +3021,17 @@ GetFormattedTimeString(PRTime aTime, nsA
   char formatedTime[24];
   if (snprintf_literal(formatedTime, "%02d/%02d/%04d %02d:%02d:%02d",
                        prtime.tm_month + 1, prtime.tm_mday, int(prtime.tm_year),
                        prtime.tm_hour     ,  prtime.tm_min,  prtime.tm_sec)) {
     CopyASCIItoUTF16(nsDependentCString(formatedTime), aFormattedTimeString);
   } else {
     // If we for whatever reason failed to find the last modified time
     // (or even the current time), fall back to what NS4.x returned.
-    aFormattedTimeString.AssignLiteral(MOZ_UTF16("01/01/1970 00:00:00"));
+    aFormattedTimeString.AssignLiteral(u"01/01/1970 00:00:00");
   }
 }
 
 void
 nsIDocument::GetLastModified(nsAString& aLastModified) const
 {
   if (!mLastModified.IsEmpty()) {
     aLastModified.Assign(mLastModified);
@@ -9738,26 +9738,26 @@ nsDocument::GetReadyState(nsAString& aRe
   return NS_OK;
 }
 
 void
 nsIDocument::GetReadyState(nsAString& aReadyState) const
 {
   switch(mReadyState) {
   case READYSTATE_LOADING :
-    aReadyState.AssignLiteral(MOZ_UTF16("loading"));
+    aReadyState.AssignLiteral(u"loading");
     break;
   case READYSTATE_INTERACTIVE :
-    aReadyState.AssignLiteral(MOZ_UTF16("interactive"));
+    aReadyState.AssignLiteral(u"interactive");
     break;
   case READYSTATE_COMPLETE :
-    aReadyState.AssignLiteral(MOZ_UTF16("complete"));
+    aReadyState.AssignLiteral(u"complete");
     break;
   default:
-    aReadyState.AssignLiteral(MOZ_UTF16("uninitialized"));
+    aReadyState.AssignLiteral(u"uninitialized");
   }
 }
 
 namespace {
 
 struct SuppressArgs
 {
   nsIDocument::SuppressionType mWhat;
--- a/dom/base/nsGenericDOMDataNode.h
+++ b/dom/base/nsGenericDOMDataNode.h
@@ -167,23 +167,23 @@ public:
 
   NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker) override;
   NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const;
   virtual nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute,
                                               int32_t aModType) const;
 
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override
   {
-    *aResult = CloneDataNode(aNodeInfo, true);
+    nsCOMPtr<nsINode> result = CloneDataNode(aNodeInfo, true);
+    result.forget(aResult);
+
     if (!*aResult) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
-    NS_ADDREF(*aResult);
-
     return NS_OK;
   }
 
   nsresult SplitData(uint32_t aOffset, nsIContent** aReturn,
                      bool aCloneAfterOriginal = true);
 
   // WebIDL API
   // Our XPCOM GetData is just fine for WebIDL
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -9480,17 +9480,17 @@ nsGlobalWindow::FindOuter(const nsAStrin
   if (aString.IsEmpty() || aShowDialog) {
     // See if the find dialog is already up using nsIWindowMediator
     nsCOMPtr<nsIWindowMediator> windowMediator =
       do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
 
     nsCOMPtr<mozIDOMWindowProxy> findDialog;
 
     if (windowMediator) {
-      windowMediator->GetMostRecentWindow(MOZ_UTF16("findInPage"),
+      windowMediator->GetMostRecentWindow(u"findInPage",
                                           getter_AddRefs(findDialog));
     }
 
     if (findDialog) {
       // The Find dialog is already open, bring it to the top.
       auto* piFindDialog = nsPIDOMWindowOuter::From(findDialog);
       aError = piFindDialog->Focus();
     } else if (finder) {
@@ -11114,17 +11114,17 @@ nsGlobalWindow::ShowSlowScriptDialog()
       if (NS_IS_LOW_SURROGATE(filenameUTF16[cutStart + cutLength])) {
         // Likewise, don't drop a trailing low surrogate here.  We want to
         // increase cutLength, since it might be 0 already so we can't very well
         // decrease it.
         ++cutLength;
       }
 
       // Insert U+2026 HORIZONTAL ELLIPSIS
-      filenameUTF16.Replace(cutStart, cutLength, NS_LITERAL_STRING("\x2026"));
+      filenameUTF16.Replace(cutStart, cutLength, NS_LITERAL_STRING(u"\x2026"));
     }
     const char16_t *formatParams[] = { filenameUTF16.get() };
     rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
                                                "KillScriptLocation",
                                                formatParams,
                                                scriptLocation);
 
     if (NS_SUCCEEDED(rv) && scriptLocation) {
@@ -11532,17 +11532,17 @@ nsGlobalWindow::Observe(nsISupports* aSu
     event->SetTrusted(true);
 
     bool dummy;
     return DispatchEvent(event, &dummy);
   }
 #endif // MOZ_B2G
 
   if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
-    MOZ_ASSERT(!NS_strcmp(aData, MOZ_UTF16("intl.accept_languages")));
+    MOZ_ASSERT(!NS_strcmp(aData, u"intl.accept_languages"));
     MOZ_ASSERT(IsInnerWindow());
 
     // The user preferred languages have changed, we need to fire an event on
     // Window object and invalidate the cache for navigator.languages. It is
     // done for every change which can be a waste of cycles but those should be
     // fairly rare.
     // We MUST invalidate navigator.languages before sending the event in the
     // very likely situation where an event handler will try to read its value.
--- a/dom/base/nsHTMLContentSerializer.cpp
+++ b/dom/base/nsHTMLContentSerializer.cpp
@@ -149,19 +149,19 @@ nsHTMLContentSerializer::SerializeHTMLAt
         valueStr = NS_LITERAL_STRING("text/html; charset=") +
           NS_ConvertASCIItoUTF16(mCharset);
       }
     }
 
     nsDependentAtomString nameStr(attrName);
     nsAutoString prefix;
     if (namespaceID == kNameSpaceID_XML) {
-      prefix.AssignLiteral(MOZ_UTF16("xml"));
+      prefix.AssignLiteral(u"xml");
     } else if (namespaceID == kNameSpaceID_XLink) {
-      prefix.AssignLiteral(MOZ_UTF16("xlink"));
+      prefix.AssignLiteral(u"xlink");
     }
 
     // Expand shorthand attribute.
     if (aNamespace == kNameSpaceID_XHTML &&
         namespaceID == kNameSpaceID_None &&
         IsShorthandAttr(attrName, aTagName) &&
         valueStr.IsEmpty()) {
       valueStr = nameStr;
--- a/dom/base/nsISelectionPrivate.idl
+++ b/dom/base/nsISelectionPrivate.idl
@@ -10,19 +10,19 @@ interface nsIDOMNode;
 interface nsISelectionListener;
 interface nsIContent;
 interface nsINode;
 
 %{C++
 class nsIFrame;
 struct nsPoint;
 struct ScrollAxis;
-template<class T> class nsTArray;
 #include "nsDirection.h"
 #include "nsIPresShell.h" // TODO: Remove this include
+#include "nsTArrayForwardDeclare.h"
 #include "mozilla/EventForwards.h"
 %}
 
 [ptr] native nsIFrame(nsIFrame);
 [ptr] native RangeArray(nsTArray<nsRange*>);
 [ref] native constTextRangeStyleRef(const mozilla::TextRangeStyle);
 [ref] native nsPointRef(nsPoint);
 native nsDirection(nsDirection);
@@ -47,17 +47,17 @@ interface nsISelectionPrivate : nsISelec
     */
     [noscript] void endBatchChanges();
 
     DOMString  toStringWithFormat(in string formatType, in unsigned long flags, in int32_t wrapColumn);
     void  addSelectionListener(in nsISelectionListener newListener);
     void  removeSelectionListener(in nsISelectionListener listenerToRemove);
 
     /* Table selection stuff
-       We should probably move this and table-related 
+       We should probably move this and table-related
        items in nsFrameSelection  to a
        new nsITableSelection interface
     */
     const long TABLESELECTION_NONE     = 0;
     const long TABLESELECTION_CELL     = 1;
     const long TABLESELECTION_ROW      = 2;
     const long TABLESELECTION_COLUMN   = 3;
     const long TABLESELECTION_TABLE    = 4;
@@ -90,17 +90,17 @@ interface nsISelectionPrivate : nsISelec
     [noscript] void setTextRangeStyle(in nsIDOMRange range,
                       in constTextRangeStyleRef textRangeStyle);
 
     /**
      * Get the direction of the selection.
      */
     [noscript, notxpcom] nsDirection getSelectionDirection();
     [noscript, notxpcom] void setSelectionDirection(in nsDirection aDirection);
-    
+
     /**
      * Returns the type of the selection (see nsISelectionController for
      * available constants).
      */
     readonly attribute short type;
 
     /**
      * Return array of ranges intersecting with the given DOM interval.
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -1616,18 +1616,18 @@ nsJSContext::EndCycleCollectionCallback(
     }
 
     nsCString gcMsg;
     if (aResults.mForcedGC) {
       gcMsg.AssignLiteral(", forced a GC");
     }
 
     NS_NAMED_MULTILINE_LITERAL_STRING(kFmt,
-      MOZ_UTF16("CC(T+%.1f)[%s] max pause: %lums, total time: %lums, slices: %lu, suspected: %lu, visited: %lu RCed and %lu%s GCed, collected: %lu RCed and %lu GCed (%lu|%lu|%lu waiting for GC)%s\n")
-      MOZ_UTF16("ForgetSkippable %lu times before CC, min: %lu ms, max: %lu ms, avg: %lu ms, total: %lu ms, max sync: %lu ms, removed: %lu"));
+      u"CC(T+%.1f)[%s] max pause: %lums, total time: %lums, slices: %lu, suspected: %lu, visited: %lu RCed and %lu%s GCed, collected: %lu RCed and %lu GCed (%lu|%lu|%lu waiting for GC)%s\n"
+      u"ForgetSkippable %lu times before CC, min: %lu ms, max: %lu ms, avg: %lu ms, total: %lu ms, max sync: %lu ms, removed: %lu");
     nsString msg;
     msg.Adopt(nsTextFormatter::smprintf(kFmt.get(), double(delta) / PR_USEC_PER_SEC,
                                         ProcessNameForCollectorLog(),
                                         gCCStats.mMaxSliceTime, gCCStats.mTotalSliceTime,
                                         aResults.mNumSlices, gCCStats.mSuspected,
                                         aResults.mVisitedRefCounted, aResults.mVisitedGCed, mergeMsg.get(),
                                         aResults.mFreedRefCounted, aResults.mFreedGCed,
                                         sCCollectedWaitingForGC, sCCollectedZonesWaitingForGC, sLikelyShortLivingObjectsNeedingGC,
@@ -1643,41 +1643,41 @@ nsJSContext::EndCycleCollectionCallback(
       do_GetService(NS_CONSOLESERVICE_CONTRACTID);
     if (cs) {
       cs->LogStringMessage(msg.get());
     }
   }
 
   if (sPostGCEventsToObserver) {
     NS_NAMED_MULTILINE_LITERAL_STRING(kJSONFmt,
-       MOZ_UTF16("{ \"timestamp\": %llu, ")
-         MOZ_UTF16("\"duration\": %lu, ")
-         MOZ_UTF16("\"max_slice_pause\": %lu, ")
-         MOZ_UTF16("\"total_slice_pause\": %lu, ")
-         MOZ_UTF16("\"max_finish_gc_duration\": %lu, ")
-         MOZ_UTF16("\"max_sync_skippable_duration\": %lu, ")
-         MOZ_UTF16("\"suspected\": %lu, ")
-         MOZ_UTF16("\"visited\": { ")
-             MOZ_UTF16("\"RCed\": %lu, ")
-             MOZ_UTF16("\"GCed\": %lu }, ")
-         MOZ_UTF16("\"collected\": { ")
-             MOZ_UTF16("\"RCed\": %lu, ")
-             MOZ_UTF16("\"GCed\": %lu }, ")
-         MOZ_UTF16("\"waiting_for_gc\": %lu, ")
-         MOZ_UTF16("\"zones_waiting_for_gc\": %lu, ")
-         MOZ_UTF16("\"short_living_objects_waiting_for_gc\": %lu, ")
-         MOZ_UTF16("\"forced_gc\": %d, ")
-         MOZ_UTF16("\"forget_skippable\": { ")
-             MOZ_UTF16("\"times_before_cc\": %lu, ")
-             MOZ_UTF16("\"min\": %lu, ")
-             MOZ_UTF16("\"max\": %lu, ")
-             MOZ_UTF16("\"avg\": %lu, ")
-             MOZ_UTF16("\"total\": %lu, ")
-             MOZ_UTF16("\"removed\": %lu } ")
-       MOZ_UTF16("}"));
+       u"{ \"timestamp\": %llu, "
+         u"\"duration\": %lu, "
+         u"\"max_slice_pause\": %lu, "
+         u"\"total_slice_pause\": %lu, "
+         u"\"max_finish_gc_duration\": %lu, "
+         u"\"max_sync_skippable_duration\": %lu, "
+         u"\"suspected\": %lu, "
+         u"\"visited\": { "
+             u"\"RCed\": %lu, "
+             u"\"GCed\": %lu }, "
+         u"\"collected\": { "
+             u"\"RCed\": %lu, "
+             u"\"GCed\": %lu }, "
+         u"\"waiting_for_gc\": %lu, "
+         u"\"zones_waiting_for_gc\": %lu, "
+         u"\"short_living_objects_waiting_for_gc\": %lu, "
+         u"\"forced_gc\": %d, "
+         u"\"forget_skippable\": { "
+             u"\"times_before_cc\": %lu, "
+             u"\"min\": %lu, "
+             u"\"max\": %lu, "
+             u"\"avg\": %lu, "
+             u"\"total\": %lu, "
+             u"\"removed\": %lu } "
+       u"}");
     nsString json;
     json.Adopt(nsTextFormatter::smprintf(kJSONFmt.get(), endCCTime, ccNowDuration,
                                          gCCStats.mMaxSliceTime,
                                          gCCStats.mTotalSliceTime,
                                          gCCStats.mMaxGCDuration,
                                          gCCStats.mMaxSkippableDuration,
                                          gCCStats.mSuspected,
                                          aResults.mVisitedRefCounted, aResults.mVisitedGCed,
--- a/dom/base/nsNodeInfoManager.cpp
+++ b/dom/base/nsNodeInfoManager.cpp
@@ -269,19 +269,18 @@ nsNodeInfoManager::GetNodeInfo(const nsA
   }
 #endif
 
   NodeInfo::NodeInfoInner tmpKey(aName, aPrefix, aNamespaceID, aNodeType);
 
   void *node = PL_HashTableLookup(mNodeInfoHash, &tmpKey);
 
   if (node) {
-    NodeInfo* nodeInfo = static_cast<NodeInfo *>(node);
-
-    NS_ADDREF(*aNodeInfo = nodeInfo);
+    RefPtr<NodeInfo> nodeInfo = static_cast<NodeInfo*>(node);
+    nodeInfo.forget(aNodeInfo);
 
     return NS_OK;
   }
 
   nsCOMPtr<nsIAtom> nameAtom = NS_Atomize(aName);
   NS_ENSURE_TRUE(nameAtom, NS_ERROR_OUT_OF_MEMORY);
 
   RefPtr<NodeInfo> newNodeInfo =
--- a/dom/base/nsNodeUtils.cpp
+++ b/dom/base/nsNodeUtils.cpp
@@ -395,17 +395,17 @@ nsNodeUtils::CloneNodeImpl(nsINode *aNod
   *aResult = nullptr;
 
   nsCOMPtr<nsINode> newNode;
   nsCOMArray<nsINode> nodesWithProperties;
   nsresult rv = Clone(aNode, aDeep, nullptr, nodesWithProperties,
                       getter_AddRefs(newNode));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  newNode.swap(*aResult);
+  newNode.forget(aResult);
   return NS_OK;
 }
 
 /* static */
 nsresult
 nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
                            nsNodeInfoManager *aNewNodeInfoManager,
                            JS::Handle<JSObject*> aReparentScope,
@@ -501,17 +501,17 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNod
     if (aNode->IsElement()) {
       Element* element = aNode->AsElement();
       oldDoc->ClearBoxObjectFor(element);
       wasRegistered = oldDoc->UnregisterActivityObserver(element);
     }
 
     aNode->mNodeInfo.swap(newNodeInfo);
     if (elem) {
-      elem->NodeInfoChanged(newNodeInfo);
+      elem->NodeInfoChanged();
     }
 
     nsIDocument* newDoc = aNode->OwnerDoc();
     if (newDoc) {
       // XXX what if oldDoc is null, we don't know if this should be
       // registered or not! Can that really happen?
       if (wasRegistered) {
         newDoc->RegisterActivityObserver(aNode->AsElement());
--- a/dom/base/nsPlainTextSerializer.cpp
+++ b/dom/base/nsPlainTextSerializer.cpp
@@ -440,17 +440,17 @@ nsPlainTextSerializer::AppendDocumentSta
 nsresult
 nsPlainTextSerializer::DoOpenContainer(nsIAtom* aTag)
 {
   // Check if we need output current node as placeholder character and ignore
   // child nodes.
   if (ShouldReplaceContainerWithPlaceholder(mElement->NodeInfo()->NameAtom())) {
     if (mIgnoredChildNodeLevel == 0) {
       // Serialize current node as placeholder character
-      Write(NS_LITERAL_STRING("\xFFFC"));
+      Write(NS_LITERAL_STRING(u"\xFFFC"));
     }
     // Ignore child nodes.
     mIgnoredChildNodeLevel++;
     return NS_OK;
   }
   if (IsIgnorableRubyAnnotation(aTag)) {
     // Ignorable ruby annotation shouldn't be replaced by a placeholder
     // character, neither any of its descendants.
@@ -596,17 +596,17 @@ nsPlainTextSerializer::DoOpenContainer(n
   else if (aTag == nsGkAtoms::td || aTag == nsGkAtoms::th) {
     // We must make sure that the content of two table cells get a
     // space between them.
 
     // To make the separation between cells most obvious and
     // importable, we use a TAB.
     if (GetLastBool(mHasWrittenCellsForRow)) {
       // Bypass |Write| so that the TAB isn't compressed away.
-      AddToLine(MOZ_UTF16("\t"), 1);
+      AddToLine(u"\t", 1);
       mInWhitespace = true;
     }
     else if (mHasWrittenCellsForRow.IsEmpty()) {
       // We don't always see a <tr> (nor a <table>) before the <td> if we're
       // copying part of a table
       PushBool(mHasWrittenCellsForRow, true); // will never be popped
     }
     else {
@@ -1113,17 +1113,17 @@ nsPlainTextSerializer::DoAddLeaf(nsIAtom
     while (line.Length() < width) {
       line.Append(char16_t('-'));
     }
     Write(line);
 
     EnsureVerticalSpace(0);
   }
   else if (mFlags & nsIDocumentEncoder::OutputNonTextContentAsPlaceholder) {
-    Write(NS_LITERAL_STRING("\xFFFC"));
+    Write(NS_LITERAL_STRING(u"\xFFFC"));
   }
   else if (aTag == nsGkAtoms::img) {
     /* Output (in decreasing order of preference)
        alt, title or nothing */
     // See <http://www.w3.org/TR/REC-html40/struct/objects.html#edef-IMG>
     nsAutoString imageDescription;
     if (NS_SUCCEEDED(GetAttributeValue(nsGkAtoms::alt,
                                        imageDescription))) {
@@ -1212,17 +1212,17 @@ nsPlainTextSerializer::Output(nsString& 
   }
   mOutputString->Append(aString);
 }
 
 static bool
 IsSpaceStuffable(const char16_t *s)
 {
   if (s[0] == '>' || s[0] == ' ' || s[0] == kNBSP ||
-      nsCRT::strncmp(s, MOZ_UTF16("From "), 5) == 0)
+      nsCRT::strncmp(s, u"From ", 5) == 0)
     return true;
   else
     return false;
 }
 
 /**
  * This function adds a piece of text to the current stored line. If we are
  * wrapping text and the stored line will become too long, a suitable
@@ -2026,9 +2026,8 @@ int32_t GetUnicharStringWidth(const char
   for (;*pwcs && n-- > 0; pwcs++)
     if ((w = GetUnicharWidth(*pwcs)) < 0)
       ++width; // Taking 1 as the width of non-printable character, for bug# 94475.
     else
       width += w;
 
   return width;
 }
-
--- a/dom/base/test/browser_use_counters.js
+++ b/dom/base/test/browser_use_counters.js
@@ -6,17 +6,22 @@ var {Promise: promise} = Cu.import("reso
 
 const gHttpTestRoot = "http://example.com/browser/dom/base/test/";
 
 /**
  * Enable local telemetry recording for the duration of the tests.
  */
 var gOldContentCanRecord = false;
 add_task(function* test_initialize() {
-   gOldContentCanRecord = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
+  // Because canRecordExtended is a per-process variable, we need to make sure
+  // that all of the pages load in the same content process. Limit the number
+  // of content processes to at most 1 (or 0 if e10s is off entirely).
+  yield SpecialPowers.pushPrefEnv({ set: [[ "dom.ipc.processCount", 1 ]] });
+
+  gOldContentCanRecord = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
     let telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry);
     let old = telemetry.canRecordExtended;
     telemetry.canRecordExtended = true;
     return old;
   });
   info("canRecord for content: " + gOldContentCanRecord);
 });
 
--- a/dom/bindings/test/test_promise_rejections_from_jsimplemented.html
+++ b/dom/bindings/test/test_promise_rejections_from_jsimplemented.html
@@ -20,105 +20,114 @@ https://bugzilla.mozilla.org/show_bug.cg
     is(exn.name, name,
        "Should have the right exception name in test " + testNumber);
     is("filename" in exn ? exn.filename : exn.fileName, filename,
        "Should have the right file name in test " + testNumber);
     is(exn.message, message,
        "Should have the right message in test " + testNumber);
     is(exn.code, code, "Should have the right .code in test " + testNumber);
     if (message === "") {
-      is(exn.name, "NS_ERROR_UNEXPECTED",
+      is(exn.name, "InternalError",
          "Should have one of our synthetic exceptions in test " + testNumber);
     }
     is(exn.stack, stack, "Should have the right stack in test " + testNumber);
   }
 
   function ensurePromiseFail(testNumber, value) {
     ok(false, "Test " + testNumber + " should not have a fulfilled promise");
   }
 
   function doTest() {
     var t = new TestInterfaceJS();
     /* Async parent frames from pushPrefEnv don't show up in e10s.  */
     var isE10S = !SpecialPowers.isMainProcess();
     var asyncStack = SpecialPowers.getBoolPref("javascript.options.asyncstack");
     var ourFile = location.href;
-    var parentFrame = (asyncStack && !isE10S) ? `Async*@${ourFile}:121:3
+    var unwrapError = "Promise rejection value is a non-unwrappable cross-compartment wrapper.";
+    var parentFrame = (asyncStack && !isE10S) ? `Async*@${ourFile}:130:3
 ` : "";
 
     Promise.all([
       t.testPromiseWithThrowingChromePromiseInit().then(
           ensurePromiseFail.bind(null, 1),
-          checkExn.bind(null, 48, "NS_ERROR_UNEXPECTED", "", undefined,
-                        ourFile, 1,
-                        `doTest@${ourFile}:48:7
+          checkExn.bind(null, 49, "InternalError", unwrapError,
+                        undefined, ourFile, 1,
+                        `doTest@${ourFile}:49:7
 ` +
                         parentFrame)),
       t.testPromiseWithThrowingContentPromiseInit(function() {
           thereIsNoSuchContentFunction1();
         }).then(
           ensurePromiseFail.bind(null, 2),
-          checkExn.bind(null, 56, "ReferenceError",
+          checkExn.bind(null, 57, "ReferenceError",
                         "thereIsNoSuchContentFunction1 is not defined",
                         undefined, ourFile, 2,
-                        `doTest/<@${ourFile}:56:11
-doTest@${ourFile}:55:7
+                        `doTest/<@${ourFile}:57:11
+doTest@${ourFile}:56:7
 ` +
                         parentFrame)),
       t.testPromiseWithThrowingChromeThenFunction().then(
           ensurePromiseFail.bind(null, 3),
-          checkExn.bind(null, 0, "NS_ERROR_UNEXPECTED", "", undefined, "", 3, "")),
+          checkExn.bind(null, 0, "InternalError", unwrapError, undefined, "", 3, asyncStack ? (`Async*doTest@${ourFile}:67:7
+` +
+                        parentFrame) : "")),
       t.testPromiseWithThrowingContentThenFunction(function() {
           thereIsNoSuchContentFunction2();
         }).then(
           ensurePromiseFail.bind(null, 4),
-          checkExn.bind(null, 70, "ReferenceError",
+          checkExn.bind(null, 73, "ReferenceError",
                         "thereIsNoSuchContentFunction2 is not defined",
                         undefined, ourFile, 4,
-                        `doTest/<@${ourFile}:70:11
+                        `doTest/<@${ourFile}:73:11
 ` +
-                        (asyncStack ? `Async*doTest@${ourFile}:69:7
+                        (asyncStack ? `Async*doTest@${ourFile}:72:7
 ` : "") +
                         parentFrame)),
       t.testPromiseWithThrowingChromeThenable().then(
           ensurePromiseFail.bind(null, 5),
-          checkExn.bind(null, 0, "NS_ERROR_UNEXPECTED", "", undefined, "", 5, "")),
+          checkExn.bind(null, 0, "InternalError", unwrapError, undefined, "", 5, asyncStack ? (`Async*doTest@${ourFile}:84:7
+` +
+                        parentFrame) : "")),
       t.testPromiseWithThrowingContentThenable({
             then: function() { thereIsNoSuchContentFunction3(); }
         }).then(
           ensurePromiseFail.bind(null, 6),
-          checkExn.bind(null, 85, "ReferenceError",
+          checkExn.bind(null, 90, "ReferenceError",
                         "thereIsNoSuchContentFunction3 is not defined",
                         undefined, ourFile, 6,
-                        `doTest/<.then@${ourFile}:85:32
-`)),
+                        `doTest/<.then@${ourFile}:90:32
+` + (asyncStack ? `Async*doTest@${ourFile}:89:7\n` + parentFrame : ""))),
       t.testPromiseWithDOMExceptionThrowingPromiseInit().then(
           ensurePromiseFail.bind(null, 7),
-          checkExn.bind(null, 93, "NotFoundError",
+          checkExn.bind(null, 98, "NotFoundError",
                         "We are a second DOMException",
                         DOMException.NOT_FOUND_ERR, ourFile, 7,
-                        `doTest@${ourFile}:93:7
+                        `doTest@${ourFile}:98:7
 ` +
                         parentFrame)),
       t.testPromiseWithDOMExceptionThrowingThenFunction().then(
           ensurePromiseFail.bind(null, 8),
-          checkExn.bind(null, asyncStack ? 101 : 0, "NetworkError",
+          checkExn.bind(null, asyncStack ? 106 : 0, "NetworkError",
                          "We are a third DOMException",
                         DOMException.NETWORK_ERR, asyncStack ? ourFile : "", 8,
-                        (asyncStack ? `Async*doTest@${ourFile}:101:7
+                        (asyncStack ? `Async*doTest@${ourFile}:106:7
 ` +
                          parentFrame : ""))),
       t.testPromiseWithDOMExceptionThrowingThenable().then(
           ensurePromiseFail.bind(null, 9),
-          checkExn.bind(null, 0, "TypeMismatchError",
+          checkExn.bind(null, asyncStack ? 114 : 0, "TypeMismatchError",
                         "We are a fourth DOMException",
-                         DOMException.TYPE_MISMATCH_ERR, "", 9, "")),
+                        DOMException.TYPE_MISMATCH_ERR,
+                        asyncStack ? ourFile : "", 9,
+                        (asyncStack ? `Async*doTest@${ourFile}:114:7
+` +
+                         parentFrame : ""))),
     ]).then(SimpleTest.finish,
-            function() {
-              ok(false, "One of our catch statements totally failed");
+            function(err) {
+              ok(false, "One of our catch statements totally failed with err" + err + ', stack: ' + (err ? err.stack : ''));
               SimpleTest.finish();
             });
   }
 
   SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]},
                             doTest);
   </script>
 </head>
--- a/dom/camera/GonkCameraControl.cpp
+++ b/dom/camera/GonkCameraControl.cpp
@@ -1297,17 +1297,17 @@ nsGonkCameraControl::StopRecordingImpl()
     ~RecordingComplete() { }
 
     NS_IMETHODIMP
     Run()
     {
       MOZ_ASSERT(NS_IsMainThread());
 
       nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-      obs->NotifyObservers(mFile, "file-watcher-notify", MOZ_UTF16("modified"));
+      obs->NotifyObservers(mFile, "file-watcher-notify", u"modified");
       return NS_OK;
     }
 
   private:
     RefPtr<DeviceStorageFile> mFile;
   };
 
   ReentrantMonitorAutoEnter mon(mRecorderMonitor);
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -1557,18 +1557,18 @@ CanvasRenderingContext2D::EnsureTarget(c
     } else {
       mTarget = mBufferProvider->BorrowDrawTarget(IntRect(0, 0, mWidth, mHeight));
     }
 
     ScheduleStableStateCallback();
 
     if (mTarget) {
       // Restore clip and transform.
-      mTarget->SetTransform(CurrentState().transform);
       for (uint32_t i = 0; i < mStyleStack.Length(); i++) {
+        mTarget->SetTransform(mStyleStack[i].transform);
         for (uint32_t c = 0; c < mStyleStack[i].clipsPushed.Length(); c++) {
           mTarget->PushClip(mStyleStack[i].clipsPushed[c]);
         }
       }
       return mRenderingMode;
     } else {
       mBufferProvider = nullptr;
     }
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -931,17 +931,17 @@ protected:
                      miterLimit(10.0f),
                      globalAlpha(1.0f),
                      shadowBlur(0.0),
                      dashOffset(0.0f),
                      op(mozilla::gfx::CompositionOp::OP_OVER),
                      fillRule(mozilla::gfx::FillRule::FILL_WINDING),
                      lineCap(mozilla::gfx::CapStyle::BUTT),
                      lineJoin(mozilla::gfx::JoinStyle::MITER_OR_BEVEL),
-                     filterString(MOZ_UTF16("none")),
+                     filterString(u"none"),
                      updateFilterOnWriteOnly(false),
                      imageSmoothingEnabled(true),
                      fontExplicitLanguage(false)
     { }
 
     ContextState(const ContextState& aOther)
         : fontGroup(aOther.fontGroup),
           fontLanguage(aOther.fontLanguage),
new file mode 100644
--- /dev/null
+++ b/dom/canvas/crashtests/1287515-1.html
@@ -0,0 +1,7 @@
+<canvas id='i0'></canvas>
+<script>
+var c=document.getElementById('i0').getContext('2d');
+c.lineWidth=Number.MAX_SAFE_INTEGER;
+c.setLineDash([1]);
+c.strokeRect(1,1,0,Number.MIN_SAFE_INTEGER);
+</script>
--- a/dom/canvas/crashtests/crashtests.list
+++ b/dom/canvas/crashtests/crashtests.list
@@ -24,8 +24,9 @@ load 1161277-1.html
 load 1183363.html
 load 1190705.html
 load 1223740-1.html
 load 1225381-1.html
 skip-if(azureCairo) load 1229983-1.html
 load 1229932-1.html
 load 1244850-1.html
 load 1246775-1.html
+skip-if(d2d) load 1287515-1.html
--- a/dom/devicestorage/DeviceStorageStatics.cpp
+++ b/dom/devicestorage/DeviceStorageStatics.cpp
@@ -698,19 +698,19 @@ DeviceStorageStatics::Observe(nsISupport
     if (NS_WARN_IF(!sInstance)) {
       return NS_OK;
     }
 
     // 'disk-space-watcher' notifications are sent when there is a modification
     // of a file in a specific location while a low device storage situation
     // exists or after recovery of a low storage situation. For Firefox OS,
     // these notifications are specific for apps storage.
-    if (!NS_strcmp(aData, MOZ_UTF16("full"))) {
+    if (!NS_strcmp(aData, u"full")) {
       sInstance->mLowDiskSpace = true;
-    } else if (!NS_strcmp(aData, MOZ_UTF16("free"))) {
+    } else if (!NS_strcmp(aData, u"free")) {
       sInstance->mLowDiskSpace = false;
     } else {
       return NS_OK;
     }
 
 
     uint32_t i = mListeners.Length();
     DS_LOG_INFO("disk space %d (%u)", sInstance->mLowDiskSpace, i);
--- a/dom/events/TextComposition.cpp
+++ b/dom/events/TextComposition.cpp
@@ -36,17 +36,17 @@
 #undef TextRangeArray
 #undef Comment
 #endif
 
 using namespace mozilla::widget;
 
 namespace mozilla {
 
-#define IDEOGRAPHIC_SPACE (NS_LITERAL_STRING("\x3000"))
+#define IDEOGRAPHIC_SPACE (NS_LITERAL_STRING(u"\x3000"))
 
 /******************************************************************************
  * TextComposition
  ******************************************************************************/
 
 bool TextComposition::sHandlingSelectionEvent = false;
 
 TextComposition::TextComposition(nsPresContext* aPresContext,
--- a/dom/filehandle/ActorsParent.h
+++ b/dom/filehandle/ActorsParent.h
@@ -8,23 +8,23 @@
 #include "mozilla/dom/FileHandleStorage.h"
 #include "mozilla/dom/PBackgroundMutableFileParent.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "nsAutoPtr.h"
 #include "nsClassHashtable.h"
 #include "nsCOMPtr.h"
 #include "nsHashKeys.h"
 #include "nsString.h"
+#include "nsTArrayForwardDeclare.h"
 #include "nsTHashtable.h"
 
 template <class> struct already_AddRefed;
 class nsIFile;
 class nsIRunnable;
 class nsIThreadPool;
-template <class> class nsTArray;
 
 namespace mozilla {
 
 namespace ipc {
 
 class PBackgroundParent;
 
 } // namespace ipc
--- a/dom/geolocation/nsGeolocation.cpp
+++ b/dom/geolocation/nsGeolocation.cpp
@@ -955,17 +955,17 @@ nsGeolocationService::StartDevice(nsIPri
       NS_FAILED(rv = mProvider->Watch(this))) {
 
     NotifyError(nsIDOMGeoPositionError::POSITION_UNAVAILABLE);
     return rv;
   }
 
   obs->NotifyObservers(mProvider,
                        "geolocation-device-events",
-                       MOZ_UTF16("starting"));
+                       u"starting");
 
   return NS_OK;
 }
 
 void
 nsGeolocationService::StopDisconnectTimer()
 {
   if (mDisconnectTimer) {
@@ -1044,17 +1044,17 @@ nsGeolocationService::StopDevice()
     return;
   }
 
   mHigherAccuracy = false;
 
   mProvider->Shutdown();
   obs->NotifyObservers(mProvider,
                        "geolocation-device-events",
-                       MOZ_UTF16("shutdown"));
+                       u"shutdown");
 }
 
 StaticRefPtr<nsGeolocationService> nsGeolocationService::sService;
 
 already_AddRefed<nsGeolocationService>
 nsGeolocationService::GetGeolocationService()
 {
   RefPtr<nsGeolocationService> result;
--- a/dom/html/HTMLFormElement.cpp
+++ b/dom/html/HTMLFormElement.cpp
@@ -927,22 +927,22 @@ HTMLFormElement::DoSecureToInsecureSubmi
     getter_AddRefs(stringBundle));
   if (NS_FAILED(rv)) {
     return rv;
   }
   nsAutoString title;
   nsAutoString message;
   nsAutoString cont;
   stringBundle->GetStringFromName(
-    MOZ_UTF16("formPostSecureToInsecureWarning.title"), getter_Copies(title));
+    u"formPostSecureToInsecureWarning.title", getter_Copies(title));
   stringBundle->GetStringFromName(
-    MOZ_UTF16("formPostSecureToInsecureWarning.message"),
+    u"formPostSecureToInsecureWarning.message",
     getter_Copies(message));
   stringBundle->GetStringFromName(
-    MOZ_UTF16("formPostSecureToInsecureWarning.continue"),
+    u"formPostSecureToInsecureWarning.continue",
     getter_Copies(cont));
   int32_t buttonPressed;
   bool checkState = false; // this is unused (ConfirmEx requires this parameter)
   rv = prompt->ConfirmEx(title.get(), message.get(),
                          (nsIPrompt::BUTTON_TITLE_IS_STRING *
                           nsIPrompt::BUTTON_POS_0) +
                          (nsIPrompt::BUTTON_TITLE_CANCEL *
                           nsIPrompt::BUTTON_POS_1),
@@ -1752,17 +1752,17 @@ HTMLFormElement::GetActionURL(nsIURI** a
 
     // let's log a message to the console that we are upgrading a request
     nsAutoCString scheme;
     rv = actionURL->GetScheme(scheme);
     NS_ENSURE_SUCCESS(rv, rv);
     NS_ConvertUTF8toUTF16 reportScheme(scheme);
 
     const char16_t* params[] = { reportSpec.get(), reportScheme.get() };
-    CSP_LogLocalizedStr(MOZ_UTF16("upgradeInsecureRequest"),
+    CSP_LogLocalizedStr(u"upgradeInsecureRequest",
                         params, ArrayLength(params),
                         EmptyString(), // aSourceFile
                         EmptyString(), // aScriptSample
                         0, // aLineNumber
                         0, // aColumnNumber
                         nsIScriptError::warningFlag, "CSP",
                         document->InnerWindowID());
   }
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -3897,30 +3897,35 @@ HTMLInputElement::CancelRangeThumbDrag(b
   }
 }
 
 void
 HTMLInputElement::SetValueOfRangeForUserEvent(Decimal aValue)
 {
   MOZ_ASSERT(aValue.isFinite());
 
+  Decimal oldValue = GetValueAsDecimal();
+
   nsAutoString val;
   ConvertNumberToString(aValue, val);
   // TODO: What should we do if SetValueInternal fails?  (The allocation
   // is small, so we should be fine here.)
   SetValueInternal(val, nsTextEditorState::eSetValue_BySetUserInput |
                         nsTextEditorState::eSetValue_Notify);
   nsRangeFrame* frame = do_QueryFrame(GetPrimaryFrame());
   if (frame) {
     frame->UpdateForValueChange();
   }
-  nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
-                                       static_cast<nsIDOMHTMLInputElement*>(this),
-                                       NS_LITERAL_STRING("input"), true,
-                                       false);
+
+  if (GetValueAsDecimal() != oldValue) {
+    nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
+                                         static_cast<nsIDOMHTMLInputElement*>(this),
+                                         NS_LITERAL_STRING("input"), true,
+                                         false);
+  }
 }
 
 void
 HTMLInputElement::StartNumberControlSpinnerSpin()
 {
   MOZ_ASSERT(!mNumberControlSpinnerIsSpinning);
 
   mNumberControlSpinnerIsSpinning = true;
@@ -7828,25 +7833,25 @@ HTMLInputElement::SetFilePickerFiltersFr
 
     int32_t filterMask = 0;
     nsString filterName;
     nsString extensionListStr;
 
     // First, check for image/audio/video filters...
     if (token.EqualsLiteral("image/*")) {
       filterMask = nsIFilePicker::filterImages;
-      filterBundle->GetStringFromName(MOZ_UTF16("imageFilter"),
+      filterBundle->GetStringFromName(u"imageFilter",
                                       getter_Copies(extensionListStr));
     } else if (token.EqualsLiteral("audio/*")) {
       filterMask = nsIFilePicker::filterAudio;
-      filterBundle->GetStringFromName(MOZ_UTF16("audioFilter"),
+      filterBundle->GetStringFromName(u"audioFilter",
                                       getter_Copies(extensionListStr));
     } else if (token.EqualsLiteral("video/*")) {
       filterMask = nsIFilePicker::filterVideo;
-      filterBundle->GetStringFromName(MOZ_UTF16("videoFilter"),
+      filterBundle->GetStringFromName(u"videoFilter",
                                       getter_Copies(extensionListStr));
     } else if (token.First() == '.') {
       if (token.Contains(';') || token.Contains('*')) {
         // Ignore this filter as it contains reserved characters
         continue;
       }
       extensionListStr = NS_LITERAL_STRING("*") + token;
       filterName = extensionListStr;
--- a/dom/html/ImageDocument.cpp
+++ b/dom/html/ImageDocument.cpp
@@ -780,17 +780,17 @@ ImageDocument::UpdateTitleAndCharset()
   }
 
   nsXPIDLString status;
   if (mImageIsResized) {
     nsAutoString ratioStr;
     ratioStr.AppendInt(NSToCoordFloor(GetRatio() * 100));
 
     const char16_t* formatString[1] = { ratioStr.get() };
-    mStringBundle->FormatStringFromName(MOZ_UTF16("ScaledImage"),
+    mStringBundle->FormatStringFromName(u"ScaledImage",
                                         formatString, 1,
                                         getter_Copies(status));
   }
 
   static const char* const formatNames[4] = 
   {
     "ImageTitleWithNeitherDimensionsNorFile",
     "ImageTitleWithoutDimensions",
--- a/dom/html/PluginDocument.cpp
+++ b/dom/html/PluginDocument.cpp
@@ -163,17 +163,17 @@ PluginDocument::StartDocumentLoad(const 
                                   bool                aReset,
                                   nsIContentSink*     aSink)
 {
   // do not allow message panes to host full-page plugins
   // returning an error causes helper apps to take over
   nsCOMPtr<nsIDocShellTreeItem> dsti (do_QueryInterface(aContainer));
   if (dsti) {
     bool isMsgPane = false;
-    dsti->NameEquals(MOZ_UTF16("messagepane"), &isMsgPane);
+    dsti->NameEquals(u"messagepane", &isMsgPane);
     if (isMsgPane) {
       return NS_ERROR_FAILURE;
     }
   }
 
   nsresult rv =
     MediaDocument::StartDocumentLoad(aCommand, aChannel, aLoadGroup, aContainer,
                                      aDocListener, aReset, aSink);
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -1299,21 +1299,21 @@ MapLangAttributeInto(const nsMappedAttri
     if (lang->GetUnit() == eCSSUnit_Null) {
       lang->SetStringValue(langValue->GetStringValue(), eCSSUnit_Ident);
     }
   }
   if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Text)) {
     nsCSSValue* emphasisPos = aData->ValueForTextEmphasisPosition();
     if (emphasisPos->GetUnit() == eCSSUnit_Null) {
       const nsAString& lang = langValue->GetStringValue();
-      if (nsStyleUtil::MatchesLanguagePrefix(lang, MOZ_UTF16("zh"))) {
+      if (nsStyleUtil::MatchesLanguagePrefix(lang, u"zh")) {
         emphasisPos->SetIntValue(NS_STYLE_TEXT_EMPHASIS_POSITION_DEFAULT_ZH,
                                 eCSSUnit_Enumerated);
-      } else if (nsStyleUtil::MatchesLanguagePrefix(lang, MOZ_UTF16("ja")) ||
-                 nsStyleUtil::MatchesLanguagePrefix(lang, MOZ_UTF16("mn"))) {
+      } else if (nsStyleUtil::MatchesLanguagePrefix(lang, u"ja") ||
+                 nsStyleUtil::MatchesLanguagePrefix(lang, u"mn")) {
         // This branch is currently no part of the spec.
         // See bug 1040668 comment 69 and comment 75.
         emphasisPos->SetIntValue(NS_STYLE_TEXT_EMPHASIS_POSITION_DEFAULT,
                                 eCSSUnit_Enumerated);
       }
     }
   }
 }
@@ -3077,17 +3077,17 @@ nsGenericHTMLFormElementWithState::Resto
     history->RemoveState(mStateKey);
     return result;
   }
 
   return false;
 }
 
 void
-nsGenericHTMLFormElementWithState::NodeInfoChanged(mozilla::dom::NodeInfo* aOldNodeInfo)
+nsGenericHTMLFormElementWithState::NodeInfoChanged()
 {
   mStateKey.SetIsVoid(true);
 }
 
 nsSize
 nsGenericHTMLElement::GetWidthHeightForImage(RefPtr<imgRequestProxy>& aImageRequest)
 {
   nsSize size(0,0);
--- a/dom/html/nsGenericHTMLElement.h
+++ b/dom/html/nsGenericHTMLElement.h
@@ -1395,17 +1395,17 @@ public:
    *         value of RestoreState() otherwise.
    */
   bool RestoreFormControlState();
 
   /**
    * Called when we have been cloned and adopted, and the information of the
    * node has been changed.
    */
-  virtual void NodeInfoChanged(mozilla::dom::NodeInfo* aOldNodeInfo) override;
+  virtual void NodeInfoChanged() override;
 
 protected:
   /* Generates the state key for saving the form state in the session if not
      computed already. The result is stored in mStateKey on success */
   nsresult GenerateStateKey();
 
   /* Used to store the key to that element in the session. Is void until
      GenerateStateKey has been used */
--- a/dom/html/nsHTMLContentSink.cpp
+++ b/dom/html/nsHTMLContentSink.cpp
@@ -167,18 +167,16 @@ protected:
   // Boolean indicating whether we've seen a <head> tag that might have had
   // attributes once already.
   bool mHaveSeenHead;
 
   // Boolean indicating whether we've notified insertion of our root content
   // yet.  We want to make sure to only do this once.
   bool mNotifiedRootInsertion;
 
-  mozilla::dom::NodeInfo* mNodeInfoCache[NS_HTML_TAG_MAX + 1];
-
   nsresult FlushTags() override;
 
   // Routines for tags that require special handling
   nsresult CloseHTML();
   nsresult OpenBody();
   nsresult CloseBody();
 
   void CloseHeadContext();
@@ -212,17 +210,17 @@ public:
   void DidAddContent(nsIContent* aContent);
   void UpdateChildCounts();
 
 private:
   // Function to check whether we've notified for the current content.
   // What this actually does is check whether we've notified for all
   // of the parent's kids.
   bool HaveNotifiedForCurrentContent() const;
-  
+
 public:
   HTMLContentSink* mSink;
   int32_t mNotifyLevel;
 
   struct Node {
     nsHTMLTag mType;
     nsGenericHTMLElement* mContent;
     uint32_t mNumFlushed;
@@ -245,17 +243,17 @@ NS_NewHTMLElement(Element** aResult, alr
   RefPtr<mozilla::dom::NodeInfo> nodeInfo = aNodeInfo;
 
   nsIParserService* parserService = nsContentUtils::GetParserService();
   if (!parserService)
     return NS_ERROR_OUT_OF_MEMORY;
 
   nsIAtom *name = nodeInfo->NameAtom();
 
-  NS_ASSERTION(nodeInfo->NamespaceEquals(kNameSpaceID_XHTML), 
+  NS_ASSERTION(nodeInfo->NamespaceEquals(kNameSpaceID_XHTML),
                "Trying to HTML elements that don't have the XHTML namespace");
 
   // Per the Custom Element specification, unknown tags that are valid custom
   // element names should be HTMLElement instead of HTMLUnknownElement.
   int32_t tag = parserService->HTMLCaseSensitiveAtomTagToId(name);
   if ((tag == eHTMLTag_userdefined &&
       nsContentUtils::IsCustomElementName(name)) ||
       aIs) {
@@ -669,44 +667,32 @@ HTMLContentSink::~HTMLContentSink()
 
   if (mCurrentContext == mHeadContext) {
     mCurrentContext = nullptr;
   }
 
   delete mCurrentContext;
 
   delete mHeadContext;
-
-  for (i = 0; uint32_t(i) < ArrayLength(mNodeInfoCache); ++i) {
-    NS_IF_RELEASE(mNodeInfoCache[i]);
-  }
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLContentSink)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLContentSink, nsContentSink)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mHTMLDocument)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoot)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mBody)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mHead)
-  for (uint32_t i = 0; i < ArrayLength(tmp->mNodeInfoCache); ++i) {
-    NS_IF_RELEASE(tmp->mNodeInfoCache[i]);
-  }
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLContentSink,
                                                   nsContentSink)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHTMLDocument)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBody)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHead)
-  for (uint32_t i = 0; i < ArrayLength(tmp->mNodeInfoCache); ++i) {
-    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mNodeInfoCache[i]");
-    cb.NoteNativeChild(tmp->mNodeInfoCache[i],
-                       NS_CYCLE_COLLECTION_PARTICIPANT(NodeInfo));
-  }
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLContentSink)
   NS_INTERFACE_TABLE_BEGIN
     NS_INTERFACE_TABLE_ENTRY(HTMLContentSink, nsIContentSink)
     NS_INTERFACE_TABLE_ENTRY(HTMLContentSink, nsIHTMLContentSink)
   NS_INTERFACE_TABLE_END
 NS_INTERFACE_TABLE_TAIL_INHERITING(nsContentSink)
@@ -716,17 +702,17 @@ NS_IMPL_RELEASE_INHERITED(HTMLContentSin
 
 nsresult
 HTMLContentSink::Init(nsIDocument* aDoc,
                       nsIURI* aURI,
                       nsISupports* aContainer,
                       nsIChannel* aChannel)
 {
   NS_ENSURE_TRUE(aContainer, NS_ERROR_NULL_POINTER);
-  
+
   nsresult rv = nsContentSink::Init(aDoc, aURI, aContainer, aChannel);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   aDoc->AddObserver(this);
   mIsDocumentObserver = true;
   mHTMLDocument = do_QueryInterface(aDoc);
@@ -834,17 +820,17 @@ HTMLContentSink::DidBuildModel(bool aTer
   // Make sure we no longer respond to document mutations.  We've flushed all
   // our notifications out, so there's no need to do anything else here.
 
   // XXXbz I wonder whether we could End() our contexts here too, or something,
   // just to make sure we no longer notify...  Or is the mIsDocumentObserver
   // thing sufficient?
   mDocument->RemoveObserver(this);
   mIsDocumentObserver = false;
-  
+
   mDocument->EndLoad();
 
   DropParserAndPerfHint();
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -895,17 +881,17 @@ HTMLContentSink::OpenBody()
   mBody = mCurrentContext->mStack[mCurrentContext->mStackPos - 1].mContent;
 
   if (mCurrentContext->mStackPos > 1) {
     int32_t parentIndex    = mCurrentContext->mStackPos - 2;
     nsGenericHTMLElement *parent = mCurrentContext->mStack[parentIndex].mContent;
     int32_t numFlushed     = mCurrentContext->mStack[parentIndex].mNumFlushed;
     int32_t childCount = parent->GetChildCount();
     NS_ASSERTION(numFlushed < childCount, "Already notified on the body?");
-    
+
     int32_t insertionPoint =
       mCurrentContext->mStack[parentIndex].mInsertionPoint;
 
     // XXX: I have yet to see a case where numFlushed is non-zero and
     // insertionPoint is not -1, but this code will try to handle
     // those cases too.
 
     uint32_t oldUpdates = mUpdatesInNotification;
@@ -1091,17 +1077,17 @@ HTMLContentSink::FlushPendingNotificatio
 
 nsresult
 HTMLContentSink::FlushTags()
 {
   if (!mNotifiedRootInsertion) {
     NotifyRootInsertion();
     return NS_OK;
   }
-  
+
   return mCurrentContext ? mCurrentContext->FlushTags() : NS_OK;
 }
 
 NS_IMETHODIMP
 HTMLContentSink::SetDocumentCharset(nsACString& aCharset)
 {
   MOZ_ASSERT_UNREACHABLE("<meta charset> case doesn't occur with about:blank");
   return NS_ERROR_NOT_IMPLEMENTED;
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -787,17 +787,17 @@ nsHTMLDocument::StartDocumentLoad(const 
     nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
     NS_ASSERTION(NS_SUCCEEDED(rv) && bundleService, "The bundle service could not be loaded");
     nsCOMPtr<nsIStringBundle> bundle;
     rv = bundleService->CreateBundle("chrome://global/locale/browser.properties",
                                      getter_AddRefs(bundle));
     NS_ASSERTION(NS_SUCCEEDED(rv) && bundle, "chrome://global/locale/browser.properties could not be loaded");
     nsXPIDLString title;
     if (bundle) {
-      bundle->GetStringFromName(MOZ_UTF16("plainText.wordWrap"), getter_Copies(title));
+      bundle->GetStringFromName(u"plainText.wordWrap", getter_Copies(title));
     }
     SetSelectedStyleSheetSet(title);
   }
 
   // parser the content of the URI
   mParser->Parse(uri, nullptr, (void *)this);
 
   return rv;
--- a/dom/html/test/mochitest.ini
+++ b/dom/html/test/mochitest.ini
@@ -452,16 +452,17 @@ skip-if = (toolkit == 'gonk' && debug) #
 [test_bug893537.html]
 [test_bug95530.html]
 [test_bug969346.html]
 [test_bug982039.html]
 [test_bug1003539.html]
 [test_bug1045270.html]
 [test_bug1146116.html]
 [test_bug1264157.html]
+[test_bug1287321.html]
 [test_change_crossorigin.html]
 [test_checked.html]
 [test_dir_attributes_reflection.html]
 [test_dl_attributes_reflection.html]
 [test_element_prototype.html]
 [test_embed_attributes_reflection.html]
 [test_formData.html]
 [test_formSubmission.html]
new file mode 100644
--- /dev/null
+++ b/dom/html/test/test_bug1287321.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1287321
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1287321</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+
+  /** Test for Bug 1287321 **/
+
+  function test() {
+    var r = document.getElementById("range");
+    var rect = r.getBoundingClientRect();
+    var y = parseInt((rect.height / 2));
+    var movement = parseInt(rect.width / 10);
+    var x = movement;
+    synthesizeMouse(r, x, y, { type: "mousedown" });
+    x += movement;
+    var eventCount = 0;
+    r.oninput = function() {
+      ++eventCount;
+    }
+    synthesizeMouse(r, x, y, { type: "mousemove" });
+    is(eventCount, 1, "Got the expected input event");
+
+    x += movement;
+    synthesizeMouse(r, x, y, { type: "mousemove" });
+    is(eventCount, 2, "Got the expected input event");
+
+    synthesizeMouse(r, x, y, { type: "mousemove" });
+    is(eventCount, 2, "Got the expected input event");
+
+    x += movement;
+    synthesizeMouse(r, x, y, { type: "mousemove" });
+    is(eventCount, 3, "Got the expected input event");
+
+    synthesizeMouse(r, x, y, { type: "mouseup" });
+    is(eventCount, 3, "Got the expected input event");
+
+    SimpleTest.finish();
+  }
+
+  SimpleTest.waitForExplicitFinish();
+  SimpleTest.waitForFocus(test);
+
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1287321">Mozilla Bug 1287321</a>
+<input type="range" id="range">
+</body>
+</html>
--- a/dom/html/test/test_bug332246.html
+++ b/dom/html/test/test_bug332246.html
@@ -32,44 +32,40 @@ https://bugzilla.mozilla.org/show_bug.cg
 </div>
 
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 /** Test for Bug 332246 **/
 
-function isWithFuzz(itIs, itShouldBe, fuzz, description) {
-  ok(Math.abs(itIs - itShouldBe) <= fuzz, `${description} - expected a value between ${itShouldBe - fuzz} and ${itShouldBe + fuzz}, got ${itIs}`);
-}
-
 var a1 = document.getElementById('a1');
 var a2 = document.getElementById('a2');
-isWithFuzz(a1.scrollHeight, 400, 1, "Wrong a1.scrollHeight");
+is(a1.scrollHeight, 400, "Wrong a1.scrollHeight");
 is(a1.offsetHeight, 100, "Wrong a1.offsetHeight");
 a2.scrollIntoView(true);
 is(a1.scrollTop, 100, "Wrong scrollTop value after a2.scrollIntoView(true)");
 a2.scrollIntoView(false);
 is(a1.scrollTop, 200, "Wrong scrollTop value after a2.scrollIntoView(false)");
 
 var b1 = document.getElementById('b1');
 var b2 = document.getElementById('b2');
-isWithFuzz(b1.scrollHeight, 420, 1, "Wrong b1.scrollHeight");
+is(b1.scrollHeight, 420, "Wrong b1.scrollHeight");
 is(b1.offsetHeight, 100, "Wrong b1.offsetHeight");
 b2.scrollIntoView(true);
 is(b1.scrollTop, 100, "Wrong scrollTop value after b2.scrollIntoView(true)");
 b2.scrollIntoView(false);
 is(b1.scrollTop, 220, "Wrong scrollTop value after b2.scrollIntoView(false)");
 
 var c1 = document.getElementById('c1');
 var c2 = document.getElementById('c2');
-isWithFuzz(c1.scrollHeight, 320, 1, "Wrong c1.scrollHeight");
+is(c1.scrollHeight, 320, "Wrong c1.scrollHeight");
 is(c1.offsetHeight, 100, "Wrong c1.offsetHeight");
 c2.scrollIntoView(true);
 is(c1.scrollTop, 100, "Wrong scrollTop value after c2.scrollIntoView(true)");
 c2.scrollIntoView(false);
-isWithFuzz(c1.scrollTop, 220, 1, "Wrong scrollTop value after c2.scrollIntoView(false)");
+is(c1.scrollTop, 220, "Wrong scrollTop value after c2.scrollIntoView(false)");
 
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/indexedDB/IDBEvents.cpp
+++ b/dom/indexedDB/IDBEvents.cpp
@@ -14,24 +14,24 @@
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::indexedDB;
 
 namespace mozilla {
 namespace dom {
 namespace indexedDB {
 
-const char16_t* kAbortEventType = MOZ_UTF16("abort");
-const char16_t* kBlockedEventType = MOZ_UTF16("blocked");
-const char16_t* kCompleteEventType = MOZ_UTF16("complete");
-const char16_t* kErrorEventType = MOZ_UTF16("error");
-const char16_t* kSuccessEventType = MOZ_UTF16("success");
-const char16_t* kUpgradeNeededEventType = MOZ_UTF16("upgradeneeded");
-const char16_t* kVersionChangeEventType = MOZ_UTF16("versionchange");
-const char16_t* kCloseEventType = MOZ_UTF16("close");
+const char16_t* kAbortEventType = u"abort";
+const char16_t* kBlockedEventType = u"blocked";
+const char16_t* kCompleteEventType = u"complete";
+const char16_t* kErrorEventType = u"error";
+const char16_t* kSuccessEventType = u"success";
+const char16_t* kUpgradeNeededEventType = u"upgradeneeded";
+const char16_t* kVersionChangeEventType = u"versionchange";
+const char16_t* kCloseEventType = u"close";
 
 already_AddRefed<nsIDOMEvent>
 CreateGenericEvent(EventTarget* aOwner,
                    const nsDependentString& aType,
                    Bubbles aBubbles,
                    Cancelable aCancelable)
 {
   RefPtr<Event> event = new Event(aOwner, nullptr, nullptr);
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -1249,30 +1249,29 @@ ContentChild::RecvInitCompositor(Endpoin
 }
 
 bool
 ContentChild::RecvInitImageBridge(Endpoint<PImageBridgeChild>&& aEndpoint)
 {
   return ImageBridgeChild::InitForContent(Move(aEndpoint));
 }
 
+bool
+ContentChild::RecvInitVRManager(Endpoint<PVRManagerChild>&& aEndpoint)
+{
+  return gfx::VRManagerChild::InitForContent(Move(aEndpoint));
+}
+
 PSharedBufferManagerChild*
 ContentChild::AllocPSharedBufferManagerChild(mozilla::ipc::Transport* aTransport,
                                               base::ProcessId aOtherProcess)
 {
   return SharedBufferManagerChild::StartUpInChildProcess(aTransport, aOtherProcess);
 }
 
-gfx::PVRManagerChild*
-ContentChild::AllocPVRManagerChild(Transport* aTransport,
-                                   ProcessId aOtherProcess)
-{
-  return gfx::VRManagerChild::StartUpInChildProcess(aTransport, aOtherProcess);
-}
-
 PBackgroundChild*
 ContentChild::AllocPBackgroundChild(Transport* aTransport,
                                     ProcessId aOtherProcess)
 {
   return BackgroundChild::Alloc(aTransport, aOtherProcess);
 }
 
 PProcessHangMonitorChild*
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -147,29 +147,27 @@ public:
   AllocPAPZChild(const TabId& aTabId) override;
   bool
   DeallocPAPZChild(PAPZChild* aActor) override;
 
   bool
   RecvInitCompositor(Endpoint<PCompositorBridgeChild>&& aEndpoint) override;
   bool
   RecvInitImageBridge(Endpoint<PImageBridgeChild>&& aEndpoint) override;
+  bool
+  RecvInitVRManager(Endpoint<PVRManagerChild>&& aEndpoint) override;
 
   PSharedBufferManagerChild*
   AllocPSharedBufferManagerChild(mozilla::ipc::Transport* aTransport,
                                   base::ProcessId aOtherProcess) override;
 
   PProcessHangMonitorChild*
   AllocPProcessHangMonitorChild(Transport* aTransport,
                                 ProcessId aOtherProcess) override;
 
-  PVRManagerChild*
-  AllocPVRManagerChild(Transport* aTransport,
-                       ProcessId aOtherProcess) override;
-
   virtual bool RecvSetProcessSandbox(const MaybeFileDesc& aBroker) override;
 
   PBackgroundChild*
   AllocPBackgroundChild(Transport* aTransport, ProcessId aOtherProcess)
                         override;
 
   virtual PBrowserChild* AllocPBrowserChild(const TabId& aTabId,
                                             const IPCTabContext& aContext,
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -270,18 +270,16 @@ using namespace mozilla::system;
 #ifdef XP_WIN
 #include "mozilla/widget/AudioSession.h"
 #endif
 
 #ifdef MOZ_CRASHREPORTER
 #include "nsThread.h"
 #endif
 
-#include "VRManagerParent.h"            // for VRManagerParent
-
 // For VP9Benchmark::sBenchmarkFpsPref
 #include "Benchmark.h"
 
 static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
 
 #if defined(XP_WIN)
 // e10s forced enable pref, defined in nsAppRunner.cpp
 extern const char* kForceEnableE10sPref;
@@ -2444,18 +2442,21 @@ ContentParent::InitInternal(ProcessPrior
         Endpoint<PImageBridgeChild> endpoint;
         DebugOnly<bool> opened =
           gpm->CreateContentImageBridge(OtherPid(), &endpoint);
         MOZ_ASSERT(opened);
         Unused << SendInitImageBridge(Move(endpoint));
       }
 
       {
-        DebugOnly<bool> opened = gfx::PVRManager::Open(this);
+        Endpoint<PVRManagerChild> endpoint;
+        DebugOnly<bool> opened =
+          gpm->CreateContentVRManager(OtherPid(), &endpoint);
         MOZ_ASSERT(opened);
+        Unused << SendInitVRManager(Move(endpoint));
       }
     }
 #ifdef MOZ_WIDGET_GONK
     DebugOnly<bool> opened = PSharedBufferManager::Open(this);
     MOZ_ASSERT(opened);
 #endif
   }
 
@@ -3236,23 +3237,16 @@ ContentParent::AllocPAPZParent(const Tab
 }
 
 bool
 ContentParent::DeallocPAPZParent(PAPZParent* aActor)
 {
   return true;
 }
 
-gfx::PVRManagerParent*
-ContentParent::AllocPVRManagerParent(Transport* aTransport,
-                                     ProcessId aOtherProcess)
-{
-  return gfx::VRManagerParent::CreateCrossProcess(aTransport, aOtherProcess);
-}
-
 PBackgroundParent*
 ContentParent::AllocPBackgroundParent(Transport* aTransport,
                                       ProcessId aOtherProcess)
 {
   return BackgroundParent::Alloc(this, aTransport, aOtherProcess);
 }
 
 PProcessHangMonitorParent*
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -752,20 +752,16 @@ private:
   PBackgroundParent*
   AllocPBackgroundParent(Transport* aTransport, ProcessId aOtherProcess)
                          override;
 
   PProcessHangMonitorParent*
   AllocPProcessHangMonitorParent(Transport* aTransport,
                                  ProcessId aOtherProcess) override;
 
-  PVRManagerParent*
-  AllocPVRManagerParent(Transport* aTransport,
-                        ProcessId aOtherProcess) override;
-
   virtual bool RecvGetProcessAttributes(ContentParentId* aCpId,
                                         bool* aIsForApp,
                                         bool* aIsForBrowser) override;
 
   virtual bool
   RecvGetXPCOMProcessAttributes(bool* aIsOffline,
                                 bool* aIsConnected,
                                 bool* aIsLangRTL,
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -382,17 +382,16 @@ struct BlobURLRegistrationData
 
 prio(normal upto urgent) sync protocol PContent
 {
     parent spawns PPluginModule;
 
     parent opens PProcessHangMonitor;
     parent opens PSharedBufferManager;
     parent opens PGMPService;
-    parent opens PVRManager;
     child opens PBackground;
 
     manages PAPZ;
     manages PBlob;
     manages PBluetooth;
     manages PBrowser;
     manages PCellBroadcast;
     manages PContentPermissionRequest;
@@ -467,16 +466,17 @@ both:
     // ignored and should be null/zero.
     async PWebBrowserPersistDocument(nullable PBrowser aBrowser,
                                      uint64_t aOuterWindowID);
 
 child:
     // Give the content process its endpoints to the compositor.
     async InitCompositor(Endpoint<PCompositorBridgeChild> compositor);
     async InitImageBridge(Endpoint<PImageBridgeChild> bridge);
+    async InitVRManager(Endpoint<PVRManagerChild> endpoint);
 
     /**
      * Enable system-level sandboxing features, if available.  Can
      * usually only be performed zero or one times.  The child may
      * abnormally exit if this fails; the details are OS-specific.
      */
     async SetProcessSandbox(MaybeFileDesc aBroker);
 
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -797,17 +797,16 @@ TabChild::Init()
     LayoutDeviceIntRect(0, 0, 0, 0),
     nullptr                  // HandleWidgetEvent
   );
 
   baseWindow->InitWindow(0, mPuppetWidget, 0, 0, 0, 0);
   baseWindow->Create();
 
   // Set the tab context attributes then pass to docShell
-  SetPrivateBrowsingAttributes(mChromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW);
   NotifyTabContextUpdated();
 
   // IPC uses a WebBrowser object for which DNS prefetching is turned off
   // by default. But here we really want it, so enable it explicitly
   nsCOMPtr<nsIWebBrowserSetup> webBrowserSetup =
     do_QueryInterface(baseWindow);
   if (webBrowserSetup) {
     webBrowserSetup->SetProperty(nsIWebBrowserSetup::SETUP_ALLOW_DNS_PREFETCH,
@@ -819,18 +818,17 @@ TabChild::Init()
 
   nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
   MOZ_ASSERT(docShell);
 
   docShell->SetAffectPrivateSessionLifetime(
       mChromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME);
   nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(WebNavigation());
   MOZ_ASSERT(loadContext);
-  loadContext->SetPrivateBrowsing(
-      mChromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW);
+  loadContext->SetPrivateBrowsing(OriginAttributesRef().mPrivateBrowsingId > 0);
   loadContext->SetRemoteTabs(
       mChromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW);
 
   // Few lines before, baseWindow->Create() will end up creating a new
   // window root in nsGlobalWindow::SetDocShell.
   // Then this chrome event handler, will be inherited to inner windows.
   // We want to also set it to the docshell so that inner windows
   // and any code that has access to the docshell
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -2666,17 +2666,17 @@ TabParent::RecvAudioChannelActivityNotif
   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
   if (os) {
     nsAutoCString topic;
     topic.Assign("audiochannel-activity-");
     topic.Append(AudioChannelService::GetAudioChannelTable()[aAudioChannel].tag);
 
     os->NotifyObservers(NS_ISUPPORTS_CAST(nsITabParent*, this),
                         topic.get(),
-                        aActive ? MOZ_UTF16("active") : MOZ_UTF16("inactive"));
+                        aActive ? u"active" : u"inactive");
   }
 
   return true;
 }
 
 already_AddRefed<nsFrameLoader>
 TabParent::GetFrameLoader(bool aUseCachedFrameLoaderAfterDestroy) const
 {
--- a/dom/mathml/nsMathMLElement.cpp
+++ b/dom/mathml/nsMathMLElement.cpp
@@ -1082,18 +1082,17 @@ nsMathMLElement::SetAttr(int32_t aNameSp
   // is important here!  The attribute is not set until SetAttr returns, and
   // we will need the updated attribute value because notifying the document
   // that content states have changed will call IntrinsicState, which will try
   // to get updated information about the visitedness from Link.
   if (aName == nsGkAtoms::href &&
       (aNameSpaceID == kNameSpaceID_None ||
        aNameSpaceID == kNameSpaceID_XLink)) {
     if (aNameSpaceID == kNameSpaceID_XLink) {
-      WarnDeprecated(MOZ_UTF16("xlink:href"),
-                     MOZ_UTF16("href"), OwnerDoc());
+      WarnDeprecated(u"xlink:href", u"href", OwnerDoc());
     }
     Link::ResetLinkState(!!aNotify, true);
   }
 
   return rv;
 }
 
 nsresult
--- a/dom/media/CubebUtils.cpp
+++ b/dom/media/CubebUtils.cpp
@@ -142,17 +142,17 @@ void InitBrandName()
   nsXPIDLString brandName;
   nsCOMPtr<nsIStringBundleService> stringBundleService =
     mozilla::services::GetStringBundleService();
   if (stringBundleService) {
     nsCOMPtr<nsIStringBundle> brandBundle;
     nsresult rv = stringBundleService->CreateBundle(kBrandBundleURL,
                                            getter_AddRefs(brandBundle));
     if (NS_SUCCEEDED(rv)) {
-      rv = brandBundle->GetStringFromName(MOZ_UTF16("brandShortName"),
+      rv = brandBundle->GetStringFromName(u"brandShortName",
                                           getter_Copies(brandName));
       NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
           "Could not get the program name for a cubeb stream.");
     }
   }
   /* cubeb expects a c-string. */
   const char* ascii = NS_LossyConvertUTF16toASCII(brandName).get();
   sBrandName = new char[brandName.Length() + 1];
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -835,24 +835,24 @@ NS_IMETHODIMP
 MediaDevice::GetType(nsAString& aType)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
 VideoDevice::GetType(nsAString& aType)
 {
-  aType.AssignLiteral(MOZ_UTF16("video"));
+  aType.AssignLiteral(u"video");
   return NS_OK;
 }
 
 NS_IMETHODIMP
 AudioDevice::GetType(nsAString& aType)
 {
-  aType.AssignLiteral(MOZ_UTF16("audio"));
+  aType.AssignLiteral(u"audio");
   return NS_OK;
 }
 
 NS_IMETHODIMP
 MediaDevice::GetId(nsAString& aID)
 {
   aID.Assign(mID);
   return NS_OK;
@@ -3047,17 +3047,17 @@ MediaManager::Observe(nsISupports* aSubj
   } else if (!strcmp(aTopic, "getUserMedia:response:deny")) {
     nsString errorMessage(NS_LITERAL_STRING("NotAllowedError"));
 
     if (aSubject) {
       nsCOMPtr<nsISupportsString> msg(do_QueryInterface(aSubject));
       MOZ_ASSERT(msg);
       msg->GetData(errorMessage);
       if (errorMessage.IsEmpty())
-        errorMessage.AssignLiteral(MOZ_UTF16("InternalError"));
+        errorMessage.AssignLiteral(u"InternalError");
     }
 
     nsString key(aData);
     RefPtr<GetUserMediaTask> task;
     mActiveCallbacks.Remove(key, getter_AddRefs(task));
     if (task) {
       task->Denied(errorMessage);
     }
--- a/dom/media/eme/EMEUtils.cpp
+++ b/dom/media/eme/EMEUtils.cpp
@@ -75,20 +75,20 @@ ParseKeySystem(const nsAString& aExpecte
     NS_WARNING("Invalid version in EME keySystem string");
     return false;
   }
   aOutCDMVersion = version;
 
   return true;
 }
 
-static const char16_t* sKeySystems[] = {
-  MOZ_UTF16("org.w3.clearkey"),
-  MOZ_UTF16("com.adobe.primetime"),
-  MOZ_UTF16("com.widevine.alpha"),
+static const char16_t *const sKeySystems[] = {
+  u"org.w3.clearkey",
+  u"com.adobe.primetime",
+  u"com.widevine.alpha",
 };
 
 bool
 ParseKeySystem(const nsAString& aInputKeySystem,
                nsAString& aOutKeySystem,
                int32_t& aOutCDMVersion)
 {
   for (const char16_t* keySystem : sKeySystems) {
--- a/dom/media/gtest/TestEME.cpp
+++ b/dom/media/gtest/TestEME.cpp
@@ -13,48 +13,48 @@ using namespace mozilla;
 struct ParseKeySystemTestCase {
   const char16_t* mInputKeySystemString;
   int32_t mOutCDMVersion;
   bool mShouldPass;
 };
 
 const ParseKeySystemTestCase ParseKeySystemTests[] = {
   {
-    MOZ_UTF16("org.w3.clearkey"),
+    u"org.w3.clearkey",
     NO_CDM_VERSION,
     true,
   }, {
-    MOZ_UTF16("org.w3.clearkey.123"),
+    u"org.w3.clearkey.123",
     123,
     true,
   }, {
-    MOZ_UTF16("org.w3.clearkey.-1"),
+    u"org.w3.clearkey.-1",
     NO_CDM_VERSION,
     false,
   }, {
-    MOZ_UTF16("org.w3.clearkey.NaN"),
+    u"org.w3.clearkey.NaN",
     NO_CDM_VERSION,
     false,
   }, {
-    MOZ_UTF16("org.w3.clearkey.0"),
+    u"org.w3.clearkey.0",
     0,
     true,
   }, {
-    MOZ_UTF16("org.w3.clearkey.123567890123567890123567890123567890123567890"),
+    u"org.w3.clearkey.123567890123567890123567890123567890123567890",
     NO_CDM_VERSION,
     false,
   }, {
-    MOZ_UTF16("org.w3.clearkey.0.1"),
+    u"org.w3.clearkey.0.1",
     NO_CDM_VERSION,
     false,
   }
 };
 
 TEST(EME, EMEParseKeySystem) {
-  const nsAutoString clearkey(MOZ_UTF16("org.w3.clearkey"));
+  const nsAutoString clearkey(u"org.w3.clearkey");
   for (const ParseKeySystemTestCase& test : ParseKeySystemTests) {
     nsAutoString keySystem;
     int32_t version;
     bool rv = ParseKeySystem(nsDependentString(test.mInputKeySystemString),
                              keySystem,
                              version);
     EXPECT_EQ(rv, test.mShouldPass) << "parse should succeed if expected to";
     if (!test.mShouldPass) {
--- a/dom/media/webrtc/MediaEngineDefault.cpp
+++ b/dom/media/webrtc/MediaEngineDefault.cpp
@@ -54,17 +54,17 @@ MediaEngineDefaultVideoSource::MediaEngi
 }
 
 MediaEngineDefaultVideoSource::~MediaEngineDefaultVideoSource()
 {}
 
 void
 MediaEngineDefaultVideoSource::GetName(nsAString& aName) const
 {
-  aName.AssignLiteral(MOZ_UTF16("Default Video Device"));
+  aName.AssignLiteral(u"Default Video Device");
   return;
 }
 
 void
 MediaEngineDefaultVideoSource::GetUUID(nsACString& aUUID) const
 {
   aUUID.AssignLiteral("1041FCBD-3F12-4F7B-9E9B-1EC556DD5676");
   return;
@@ -382,17 +382,17 @@ MediaEngineDefaultAudioSource::MediaEngi
 }
 
 MediaEngineDefaultAudioSource::~MediaEngineDefaultAudioSource()
 {}
 
 void
 MediaEngineDefaultAudioSource::GetName(nsAString& aName) const
 {
-  aName.AssignLiteral(MOZ_UTF16("Default Audio Device"));
+  aName.AssignLiteral(u"Default Audio Device");
   return;
 }
 
 void
 MediaEngineDefaultAudioSource::GetUUID(nsACString& aUUID) const
 {
   aUUID.AssignLiteral("B7CBD7C1-53EF-42F9-8353-73F61C70C092");
   return;
--- a/dom/media/webrtc/MediaEngineTabVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineTabVideoSource.cpp
@@ -117,17 +117,17 @@ MediaEngineTabVideoSource::InitRunnable:
   nsCOMPtr<nsIRunnable> start(new StartRunnable(mVideoSource));
   start->Run();
   return NS_OK;
 }
 
 void
 MediaEngineTabVideoSource::GetName(nsAString_internal& aName) const
 {
-  aName.AssignLiteral(MOZ_UTF16("&getUserMedia.videoSource.tabShare;"));
+  aName.AssignLiteral(u"&getUserMedia.videoSource.tabShare;");
 }
 
 void
 MediaEngineTabVideoSource::GetUUID(nsACString_internal& aUuid) const
 {
   aUuid.AssignLiteral("tab");
 }
 
--- a/dom/network/interfaces/nsITCPSocketCallback.idl
+++ b/dom/network/interfaces/nsITCPSocketCallback.idl
@@ -7,17 +7,17 @@
  * to highly privileged apps. It provides a buffered, non-blocking
  * interface for sending. For receiving, it uses an asynchronous,
  * event handler based interface.
  */
 
 #include "domstubs.idl"
 
 %{C++
-template<class T> class InfallibleTArray;
+#include "nsTArrayForwardDeclare.h"
 %}
 [ref] native nsUint8TArrayRef(InfallibleTArray<uint8_t>);
 [ptr] native JSContextPtr(JSContext);
 
 
 /*
  * This interface is implemented in TCPSocket.cpp as an internal interface
  * for use in cross-process socket implementation.
--- a/dom/notification/Notification.cpp
+++ b/dom/notification/Notification.cpp
@@ -834,21 +834,21 @@ NotificationTelemetryService::RecordSend
 
 NS_IMETHODIMP
 NotificationTelemetryService::Observe(nsISupports* aSubject,
                                       const char* aTopic,
                                       const char16_t* aData)
 {
   uint32_t capability;
   if (strcmp("perm-changed", aTopic) ||
-      !NS_strcmp(MOZ_UTF16("cleared"), aData) ||
+      !NS_strcmp(u"cleared", aData) ||
       !GetNotificationPermission(aSubject, &capability)) {
     return NS_OK;
   }
-  if (!NS_strcmp(MOZ_UTF16("deleted"), aData)) {
+  if (!NS_strcmp(u"deleted", aData)) {
     if (capability == nsIPermissionManager::DENY_ACTION) {
       Telemetry::Accumulate(
         Telemetry::WEB_NOTIFICATION_PERMISSION_REMOVED, 0);
     } else if (capability == nsIPermissionManager::ALLOW_ACTION) {
       Telemetry::Accumulate(
         Telemetry::WEB_NOTIFICATION_PERMISSION_REMOVED, 1);
     }
   }
--- a/dom/performance/PerformanceObserver.cpp
+++ b/dom/performance/PerformanceObserver.cpp
@@ -129,21 +129,21 @@ PerformanceObserver::QueueEntry(Performa
   aEntry->GetEntryType(entryType);
   if (!mEntryTypes.Contains<nsString>(entryType)) {
     return;
   }
 
   mQueuedEntries.AppendElement(aEntry);
 }
 
-static const char16_t* sValidTypeNames[4] = {
-  MOZ_UTF16("mark"),
-  MOZ_UTF16("measure"),
-  MOZ_UTF16("resource"),
-  MOZ_UTF16("server")
+static const char16_t *const sValidTypeNames[4] = {
+  u"mark",
+  u"measure",
+  u"resource",
+  u"server"
 };
 
 void
 PerformanceObserver::Observe(const PerformanceObserverInit& aOptions,
                              ErrorResult& aRv)
 {
   if (aOptions.mEntryTypes.IsEmpty()) {
     aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
--- a/dom/plugins/ipc/PluginHangUIParent.cpp
+++ b/dom/plugins/ipc/PluginHangUIParent.cpp
@@ -382,17 +382,17 @@ PluginHangUIParent::GetHangUIOwnerWindow
   windowHandle = nullptr;
 
   nsresult rv;
   nsCOMPtr<nsIWindowMediator> winMediator(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID,
                                                         &rv));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<mozIDOMWindowProxy> navWin;
-  rv = winMediator->GetMostRecentWindow(MOZ_UTF16("navigator:browser"),
+  rv = winMediator->GetMostRecentWindow(u"navigator:browser",
                                         getter_AddRefs(navWin));
   NS_ENSURE_SUCCESS(rv, rv);
   if (!navWin) {
     return NS_ERROR_FAILURE;
   }
 
   nsPIDOMWindowOuter* win = nsPIDOMWindowOuter::From(navWin);
   nsCOMPtr<nsIWidget> widget = WidgetUtils::DOMWindowToWidget(win);
--- a/dom/presentation/PresentationDeviceManager.cpp
+++ b/dom/presentation/PresentationDeviceManager.cpp
@@ -176,17 +176,17 @@ PresentationDeviceManager::AddDevice(nsI
   MOZ_ASSERT(NS_IsMainThread());
 
   if (NS_WARN_IF(mDevices.Contains(aDevice))) {
     return NS_ERROR_FAILURE;
   }
 
   mDevices.AppendElement(aDevice);
 
-  NotifyDeviceChange(aDevice, MOZ_UTF16("add"));
+  NotifyDeviceChange(aDevice, u"add");
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PresentationDeviceManager::RemoveDevice(nsIPresentationDevice* aDevice)
 {
   NS_ENSURE_ARG(aDevice);
@@ -194,32 +194,32 @@ PresentationDeviceManager::RemoveDevice(
 
   int32_t index = mDevices.IndexOf(aDevice);
   if (NS_WARN_IF(index < 0)) {
     return NS_ERROR_FAILURE;
   }
 
   mDevices.RemoveElementAt(index);
 
-  NotifyDeviceChange(aDevice, MOZ_UTF16("remove"));
+  NotifyDeviceChange(aDevice, u"remove");
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PresentationDeviceManager::UpdateDevice(nsIPresentationDevice* aDevice)
 {
   NS_ENSURE_ARG(aDevice);
   MOZ_ASSERT(NS_IsMainThread());
 
   if (NS_WARN_IF(!mDevices.Contains(aDevice))) {
     return NS_ERROR_FAILURE;
   }
 
-  NotifyDeviceChange(aDevice, MOZ_UTF16("update"));
+  NotifyDeviceChange(aDevice, u"update");
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PresentationDeviceManager::OnSessionRequest(nsIPresentationDevice* aDevice,
                                             const nsAString& aUrl,
                                             const nsAString& aPresentationId,
--- a/dom/quota/ActorsChild.cpp
+++ b/dom/quota/ActorsChild.cpp
@@ -140,17 +140,19 @@ QuotaUsageRequestChild::HandleResponse(n
 }
 
 void
 QuotaUsageRequestChild::HandleResponse(const UsageResponse& aResponse)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mRequest);
 
-  mRequest->SetResult(aResponse.usage(), aResponse.fileUsage());
+  mRequest->SetResult(aResponse.usage(),
+                      aResponse.fileUsage(),
+                      aResponse.limit());
 }
 
 void
 QuotaUsageRequestChild::ActorDestroy(ActorDestroyReason aWhy)
 {
   AssertIsOnOwningThread();
 
   if (mRequest) {
--- a/dom/quota/ActorsParent.cpp
+++ b/dom/quota/ActorsParent.cpp
@@ -1003,21 +1003,26 @@ private:
   virtual bool
   RecvStopIdleMaintenance() override;
 };
 
 class GetUsageOp final
   : public NormalOriginOperationBase
   , public PQuotaUsageRequestParent
 {
+  // If mGetGroupUsage is false, we use mUsageInfo to record the origin usage
+  // and the file usage. Otherwise, we use it to record the group usage and the
+  // limit.
   UsageInfo mUsageInfo;
 
   const UsageParams mParams;
+  nsCString mSuffix;
   nsCString mGroup;
   bool mIsApp;
+  bool mGetGroupUsage;
 
 public:
   explicit GetUsageOp(const UsageRequestParams& aParams);
 
   bool
   Init(Quota* aQuota);
 
 private:
@@ -4578,16 +4583,50 @@ QuotaManager::GetGroupLimit() const
   uint64_t x = std::min<uint64_t>(mTemporaryStorageLimit * .20, 2 GB);
 
   // In low-storage situations, make an exception (while not exceeding the total
   // storage limit).
   return std::min<uint64_t>(mTemporaryStorageLimit,
                             std::max<uint64_t>(x, 10 MB));
 }
 
+void
+QuotaManager::GetGroupUsageAndLimit(const nsACString& aGroup,
+                                    UsageInfo* aUsageInfo)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aUsageInfo);
+
+  {
+    MutexAutoLock lock(mQuotaMutex);
+
+    aUsageInfo->SetLimit(GetGroupLimit());
+    aUsageInfo->ResetUsage();
+
+    GroupInfoPair* pair;
+    if (!mGroupInfoPairs.Get(aGroup, &pair)) {
+      return;
+    }
+
+    // Calculate temporary group usage
+    RefPtr<GroupInfo> temporaryGroupInfo =
+      pair->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY);
+    if (temporaryGroupInfo) {
+      aUsageInfo->AppendToDatabaseUsage(temporaryGroupInfo->mUsage);
+    }
+
+    // Calculate default group usage
+    RefPtr<GroupInfo> defaultGroupInfo =
+      pair->LockedGetGroupInfo(PERSISTENCE_TYPE_DEFAULT);
+    if (defaultGroupInfo) {
+      aUsageInfo->AppendToDatabaseUsage(defaultGroupInfo->mUsage);
+    }
+  }
+}
+
 // static
 void
 QuotaManager::GetStorageId(PersistenceType aPersistenceType,
                            const nsACString& aOrigin,
                            Client::Type aClientType,
                            nsACString& aDatabaseId)
 {
   nsAutoCString str;
@@ -5840,16 +5879,17 @@ Quota::RecvStopIdleMaintenance()
   return true;
 }
 
 GetUsageOp::GetUsageOp(const UsageRequestParams& aParams)
   : NormalOriginOperationBase(Nullable<PersistenceType>(),
                               OriginScope::FromNull(),
                               /* aExclusive */ false)
   , mParams(aParams.get_UsageParams())
+  , mGetGroupUsage(aParams.get_UsageParams().getGroupUsage())
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aParams.type() == UsageRequestParams::TUsageParams);
 }
 
 bool
 GetUsageOp::Init(Quota* aQuota)
 {
@@ -5875,17 +5915,17 @@ GetUsageOp::DoInitOnMainThread()
   nsCOMPtr<nsIPrincipal> principal =
     PrincipalInfoToPrincipal(principalInfo, &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // Figure out which origin we're dealing with.
   nsCString origin;
-  rv = QuotaManager::GetInfoFromPrincipal(principal, nullptr, &mGroup, &origin,
+  rv = QuotaManager::GetInfoFromPrincipal(principal, &mSuffix, &mGroup, &origin,
                                           &mIsApp);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   mOriginScope.SetFromOrigin(origin);
 
   return NS_OK;
@@ -5990,22 +6030,44 @@ GetUsageOp::AddToUsage(QuotaManager* aQu
 
   return NS_OK;
 }
 
 nsresult
 GetUsageOp::DoDirectoryWork(QuotaManager* aQuotaManager)
 {
   AssertIsOnIOThread();
+  MOZ_ASSERT(mUsageInfo.TotalUsage() == 0);
 
   PROFILER_LABEL("Quota", "GetUsageOp::DoDirectoryWork",
                  js::ProfileEntry::Category::OTHER);
 
+  nsresult rv;
+
+  if (mGetGroupUsage) {
+    nsCOMPtr<nsIFile> directory;
+
+    // Ensure origin is initialized first. It will initialize all origins for
+    // temporary storage including origins belonging to our group.
+    rv = aQuotaManager->EnsureOriginIsInitialized(PERSISTENCE_TYPE_TEMPORARY,
+                                                  mSuffix, mGroup,
+                                                  mOriginScope.GetOrigin(),
+                                                  mIsApp,
+                                                  getter_AddRefs(directory));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    // Get cached usage and limit (the method doesn't have to stat any files).
+    aQuotaManager->GetGroupUsageAndLimit(mGroup, &mUsageInfo);
+
+    return NS_OK;
+  }
+
   // Add all the persistent/temporary/default storage files we care about.
-  nsresult rv;
   for (const PersistenceType type : kAllPersistenceTypes) {
     rv = AddToUsage(aQuotaManager, type);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   return NS_OK;
@@ -6024,18 +6086,27 @@ GetUsageOp::SendResults()
     if (mUsageInfo.Canceled()) {
       mResultCode = NS_ERROR_FAILURE;
     }
 
     UsageRequestResponse response;
 
     if (NS_SUCCEEDED(mResultCode)) {
       UsageResponse usageResponse;
+
+      // We'll get the group usage when mGetGroupUsage is true and get the
+      // origin usage when mGetGroupUsage is false.
       usageResponse.usage() = mUsageInfo.TotalUsage();
-      usageResponse.fileUsage() = mUsageInfo.FileUsage();
+
+      if (mGetGroupUsage) {
+        usageResponse.limit() = mUsageInfo.Limit();
+      } else {
+        usageResponse.fileUsage() = mUsageInfo.FileUsage();
+      }
+
       response = usageResponse;
     } else {
       response = mResultCode;
     }
 
     Unused << PQuotaUsageRequestParent::Send__delete__(this, response);
   }
 }
--- a/dom/quota/PQuota.ipdl
+++ b/dom/quota/PQuota.ipdl
@@ -15,16 +15,17 @@ using mozilla::dom::quota::PersistenceTy
 
 namespace mozilla {
 namespace dom {
 namespace quota {
 
 struct UsageParams
 {
   PrincipalInfo principalInfo;
+  bool getGroupUsage;
 };
 
 union UsageRequestParams
 {
   UsageParams;
 };
 
 struct ClearOriginParams
--- a/dom/quota/PQuotaUsageRequest.ipdl
+++ b/dom/quota/PQuotaUsageRequest.ipdl
@@ -7,16 +7,17 @@ include protocol PQuota;
 namespace mozilla {
 namespace dom {
 namespace quota {
 
 struct UsageResponse
 {
   uint64_t usage;
   uint64_t fileUsage;
+  uint64_t limit;
 };
 
 union UsageRequestResponse
 {
   nsresult;
   UsageResponse;
 };
 
--- a/dom/quota/QuotaManager.h
+++ b/dom/quota/QuotaManager.h
@@ -342,16 +342,20 @@ public:
     MOZ_ASSERT(aPersistenceType == PERSISTENCE_TYPE_DEFAULT);
 
     return mDefaultStoragePath;
   }
 
   uint64_t
   GetGroupLimit() const;
 
+  void
+  GetGroupUsageAndLimit(const nsACString& aGroup,
+                        UsageInfo* aUsageInfo);
+
   static void
   GetStorageId(PersistenceType aPersistenceType,
                const nsACString& aOrigin,
                Client::Type aClientType,
                nsACString& aDatabaseId);
 
   static nsresult
   GetInfoFromPrincipal(nsIPrincipal* aPrincipal,
--- a/dom/quota/QuotaManagerService.cpp
+++ b/dom/quota/QuotaManagerService.cpp
@@ -492,39 +492,41 @@ NS_IMPL_ADDREF(QuotaManagerService)
 NS_IMPL_RELEASE_WITH_DESTROY(QuotaManagerService, Destroy())
 NS_IMPL_QUERY_INTERFACE(QuotaManagerService,
                         nsIQuotaManagerService,
                         nsIObserver)
 
 NS_IMETHODIMP
 QuotaManagerService::GetUsageForPrincipal(nsIPrincipal* aPrincipal,
                                           nsIQuotaUsageCallback* aCallback,
+                                          bool aGetGroupUsage,
                                           nsIQuotaUsageRequest** _retval)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aPrincipal);
   MOZ_ASSERT(aCallback);
   MOZ_ASSERT(nsContentUtils::IsCallerChrome());
 
   RefPtr<UsageRequest> request = new UsageRequest(aPrincipal, aCallback);
 
   UsageParams params;
 
   PrincipalInfo& principalInfo = params.principalInfo();
-
   nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (principalInfo.type() != PrincipalInfo::TContentPrincipalInfo &&
       principalInfo.type() != PrincipalInfo::TSystemPrincipalInfo) {
     return NS_ERROR_UNEXPECTED;
   }
 
+  params.getGroupUsage() = aGetGroupUsage;
+
   nsAutoPtr<PendingRequestInfo> info(new UsageRequestInfo(request, params));
 
   rv = InitiateRequest(info);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   request.forget(_retval);
--- a/dom/quota/QuotaRequests.cpp
+++ b/dom/quota/QuotaRequests.cpp
@@ -87,21 +87,22 @@ RequestBase::GetResultCode(nsresult* aRe
     return NS_ERROR_FAILURE;
   }
 
   *aResultCode = mResultCode;
   return NS_OK;
 }
 
 UsageRequest::UsageRequest(nsIPrincipal* aPrincipal,
-	                         nsIQuotaUsageCallback* aCallback)
+                           nsIQuotaUsageCallback* aCallback)
   : RequestBase(aPrincipal)
   , mCallback(aCallback)
   , mUsage(0)
   , mFileUsage(0)
+  , mLimit(0)
   , mBackgroundActor(nullptr)
   , mCanceled(false)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aPrincipal);
   MOZ_ASSERT(aCallback);
 }
 
@@ -120,23 +121,24 @@ UsageRequest::SetBackgroundActor(QuotaUs
   mBackgroundActor = aBackgroundActor;
 
   if (mCanceled) {
     mBackgroundActor->SendCancel();
   }
 }
 
 void
-UsageRequest::SetResult(uint64_t aUsage, uint64_t aFileUsage)
+UsageRequest::SetResult(uint64_t aUsage, uint64_t aFileUsage, uint64_t aLimit)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(!mHaveResultOrErrorCode);
 
   mUsage = aUsage;
   mFileUsage = aFileUsage;
+  mLimit = aLimit;
   mHaveResultOrErrorCode = true;
 
   FireCallback();
 }
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(UsageRequest, RequestBase, mCallback)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(UsageRequest)
@@ -169,16 +171,30 @@ UsageRequest::GetFileUsage(uint64_t* aFi
     return NS_ERROR_FAILURE;
   }
 
   *aFileUsage = mFileUsage;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+UsageRequest::GetLimit(uint64_t* aLimit)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aLimit);
+
+  if (!mHaveResultOrErrorCode) {
+    return NS_ERROR_FAILURE;
+  }
+
+  *aLimit = mLimit;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 UsageRequest::GetCallback(nsIQuotaUsageCallback** aCallback)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aCallback);
 
   NS_IF_ADDREF(*aCallback = mCallback);
   return NS_OK;
 }
--- a/dom/quota/QuotaRequests.h
+++ b/dom/quota/QuotaRequests.h
@@ -69,16 +69,19 @@ class UsageRequest final
   : public RequestBase
   , public nsIQuotaUsageRequest
 {
   nsCOMPtr<nsIQuotaUsageCallback> mCallback;
 
   uint64_t mUsage;
   uint64_t mFileUsage;
 
+  // Group Limit.
+  uint64_t mLimit;
+
   QuotaUsageRequestChild* mBackgroundActor;
 
   bool mCanceled;
 
 public:
   UsageRequest(nsIPrincipal* aPrincipal,
                nsIQuotaUsageCallback* aCallback);
 
@@ -89,17 +92,17 @@ public:
   ClearBackgroundActor()
   {
     AssertIsOnOwningThread();
 
     mBackgroundActor = nullptr;
   }
 
   void
-  SetResult(uint64_t aUsage, uint64_t aFileUsage);
+  SetResult(uint64_t aUsage, uint64_t aFileUsage, uint64_t aLimit);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_FORWARD_NSIQUOTAREQUESTBASE(RequestBase::)
   NS_DECL_NSIQUOTAUSAGEREQUEST
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(UsageRequest, RequestBase)
 
 private:
   ~UsageRequest();
--- a/dom/quota/UsageInfo.h
+++ b/dom/quota/UsageInfo.h
@@ -13,17 +13,17 @@
 #include "mozilla/CheckedInt.h"
 
 BEGIN_QUOTA_NAMESPACE
 
 class UsageInfo
 {
 public:
   UsageInfo()
-  : mCanceled(false), mDatabaseUsage(0), mFileUsage(0)
+  : mCanceled(false), mDatabaseUsage(0), mFileUsage(0), mLimit(0)
   { }
 
   virtual ~UsageInfo()
   { }
 
   bool
   Canceled()
   {
@@ -47,29 +47,41 @@ public:
   }
 
   void
   AppendToFileUsage(uint64_t aUsage)
   {
     IncrementUsage(&mFileUsage, aUsage);
   }
 
+  void
+  SetLimit(uint64_t aLimit)
+  {
+    mLimit = aLimit;
+  }
+
   uint64_t
   DatabaseUsage()
   {
     return mDatabaseUsage;
   }
 
   uint64_t
   FileUsage()
   {
     return mFileUsage;
   }
 
   uint64_t
+  Limit()
+  {
+    return mLimit;
+  }
+
+  uint64_t
   TotalUsage()
   {
     uint64_t totalUsage = mDatabaseUsage;
     IncrementUsage(&totalUsage, mFileUsage);
     return totalUsage;
   }
 
   void
@@ -93,13 +105,14 @@ public:
   }
 
 protected:
   mozilla::Atomic<bool> mCanceled;
 
 private:
   uint64_t mDatabaseUsage;
   uint64_t mFileUsage;
+  uint64_t mLimit;
 };
 
 END_QUOTA_NAMESPACE
 
 #endif // mozilla_dom_quota_usageinfo_h__
--- a/dom/quota/nsIQuotaManagerService.idl
+++ b/dom/quota/nsIQuotaManagerService.idl
@@ -17,20 +17,26 @@ interface nsIQuotaManagerService : nsISu
   /**
    * Schedules an asynchronous callback that will return the total amount of
    * disk space being used by storages for the given origin.
    *
    * @param aPrincipal
    *        A principal for the origin whose usage is being queried.
    * @param aCallback
    *        The callback that will be called when the usage is available.
+   * @param aGetGroupUsage
+   *        An optional flag to indicate whether getting group usage and limit
+   *        or origin usage and file usage. The default value is false.
+   * Note:  Origin usage here represents total usage of an origin. However,
+   *        group usage here represents only non-persistent usage of a group.
    */
   nsIQuotaUsageRequest
   getUsageForPrincipal(in nsIPrincipal aPrincipal,
-                       in nsIQuotaUsageCallback aCallback);
+                       in nsIQuotaUsageCallback aCallback,
+                       [optional] in boolean aGetGroupUsage);
 
   /**
    * Removes all storages. The files may not be deleted immediately depending
    * on prohibitive concurrent operations.
    * Be careful, this removes *all* the data that has ever been stored!
    *
    * If the dom.quotaManager.testing preference is not true the call will be
    * a no-op.
--- a/dom/quota/nsIQuotaRequests.idl
+++ b/dom/quota/nsIQuotaRequests.idl
@@ -20,16 +20,18 @@ interface nsIQuotaRequestBase : nsISuppo
 
 [scriptable, uuid(166e28e6-cf6d-4927-a6d7-b51bca9d3469)]
 interface nsIQuotaUsageRequest : nsIQuotaRequestBase
 {
   readonly attribute unsigned long long usage;
 
   readonly attribute unsigned long long fileUsage;
 
+  readonly attribute unsigned long long limit;
+
   attribute nsIQuotaUsageCallback callback;
 
   void
   cancel();
 };
 
 [scriptable, uuid(22890e3e-ff25-4372-9684-d901060e2f6c)]
 interface nsIQuotaRequest : nsIQuotaRequestBase
--- a/dom/security/ContentVerifier.cpp
+++ b/dom/security/ContentVerifier.cpp
@@ -2,374 +2,229 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ContentVerifier.h"
 
 #include "mozilla/fallible.h"
 #include "mozilla/Logging.h"
-#include "mozilla/Preferences.h"
-#include "mozilla/StaticPtr.h"
-#include "nsCharSeparatedTokenizer.h"
+#include "MainThreadUtils.h"
 #include "nsIInputStream.h"
 #include "nsIRequest.h"
-#include "nssb64.h"
-#include "nsSecurityHeaderParser.h"
 #include "nsServiceManagerUtils.h"
 #include "nsStringStream.h"
-#include "nsThreadUtils.h"
 
 using namespace mozilla;
 
 static LazyLogModule gContentVerifierPRLog("ContentVerifier");
 #define CSV_LOG(args) MOZ_LOG(gContentVerifierPRLog, LogLevel::Debug, args)
 
-// Content-Signature prefix
-const nsLiteralCString kPREFIX = NS_LITERAL_CSTRING("Content-Signature:\x00");
-
-NS_IMPL_ISUPPORTS(ContentVerifier, nsIStreamListener, nsISupports);
+NS_IMPL_ISUPPORTS(ContentVerifier,
+                  nsIContentSignatureReceiverCallback,
+                  nsIStreamListener);
 
 nsresult
-ContentVerifier::Init(const nsAString& aContentSignatureHeader)
+ContentVerifier::Init(const nsACString& aContentSignatureHeader,
+                      nsIRequest* aRequest, nsISupports* aContext)
 {
-  mVks = Preferences::GetString("browser.newtabpage.remote.keys");
-
-  if (aContentSignatureHeader.IsEmpty() || mVks.IsEmpty()) {
-    CSV_LOG(
-      ("Content-Signature header and verification keys must not be empty!\n"));
+  MOZ_ASSERT(NS_IsMainThread());
+  if (aContentSignatureHeader.IsEmpty()) {
+    CSV_LOG(("Content-Signature header must not be empty!\n"));
     return NS_ERROR_INVALID_SIGNATURE;
   }
 
-  nsresult rv = ParseContentSignatureHeader(aContentSignatureHeader);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_SIGNATURE);
-  return CreateContext();
+  // initialise the content signature "service"
+  nsresult rv;
+  mVerifier =
+    do_CreateInstance("@mozilla.org/security/contentsignatureverifier;1", &rv);
+  if (NS_FAILED(rv) || !mVerifier) {
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+
+  // Keep references to the request and context. We need them in FinishSignature
+  // and the ContextCreated callback.
+  mContentRequest = aRequest;
+  mContentContext = aContext;
+
+  rv = mVerifier->CreateContextWithoutCertChain(
+    this, aContentSignatureHeader,
+    NS_LITERAL_CSTRING("remote-newtab-signer.mozilla.org"));
+  if (NS_FAILED(rv)){
+    mVerifier = nullptr;
+  }
+  return rv;
 }
 
 /**
  * Implement nsIStreamListener
  * We buffer the entire content here and kick off verification
  */
 NS_METHOD
 AppendNextSegment(nsIInputStream* aInputStream, void* aClosure,
                   const char* aRawSegment, uint32_t aToOffset, uint32_t aCount,
                   uint32_t* outWrittenCount)
 {
   FallibleTArray<nsCString>* decodedData =
     static_cast<FallibleTArray<nsCString>*>(aClosure);
-  nsAutoCString segment(aRawSegment, aCount);
+  nsDependentCSubstring segment(aRawSegment, aCount);
   if (!decodedData->AppendElement(segment, fallible)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
   *outWrittenCount = aCount;
   return NS_OK;
 }
 
+void
+ContentVerifier::FinishSignature()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  nsCOMPtr<nsIStreamListener> nextListener;
+  nextListener.swap(mNextListener);
+
+  // Verify the content:
+  // If this fails, we return an invalid signature error to load a fallback page.
+  // If everthing is good, we return a new stream to the next listener and kick
+  // that one off.
+  bool verified = false;
+  nsresult rv = NS_OK;
+
+  // If the content signature check fails, stop the load
+  // and return a signature error. NSS resources are freed by the
+  // ContentSignatureVerifier on destruction.
+  if (NS_FAILED(mVerifier->End(&verified)) || !verified) {
+    CSV_LOG(("failed to verify content\n"));
+    (void)nextListener->OnStopRequest(mContentRequest, mContentContext,
+                                      NS_ERROR_INVALID_SIGNATURE);
+    return;
+  }
+  CSV_LOG(("Successfully verified content signature.\n"));
+
+  // We emptied the input stream so we have to create a new one from mContent
+  // to hand it to the consuming listener.
+  uint64_t offset = 0;
+  for (uint32_t i = 0; i < mContent.Length(); ++i) {
+    nsCOMPtr<nsIInputStream> oInStr;
+    rv = NS_NewCStringInputStream(getter_AddRefs(oInStr), mContent[i]);
+    if (NS_FAILED(rv)) {
+      break;
+    }
+    // let the next listener know that there is data in oInStr
+    rv = nextListener->OnDataAvailable(mContentRequest, mContentContext, oInStr,
+                                       offset, mContent[i].Length());
+    offset += mContent[i].Length();
+    if (NS_FAILED(rv)) {
+      break;
+    }
+  }
+
+  // propagate OnStopRequest and return
+  nextListener->OnStopRequest(mContentRequest, mContentContext, rv);
+}
+
 NS_IMETHODIMP
 ContentVerifier::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
 {
+  MOZ_CRASH("This OnStartRequest should've never been called!");
   return NS_OK;
 }
 
 NS_IMETHODIMP
 ContentVerifier::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
                                nsresult aStatus)
 {
-  // Verify the content:
-  // If this fails, we return an invalid signature error to load a fallback page.
-  // If everthing is good, we return a new stream to the next listener and kick
-  // that one of.
-  CSV_LOG(("VerifySignedContent, b64signature: %s\n", mSignature.get()));
-  CSV_LOG(("VerifySignedContent, key: \n[\n%s\n]\n", mKey.get()));
-  bool verified = false;
-  nsresult rv = End(&verified);
-  if (NS_FAILED(rv) || !verified || NS_FAILED(aStatus)) {
-    // cancel the request and return error
-    if (NS_FAILED(aStatus)) {
-      rv = aStatus;
-    } else {
-      rv = NS_ERROR_INVALID_SIGNATURE;
-    }
-    CSV_LOG(("failed to verify content\n"));
-    mNextListener->OnStartRequest(aRequest, aContext);
-    mNextListener->OnStopRequest(aRequest, aContext, rv);
-    return NS_ERROR_INVALID_SIGNATURE;
-  }
-  CSV_LOG(("Successfully verified content signature.\n"));
-
-  // start the next listener
-  rv = mNextListener->OnStartRequest(aRequest, aContext);
-  if (NS_SUCCEEDED(rv)) {
-    // We emptied aInStr so we have to create a new one from buf to hand it
-    // to the consuming listener.
-    for (uint32_t i = 0; i < mContent.Length(); ++i) {
-      nsCOMPtr<nsIInputStream> oInStr;
-      rv = NS_NewCStringInputStream(getter_AddRefs(oInStr), mContent[i]);
-      if (NS_FAILED(rv)) {
-        break;
-      }
-      // let the next listener know that there is data in oInStr
-      rv = mNextListener->OnDataAvailable(aRequest, aContext, oInStr, 0,
-                                          mContent[i].Length());
-      if (NS_FAILED(rv)) {
-        break;
-      }
-    }
+  // If we don't have a next listener, we handed off this request already.
+  // Return, there's nothing to do here.
+  if (!mNextListener) {
+    return NS_OK;
   }
 
-  // propagate OnStopRequest and return
-  return mNextListener->OnStopRequest(aRequest, aContext, rv);
+  if (NS_FAILED(aStatus)) {
+    CSV_LOG(("Stream failed\n"));
+    nsCOMPtr<nsIStreamListener> nextListener;
+    nextListener.swap(mNextListener);
+    return nextListener->OnStopRequest(aRequest, aContext, aStatus);
+  }
+
+  mContentRead = true;
+
+  // If the ContentSignatureVerifier is initialised, finish the verification.
+  if (mContextCreated) {
+    FinishSignature();
+    return aStatus;
+  }
+
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 ContentVerifier::OnDataAvailable(nsIRequest* aRequest, nsISupports* aContext,
                                  nsIInputStream* aInputStream, uint64_t aOffset,
                                  uint32_t aCount)
 {
   // buffer the entire stream
   uint32_t read;
   nsresult rv = aInputStream->ReadSegments(AppendNextSegment, &mContent, aCount,
                                            &read);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  // update the signature verifier
-  return Update(mContent[mContent.Length()-1]);
-}
-
-/**
- * ContentVerifier logic and utils
- */
-
-nsresult
-ContentVerifier::GetVerificationKey(const nsAString& aKeyId)
-{
-  // get verification keys from the pref and see if we have |aKeyId|
-  nsCharSeparatedTokenizer tokenizerVK(mVks, ';');
-  while (tokenizerVK.hasMoreTokens()) {
-    nsDependentSubstring token = tokenizerVK.nextToken();
-    nsCharSeparatedTokenizer tokenizerKey(token, '=');
-    nsString prefKeyId;
-    if (tokenizerKey.hasMoreTokens()) {
-      prefKeyId = tokenizerKey.nextToken();
-    }
-    nsString key;
-    if (tokenizerKey.hasMoreTokens()) {
-      key = tokenizerKey.nextToken();
-    }
-    if (prefKeyId.Equals(aKeyId)) {
-      mKey.Assign(NS_ConvertUTF16toUTF8(key));
-      return NS_OK;
-    }
-  }
-
-  // we didn't find the appropriate key
-  return NS_ERROR_INVALID_SIGNATURE;
-}
-
-nsresult
-ContentVerifier::ParseContentSignatureHeader(
-  const nsAString& aContentSignatureHeader)
-{
-  // We only support p384 ecdsa according to spec
-  NS_NAMED_LITERAL_CSTRING(keyid_var, "keyid");
-  NS_NAMED_LITERAL_CSTRING(signature_var, "p384ecdsa");
-
-  nsAutoString contentSignature;
-  nsAutoString keyId;
-  nsAutoCString header = NS_ConvertUTF16toUTF8(aContentSignatureHeader);
-  nsSecurityHeaderParser parser(header.get());
-  nsresult rv = parser.Parse();
-  if (NS_FAILED(rv)) {
-    CSV_LOG(("ContentVerifier: could not parse ContentSignature header\n"));
-    return NS_ERROR_INVALID_SIGNATURE;
-  }
-  LinkedList<nsSecurityHeaderDirective>* directives = parser.GetDirectives();
-
-  for (nsSecurityHeaderDirective* directive = directives->getFirst();
-       directive != nullptr; directive = directive->getNext()) {
-    CSV_LOG(("ContentVerifier: found directive %s\n", directive->mName.get()));
-    if (directive->mName.Length() == keyid_var.Length() &&
-        directive->mName.EqualsIgnoreCase(keyid_var.get(),
-                                          keyid_var.Length())) {
-      if (!keyId.IsEmpty()) {
-        CSV_LOG(("ContentVerifier: found two keyIds\n"));
-        return NS_ERROR_INVALID_SIGNATURE;
-      }
-
-      CSV_LOG(("ContentVerifier: found a keyid directive\n"));
-      keyId = NS_ConvertUTF8toUTF16(directive->mValue);
-      rv = GetVerificationKey(keyId);
-      NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_SIGNATURE);
-    }
-    if (directive->mName.Length() == signature_var.Length() &&
-        directive->mName.EqualsIgnoreCase(signature_var.get(),
-                                          signature_var.Length())) {
-      if (!contentSignature.IsEmpty()) {
-        CSV_LOG(("ContentVerifier: found two ContentSignatures\n"));
-        return NS_ERROR_INVALID_SIGNATURE;
-      }
-
-      CSV_LOG(("ContentVerifier: found a ContentSignature directive\n"));
-      contentSignature = NS_ConvertUTF8toUTF16(directive->mValue);
-      mSignature = directive->mValue;
-    }
-  }
-
-  // we have to ensure that we found a key and a signature at this point
-  if (mKey.IsEmpty()) {
-    CSV_LOG(("ContentVerifier: got a Content-Signature header but didn't find "
-             "an appropriate key.\n"));
-    return NS_ERROR_INVALID_SIGNATURE;
-  }
-  if (mSignature.IsEmpty()) {
-    CSV_LOG(("ContentVerifier: got a Content-Signature header but didn't find "
-             "a signature.\n"));
-    return NS_ERROR_INVALID_SIGNATURE;
+  // Update the signature verifier if the context has been created.
+  if (mContextCreated) {
+    return mVerifier->Update(mContent.LastElement());
   }
 
   return NS_OK;
 }
 
-/**
- * Parse signature, public key, and algorithm data for input to verification
- * functions in VerifyData and CreateContext.
- *
- * https://datatracker.ietf.org/doc/draft-thomson-http-content-signature/
- * If aSignature is a content signature, the function returns
- * NS_ERROR_INVALID_SIGNATURE if anything goes wrong. Only p384 with sha384
- * is supported and aSignature is a raw signature (r||s).
- */
-nsresult
-ContentVerifier::ParseInput(ScopedSECKEYPublicKey& aPublicKeyOut,
-                            ScopedSECItem& aSignatureItemOut,
-                            SECOidTag& aOidOut,
-                            const nsNSSShutDownPreventionLock&)
+NS_IMETHODIMP
+ContentVerifier::ContextCreated(bool successful)
 {
-  // Base 64 decode the key
-  ScopedSECItem keyItem(::SECITEM_AllocItem(nullptr, nullptr, 0));
-  if (!keyItem ||
-      !NSSBase64_DecodeBuffer(nullptr, keyItem,
-                              mKey.get(),
-                              mKey.Length())) {
-    return NS_ERROR_INVALID_SIGNATURE;
-  }
+  MOZ_ASSERT(NS_IsMainThread());
+  if (!successful) {
+    // If we don't have a next listener, the request has been handed off already.
+    if (!mNextListener) {
+      return NS_OK;
+    }
+    // Get local reference to mNextListener and null it to ensure that we don't
+    // call it twice.
+    nsCOMPtr<nsIStreamListener> nextListener;
+    nextListener.swap(mNextListener);
+
+    // Make sure that OnStartRequest was called and we have a request.
+    MOZ_ASSERT(mContentRequest);
 
-  // Extract the public key from the keyItem
-  ScopedCERTSubjectPublicKeyInfo pki(
-    SECKEY_DecodeDERSubjectPublicKeyInfo(keyItem));
-  if (!pki) {
-    return NS_ERROR_INVALID_SIGNATURE;
-  }
-  aPublicKeyOut = SECKEY_ExtractPublicKey(pki.get());
+    // In this case something went wrong with the cert. Let's stop this load.
+    CSV_LOG(("failed to get a valid cert chain\n"));
+    if (mContentRequest && nextListener) {
+      mContentRequest->Cancel(NS_ERROR_INVALID_SIGNATURE);
+      nsresult rv = nextListener->OnStopRequest(mContentRequest, mContentContext,
+                                                NS_ERROR_INVALID_SIGNATURE);
+      mContentRequest = nullptr;
+      mContentContext = nullptr;
+      return rv;
+    }
 
-  // in case we were not able to extract a key
-  if (!aPublicKeyOut) {
-    return NS_ERROR_INVALID_SIGNATURE;
-  }
-
-  // Base 64 decode the signature
-  ScopedSECItem rawSignatureItem(::SECITEM_AllocItem(nullptr, nullptr, 0));
-  if (!rawSignatureItem ||
-      !NSSBase64_DecodeBuffer(nullptr, rawSignatureItem,
-                              mSignature.get(),
-                              mSignature.Length())) {
-    return NS_ERROR_INVALID_SIGNATURE;
+    // We should never get here!
+    MOZ_ASSERT_UNREACHABLE(
+      "ContentVerifier was used without getting OnStartRequest!");
+    return NS_OK;
   }
 
-  // get signature object and oid
-  if (!aSignatureItemOut) {
-    return NS_ERROR_INVALID_SIGNATURE;
-  }
-  // We have a raw ecdsa signature r||s so we have to DER-encode it first
-  // Note that we have to check rawSignatureItem->len % 2 here as
-  // DSAU_EncodeDerSigWithLen asserts this
-  if (rawSignatureItem->len == 0 || rawSignatureItem->len % 2 != 0) {
-    return NS_ERROR_INVALID_SIGNATURE;
-  }
-  if (DSAU_EncodeDerSigWithLen(aSignatureItemOut, rawSignatureItem,
-                               rawSignatureItem->len) != SECSuccess) {
-    return NS_ERROR_INVALID_SIGNATURE;
-  }
-  aOidOut = SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE;
-
-  return NS_OK;
-}
-
-/**
- * Create a context for a signature verification.
- * It sets signature, public key, and algorithms that should be used to verify
- * the data. It also updates the verification buffer with the content-signature
- * prefix.
- */
-nsresult
-ContentVerifier::CreateContext()
-{
-  nsNSSShutDownPreventionLock locker;
-  if (isAlreadyShutDown()) {
-    return NS_ERROR_INVALID_SIGNATURE;
+  // In this case the content verifier is initialised and we have to feed it
+  // the buffered content.
+  mContextCreated = true;
+  for (size_t i = 0; i < mContent.Length(); ++i) {
+    if (NS_FAILED(mVerifier->Update(mContent[i]))) {
+      // Bail out if this fails. We can't return an error here, but if this
+      // failed, NS_ERROR_INVALID_SIGNATURE is returned in FinishSignature.
+      break;
+    }
   }
 
-  // Bug 769521: We have to change b64 url to regular encoding as long as we
-  // don't have a b64 url decoder. This should change soon, but in the meantime
-  // we have to live with this.
-  mSignature.ReplaceChar('-', '+');
-  mSignature.ReplaceChar('_', '/');
-
-  ScopedSECKEYPublicKey publicKey;
-  ScopedSECItem signatureItem(::SECITEM_AllocItem(nullptr, nullptr, 0));
-  SECOidTag oid;
-  nsresult rv = ParseInput(publicKey, signatureItem, oid, locker);
-  if (NS_FAILED(rv)) {
-    return NS_ERROR_INVALID_SIGNATURE;
-  }
-
-  mCx = UniqueVFYContext(VFY_CreateContext(publicKey, signatureItem, oid, NULL));
-  if (!mCx) {
-    return NS_ERROR_INVALID_SIGNATURE;
-  }
-
-  if (VFY_Begin(mCx.get()) != SECSuccess) {
-    return NS_ERROR_INVALID_SIGNATURE;
-  }
-
-  // add the prefix to the verification buffer
-  return Update(kPREFIX);
-}
-
-/**
- * Add data to the context that should be verified.
- */
-nsresult
-ContentVerifier::Update(const nsACString& aData)
-{
-  nsNSSShutDownPreventionLock locker;
-  if (isAlreadyShutDown()) {
-    return NS_ERROR_INVALID_SIGNATURE;
-  }
-
-  if (!aData.IsEmpty()) {
-    if (VFY_Update(mCx.get(),
-                   (const unsigned char*)nsPromiseFlatCString(aData).get(),
-                   aData.Length()) != SECSuccess) {
-      return NS_ERROR_INVALID_SIGNATURE;
-    }
+  // We read all content, let's verify the signature.
+  if (mContentRead) {
+    FinishSignature();
   }
 
   return NS_OK;
 }
-
-/**
- * Finish signature verification and return the result in _retval.
- */
-nsresult
-ContentVerifier::End(bool* _retval)
-{
-  nsNSSShutDownPreventionLock locker;
-  if (isAlreadyShutDown()) {
-    return NS_ERROR_INVALID_SIGNATURE;
-  }
-
-  *_retval = (VFY_End(mCx.get()) == SECSuccess);
-
-  return NS_OK;
-}
\ No newline at end of file
--- a/dom/security/ContentVerifier.h
+++ b/dom/security/ContentVerifier.h
@@ -2,97 +2,63 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_ContentVerifier_h
 #define mozilla_dom_ContentVerifier_h
 
 #include "nsCOMPtr.h"
+#include "nsIContentSignatureVerifier.h"
 #include "nsIObserver.h"
 #include "nsIStreamListener.h"
-#include "nsNSSShutDown.h"
 #include "nsString.h"
 #include "nsTArray.h"
-#include "ScopedNSSTypes.h"
 
 /**
  * Mediator intercepting OnStartRequest in nsHttpChannel, blocks until all
  * data is read from the input stream, verifies the content signature and
  * releases the request to the next listener if the verification is successful.
  * If the verification fails or anything else goes wrong, a
  * NS_ERROR_INVALID_SIGNATURE is thrown.
  */
 class ContentVerifier : public nsIStreamListener
-                      , public nsNSSShutDownObject
+                      , public nsIContentSignatureReceiverCallback
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSISTREAMLISTENER
   NS_DECL_NSIREQUESTOBSERVER
+  NS_DECL_NSICONTENTSIGNATURERECEIVERCALLBACK
 
   explicit ContentVerifier(nsIStreamListener* aMediatedListener,
                            nsISupports* aMediatedContext)
     : mNextListener(aMediatedListener)
-    , mContext(aMediatedContext)
-    , mCx(nullptr) {}
-
-  nsresult Init(const nsAString& aContentSignatureHeader);
+    , mContextCreated(false)
+    , mContentRead(false) {}
 
-  // nsNSSShutDownObject
-  virtual void virtualDestroyNSSReference() override
-  {
-    destructorSafeDestroyNSSReference();
-  }
+  nsresult Init(const nsACString& aContentSignatureHeader, nsIRequest* aRequest,
+                nsISupports* aContext);
 
 protected:
-  virtual ~ContentVerifier()
-  {
-    nsNSSShutDownPreventionLock locker;
-    if (isAlreadyShutDown()) {
-      return;
-    }
-    destructorSafeDestroyNSSReference();
-    shutdown(calledFromObject);
-  }
-
-  void destructorSafeDestroyNSSReference()
-  {
-    mCx = nullptr;
-  }
+  virtual ~ContentVerifier() {}
 
 private:
-  nsresult ParseContentSignatureHeader(const nsAString& aContentSignatureHeader);
-  nsresult GetVerificationKey(const nsAString& aKeyId);
-
-  // utility function to parse input before put into verification functions
-  nsresult ParseInput(mozilla::ScopedSECKEYPublicKey& aPublicKeyOut,
-                      mozilla::ScopedSECItem& aSignatureItemOut,
-                      SECOidTag& aOidOut,
-                      const nsNSSShutDownPreventionLock&);
+  void FinishSignature();
 
-  // create a verifier context and store it in mCx
-  nsresult CreateContext();
-
-  // Adds data to the context that was used to generate the signature.
-  nsresult Update(const nsACString& aData);
-
-  // Finalises the signature and returns the result of the signature
-  // verification.
-  nsresult End(bool* _retval);
-
+  // buffered content to verify
+  FallibleTArray<nsCString> mContent;
   // content and next listener for nsIStreamListener
   nsCOMPtr<nsIStreamListener> mNextListener;
-  nsCOMPtr<nsISupports> mContext;
-
-  // verifier context for incrementel verifications
-  mozilla::UniqueVFYContext mCx;
-  // buffered content to verify
-  FallibleTArray<nsCString> mContent;
-  // signature to verify
-  nsCString mSignature;
-  // verification key
-  nsCString mKey;
-  // verification key preference
-  nsString mVks;
+  // the verifier
+  nsCOMPtr<nsIContentSignatureVerifier> mVerifier;
+  // holding a pointer to the content request and context to resume/cancel it
+  nsCOMPtr<nsIRequest> mContentRequest;
+  nsCOMPtr<nsISupports> mContentContext;
+  // Semaphors to indicate that the verifying context was created, the entire
+  // content was read resp. The context gets created by ContentSignatureVerifier
+  // and mContextCreated is set in the ContextCreated callback. The content is
+  // read, i.e. mContentRead is set, when the content OnStopRequest is called.
+  bool mContextCreated;
+  bool mContentRead;
 };
 
 #endif /* mozilla_dom_ContentVerifier_h */
--- a/dom/security/nsCSPContext.cpp
+++ b/dom/security/nsCSPContext.cpp
@@ -887,17 +887,17 @@ nsCSPContext::SendReports(nsISupports* a
   for (uint32_t r = 0; r < reportURIs.Length(); r++) {
     nsAutoCString reportURICstring = NS_ConvertUTF16toUTF8(reportURIs[r]);
     // try to create a new uri from every report-uri string
     rv = NS_NewURI(getter_AddRefs(reportURI), reportURIs[r]);
     if (NS_FAILED(rv)) {
       const char16_t* params[] = { reportURIs[r].get() };
       CSPCONTEXTLOG(("Could not create nsIURI for report URI %s",
                      reportURICstring.get()));
-      logToConsole(MOZ_UTF16("triedToSendReport"), params, ArrayLength(params),
+      logToConsole(u"triedToSendReport", params, ArrayLength(params),
                    aSourceFile, aScriptSample, aLineNum, 0, nsIScriptError::errorFlag);
       continue; // don't return yet, there may be more URIs
     }
 
     // try to create a new channel for every report-uri
     nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL | nsIChannel::LOAD_CLASSIFY_URI;
     if (doc) {
       rv = NS_NewChannel(getter_AddRefs(reportChannel),
@@ -928,17 +928,17 @@ nsCSPContext::SendReports(nsISupports* a
 
     // log a warning to console if scheme is not http or https
     bool isHttpScheme =
       (NS_SUCCEEDED(reportURI->SchemeIs("http", &isHttpScheme)) && isHttpScheme) ||
       (NS_SUCCEEDED(reportURI->SchemeIs("https", &isHttpScheme)) && isHttpScheme);
 
     if (!isHttpScheme) {
       const char16_t* params[] = { reportURIs[r].get() };
-      logToConsole(MOZ_UTF16("reportURInotHttpsOrHttp2"), params, ArrayLength(params),
+      logToConsole(u"reportURInotHttpsOrHttp2", params, ArrayLength(params),
                    aSourceFile, aScriptSample, aLineNum, 0, nsIScriptError::errorFlag);
       continue;
     }
 
     // make sure this is an anonymous request (no cookies) so in case the
     // policy URI is injected, it can't be abused for CSRF.
     nsLoadFlags flags;
     rv = reportChannel->GetLoadFlags(&flags);
@@ -991,17 +991,17 @@ nsCSPContext::SendReports(nsISupports* a
     // AsyncOpen should not fail, but could if there's no load group (like if
     // SetRequestContext is not given a channel).  This should fail quietly and
     // not return an error since it's really ok if reports don't go out, but
     // it's good to log the error locally.
 
     if (NS_FAILED(rv)) {
       const char16_t* params[] = { reportURIs[r].get() };
       CSPCONTEXTLOG(("AsyncOpen failed for report URI %s", params[0]));
-      logToConsole(MOZ_UTF16("triedToSendReport"), params, ArrayLength(params),
+      logToConsole(u"triedToSendReport", params, ArrayLength(params),
                    aSourceFile, aScriptSample, aLineNum, 0, nsIScriptError::errorFlag);
     } else {
       CSPCONTEXTLOG(("Sent violation report to URI %s", reportURICstring.get()));
     }
   }
   return NS_OK;
 }
 
@@ -1076,18 +1076,18 @@ class CSPReportSenderRunnable final : pu
       } else if (blockedString) {
         blockedString->GetData(blockedDataStr);
       }
 
       if (blockedDataStr.Length() > 0) {
         nsString blockedDataChar16 = NS_ConvertUTF8toUTF16(blockedDataStr);
         const char16_t* params[] = { mViolatedDirective.get(),
                                      blockedDataChar16.get() };
-        mCSPContext->logToConsole(mReportOnlyFlag ? MOZ_UTF16("CSPROViolationWithURI") :
-                                                    MOZ_UTF16("CSPViolationWithURI"),
+        mCSPContext->logToConsole(mReportOnlyFlag ? u"CSPROViolationWithURI" :
+                                                    u"CSPViolationWithURI",
                                   params, ArrayLength(params), mSourceFile, mScriptSample,
                                   mLineNum, 0, nsIScriptError::errorFlag);
       }
       return NS_OK;
     }
 
   private:
     nsCOMPtr<nsISupports>   mBlockedContentSource;
@@ -1350,17 +1350,17 @@ nsCSPContext::GetCSPSandboxFlags(uint32_
       // continue the loop checking for an enforcement policy.
       nsAutoString policy;
       mPolicies[i]->toString(policy);
 
       CSPCONTEXTLOG(("nsCSPContext::GetCSPSandboxFlags, report only policy, ignoring sandbox in: %s",
                     policy.get()));
 
       const char16_t* params[] = { policy.get() };
-      logToConsole(MOZ_UTF16("ignoringReportOnlyDirective"), params, ArrayLength(params),
+      logToConsole(u"ignoringReportOnlyDirective", params, ArrayLength(params),
                    EmptyString(), EmptyString(), 0, 0, nsIScriptError::warningFlag);
     }
   }
 
   return NS_OK;
 }
 
 /* ========== CSPViolationReportListener implementation ========== */
@@ -1446,17 +1446,17 @@ CSPReportRedirectSink::AsyncOnChannelRed
   nsCOMPtr<nsIURI> uri;
   rv = aOldChannel->GetURI(getter_AddRefs(uri));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
   NS_ASSERTION(observerService, "Observer service required to log CSP violations");
   observerService->NotifyObservers(uri,
                                    CSP_VIOLATION_TOPIC,
-                                   MOZ_UTF16("denied redirect while sending violation report"));
+                                   u"denied redirect while sending violation report");
 
   return NS_BINDING_REDIRECTED;
 }
 
 NS_IMETHODIMP
 CSPReportRedirectSink::GetInterface(const nsIID& aIID, void** aResult)
 {
   if (aIID.Equals(NS_GET_IID(nsINetworkInterceptController)) &&
--- a/dom/security/nsCSPParser.cpp
+++ b/dom/security/nsCSPParser.cpp
@@ -1156,48 +1156,48 @@ nsCSPParser::directive()
 
   CSPPARSERLOG(("nsCSPParser::directive, mCurToken: %s, mCurValue: %s",
                NS_ConvertUTF16toUTF8(mCurToken).get(),
                NS_ConvertUTF16toUTF8(mCurValue).get()));
 
   // Make sure that the directive-srcs-array contains at least
   // one directive and one src.
   if (mCurDir.Length() < 1) {
-    const char16_t* params[] = { MOZ_UTF16("directive missing") };
+    const char16_t* params[] = { u"directive missing" };
     logWarningErrorToConsole(nsIScriptError::warningFlag, "failedToParseUnrecognizedSource",
                              params, ArrayLength(params));
     return;
   }
 
   // Try to create a new CSPDirective
   nsCSPDirective* cspDir = directiveName();
   if (!cspDir) {
     // if we can not create a CSPDirective, we can skip parsing the srcs for that array
     return;
   }
 
   // special case handling for block-all-mixed-content, which is only specified
   // by a directive name but does not include any srcs.
   if (cspDir->equals(nsIContentSecurityPolicy::BLOCK_ALL_MIXED_CONTENT)) {
     if (mCurDir.Length() > 1) {
-      const char16_t* params[] = { MOZ_UTF16("block-all-mixed-content") };
+      const char16_t* params[] = { u"block-all-mixed-content" };
       logWarningErrorToConsole(nsIScriptError::warningFlag,
                                "ignoreSrcForDirective",
                                params, ArrayLength(params));
     }
     // add the directive and return
     mPolicy->addDirective(cspDir);
     return;
   }
 
   // special case handling for upgrade-insecure-requests, which is only specified
   // by a directive name but does not include any srcs.
   if (cspDir->equals(nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE)) {
     if (mCurDir.Length() > 1) {
-      const char16_t* params[] = { MOZ_UTF16("upgrade-insecure-requests") };
+      const char16_t* params[] = { u"upgrade-insecure-requests" };
       logWarningErrorToConsole(nsIScriptError::warningFlag,
                                "ignoreSrcForDirective",
                                params, ArrayLength(params));
     }
     // add the directive and return
     mPolicy->addUpgradeInsecDir(static_cast<nsUpgradeInsecureDirective*>(cspDir));
     return;
   }
@@ -1228,17 +1228,17 @@ nsCSPParser::directive()
   // Ignore unsafe-inline within script-src or style-src if nonce
   // or hash is specified, see:
   // http://www.w3.org/TR/CSP2/#directive-script-src
   if ((cspDir->equals(nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE) ||
        cspDir->equals(nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE)) &&
       mHasHashOrNonce && mUnsafeInlineKeywordSrc) {
     mUnsafeInlineKeywordSrc->invalidate();
     // log to the console that unsafe-inline will be ignored
-    const char16_t* params[] = { MOZ_UTF16("'unsafe-inline'") };
+    const char16_t* params[] = { u"'unsafe-inline'" };
     logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringSrcWithinScriptStyleSrc",
                              params, ArrayLength(params));
   }
 
   // Add the newly created srcs to the directive and add the directive to the policy
   cspDir->addSrcs(srcs);
   mPolicy->addDirective(cspDir);
 }
--- a/dom/security/nsCSPUtils.cpp
+++ b/dom/security/nsCSPUtils.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsAttrValue.h"
+#include "nsCharSeparatedTokenizer.h"
 #include "nsContentUtils.h"
 #include "nsCSPUtils.h"
 #include "nsDebug.h"
 #include "nsIConsoleService.h"
 #include "nsICryptoHash.h"
 #include "nsIScriptError.h"
 #include "nsIServiceManager.h"
 #include "nsIStringBundle.h"
--- a/dom/security/nsMixedContentBlocker.cpp
+++ b/dom/security/nsMixedContentBlocker.cpp
@@ -694,17 +694,17 @@ nsMixedContentBlocker::ShouldLoad(bool a
   if (docShell->GetDocument()->GetBlockAllMixedContent(isPreload)) {
     // log a message to the console before returning.
     nsAutoCString spec;
     rv = aContentLocation->GetSpec(spec);
     NS_ENSURE_SUCCESS(rv, rv);
     NS_ConvertUTF8toUTF16 reportSpec(spec);
 
     const char16_t* params[] = { reportSpec.get()};
-    CSP_LogLocalizedStr(MOZ_UTF16("blockAllMixedContent"),
+    CSP_LogLocalizedStr(u"blockAllMixedContent",
                         params, ArrayLength(params),
                         EmptyString(), // aSourceFile
                         EmptyString(), // aScriptSample
                         0, // aLineNumber
                         0, // aColumnNumber
                         nsIScriptError::errorFlag, "CSP",
                         docShell->GetDocument()->InnerWindowID());
     *aDecision = REJECT_REQUEST;
--- a/dom/security/test/contentverifier/browser.ini
+++ b/dom/security/test/contentverifier/browser.ini
@@ -5,12 +5,15 @@ support-files =
   file_about_newtab_bad.html
   file_about_newtab_bad_csp.html
   file_about_newtab_bad_csp_signature
   file_about_newtab_good_signature
   file_about_newtab_bad_signature
   file_about_newtab_broken_signature
   file_about_newtab_sri.html
   file_about_newtab_sri_signature
+  goodChain.pem
+  head.js
   script.js
   style.css
 
 [browser_verify_content_about_newtab.js]
+[browser_verify_content_about_newtab2.js]
--- a/dom/security/test/contentverifier/browser_verify_content_about_newtab.js
+++ b/dom/security/test/contentverifier/browser_verify_content_about_newtab.js
@@ -1,243 +1,20 @@
-/*
- * Test Content-Signature for remote about:newtab
- *  - Bug 1226928 - allow about:newtab to load remote content
- *
- * This tests content-signature verification on remote about:newtab in the
- * following cases (see TESTS, all failed loads display about:blank fallback):
- * - good case (signature should verify and correct page is displayed)
- * - reload of newtab when the siganture was invalidated after the last correct
- *   load
- * - malformed content-signature header
- * - malformed keyid directive
- * - malformed p384ecdsa directive
- * - wrong signature (this is not a siganture for the delivered document)
- * - invalid signature (this is not even a signature)
- * - loading a file that doesn't fit the key or signature
- * - cache poisoning (load a malicious remote page not in newtab, subsequent
- *   newtab load has to load the fallback)
- */
-
-const ABOUT_NEWTAB_URI = "about:newtab";
-
-const BASE = "https://example.com/browser/dom/security/test/contentverifier/file_contentserver.sjs?";
-const URI_GOOD = BASE + "sig=good&key=good&file=good&header=good";
-
-const INVALIDATE_FILE = BASE + "invalidateFile=yep";
-const VALIDATE_FILE = BASE + "validateFile=yep";
-
-const URI_HEADER_BASE = BASE + "sig=good&key=good&file=good&header=";
-const URI_ERROR_HEADER = URI_HEADER_BASE + "error";
-const URI_KEYERROR_HEADER = URI_HEADER_BASE + "errorInKeyid";
-const URI_SIGERROR_HEADER = URI_HEADER_BASE + "errorInSignature";
-const URI_NO_HEADER = URI_HEADER_BASE + "noHeader";
-
-const URI_BAD_SIG = BASE + "sig=bad&key=good&file=good&header=good";
-const URI_BROKEN_SIG = BASE + "sig=broken&key=good&file=good&header=good";
-const URI_BAD_KEY = BASE + "sig=good&key=bad&file=good&header=good";
-const URI_BAD_FILE = BASE + "sig=good&key=good&file=bad&header=good";
-const URI_BAD_ALL = BASE + "sig=bad&key=bad&file=bad&header=bad";
-const URI_BAD_CSP = BASE + "sig=bad-csp&key=good&file=bad-csp&header=good";
-
-const URI_BAD_FILE_CACHED = BASE + "sig=good&key=good&file=bad&header=good&cached=true";
-
-const GOOD_ABOUT_STRING = "Just a fully good testpage for Bug 1226928";
-const BAD_ABOUT_STRING = "Just a bad testpage for Bug 1226928";
-const ABOUT_BLANK = "<head></head><body></body>";
-
-const URI_CLEANUP = BASE + "cleanup=true";
-const CLEANUP_DONE = "Done";
-
-const URI_SRI = BASE + "sig=sri&key=good&file=sri&header=good";
-const STYLESHEET_WITHOUT_SRI_BLOCKED = "Stylesheet without SRI blocked";
-const STYLESHEET_WITH_SRI_BLOCKED = "Stylesheet with SRI blocked";
-const STYLESHEET_WITH_SRI_LOADED = "Stylesheet with SRI loaded";
-const SCRIPT_WITHOUT_SRI_BLOCKED = "Script without SRI blocked";
-const SCRIPT_WITH_SRI_BLOCKED = "Script with SRI blocked";
-const SCRIPT_WITH_SRI_LOADED = "Script with SRI loaded";
-
-const CSP_TEST_SUCCESS_STRING = "CSP violation test succeeded.";
-
-// Needs to sync with pref "security.signed_content.CSP.default".
-const SIGNED_CONTENT_CSP = `{"csp-policies":[{"report-only":false,"script-src":["https://example.com","'unsafe-inline'"],"style-src":["https://example.com"]}]}`;
 
 const TESTS = [
   // { newtab (aboutURI) or regular load (url) : url,
   //   testStrings : expected strings in the loaded page }
   { "aboutURI" : URI_GOOD, "testStrings" : [GOOD_ABOUT_STRING] },
   { "aboutURI" : URI_ERROR_HEADER, "testStrings" : [ABOUT_BLANK] },
-  { "aboutURI" : URI_KEYERROR_HEADER, "testStrings" : [ABOUT_BLANK] },
-  { "aboutURI" : URI_SIGERROR_HEADER, "testStrings" : [ABOUT_BLANK] },
-  { "aboutURI" : URI_NO_HEADER, "testStrings" : [ABOUT_BLANK] },
-  { "aboutURI" : URI_BAD_SIG, "testStrings" : [ABOUT_BLANK] },
-  { "aboutURI" : URI_BROKEN_SIG, "testStrings" : [ABOUT_BLANK] },
-  { "aboutURI" : URI_BAD_KEY, "testStrings" : [ABOUT_BLANK] },
-  { "aboutURI" : URI_BAD_FILE, "testStrings" : [ABOUT_BLANK] },
-  { "aboutURI" : URI_BAD_ALL, "testStrings" : [ABOUT_BLANK] },
   { "url" : URI_BAD_FILE_CACHED, "testStrings" : [BAD_ABOUT_STRING] },
   { "aboutURI" : URI_BAD_FILE_CACHED, "testStrings" : [ABOUT_BLANK] },
   { "aboutURI" : URI_GOOD, "testStrings" : [GOOD_ABOUT_STRING] },
   { "aboutURI" : URI_SRI, "testStrings" : [
     STYLESHEET_WITHOUT_SRI_BLOCKED,
     STYLESHEET_WITH_SRI_LOADED,
     SCRIPT_WITHOUT_SRI_BLOCKED,
     SCRIPT_WITH_SRI_LOADED,
     ]},
   { "aboutURI" : URI_BAD_CSP, "testStrings" : [CSP_TEST_SUCCESS_STRING] },
   { "url" : URI_CLEANUP, "testStrings" : [CLEANUP_DONE] },
 ];
 
-var browser = null;
-var aboutNewTabService = Cc["@mozilla.org/browser/aboutnewtab-service;1"]
-                           .getService(Ci.nsIAboutNewTabService);
-
-function pushPrefs(...aPrefs) {
-  return new Promise((resolve) => {
-    SpecialPowers.pushPrefEnv({"set": aPrefs}, resolve);
-  });
-}
-
-/*
- * run tests with input from TESTS
- */
-function doTest(aExpectedStrings, reload, aUrl, aNewTabPref) {
-  // set about:newtab location for this test if it's a newtab test
-  if (aNewTabPref) {
-    aboutNewTabService.newTabURL = aNewTabPref;
-  }
-
-  // set prefs
-  yield pushPrefs(
-      ["browser.newtabpage.remote.content-signing-test", true],
-      ["browser.newtabpage.remote", true], [
-        "browser.newtabpage.remote.keys",
-        "RemoteNewTabNightlyv0=BO9QHuP6E2eLKybql8iuD4o4Np9YFDfW3D+k" +
-        "a70EcXXTqZcikc7Am1CwyP1xBDTpEoe6gb9SWzJmaDW3dNh1av2u90VkUM" +
-        "B7aHIrImjTjLNg/1oC8GRcTKM4+WzbKF00iA==;OtherKey=eKQJ2fNSId" +
-        "CFzL6N326EzZ/5LCeFU5eyq3enwZ5MLmvOw+3gycr4ZVRc36/EiSPsQYHE" +
-        "3JvJs1EKs0QCaguHFOZsHwqXMPicwp/gLdeYbuOmN2s1SEf/cxw8GtcxSA" +
-        "kG;RemoteNewTab=MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE4k3FmG7dFo" +
-        "Ot3Tuzl76abTRtK8sb/r/ibCSeVKa96RbrOX2ciscz/TT8wfqBYS/8cN4z" +
-        "Me1+f7wRmkNrCUojZR1ZKmYM2BeiUOMlMoqk2O7+uwsn1DwNQSYP58TkvZt6"
-      ]);
-
-  if (aNewTabPref === URI_BAD_CSP) {
-    // Use stricter CSP to test CSP violation.
-    yield pushPrefs(["security.signed_content.CSP.default", "script-src 'self'; style-src 'self'"]);
-  } else {
-    // Use weaker CSP to test normal content.
-    yield pushPrefs(["security.signed_content.CSP.default", "script-src 'self' 'unsafe-inline'; style-src 'self'"]);
-  }
-
-  // start the test
-  yield BrowserTestUtils.withNewTab({
-      gBrowser,
-      url: aUrl,
-    },
-    function * (browser) {
-      // check if everything's set correct for testing
-      ok(Services.prefs.getBoolPref(
-          "browser.newtabpage.remote.content-signing-test"),
-          "sanity check: remote newtab signing test should be used");
-      ok(Services.prefs.getBoolPref("browser.newtabpage.remote"),
-          "sanity check: remote newtab should be used");
-      // we only check this if we really do a newtab test
-      if (aNewTabPref) {
-        ok(aboutNewTabService.overridden,
-            "sanity check: default URL for about:newtab should be overriden");
-        is(aboutNewTabService.newTabURL, aNewTabPref,
-            "sanity check: default URL for about:newtab should return the new URL");
-      }
-
-      // Every valid remote newtab page must have built-in CSP.
-      let shouldHaveCSP = ((aUrl === ABOUT_NEWTAB_URI) &&
-                          (aNewTabPref === URI_GOOD || aNewTabPref === URI_SRI));
-
-      if (shouldHaveCSP) {
-        is(browser.contentDocument.nodePrincipal.cspJSON, SIGNED_CONTENT_CSP,
-           "Valid remote newtab page must have built-in CSP.");
-      }
-
-      yield ContentTask.spawn(
-          browser, aExpectedStrings, function * (aExpectedStrings) {
-            for (let expectedString of aExpectedStrings) {
-              ok(content.document.documentElement.innerHTML.includes(expectedString),
-                 "Expect the following value in the result\n" + expectedString +
-                 "\nand got " + content.document.documentElement.innerHTML);
-            }
-          });
-
-      // for good test cases we check if a reload fails if the remote page
-      // changed from valid to invalid in the meantime
-      if (reload) {
-        yield BrowserTestUtils.withNewTab({
-            gBrowser,
-            url: INVALIDATE_FILE,
-          },
-          function * (browser2) {
-            yield ContentTask.spawn(browser2, null, function * () {
-              ok(content.document.documentElement.innerHTML.includes("Done"),
-                 "Expect the following value in the result\n" + "Done" +
-                 "\nand got " + content.document.documentElement.innerHTML);
-            });
-          }
-        );
-
-        browser.reload();
-        yield BrowserTestUtils.browserLoaded(browser);
-
-        let expectedStrings = [ABOUT_BLANK];
-        if (aNewTabPref == URI_SRI) {
-          expectedStrings = [
-            STYLESHEET_WITHOUT_SRI_BLOCKED,
-            STYLESHEET_WITH_SRI_BLOCKED,
-            SCRIPT_WITHOUT_SRI_BLOCKED,
-            SCRIPT_WITH_SRI_BLOCKED
-          ];
-        }
-        yield ContentTask.spawn(browser, expectedStrings,
-          function * (expectedStrings) {
-            for (let expectedString of expectedStrings) {
-              ok(content.document.documentElement.innerHTML.includes(expectedString),
-                 "Expect the following value in the result\n" + expectedString +
-                 "\nand got " + content.document.documentElement.innerHTML);
-            }
-          }
-        );
-
-        yield BrowserTestUtils.withNewTab({
-            gBrowser,
-            url: VALIDATE_FILE,
-          },
-          function * (browser2) {
-            yield ContentTask.spawn(browser2, null, function * () {
-              ok(content.document.documentElement.innerHTML.includes("Done"),
-                 "Expect the following value in the result\n" + "Done" +
-                 "\nand got " + content.document.documentElement.innerHTML);
-              });
-          }
-        );
-      }
-    }
-  );
-}
-
-add_task(function * test() {
-  // run tests from TESTS
-  for (let i = 0; i < TESTS.length; i++) {
-    let testCase = TESTS[i];
-    let url = "", aNewTabPref = "";
-    let reload = false;
-    var aExpectedStrings = testCase.testStrings;
-    if (testCase.aboutURI) {
-      url = ABOUT_NEWTAB_URI;
-      aNewTabPref = testCase.aboutURI;
-      if (aNewTabPref == URI_GOOD || aNewTabPref == URI_SRI) {
-        reload = true;
-      }
-    } else {
-      url = testCase.url;
-    }
-
-    yield doTest(aExpectedStrings, reload, url, aNewTabPref);
-  }
-});
+add_task(runTests);
new file mode 100644
--- /dev/null
+++ b/dom/security/test/contentverifier/browser_verify_content_about_newtab2.js
@@ -0,0 +1,19 @@
+
+const TESTS = [
+  // { newtab (aboutURI) or regular load (url) : url,
+  //   testStrings : expected strings in the loaded page }
+  { "aboutURI" : URI_GOOD, "testStrings" : [GOOD_ABOUT_STRING] },
+  { "aboutURI" : URI_ERROR_HEADER, "testStrings" : [ABOUT_BLANK] },
+  { "aboutURI" : URI_KEYERROR_HEADER, "testStrings" : [ABOUT_BLANK] },
+  { "aboutURI" : URI_SIGERROR_HEADER, "testStrings" : [ABOUT_BLANK] },
+  { "aboutURI" : URI_NO_HEADER, "testStrings" : [ABOUT_BLANK] },
+  { "aboutURI" : URI_BAD_SIG, "testStrings" : [ABOUT_BLANK] },
+  { "aboutURI" : URI_BROKEN_SIG, "testStrings" : [ABOUT_BLANK] },
+  { "aboutURI" : URI_BAD_X5U, "testStrings" : [ABOUT_BLANK] },
+  { "aboutURI" : URI_HTTP_X5U, "testStrings" : [ABOUT_BLANK] },
+  { "aboutURI" : URI_BAD_FILE, "testStrings" : [ABOUT_BLANK] },
+  { "aboutURI" : URI_BAD_ALL, "testStrings" : [ABOUT_BLANK] },
+  { "url" : URI_CLEANUP, "testStrings" : [CLEANUP_DONE] },
+];
+
+add_task(runTests);
--- a/dom/security/test/contentverifier/file_about_newtab_bad_csp_signature
+++ b/dom/security/test/contentverifier/file_about_newtab_bad_csp_signature
@@ -1,1 +1,1 @@
-8qXVAqzuF3TsF6C750u_v_JiRu90WJXf_0xT9x0S4Fgmvolgtfu-KSWq3lYpmk2dxO8u64zaHM3iguZdWAqcSL82RFtV7OPiprt16omCbHCKfVi-Bt_rXILRlexgmRl_
\ No newline at end of file
+oiypz3lb-IyJsmKNsnlp2zDrqncste8yONn9WUE6ksgJWMhSEQ9lp8vRqN0W3JPwJb6uSk16RI-tDv7uy0jxon5jL1BZpqlqIpvimg7FCQEedMKoHZwtE9an-e95sOTd
\ No newline at end of file
--- a/dom/security/test/contentverifier/file_about_newtab_good_signature
+++ b/dom/security/test/contentverifier/file_about_newtab_good_signature
@@ -1,1 +1,1 @@
-XBKzej3i6TAFZc3VZsuCekn-4dYWJBE4-b3OOtKrOV-JIzIvAnAhnOV1aj-kEm07kh-FciIxV-Xk2QUQlRQzHO7oW7E4mXkMKkbbAcvL0CFrItTObhfhKnBnpAE9ql1O
\ No newline at end of file
+-mqpvTYdZX4HYQDW1nScojL7ICw5yj8UF2gzxyLbSCx9UIfHH-gWZ40F_PFtqjHxoC1J3dHDb3VedVhOYczdaLrNKbRvPrlnkdGx7Rl8qEBrtZpF1py1Z9uAGoCrgUHa
\ No newline at end of file
--- a/dom/security/test/contentverifier/file_about_newtab_sri_signature
+++ b/dom/security/test/contentverifier/file_about_newtab_sri_signature
@@ -1,1 +1,1 @@
-i5jOnrZWwyNwrTcIjfJ6fUR-8MhhvhtMvQbdrUD7j8aHTybNolv25v9NwJAT6rVU6kgkxmD_st9Kla086CQmzYQdLhKfzgLbTDXz0-1j23fQnyjsP1_4MNIu2xTea11p
\ No newline at end of file
+yoIyAYiiEzdP1zpkRy3KaqdsjUy62Notku89cytwVwcH0x6fKsMCdM-df1wbk9N28CSTaIOW5kcSenFy5K3nU-zPIoqZDjQo6aSjF8hF6lrw1a1xbhfl9K3g4YJsuWsO
\ No newline at end of file
--- a/dom/security/test/contentverifier/file_contentserver.sjs
+++ b/dom/security/test/contentverifier/file_contentserver.sjs
@@ -1,37 +1,51 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 // sjs for remote about:newtab (bug 1226928)
+"use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 Cu.import("resource://gre/modules/NetUtil.jsm");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.importGlobalProperties(["URLSearchParams"]);
 
 const path = "browser/dom/security/test/contentverifier/";
 
 const goodFileName = "file_about_newtab.html";
 const goodFileBase = path + goodFileName;
 const goodFile = FileUtils.getDir("TmpD", [], true);
 goodFile.append(goodFileName);
 const goodSignature = path + "file_about_newtab_good_signature";
-const goodKeyId = "RemoteNewTab";
+const goodX5UString = "\"https://example.com/browser/dom/security/test/contentverifier/file_contentserver.sjs?x5u=default\"";
 
 const scriptFileName = "script.js";
 const cssFileName = "style.css";
 const badFile = path + "file_about_newtab_bad.html";
 const brokenSignature = path + "file_about_newtab_broken_signature";
 const badSignature = path + "file_about_newtab_bad_signature";
-const badKeyId = "OldRemoteNewTabKey";
+const badX5UString = "\"https://example.com/browser/dom/security/test/contentverifier/file_contentserver.sjs?x5u=bad\"";
+const httpX5UString = "\"http://example.com/browser/dom/security/test/contentverifier/file_contentserver.sjs?x5u=default\"";
 
 const sriFile = path + "file_about_newtab_sri.html";
 const sriSignature = path + "file_about_newtab_sri_signature";
 
 const badCspFile = path + "file_about_newtab_bad_csp.html";
 const badCspSignature = path + "file_about_newtab_bad_csp_signature";
 
+// This cert chain is copied from
+// security/manager/ssl/tests/unit/test_content_signing/
+// using the certificates
+// * content_signing_remote_newtab_ee.pem
+// * content_signing_int.pem
+// * content_signing_root.pem
+const goodCertChainPath = path + "goodChain.pem";
+
 const tempFileNames = [goodFileName, scriptFileName, cssFileName];
 
 // we copy the file to serve as newtab to a temp directory because
 // we modify it during tests.
 setupTestFiles();
 
 function setupTestFiles() {
   for (let fileName of tempFileNames) {
@@ -110,24 +124,25 @@ function cleanupTestFiles() {
  * sig=good&key=good&file=good&header=good&cached=no to serve pages with
  * content signatures
  *
  * it further handles invalidateFile=yep and validateFile=yep to change the
  * served file
  */
 function handleRequest(request, response) {
   let params = new URLSearchParams(request.queryString);
-  let keyType = params.get("key");
+  let x5uType = params.get("x5u");
   let signatureType = params.get("sig");
   let fileType = params.get("file");
   let headerType = params.get("header");
   let cached = params.get("cached");
   let invalidateFile = params.get("invalidateFile");
   let validateFile = params.get("validateFile");
   let resource = params.get("resource");
+  let x5uParam = params.get("x5u");
 
   if (params.get("cleanup")) {
     cleanupTestFiles();
     response.setHeader("Content-Type", "text/html", false);
     response.write("Done");
     return;
   }
 
@@ -166,36 +181,46 @@ function handleRequest(request, response
         r = "Error";
       }
     }
     response.setHeader("Content-Type", "text/html", false);
     response.write(r);
     return;
   }
 
+  // we have to return the certificate chain on request for the x5u parameter
+  if (x5uParam && x5uParam == "default") {
+    response.setHeader("Cache-Control", "max-age=216000", false);
+    response.setHeader("Content-Type", "text/plain", false);
+    response.write(loadFile(getFileName(goodCertChainPath, "CurWorkD")));
+    return;
+  }
+
   // avoid confusing cache behaviours
   if (!cached) {
     response.setHeader("Cache-Control", "no-cache", false);
   } else {
     response.setHeader("Cache-Control", "max-age=3600", false);
   }
 
   // send HTML to test allowed/blocked behaviours
   response.setHeader("Content-Type", "text/html", false);
 
   // set signature header and key for Content-Signature header
   /* By default a good content-signature header is returned. Any broken return
    * value has to be indicated in the url.
    */
   let csHeader = "";
-  let keyId = goodKeyId;
+  let x5uString = goodX5UString;
   let signature = goodSignature;
   let file = goodFile;
-  if (keyType == "bad") {
-    keyId = badKeyId;
+  if (x5uType == "bad") {
+    x5uString = badX5UString;
+  } else if (x5uType == "http") {
+    x5uString = httpX5UString;
   }
   if (signatureType == "bad") {
     signature = badSignature;
   } else if (signatureType == "broken") {
     signature = brokenSignature;
   } else if (signatureType == "sri") {
     signature = sriSignature;
   } else if (signatureType == "bad-csp") {
@@ -206,29 +231,29 @@ function handleRequest(request, response
   } else if (fileType == "sri") {
     file = getFileName(sriFile, "CurWorkD");
   } else if (fileType == "bad-csp") {
     file = getFileName(badCspFile, "CurWorkD");
   }
 
   if (headerType == "good") {
     // a valid content-signature header
-    csHeader = "keyid=" + keyId + ";p384ecdsa=" +
+    csHeader = "x5u=" + x5uString + ";p384ecdsa=" +
                loadFile(getFileName(signature, "CurWorkD"));
   } else if (headerType == "error") {
     // this content-signature header is missing ; before p384ecdsa
-    csHeader = "keyid=" + keyId + "p384ecdsa=" +
+    csHeader = "x5u=" + x5uString + "p384ecdsa=" +
                loadFile(getFileName(signature, "CurWorkD"));
-  } else if (headerType == "errorInKeyid") {
+  } else if (headerType == "errorInX5U") {
     // this content-signature header is missing the keyid directive
-    csHeader = "keid=" + keyId + ";p384ecdsa=" +
+    csHeader = "x6u=" + x5uString + ";p384ecdsa=" +
                loadFile(getFileName(signature, "CurWorkD"));
   } else if (headerType == "errorInSignature") {
     // this content-signature header is missing the p384ecdsa directive
-    csHeader = "keyid=" + keyId + ";p385ecdsa=" +
+    csHeader = "x5u=" + x5uString + ";p385ecdsa=" +
                loadFile(getFileName(signature, "CurWorkD"));
   }
 
   if (csHeader) {
     response.setHeader("Content-Signature", csHeader, false);
   }
   let result = loadFile(file);
 
new file mode 100644
--- /dev/null
+++ b/dom/security/test/contentverifier/goodChain.pem
@@ -0,0 +1,51 @@
+-----BEGIN CERTIFICATE-----
+MIICSTCCATOgAwIBAgIUWQzTTfKLNZgX5ngi/ENiI2DO2kowCwYJKoZIhvcNAQEL
+MBExDzANBgNVBAMMBmludC1DQTAiGA8yMDE0MTEyNzAwMDAwMFoYDzIwMTcwMjA0
+MDAwMDAwWjAUMRIwEAYDVQQDDAllZS1pbnQtQ0EwdjAQBgcqhkjOPQIBBgUrgQQA
+IgNiAAShaHJDNitcexiJ83kVRhWhxz+0je6GPgIpFdtgjiUt5LcTLajOmOgxU05q
+nAwLCcjWOa3oMgbluoE0c6EfozDgXajJbkOD/ieHPalxA74oiM/wAvBa9xof3cyD
+dKpuqc6jRDBCMBMGA1UdJQQMMAoGCCsGAQUFBwMDMCsGA1UdEQQkMCKCIHJlbW90
+ZS1uZXd0YWItc2lnbmVyLm1vemlsbGEub3JnMAsGCSqGSIb3DQEBCwOCAQEAc2nE
+feYpA8WFyiPfZi56NgVgc8kXSKRNgplDtBHXK7gT7ICNQTSKkt+zHxnS9tAoXoix
+OGKsyp/8LNIYGMr4vHVNyOGnxuiLzAYjmDxXhp3t36xOFlU5Y7UaKf9G4feMXrNH
++q1SPYlP84keo1MaC5yhTZTTmJMKkRBsCbIVhfDnL3BUczxVZmk9F+7qK/trL222
+RoAaTZW5hdXUZrX630CYs1sQHWgL0B5rg2y9bwFk7toQ34JbjS0Z25e/MZUtFz19
+5tSjAZQHlLE6fAYZ3knrxF9xVMJCZf7gQqVphJzBtgy9yvTAtlMsrf6XS6sRRngz
+27HBxIpd4tYniYrtfg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIC0TCCAbugAwIBAgIULYyr3v/0zZ+XiR22NH7hOcnj2FcwCwYJKoZIhvcNAQEL
+MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTQxMTI3MDAwMDAwWhgPMjAxNzAyMDQwMDAw
+MDBaMBExDzANBgNVBAMMBmludC1DQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72x
+nAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lM
+wmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF
+4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20
+yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xx
+j5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMlMCMwDAYDVR0TBAUwAwEB/zAT
+BgNVHSUEDDAKBggrBgEFBQcDAzALBgkqhkiG9w0BAQsDggEBADfRBKSM08JF6vqz
+0EA+KNc0XIEAWApuHuwX6XXWeLgo6QN4E/9qfrsaO+C366WT+JDsjDOi40wW46SA
+XbguxtZQeZasNDUWp/leZix4RSJoHB7OllG1rgZJfN76zKVaXRGUmyQObkMMOJZe
+wIA0OBURT8ik9Z89pD0IWrqscds71Edfjt0hHgg63wVvIaklReZXvFOD3VmSCPNn
+2wB6ZzECcbhJpnzxZdsoMSGH0C6apYnNNTjqZjO90JVm/Ph/7nbi/KncYXA6ccl6
+Jz2mfiAquWIua2+CzBGbqjZVSATTpWCp+cXQJE1xka+hWUaL5HPTq1bTULRFlauZ
+HGl5lJk=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICzTCCAbegAwIBAgIUIVkGGA8HiO3RIKGjdOjVi+d6EVkwCwYJKoZIhvcNAQEL
+MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTQxMTI3MDAwMDAwWhgPMjAxNzAyMDQwMDAw
+MDBaMA0xCzAJBgNVBAMMAmNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptu
+Gobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO
+7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgf
+qDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/yt
+HSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcx
+uLP+SSP6clHEMdUDrNoYCjXtjQIDAQABoyUwIzAMBgNVHRMEBTADAQH/MBMGA1Ud
+JQQMMAoGCCsGAQUFBwMDMAsGCSqGSIb3DQEBCwOCAQEAlpbRzRIPnf43AwGfMvKP
+zOtntRy2nE9GlmY9I00uioHUnUrPLs8aw3UDtyiDWMGqcYysXGx9EX2Vk0POS4gf
+G6PA95F6GxTtbzIEZmTPVuzA/cfc9HU3HXDPqh+dySJ8/Ta4c4vX1lgeGGAvstNe
+q+9DaCGXs8MqMF8KtXNmOm3eS9q622hKEvTVEoxqj1t365kwKHaNpbObddQ6Xcny
+akvfh2L+8QbJSflcm8fL/JTup/2/cRG1ytOsaiXEr9JBEITOtQO0Ot/4Qzq+MJjv
+weaJ3hZ0c+cTy3tEvt+I7+lnW4Q5dB7aLR2/BZfLubhxz1SUVMuHfLH64fc0Uf1Q
+Nw==
+-----END CERTIFICATE-----
new file mode 100644
--- /dev/null
+++ b/dom/security/test/contentverifier/head.js
@@ -0,0 +1,210 @@
+/*
+ * Test Content-Signature for remote about:newtab
+ *  - Bug 1226928 - allow about:newtab to load remote content
+ *
+ * This tests content-signature verification on remote about:newtab in the
+ * following cases (see TESTS, all failed loads display about:blank fallback):
+ * - good case (signature should verify and correct page is displayed)
+ * - reload of newtab when the siganture was invalidated after the last correct
+ *   load
+ * - malformed content-signature header
+ * - malformed keyid directive
+ * - malformed p384ecdsa directive
+ * - wrong signature (this is not a siganture for the delivered document)
+ * - invalid signature (this is not even a signature)
+ * - loading a file that doesn't fit the key or signature
+ * - cache poisoning (load a malicious remote page not in newtab, subsequent
+ *   newtab load has to load the fallback)
+ */
+
+const ABOUT_NEWTAB_URI = "about:newtab";
+
+const BASE = "https://example.com/browser/dom/security/test/contentverifier/file_contentserver.sjs?";
+const URI_GOOD = BASE + "sig=good&x5u=good&file=good&header=good";
+
+const INVALIDATE_FILE = BASE + "invalidateFile=yep";
+const VALIDATE_FILE = BASE + "validateFile=yep";
+
+const URI_HEADER_BASE = BASE + "sig=good&x5u=good&file=good&header=";
+const URI_ERROR_HEADER = URI_HEADER_BASE + "error";
+const URI_KEYERROR_HEADER = URI_HEADER_BASE + "errorInX5U";
+const URI_SIGERROR_HEADER = URI_HEADER_BASE + "errorInSignature";
+const URI_NO_HEADER = URI_HEADER_BASE + "noHeader";
+
+const URI_BAD_SIG = BASE + "sig=bad&x5u=good&file=good&header=good";
+const URI_BROKEN_SIG = BASE + "sig=broken&x5u=good&file=good&header=good";
+const URI_BAD_X5U = BASE + "sig=good&x5u=bad&file=good&header=good";
+const URI_HTTP_X5U = BASE + "sig=good&x5u=http&file=good&header=good";
+const URI_BAD_FILE = BASE + "sig=good&x5u=good&file=bad&header=good";
+const URI_BAD_ALL = BASE + "sig=bad&x5u=bad&file=bad&header=bad";
+const URI_BAD_CSP = BASE + "sig=bad-csp&x5u=good&file=bad-csp&header=good";
+
+const URI_BAD_FILE_CACHED = BASE + "sig=good&x5u=good&file=bad&header=good&cached=true";
+
+const GOOD_ABOUT_STRING = "Just a fully good testpage for Bug 1226928";
+const BAD_ABOUT_STRING = "Just a bad testpage for Bug 1226928";
+const ABOUT_BLANK = "<head></head><body></body>";
+
+const URI_CLEANUP = BASE + "cleanup=true";
+const CLEANUP_DONE = "Done";
+
+const URI_SRI = BASE + "sig=sri&x5u=good&file=sri&header=good";
+const STYLESHEET_WITHOUT_SRI_BLOCKED = "Stylesheet without SRI blocked";
+const STYLESHEET_WITH_SRI_BLOCKED = "Stylesheet with SRI blocked";
+const STYLESHEET_WITH_SRI_LOADED = "Stylesheet with SRI loaded";
+const SCRIPT_WITHOUT_SRI_BLOCKED = "Script without SRI blocked";
+const SCRIPT_WITH_SRI_BLOCKED = "Script with SRI blocked";
+const SCRIPT_WITH_SRI_LOADED = "Script with SRI loaded";
+
+const CSP_TEST_SUCCESS_STRING = "CSP violation test succeeded.";
+
+// Needs to sync with pref "security.signed_content.CSP.default".
+const SIGNED_CONTENT_CSP = `{"csp-policies":[{"report-only":false,"script-src":["https://example.com","'unsafe-inline'"],"style-src":["https://example.com"]}]}`;
+
+var browser = null;
+var aboutNewTabService = Cc["@mozilla.org/browser/aboutnewtab-service;1"]
+                           .getService(Ci.nsIAboutNewTabService);
+
+function pushPrefs(...aPrefs) {
+  return new Promise((resolve) => {
+    SpecialPowers.pushPrefEnv({"set": aPrefs}, resolve);
+  });
+}
+
+/*
+ * run tests with input from TESTS
+ */
+function doTest(aExpectedStrings, reload, aUrl, aNewTabPref) {
+  // set about:newtab location for this test if it's a newtab test
+  if (aNewTabPref) {
+    aboutNewTabService.newTabURL = aNewTabPref;
+  }
+
+  // set prefs
+  yield pushPrefs(
+      ["browser.newtabpage.remote.content-signing-test", true],
+      ["browser.newtabpage.remote", true],
+      ["security.content.signature.root_hash",
+       "65:AE:D8:1E:B5:12:AE:B0:6B:38:58:BC:7C:47:35:3D:D4:EA:25:F1:63:DA:08:BB:86:3A:2E:97:39:66:8F:55"]);
+
+  if (aNewTabPref === URI_BAD_CSP) {
+    // Use stricter CSP to test CSP violation.
+    yield pushPrefs(["security.signed_content.CSP.default", "script-src 'self'; style-src 'self'"]);
+  } else {
+    // Use weaker CSP to test normal content.
+    yield pushPrefs(["security.signed_content.CSP.default", "script-src 'self' 'unsafe-inline'; style-src 'self'"]);
+  }
+
+  // start the test
+  yield BrowserTestUtils.withNewTab({
+      gBrowser,
+      url: aUrl,
+    },
+    function * (browser) {
+      // check if everything's set correct for testing
+      ok(Services.prefs.getBoolPref(
+          "browser.newtabpage.remote.content-signing-test"),
+          "sanity check: remote newtab signing test should be used");
+      ok(Services.prefs.getBoolPref("browser.newtabpage.remote"),
+          "sanity check: remote newtab should be used");
+      // we only check this if we really do a newtab test
+      if (aNewTabPref) {
+        ok(aboutNewTabService.overridden,
+            "sanity check: default URL for about:newtab should be overriden");
+        is(aboutNewTabService.newTabURL, aNewTabPref,
+            "sanity check: default URL for about:newtab should return the new URL");
+      }
+
+      // Every valid remote newtab page must have built-in CSP.
+      let shouldHaveCSP = ((aUrl === ABOUT_NEWTAB_URI) &&
+                          (aNewTabPref === URI_GOOD || aNewTabPref === URI_SRI));
+
+      if (shouldHaveCSP) {
+        is(browser.contentDocument.nodePrincipal.cspJSON, SIGNED_CONTENT_CSP,
+           "Valid remote newtab page must have built-in CSP.");
+      }
+
+      yield ContentTask.spawn(
+          browser, aExpectedStrings, function * (aExpectedStrings) {
+            for (let expectedString of aExpectedStrings) {
+              ok(content.document.documentElement.innerHTML.includes(expectedString),
+                 "Expect the following value in the result\n" + expectedString +
+                 "\nand got " + content.document.documentElement.innerHTML);
+            }
+          });
+
+      // for good test cases we check if a reload fails if the remote page
+      // changed from valid to invalid in the meantime
+      if (reload) {
+        yield BrowserTestUtils.withNewTab({
+            gBrowser,
+            url: INVALIDATE_FILE,
+          },
+          function * (browser2) {
+            yield ContentTask.spawn(browser2, null, function * () {
+              ok(content.document.documentElement.innerHTML.includes("Done"),
+                 "Expect the following value in the result\n" + "Done" +
+                 "\nand got " + content.document.documentElement.innerHTML);
+            });
+          }
+        );
+
+        browser.reload();
+        yield BrowserTestUtils.browserLoaded(browser);
+
+        let expectedStrings = [ABOUT_BLANK];
+        if (aNewTabPref == URI_SRI) {
+          expectedStrings = [
+            STYLESHEET_WITHOUT_SRI_BLOCKED,
+            STYLESHEET_WITH_SRI_BLOCKED,
+            SCRIPT_WITHOUT_SRI_BLOCKED,
+            SCRIPT_WITH_SRI_BLOCKED
+          ];
+        }
+        yield ContentTask.spawn(browser, expectedStrings,
+          function * (expectedStrings) {
+            for (let expectedString of expectedStrings) {
+              ok(content.document.documentElement.innerHTML.includes(expectedString),
+                 "Expect the following value in the result\n" + expectedString +
+                 "\nand got " + content.document.documentElement.innerHTML);
+            }
+          }
+        );
+
+        yield BrowserTestUtils.withNewTab({
+            gBrowser,
+            url: VALIDATE_FILE,
+          },
+          function * (browser2) {
+            yield ContentTask.spawn(browser2, null, function * () {
+              ok(content.document.documentElement.innerHTML.includes("Done"),
+                 "Expect the following value in the result\n" + "Done" +
+                 "\nand got " + content.document.documentElement.innerHTML);
+              });
+          }
+        );
+      }
+    }
+  );
+}
+
+function runTests() {
+  // run tests from TESTS
+  for (let i = 0; i < TESTS.length; i++) {
+    let testCase = TESTS[i];
+    let url = "", aNewTabPref = "";
+    let reload = false;
+    var aExpectedStrings = testCase.testStrings;
+    if (testCase.aboutURI) {
+      url = ABOUT_NEWTAB_URI;
+      aNewTabPref = testCase.aboutURI;
+      if (aNewTabPref == URI_GOOD || aNewTabPref == URI_SRI) {
+        reload = true;
+      }
+    } else {
+      url = testCase.url;
+    }
+
+    yield doTest(aExpectedStrings, reload, url, aNewTabPref);
+  }
+}
--- a/dom/storage/DOMStorage.cpp
+++ b/dom/storage/DOMStorage.cpp
@@ -213,18 +213,18 @@ DOMStorage::BroadcastChangeNotification(
   // Note, this DOM event should never reach JS. It is cloned later in
   // nsGlobalWindow.
   RefPtr<StorageEvent> event =
     StorageEvent::Constructor(nullptr, NS_LITERAL_STRING("storage"), dict);
 
   RefPtr<StorageNotifierRunnable> r =
     new StorageNotifierRunnable(event,
                                 GetType() == LocalStorage
-                                  ? MOZ_UTF16("localStorage")
-                                  : MOZ_UTF16("sessionStorage"));
+                                  ? u"localStorage"
+                                  : u"sessionStorage");
   NS_DispatchToMainThread(r);
 }
 
 static const char kPermissionType[] = "cookie";
 static const char kStorageEnabled[] = "dom.storage.enabled";
 
 // static, public
 bool
--- a/dom/svg/SVGLength.cpp
+++ b/dom/svg/SVGLength.cpp
@@ -20,17 +20,17 @@ namespace mozilla {
 static void GetUnitString(nsAString& unit, uint16_t unitType);
 static uint16_t GetUnitTypeForString(const nsAString& unitStr);
 
 void
 SVGLength::GetValueAsString(nsAString &aValue) const
 {
   char16_t buf[24];
   nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
-                            MOZ_UTF16("%g"),
+                            u"%g",
                             (double)mValue);
   aValue.Assign(buf);
 
   nsAutoString unitString;
   GetUnitString(unitString, mUnit);
   aValue.Append(unitString);
 }
 
--- a/dom/svg/SVGNumberList.cpp
+++ b/dom/svg/SVGNumberList.cpp
@@ -28,17 +28,17 @@ SVGNumberList::GetValueAsString(nsAStrin
 {
   aValue.Truncate();
   char16_t buf[24];
   uint32_t last = mNumbers.Length() - 1;
   for (uint32_t i = 0; i < mNumbers.Length(); ++i) {
     // Would like to use aValue.AppendPrintf("%f", mNumbers[i]), but it's not
     // possible to always avoid trailing zeros.
     nsTextFormatter::snprintf(buf, ArrayLength(buf),
-                              MOZ_UTF16("%g"),
+                              u"%g",
                               double(mNumbers[i]));
     // We ignore OOM, since it's not useful for us to return an error.
     aValue.Append(buf);
     if (i != last) {
       aValue.Append(' ');
     }
   }
 }
--- a/dom/svg/SVGPathSegUtils.cpp
+++ b/dom/svg/SVGPathSegUtils.cpp
@@ -32,51 +32,51 @@ SVGPathSegUtils::GetValueAsString(const 
   uint32_t type = DecodeType(aSeg[0]);
   char16_t typeAsChar = GetPathSegTypeAsLetter(type);
 
   // Special case arcs:
   if (IsArcType(type)) {
     bool largeArcFlag = aSeg[4] != 0.0f;
     bool sweepFlag = aSeg[5] != 0.0f;
     nsTextFormatter::ssprintf(aValue,
-                              MOZ_UTF16("%c%g,%g %g %d,%d %g,%g"),
+                              u"%c%g,%g %g %d,%d %g,%g",
                               typeAsChar, aSeg[1], aSeg[2], aSeg[3],
                               largeArcFlag, sweepFlag, aSeg[6], aSeg[7]);
   } else {
 
     switch (ArgCountForType(type)) {
     case 0:
       aValue = typeAsChar;
       break;
 
     case 1:
-      nsTextFormatter::ssprintf(aValue, MOZ_UTF16("%c%g"),
+      nsTextFormatter::ssprintf(aValue, u"%c%g",
                                 typeAsChar, aSeg[1]);
       break;
 
     case 2:
-      nsTextFormatter::ssprintf(aValue, MOZ_UTF16("%c%g,%g"),
+      nsTextFormatter::ssprintf(aValue, u"%c%g,%g",
                                 typeAsChar, aSeg[1], aSeg[2]);
       break;
 
     case 4:
-      nsTextFormatter::ssprintf(aValue, MOZ_UTF16("%c%g,%g %g,%g"),
+      nsTextFormatter::ssprintf(aValue, u"%c%g,%g %g,%g",
                                 typeAsChar, aSeg[1], aSeg[2], aSeg[3], aSeg[4]);
       break;
 
     case 6:
       nsTextFormatter::ssprintf(aValue,
-                                MOZ_UTF16("%c%g,%g %g,%g %g,%g"),
+                                u"%c%g,%g %g,%g %g,%g",
                                 typeAsChar, aSeg[1], aSeg[2], aSeg[3], aSeg[4],
                                 aSeg[5], aSeg[6]);
       break;
 
     default:
       MOZ_ASSERT(false, "Unknown segment type");
-      aValue = MOZ_UTF16("<unknown-segment-type>");
+      aValue = u"<unknown-segment-type>";
       return;
     }
   }
   
   // nsTextFormatter::ssprintf is one of the nsTextFormatter methods that
   // randomly appends '\0' to its output string, which means that the length
   // of the output string is one too long. We need to manually remove that '\0'
   // until nsTextFormatter is fixed.
--- a/dom/svg/SVGPointList.cpp
+++ b/dom/svg/SVGPointList.cpp
@@ -27,17 +27,17 @@ SVGPointList::GetValueAsString(nsAString
 {
   aValue.Truncate();
   char16_t buf[50];
   uint32_t last = mItems.Length() - 1;
   for (uint32_t i = 0; i < mItems.Length(); ++i) {
     // Would like to use aValue.AppendPrintf("%f,%f", item.mX, item.mY),
     // but it's not possible to always avoid trailing zeros.
     nsTextFormatter::snprintf(buf, ArrayLength(buf),
-                              MOZ_UTF16("%g,%g"),
+                              u"%g,%g",
                               double(mItems[i].mX), double(mItems[i].mY));
     // We ignore OOM, since it's not useful for us to return an error.
     aValue.Append(buf);
     if (i != last) {
       aValue.Append(' ');
     }
   }
 }
--- a/dom/svg/nsSVGAngle.cpp
+++ b/dom/svg/nsSVGAngle.cpp
@@ -81,17 +81,17 @@ GetUnitTypeForString(const nsAString& un
   return SVG_ANGLETYPE_UNKNOWN;
 }
 
 static void
 GetValueString(nsAString &aValueAsString, float aValue, uint16_t aUnitType)
 {
   char16_t buf[24];
   nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
-                            MOZ_UTF16("%g"),
+                            u"%g",
                             (double)aValue);
   aValueAsString.Assign(buf);
 
   nsAutoString unitString;
   GetUnitString(unitString, aUnitType);
   aValueAsString.Append(unitString);
 }
 
--- a/dom/svg/nsSVGLength2.cpp
+++ b/dom/svg/nsSVGLength2.cpp
@@ -84,17 +84,17 @@ GetUnitTypeForString(const nsAString& un
   return nsIDOMSVGLength::SVG_LENGTHTYPE_UNKNOWN;
 }
 
 static void
 GetValueString(nsAString &aValueAsString, float aValue, uint16_t aUnitType)
 {
   char16_t buf[24];
   nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
-                            MOZ_UTF16("%g"),
+                            u"%g",
                             (double)aValue);
   aValueAsString.Assign(buf);
 
   nsAutoString unitString;
   GetUnitString(unitString, aUnitType);
   aValueAsString.Append(unitString);
 }
 
--- a/dom/svg/nsSVGTransform.cpp
+++ b/dom/svg/nsSVGTransform.cpp
@@ -20,51 +20,51 @@ nsSVGTransform::GetValueAsString(nsAStri
 {
   char16_t buf[256];
 
   switch (mType) {
     case SVG_TRANSFORM_TRANSLATE:
       // The spec say that if Y is not provided, it is assumed to be zero.
       if (mMatrix._32 != 0)
         nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
-            MOZ_UTF16("translate(%g, %g)"),
+            u"translate(%g, %g)",
             mMatrix._31, mMatrix._32);
       else
         nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
-            MOZ_UTF16("translate(%g)"),
+            u"translate(%g)",
             mMatrix._31);
       break;
     case SVG_TRANSFORM_ROTATE:
       if (mOriginX != 0.0f || mOriginY != 0.0f)
         nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
-            MOZ_UTF16("rotate(%g, %g, %g)"),
+            u"rotate(%g, %g, %g)",
             mAngle, mOriginX, mOriginY);
       else
         nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
-            MOZ_UTF16("rotate(%g)"), mAngle);
+            u"rotate(%g)", mAngle);
       break;
     case SVG_TRANSFORM_SCALE:
       if (mMatrix._11 != mMatrix._22)
         nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
-            MOZ_UTF16("scale(%g, %g)"), mMatrix._11, mMatrix._22);
+            u"scale(%g, %g)", mMatrix._11, mMatrix._22);
       else
         nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
-            MOZ_UTF16("scale(%g)"), mMatrix._11);
+            u"scale(%g)", mMatrix._11);
       break;
     case SVG_TRANSFORM_SKEWX:
       nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
-                                MOZ_UTF16("skewX(%g)"), mAngle);
+                                u"skewX(%g)", mAngle);
       break;
     case SVG_TRANSFORM_SKEWY:
       nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
-                                MOZ_UTF16("skewY(%g)"), mAngle);
+                                u"skewY(%g)", mAngle);
       break;
     case SVG_TRANSFORM_MATRIX:
       nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
-          MOZ_UTF16("matrix(%g, %g, %g, %g, %g, %g)"),
+          u"matrix(%g, %g, %g, %g, %g, %g)",
                             mMatrix._11, mMatrix._12,
                             mMatrix._21, mMatrix._22,
                             mMatrix._31, mMatrix._32);
       break;
     default:
       buf[0] = '\0';
       NS_ERROR("unknown transformation type");
       break;
--- a/dom/svg/nsSVGViewBox.cpp
+++ b/dom/svg/nsSVGViewBox.cpp
@@ -202,17 +202,17 @@ void
 nsSVGViewBox::GetBaseValueString(nsAString& aValue) const
 {
   if (mBaseVal.none) {
     aValue.AssignLiteral("none");
     return;
   }
   char16_t buf[200];
   nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
-                            MOZ_UTF16("%g %g %g %g"),
+                            u"%g %g %g %g",
                             (double)mBaseVal.x, (double)mBaseVal.y,
                             (double)mBaseVal.width, (double)mBaseVal.height);
   aValue.Assign(buf);
 }
 
 
 already_AddRefed<dom::SVGAnimatedRect>
 nsSVGViewBox::ToSVGAnimatedRect(nsSVGElement* aSVGElement)
--- a/dom/system/gonk/AudioManager.cpp
+++ b/dom/system/gonk/AudioManager.cpp
@@ -51,20 +51,20 @@ using namespace mozilla::dom;
 using namespace mozilla::dom::gonk;
 using namespace android;
 using namespace mozilla;
 using namespace mozilla::dom::bluetooth;
 
 #undef LOG
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "AudioManager" , ## args)
 
-#define HEADPHONES_STATUS_HEADSET     MOZ_UTF16("headset")
-#define HEADPHONES_STATUS_HEADPHONE   MOZ_UTF16("headphone")
-#define HEADPHONES_STATUS_OFF         MOZ_UTF16("off")
-#define HEADPHONES_STATUS_UNKNOWN     MOZ_UTF16("unknown")
+#define HEADPHONES_STATUS_HEADSET     u"headset"
+#define HEADPHONES_STATUS_HEADPHONE   u"headphone"
+#define HEADPHONES_STATUS_OFF         u"off"
+#define HEADPHONES_STATUS_UNKNOWN     u"unknown"
 #define HEADPHONES_STATUS_CHANGED     "headphones-status-changed"
 #define MOZ_SETTINGS_CHANGE_ID        "mozsettings-changed"
 #define AUDIO_CHANNEL_PROCESS_CHANGED "audio-channel-process-changed"
 #define AUDIO_POLICY_SERVICE_NAME     "media.audio_policy"
 #define SETTINGS_SERVICE              "@mozilla.org/settingsService;1"
 
 // Refer AudioService.java from Android
 static const uint32_t sMaxStreamVolumeTbl[AUDIO_STREAM_CNT] = {
--- a/dom/system/gonk/GonkGPSGeolocationProvider.cpp
+++ b/dom/system/gonk/GonkGPSGeolocationProvider.cpp
@@ -173,39 +173,37 @@ public:
     nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
     obsService->NotifyObservers(provider, "geolocation-device-events", mData);
     return NS_OK;
   }
 private:
   const char16_t* mData;
 };
 
-
-
 void
 GonkGPSGeolocationProvider::StatusCallback(GpsStatus* status)
 {
   const char* msgStream=0;
   switch (status->status) {
     case GPS_STATUS_NONE:
       msgStream = "geo: GPS_STATUS_NONE\n";
       break;
     case GPS_STATUS_SESSION_BEGIN:
       msgStream = "geo: GPS_STATUS_SESSION_BEGIN\n";
       break;
     case GPS_STATUS_SESSION_END:
       msgStream = "geo: GPS_STATUS_SESSION_END\n";
       break;
     case GPS_STATUS_ENGINE_ON:
       msgStream = "geo: GPS_STATUS_ENGINE_ON\n";
-      NS_DispatchToMainThread(new NotifyObserversGPSTask( MOZ_UTF16("GPSStarting")));
+      NS_DispatchToMainThread(new NotifyObserversGPSTask(u"GPSStarting"));
       break;
     case GPS_STATUS_ENGINE_OFF:
       msgStream = "geo: GPS_STATUS_ENGINE_OFF\n";
-      NS_DispatchToMainThread(new NotifyObserversGPSTask( MOZ_UTF16("GPSShutdown")));
+      NS_DispatchToMainThread(new NotifyObserversGPSTask(u"GPSShutdown"));
       break;
     default:
       msgStream = "geo: Unknown GPS status\n";
       break;
   }
   if (gDebug_isLoggingEnabled){
     DBG("%s", msgStream);
   }
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -67,16 +67,17 @@ var ecmaGlobals =
     {name: "Intl", android: false},
     "Iterator",
     "JSON",
     "Map",
     "Math",
     {name: "NaN", xbl: false},
     "Number",
     "Object",
+    "Promise",
     "Proxy",
     "RangeError",
     "ReferenceError",
     "Reflect",
     "RegExp",
     "Set",
     {name: "SharedArrayBuffer", nightly: true},
     {name: "SIMD", nightly: true},
@@ -957,18 +958,16 @@ var interfaceNamesInGlobalScope =
     {name: "PresentationSession", disabled: true, permission: ["presentation"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "PresentationSessionConnectEvent", disabled: true, permission: ["presentation"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "ProcessingInstruction",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "ProgressEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    "Promise",
-// IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "PushManager", b2g: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "PushSubscription", b2g: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "PushSubscriptionOptions", b2g: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "RadioNodeList",
 // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/dom/tests/mochitest/general/test_offsets.html
+++ b/dom/tests/mochitest/general/test_offsets.html
@@ -7,22 +7,18 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
 
 <style>
   input {
     box-sizing: content-box;
   }
 </style>
 </head>
-
-<!-- We set a transform on the body element so that it creates a reference frame.
-     This makes sure that snapping of scrolled areas for the contained elements
-     is not influenced by offsets outside of this document. -->
 <body id="body" onload="setTimeout(testElements, 0, 'testelements', SimpleTest.finish);"
-      style="margin: 1px; border: 2px solid black; padding: 4px; transform: translateY(1px);">
+      style="margin: 1px; border: 2px solid black; padding: 4px;">
 
 <div id="testelements" style="margin: 0; border: 0; padding: 0;">
   <div id="div1" style="margin: 0; margin-left: 6px; margin-top: 2px; border: 1px solid green; padding: 6px; width: 50px; height: 20px"
          _offsetLeft="13" _offsetTop="9" _offsetWidth="64" _offsetHeight="34"
          _scrollWidth="62" _scrollHeight="32"
          _clientLeft="1" _clientTop="1" _clientWidth="62" _clientHeight="32"></div>
   <div id="noscroll" style="margin: 2px; border: 1px solid blue; padding: 3px;"
        _offsetLeft="10" _offsetTop="12" _offsetWidth="64" _offsetHeight="34"
@@ -54,17 +50,17 @@
   <div id="fixed" style="position: fixed; margin: 2px; border: 1px solid orange; padding: 7px; left: 87px; top: 12px;">
     This is some fixed positioned text.
     <div id="fixed-block" _offsetParent="fixed">
       <div id="fixed-replaced" _offsetParent="fixed" style="margin: 1px; border: 0; padding: 3px;"></div>
     </div>
   </div>
 
   <div id="scrollbox"
-       style="overflow: scroll; padding-left: 0px; margin: 3px; border: 4px solid green; max-width: 80px; max-height: 70px"
+       style="overflow: scroll; padding-left: 0px; margin: 3px; border: 4px solid green; max-width: 80px; max-height: 70px;"
        _scrollWidth="62" _scrollHeight="32"
        _clientLeft="1" _clientTop="1" _clientWidth="62" _clientHeight="32"><p id="p1" style="margin: 0; padding: 0;">One</p>
     <p id="p2">Two</p>
     <p id="scrollchild">Three</p>
     <p id="lastlinebox" style="margin: 0; padding: 0;"><input id="lastline" type="button"
                                style="margin: 0px; border: 2px solid red;"
                                value="This button is much longer than the others">
   </p></div>
--- a/dom/tests/mochitest/general/test_offsets.js
+++ b/dom/tests/mochitest/general/test_offsets.js
@@ -176,35 +176,22 @@ function checkClientState(element, left,
 }
 
 function checkCoord(element, type, val, testname)
 {
   if (val != -10000)
     is(element[type], Math.round(val), testname + " " + type);
 }
 
-function checkCoordFuzzy(element, type, val, fuzz, testname)
-{
-  if (val != -10000)
-    ok(Math.abs(element[type] - Math.round(val)) <= fuzz, testname + " " + type);
-}
-
 function checkCoords(element, type, left, top, width, height, testname)
 {
   checkCoord(element, type + "Left", left, testname);
   checkCoord(element, type + "Top", top, testname);
-
-  if (type == "scroll") {
-    // scrollWidth and scrollHeight can deviate by 1 pixel due to snapping.
-    checkCoordFuzzy(element, type + "Width", width, 1, testname);
-    checkCoordFuzzy(element, type + "Height", height, 1, testname);
-  } else {
-    checkCoord(element, type + "Width", width, testname);
-    checkCoord(element, type + "Height", height, testname);
-  }
+  checkCoord(element, type + "Width", width, testname);
+  checkCoord(element, type + "Height", height, testname);
 
   if (element instanceof SVGElement)
     return;
 
   if (element.id == "outerpopup" && !element.parentNode.open) // closed popup
     return;
 
   if (element.id == "div-displaynone" || element.id == "nonappended") // hidden elements
--- a/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js
+++ b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js
@@ -42,16 +42,17 @@ var ecmaGlobals =
     {name: "Intl", android: false},
     "Iterator",
     "JSON",
     "Map",
     "Math",
     "NaN",
     "Number",
     "Object",
+    "Promise",
     "Proxy",
     "RangeError",
     "ReferenceError",
     "Reflect",
     "RegExp",
     "Set",
     {name: "SharedArrayBuffer", nightly: true},
     {name: "SIMD", nightly: true},
@@ -170,18 +171,16 @@ var interfaceNamesInGlobalScope =
     "PerformanceMark",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "PerformanceMeasure",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     { name: "PerformanceObserver", nightly: true },
 // IMPORTANT: Do not change this list without review from a DOM peer!
     { name: "PerformanceObserverEntryList", nightly: true },
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    "Promise",
-// IMPORTANT: Do not change this list without review from a DOM peer!
     { name: "PushEvent", b2g: false },
 // IMPORTANT: Do not change this list without review from a DOM peer!
     { name: "PushManager", b2g: false },
 // IMPORTANT: Do not change this list without review from a DOM peer!
     { name: "PushMessageData", b2g: false },
 // IMPORTANT: Do not change this list without review from a DOM peer!
     { name: "PushSubscription", b2g: false },
 // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/dom/workers/test/test_worker_interfaces.js
+++ b/dom/workers/test/test_worker_interfaces.js
@@ -42,16 +42,17 @@ var ecmaGlobals =
     {name: "Intl", android: false},
     "Iterator",
     "JSON",
     "Map",
     "Math",
     "NaN",
     "Number",
     "Object",
+    "Promise",
     "Proxy",
     "RangeError",
     "ReferenceError",
     "Reflect",
     "RegExp",
     "Set",
     {name: "SharedArrayBuffer", nightly: true},
     {name: "SIMD", nightly: true},
@@ -162,18 +163,16 @@ var interfaceNamesInGlobalScope =
     "PerformanceMark",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "PerformanceMeasure",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     { name: "PerformanceObserver", nightly: true },
 // IMPORTANT: Do not change this list without review from a DOM peer!
     { name: "PerformanceObserverEntryList", nightly: true },
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    "Promise",
-// IMPORTANT: Do not change this list without review from a DOM peer!
     { name: "PushManager", b2g: false },
 // IMPORTANT: Do not change this list without review from a DOM peer!
     { name: "PushSubscription", b2g: false },
 // IMPORTANT: Do not change this list without review from a DOM peer!
     { name: "PushSubscriptionOptions", b2g: false },
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Request",
 // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/dom/xbl/nsXBLDocumentInfo.cpp
+++ b/dom/xbl/nsXBLDocumentInfo.cpp
@@ -241,17 +241,17 @@ nsXBLDocumentInfo::ReadPrototypeBindings
       break;
 
     rv = nsXBLPrototypeBinding::ReadNewBinding(stream, docInfo, doc, flags);
     if (NS_FAILED(rv)) {
       return rv;
     }
   }
 
-  docInfo.swap(*aDocInfo);
+  docInfo.forget(aDocInfo);
   return NS_OK;
 }
 
 nsresult
 nsXBLDocumentInfo::WritePrototypeBindings()
 {
   // Only write out bindings with the system principal
   if (!nsContentUtils::IsSystemPrincipal(mDocument->NodePrincipal()))
--- a/dom/xbl/nsXBLProtoImplField.cpp
+++ b/dom/xbl/nsXBLProtoImplField.cpp
@@ -489,10 +489,10 @@ nsXBLProtoImplField::Write(nsIObjectOutp
 
   nsresult rv = aStream->Write8(type);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = aStream->WriteWStringZ(mName);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = aStream->Write32(mLineNumber);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  return aStream->WriteWStringZ(mFieldText ? mFieldText : MOZ_UTF16(""));
+  return aStream->WriteWStringZ(mFieldText ? mFieldText : u"");
 }
--- a/dom/xbl/nsXBLPrototypeHandler.cpp
+++ b/dom/xbl/nsXBLPrototypeHandler.cpp
@@ -997,10 +997,10 @@ nsXBLPrototypeHandler::Write(nsIObjectOu
   rv = aStream->Write32(mDetail);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = aStream->WriteWStringZ(nsDependentAtomString(mEventName).get());
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = aStream->Write32(mLineNumber);
   NS_ENSURE_SUCCESS(rv, rv);
-  return aStream->WriteWStringZ(mHandlerText ? mHandlerText : MOZ_UTF16(""));
+  return aStream->WriteWStringZ(mHandlerText ? mHandlerText : u"");
 }
--- a/dom/xbl/nsXBLService.cpp
+++ b/dom/xbl/nsXBLService.cpp
@@ -493,17 +493,17 @@ nsXBLService::LoadBindings(nsIContent* a
 
     // Set up our properties
     rv = newBinding->InstallImplementation();
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Figure out if we have any scoped sheets.  If so, we do a second resolve.
     *aResolveStyle = newBinding->HasStyleSheets();
 
-    newBinding.swap(*aBinding);
+    newBinding.forget(aBinding);
   }
 
   return NS_OK;
 }
 
 nsresult
 nsXBLService::FlushStyleBindings(nsIContent* aContent)
 {
--- a/dom/xml/nsXMLContentSink.cpp
+++ b/dom/xml/nsXMLContentSink.cpp
@@ -1324,18 +1324,18 @@ nsXMLContentSink::ReportError(const char
     mXSLTProcessor->CancelLoads();
     mXSLTProcessor = nullptr;
   }
 
   // release the nodes on stack
   mContentStack.Clear();
   mNotifyLevel = 0;
 
-  rv = HandleProcessingInstruction(MOZ_UTF16("xml-stylesheet"),
-                                   MOZ_UTF16("href=\"chrome://global/locale/intl.css\" type=\"text/css\""));
+  rv = HandleProcessingInstruction(u"xml-stylesheet",
+                                   u"href=\"chrome://global/locale/intl.css\" type=\"text/css\"");
   NS_ENSURE_SUCCESS(rv, rv);
 
   const char16_t* noAtts[] = { 0, 0 };
 
   NS_NAMED_LITERAL_STRING(errorNs,
                           "http://www.mozilla.org/newlayout/xml/parsererror.xml");
 
   nsAutoString parsererror(errorNs);
--- a/dom/xslt/xpath/txPathExpr.cpp
+++ b/dom/xslt/xpath/txPathExpr.cpp
@@ -58,17 +58,17 @@ PathExpr::evaluate(txIEvalContext* aCont
 
     NS_ENSURE_TRUE(res->getResultType() == txAExprResult::NODESET,
                    NS_ERROR_XSLT_NODESET_EXPECTED);
 
     RefPtr<txNodeSet> nodes = static_cast<txNodeSet*>
                                            (static_cast<txAExprResult*>
                                                        (res));
     if (nodes->isEmpty()) {
-        res.swap(*aResult);
+        res.forget(aResult);
 
         return NS_OK;
     }
     res = nullptr; // To allow recycling
 
     // Evaluate remaining steps
     uint32_t i, len = mItems.Length();
     for (i = 1; i < len; ++i) {
--- a/dom/xslt/xslt/txEXSLTFunctions.cpp
+++ b/dom/xslt/xslt/txEXSLTFunctions.cpp
@@ -253,17 +253,17 @@ txEXSLTFunctionCall::evaluate(txIEvalCon
     switch (mType) {
         case NODE_SET:
         {
             RefPtr<txAExprResult> exprResult;
             rv = mParams[0]->evaluate(aContext, getter_AddRefs(exprResult));
             NS_ENSURE_SUCCESS(rv, rv);
 
             if (exprResult->getResultType() == txAExprResult::NODESET) {
-                exprResult.swap(*aResult);
+                exprResult.forget(aResult);
             }
             else {
                 RefPtr<txNodeSet> resultSet;
                 rv = aContext->recycler()->
                     getNodeSet(getter_AddRefs(resultSet));
                 NS_ENSURE_SUCCESS(rv, rv);
 
                 if (exprResult->getResultType() ==
--- a/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp
+++ b/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp
@@ -350,19 +350,17 @@ txStylesheetSink::GetInterface(const nsI
         nsCOMPtr<nsIWindowWatcher> wwatcher =
             do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
         NS_ENSURE_SUCCESS(rv, rv);
 
         nsCOMPtr<nsIAuthPrompt> prompt;
         rv = wwatcher->GetNewAuthPrompter(nullptr, getter_AddRefs(prompt));
         NS_ENSURE_SUCCESS(rv, rv);
 
-        nsIAuthPrompt* rawPtr = nullptr;
-        prompt.swap(rawPtr);
-        *aResult = rawPtr;
+        prompt.forget(aResult);
 
         return NS_OK;
     }
 
     return NS_ERROR_NO_INTERFACE;
 }
 
 class txCompileObserver final : public txACompileObserver
--- a/dom/xslt/xslt/txMozillaXSLTProcessor.cpp
+++ b/dom/xslt/xslt/txMozillaXSLTProcessor.cpp
@@ -1089,22 +1089,22 @@ txMozillaXSLTProcessor::reportError(nsre
 
             nsXPIDLString errorMessage;
             nsCOMPtr<nsIStringBundle> bundle;
             sbs->CreateBundle(XSLT_MSGS_URL, getter_AddRefs(bundle));
 
             if (bundle) {
                 const char16_t* error[] = { errorText.get() };
                 if (mStylesheet) {
-                    bundle->FormatStringFromName(MOZ_UTF16("TransformError"),
+                    bundle->FormatStringFromName(u"TransformError",
                                                  error, 1,
                                                  getter_Copies(errorMessage));
                 }
                 else {
-                    bundle->FormatStringFromName(MOZ_UTF16("LoadingError"),
+                    bundle->FormatStringFromName(u"LoadingError",
                                                  error, 1,
                                                  getter_Copies(errorMessage));
                 }
             }
             mErrorText.Assign(errorMessage);
         }
     }
 
--- a/dom/xul/XULDocument.cpp
+++ b/dom/xul/XULDocument.cpp
@@ -1429,17 +1429,17 @@ XULDocument::GetPopupNode(nsIDOMNode** a
         nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
         if (pm) {
             node = pm->GetLastTriggerPopupNode(this);
         }
     }
 
     if (node && nsContentUtils::CanCallerAccess(node)
         && GetScopeObjectOfNode(node)) {
-        node.swap(*aNode);
+        node.forget(aNode);
     }
 
     return NS_OK;
 }
 
 already_AddRefed<nsINode>
 XULDocument::GetPopupNode()
 {
@@ -1541,17 +1541,17 @@ NS_IMETHODIMP
 XULDocument::GetTooltipNode(nsIDOMNode** aNode)
 {
     *aNode = nullptr;
 
     nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
     if (pm) {
         nsCOMPtr<nsIDOMNode> node = pm->GetLastTriggerTooltipNode(this);
         if (node && nsContentUtils::CanCallerAccess(node))
-            node.swap(*aNode);
+            node.forget(aNode);
     }
 
     return NS_OK;
 }
 
 already_AddRefed<nsINode>
 XULDocument::GetTooltipNode()
 {
@@ -3574,17 +3574,17 @@ XULDocument::CreateElementFromPrototype(
                            NOT_FROM_PARSER);
         if (NS_FAILED(rv))
             return rv;
 
         rv = AddAttributes(aPrototype, result);
         if (NS_FAILED(rv)) return rv;
     }
 
-    result.swap(*aResult);
+    result.forget(aResult);
 
     return NS_OK;
 }
 
 nsresult
 XULDocument::CreateOverlayElement(nsXULPrototypeElement* aPrototype,
                                   Element** aResult)
 {
--- a/dom/xul/nsXULPopupListener.cpp
+++ b/dom/xul/nsXULPopupListener.cpp
@@ -22,17 +22,17 @@
 #include "nsIScriptContext.h"
 #include "nsIDOMWindow.h"
 #include "nsIDOMXULDocument.h"
 #include "nsIDocument.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsLayoutUtils.h"
-#include "nsHTMLReflowState.h"
+#include "mozilla/ReflowInput.h"
 #include "nsIObjectLoadingContent.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent()
 #include "mozilla/dom/EventTarget.h"
 #include "mozilla/dom/FragmentOrElement.h"
 
--- a/dom/xul/templates/nsXULContentUtils.cpp
+++ b/dom/xul/templates/nsXULContentUtils.cpp
@@ -89,17 +89,17 @@ nsXULContentUtils::Init()
 #define XUL_RESOURCE(ident, uri)                              \
   PR_BEGIN_MACRO                                              \
    rv = gRDF->GetResource(NS_LITERAL_CSTRING(uri), &(ident)); \
    if (NS_FAILED(rv)) return rv;                              \
   PR_END_MACRO
 
 #define XUL_LITERAL(ident, val)                                   \
   PR_BEGIN_MACRO                                                  \
-   rv = gRDF->GetLiteral(MOZ_UTF16(val), &(ident));               \
+   rv = gRDF->GetLiteral(val, &(ident));                          \
    if (NS_FAILED(rv)) return rv;                                  \
   PR_END_MACRO
 
 #include "nsXULResourceList.h"
 #undef XUL_RESOURCE
 #undef XUL_LITERAL
 
     gFormat = nsIDateTimeFormat::Create().take();
--- a/dom/xul/templates/nsXULResourceList.h
+++ b/dom/xul/templates/nsXULResourceList.h
@@ -5,9 +5,9 @@
 
 // N.B., no include guard! We'll include this multiple times in some
 // files.
 
 XUL_RESOURCE(NC_child,  NC_NAMESPACE_URI "child");
 XUL_RESOURCE(NC_Folder, NC_NAMESPACE_URI "Folder");
 XUL_RESOURCE(NC_open,   NC_NAMESPACE_URI "open");
 
-XUL_LITERAL(true_, "true");
+XUL_LITERAL(true_, u"true");
--- a/editor/composer/nsEditingSession.cpp
+++ b/editor/composer/nsEditingSession.cpp
@@ -1028,17 +1028,17 @@ nsEditingSession::EndDocumentLoad(nsIWeb
 void
 nsEditingSession::TimerCallback(nsITimer* aTimer, void* aClosure)
 {
   nsCOMPtr<nsIDocShell> docShell = do_QueryReferent(static_cast<nsIWeakReference*> (aClosure));
   if (docShell)
   {
     nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(docShell));
     if (webNav)
-      webNav->LoadURI(MOZ_UTF16("about:blank"),
+      webNav->LoadURI(u"about:blank",
                       0, nullptr, nullptr, nullptr);
   }
 }
 
 /*---------------------------------------------------------------------------
 
   StartPageLoad
 
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -1207,22 +1207,20 @@ HTMLEditor::ReplaceHeadContentsWithHTML(
   NS_ENSURE_TRUE(headNode, NS_ERROR_NULL_POINTER);
 
   // First, make sure there are no return chars in the source.  Bad things
   // happen if you insert returns (instead of dom newlines, \n) into an editor
   // document.
   nsAutoString inputString (aSourceToInsert);  // hope this does copy-on-write
 
   // Windows linebreaks: Map CRLF to LF:
-  inputString.ReplaceSubstring(MOZ_UTF16("\r\n"),
-                               MOZ_UTF16("\n"));
+  inputString.ReplaceSubstring(u"\r\n", u"\n");
 
   // Mac linebreaks: Map any remaining CR to LF:
-  inputString.ReplaceSubstring(MOZ_UTF16("\r"),
-                               MOZ_UTF16("\n"));
+  inputString.ReplaceSubstring(u"\r", u"\n");
 
   AutoEditBatch beginBatching(this);
 
   // Get the first range in the selection, for context:
   RefPtr<nsRange> range = selection->GetRangeAt(0);
   NS_ENSURE_TRUE(range, NS_ERROR_NULL_POINTER);
 
   ErrorResult err;
--- a/embedding/browser/nsDocShellTreeOwner.cpp
+++ b/embedding/browser/nsDocShellTreeOwner.cpp
@@ -6,17 +6,17 @@
 
 // Local Includes
 #include "nsDocShellTreeOwner.h"
 #include "nsWebBrowser.h"
 
 // Helper Classes
 #include "nsStyleCoord.h"
 #include "nsSize.h"
-#include "nsHTMLReflowState.h"
+#include "mozilla/ReflowInput.h"
 #include "nsIServiceManager.h"
 #include "nsComponentManagerUtils.h"
 #include "nsXPIDLString.h"
 #include "nsIAtom.h"
 #include "nsReadableUtils.h"
 #include "nsUnicharUtils.h"
 #include "nsISimpleEnumerator.h"
 #include "mozilla/LookAndFeel.h"
--- a/embedding/components/commandhandler/nsCommandManager.cpp
+++ b/embedding/components/commandhandler/nsCommandManager.cpp
@@ -77,17 +77,17 @@ nsCommandManager::CommandStatusChanged(c
   if (commandObservers) {
     // XXX Should we worry about observers removing themselves from Observe()?
     int32_t i, numItems = commandObservers->Length();
     for (i = 0; i < numItems; ++i) {
       nsCOMPtr<nsIObserver> observer = commandObservers->ElementAt(i);
       // should we get the command state to pass here? This might be expensive.
       observer->Observe(NS_ISUPPORTS_CAST(nsICommandManager*, this),
                         aCommandName,
-                        MOZ_UTF16("command_status_changed"));
+                        u"command_status_changed");
     }
   }
 
   return NS_OK;
 }
 
 #if 0
 #pragma mark -
--- a/extensions/cookie/nsPermissionManager.cpp
+++ b/extensions/cookie/nsPermissionManager.cpp
@@ -1690,17 +1690,17 @@ nsPermissionManager::AddInternal(nsIPrin
       }
 
       if (aNotifyOperation == eNotify) {
         NotifyObserversWithPermission(aPrincipal,
                                       mTypeArray[typeIndex],
                                       aPermission,
                                       aExpireType,
                                       aExpireTime,
-                                      MOZ_UTF16("added"));
+                                      u"added");
       }
 
       break;
     }
 
   case eOperationRemoving:
     {
       PermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
@@ -1714,17 +1714,17 @@ nsPermissionManager::AddInternal(nsIPrin
                  nsIPermissionManager::EXPIRE_NEVER, 0, 0);
 
       if (aNotifyOperation == eNotify) {
         NotifyObserversWithPermission(aPrincipal,
                                       mTypeArray[typeIndex],
                                       oldPermissionEntry.mPermission,
                                       oldPermissionEntry.mExpireType,
                                       oldPermissionEntry.mExpireTime,
-                                      MOZ_UTF16("deleted"));
+                                      u"deleted");
       }
 
       // If there are no more permissions stored for that entry, clear it.
       if (entry->GetPermissions().IsEmpty()) {
         mPermissionTable.RemoveEntry(entry);
       }
 
       break;
@@ -1760,17 +1760,17 @@ nsPermissionManager::AddInternal(nsIPrin
                  aPermission, aExpireType, aExpireTime, aModificationTime);
 
       if (aNotifyOperation == eNotify) {
         NotifyObserversWithPermission(aPrincipal,
                                       mTypeArray[typeIndex],
                                       aPermission,
                                       aExpireType,
                                       aExpireTime,
-                                      MOZ_UTF16("changed"));
+                                      u"changed");
       }
 
       break;
     }
   case eOperationReplacingDefault:
     {
       // this is handling the case when we have an existing permission
       // entry that was created as a "default" (and thus isn't in the DB) with
@@ -1808,17 +1808,17 @@ nsPermissionManager::AddInternal(nsIPrin
       }
 
       if (aNotifyOperation == eNotify) {
         NotifyObserversWithPermission(aPrincipal,
                                       mTypeArray[typeIndex],
                                       aPermission,
                                       aExpireType,
                                       aExpireTime,
-                                      MOZ_UTF16("changed"));
+                                      u"changed");
       }
 
     }
     break;
   }
 
   return NS_OK;
 }
@@ -1919,17 +1919,17 @@ nsPermissionManager::RemoveAllInternal(b
   // database is authoritative, we do not need confirmation from the
   // on-disk database to notify observers.
   RemoveAllFromMemory();
 
   // Re-import the defaults
   ImportDefaults();
 
   if (aNotifyObservers) {
-    NotifyObservers(nullptr, MOZ_UTF16("cleared"));
+    NotifyObservers(nullptr, u"cleared");
   }
 
   // clear the db
   if (mDBConn) {
     nsCOMPtr<mozIStorageAsyncStatement> removeStmt;
     nsresult rv = mDBConn->
       CreateAsyncStatement(NS_LITERAL_CSTRING(
          "DELETE FROM moz_perms"
@@ -2448,32 +2448,32 @@ nsPermissionManager::RemoveExpiredPermis
 
         entry->GetPermissions().RemoveElementAt(i);
 
         NotifyObserversWithPermission(principal,
                                       mTypeArray.ElementAt(oldPermEntry.mType),
                                       oldPermEntry.mPermission,
                                       oldPermEntry.mExpireType,
                                       oldPermEntry.mExpireTime,
-                                      MOZ_UTF16("deleted"));
+                                      u"deleted");
 
         --i;
         continue;
       }
 
       permEntry.mPermission = permEntry.mNonSessionPermission;
       permEntry.mExpireType = permEntry.mNonSessionExpireType;
       permEntry.mExpireTime = permEntry.mNonSessionExpireTime;
 
       NotifyObserversWithPermission(principal,
                                     mTypeArray.ElementAt(permEntry.mType),
                                     permEntry.mPermission,
                                     permEntry.mExpireType,
                                     permEntry.mExpireTime,
-                                    MOZ_UTF16("changed"));
+                                    u"changed");
     }
   }
 
   return NS_OK;
 }
 
 //*****************************************************************************
 //*** nsPermissionManager private methods
--- a/extensions/gio/nsGIOProtocolHandler.cpp
+++ b/extensions/gio/nsGIOProtocolHandler.cpp
@@ -821,27 +821,27 @@ mount_operation_ask_password (GMountOper
     return;
   }
   nsAutoString nsmessage;
 
   if (flags & G_ASK_PASSWORD_NEED_PASSWORD) {
     if (flags & G_ASK_PASSWORD_NEED_USERNAME) {
       if (!realm.IsEmpty()) {
         const char16_t *strings[] = { realm.get(), dispHost.get() };
-        bundle->FormatStringFromName(MOZ_UTF16("EnterLoginForRealm2"),
+        bundle->FormatStringFromName(u"EnterLoginForRealm2",
                                      strings, 2, getter_Copies(nsmessage));
       } else {
         const char16_t *strings[] = { dispHost.get() };
-        bundle->FormatStringFromName(MOZ_UTF16("EnterUserPasswordFor2"),
+        bundle->FormatStringFromName(u"EnterUserPasswordFor2",
                                      strings, 1, getter_Copies(nsmessage));
       }
     } else {
       NS_ConvertUTF8toUTF16 userName(default_user);
       const char16_t *strings[] = { userName.get(), dispHost.get() };
-      bundle->FormatStringFromName(MOZ_UTF16("EnterPasswordFor"),
+      bundle->FormatStringFromName(u"EnterPasswordFor",
                                    strings, 2, getter_Copies(nsmessage));
     }
   } else {
     g_warning("Unknown mount operation request (flags: %x)", flags);
   }
 
   if (nsmessage.IsEmpty()) {
     g_mount_operation_reply(mount_op, G_MOUNT_OPERATION_ABORTED);
--- a/extensions/pref/autoconfig/src/nsAutoConfig.cpp
+++ b/extensions/pref/autoconfig/src/nsAutoConfig.cpp
@@ -509,21 +509,21 @@ nsresult nsAutoConfig::PromptForEMailAdd
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIStringBundle> bundle;
     rv = bundleService->CreateBundle("chrome://autoconfig/locale/autoconfig.properties",
                                 getter_AddRefs(bundle));
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsXPIDLString title;
-    rv = bundle->GetStringFromName(MOZ_UTF16("emailPromptTitle"), getter_Copies(title));
+    rv = bundle->GetStringFromName(u"emailPromptTitle", getter_Copies(title));
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsXPIDLString err;
-    rv = bundle->GetStringFromName(MOZ_UTF16("emailPromptMsg"), getter_Copies(err));
+    rv = bundle->GetStringFromName(u"emailPromptMsg", getter_Copies(err));
     NS_ENSURE_SUCCESS(rv, rv);
     bool check = false;
     nsXPIDLString emailResult;
     bool success;
     rv = promptService->Prompt(nullptr, title.get(), err.get(), getter_Copies(emailResult), nullptr, &check, &success);
     if (!success)
       return NS_ERROR_FAILURE;
     NS_ENSURE_SUCCESS(rv, rv);
--- a/extensions/pref/autoconfig/src/nsReadConfig.cpp
+++ b/extensions/pref/autoconfig/src/nsReadConfig.cpp
@@ -51,22 +51,22 @@ static void DisplayError(void)
 
     nsCOMPtr<nsIStringBundle> bundle;
     bundleService->CreateBundle("chrome://autoconfig/locale/autoconfig.properties",
                                 getter_AddRefs(bundle));
     if (!bundle)
         return;
 
     nsXPIDLString title;
-    rv = bundle->GetStringFromName(MOZ_UTF16("readConfigTitle"), getter_Copies(title));
+    rv = bundle->GetStringFromName(u"readConfigTitle", getter_Copies(title));
     if (NS_FAILED(rv))
         return;
 
     nsXPIDLString err;
-    rv = bundle->GetStringFromName(MOZ_UTF16("readConfigMsg"), getter_Copies(err));
+    rv = bundle->GetStringFromName(u"readConfigMsg", getter_Copies(err));
     if (NS_FAILED(rv))
         return;
 
     promptService->Alert(nullptr, title.get(), err.get());
 }
 
 // nsISupports Implementation
 
--- a/gfx/2d/DrawTargetSkia.cpp
+++ b/gfx/2d/DrawTargetSkia.cpp
@@ -221,22 +221,21 @@ SetPaintPattern(SkPaint& aPaint, const P
     case PatternType::COLOR: {
       Color color = static_cast<const ColorPattern&>(aPattern).mColor;
       aPaint.setColor(ColorToSkColor(color, aAlpha));
       break;
     }
     case PatternType::LINEAR_GRADIENT: {
       const LinearGradientPattern& pat = static_cast<const LinearGradientPattern&>(aPattern);
       GradientStopsSkia *stops = static_cast<GradientStopsSkia*>(pat.mStops.get());
-      SkShader::TileMode mode = ExtendModeToTileMode(stops->mExtendMode, Axis::BOTH);
-
-      if (stops->mCount < 2 ||
+      if (!stops || stops->mCount < 2 ||
           !pat.mBegin.IsFinite() || !pat.mEnd.IsFinite()) {
         aPaint.setColor(SK_ColorTRANSPARENT);
       } else {
+        SkShader::TileMode mode = ExtendModeToTileMode(stops->mExtendMode, Axis::BOTH);
         SkPoint points[2];
         points[0] = SkPoint::Make(SkFloatToScalar(pat.mBegin.x), SkFloatToScalar(pat.mBegin.y));
         points[1] = SkPoint::Make(SkFloatToScalar(pat.mEnd.x), SkFloatToScalar(pat.mEnd.y));
 
         SkMatrix mat;
         GfxMatrixToSkiaMatrix(pat.mMatrix, mat);
         sk_sp<SkShader> shader = SkGradientShader::MakeLinear(points,
                                                               &stops->mColors.front(),
@@ -245,23 +244,22 @@ SetPaintPattern(SkPaint& aPaint, const P
                                                               mode, 0, &mat);
         aPaint.setShader(shader);
       }
       break;
     }
     case PatternType::RADIAL_GRADIENT: {
       const RadialGradientPattern& pat = static_cast<const RadialGradientPattern&>(aPattern);
       GradientStopsSkia *stops = static_cast<GradientStopsSkia*>(pat.mStops.get());
-      SkShader::TileMode mode = ExtendModeToTileMode(stops->mExtendMode, Axis::BOTH);
-
-      if (stops->mCount < 2 ||
+      if (!stops || stops->mCount < 2 ||
           !pat.mCenter1.IsFinite() || !IsFinite(pat.mRadius1) ||
           !pat.mCenter2.IsFinite() || !IsFinite(pat.mRadius2)) {
         aPaint.setColor(SK_ColorTRANSPARENT);
       } else {
+        SkShader::TileMode mode = ExtendModeToTileMode(stops->mExtendMode, Axis::BOTH);
         SkPoint points[2];
         points[0] = SkPoint::Make(SkFloatToScalar(pat.mCenter1.x), SkFloatToScalar(pat.mCenter1.y));
         points[1] = SkPoint::Make(SkFloatToScalar(pat.mCenter2.x), SkFloatToScalar(pat.mCenter2.y));
 
         SkMatrix mat;
         GfxMatrixToSkiaMatrix(pat.mMatrix, mat);
         sk_sp<SkShader> shader = SkGradientShader::MakeTwoPointConical(points[0],
                                                                        SkFloatToScalar(pat.mRadius1),
--- a/gfx/2d/SFNTNameTable.cpp
+++ b/gfx/2d/SFNTNameTable.cpp
@@ -211,17 +211,17 @@ SFNTNameTable::GetU16FullName(mozilla::u
   }
 
   mozilla::u16string styleName;
   if (!ReadU16Name(StyleMatchers(), styleName)) {
     return false;
   }
 
   aU16FullName.assign(Move(familyName));
-  aU16FullName.append(MOZ_UTF16(" "));
+  aU16FullName.append(u" ");
   aU16FullName.append(styleName);
   return true;
 }
 
 bool
 SFNTNameTable::ReadU16Name(const NameRecordMatchers& aMatchers,
                            mozilla::u16string& aU16Name)
 {
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -6,16 +6,20 @@
 
 #include "GLContext.h"
 
 #include <algorithm>
 #include <stdio.h>
 #include <string.h>
 #include <ctype.h>
 #include <vector>
+#ifdef MOZ_WIDGET_ANDROID
+#include <fcntl.h>
+#include <sys/mman.h>
+#endif
 
 #include "GLBlitHelper.h"
 #include "GLReadTexImageHelper.h"
 #include "GLScreenBuffer.h"
 
 #include "gfxCrashReporterUtils.h"
 #include "gfxEnv.h"
 #include "gfxUtils.h"
@@ -27,29 +31,34 @@
 #include "prlink.h"
 #include "ScopedGLHelpers.h"
 #include "SharedSurfaceGL.h"
 #include "GfxTexturesReporter.h"
 #include "TextureGarbageBin.h"
 #include "gfx2DGlue.h"
 #include "gfxPrefs.h"
 #include "mozilla/IntegerPrintfMacros.h"
+#include "mozilla/gfx/Logging.h"
 
 #include "OGLShaderProgram.h" // for ShaderProgramType
 
 #include "mozilla/DebugOnly.h"
 
 #ifdef XP_MACOSX
 #include <CoreServices/CoreServices.h>
 #endif
 
 #if defined(MOZ_WIDGET_COCOA)
 #include "nsCocoaFeatures.h"
 #endif
 
+#ifdef MOZ_WIDGET_ANDROID
+#include "AndroidBridge.h"
+#endif
+
 namespace mozilla {
 namespace gl {
 
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 
 #ifdef MOZ_GL_DEBUG
 unsigned GLContext::sCurrentGLContextTLS = -1;
@@ -456,16 +465,17 @@ GLContext::GLContext(CreateContextFlags 
     mLockedSurface(nullptr),
     mMaxTextureSize(0),
     mMaxCubeMapTextureSize(0),
     mMaxTextureImageSize(0),
     mMaxRenderbufferSize(0),
     mMaxSamples(0),
     mNeedsTextureSizeChecks(false),
     mNeedsFlushBeforeDeleteFB(false),
+    mTextureAllocCrashesOnMapFailure(false),
     mWorkAroundDriverBugs(true),
     mHeavyGLCallsSinceLastFlush(false)
 {
     mMaxViewportDims[0] = 0;
     mMaxViewportDims[1] = 0;
     mOwningThreadId = PlatformThread::CurrentId();
 }
 
@@ -800,17 +810,19 @@ GLContext::InitWithPrefixImpl(const char
 
     // The order of these strings must match up with the order of the enum
     // defined in GLContext.h for renderer IDs.
     const char* rendererMatchStrings[size_t(GLRenderer::Other)] = {
         "Adreno 200",
         "Adreno 205",
         "Adreno (TM) 200",
         "Adreno (TM) 205",
+        "Adreno (TM) 305",
         "Adreno (TM) 320",
+        "Adreno (TM) 330",
         "Adreno (TM) 420",
         "Mali-400 MP",
         "PowerVR SGX 530",
         "PowerVR SGX 540",
         "NVIDIA Tegra",
         "Android Emulator",
         "Gallium 0.4 on llvmpipe",
         "Intel HD Graphics 3000 OpenGL Engine",
@@ -1043,16 +1055,27 @@ GLContext::InitWithPrefixImpl(const char
     }
 #endif
     if (mWorkAroundDriverBugs &&
         Renderer() == GLRenderer::AdrenoTM420) {
         // see bug 1194923. Calling glFlush before glDeleteFramebuffers
         // prevents occasional driver crash.
         mNeedsFlushBeforeDeleteFB = true;
     }
+#ifdef MOZ_WIDGET_ANDROID
+    if (mWorkAroundDriverBugs &&
+        (Renderer() == GLRenderer::AdrenoTM305 ||
+         Renderer() == GLRenderer::AdrenoTM320 ||
+         Renderer() == GLRenderer::AdrenoTM330) &&
+        AndroidBridge::Bridge()->GetAPIVersion() < 21) {
+        // Bug 1164027. Driver crashes when functions such as
+        // glTexImage2D fail due to virtual memory exhaustion.
+        mTextureAllocCrashesOnMapFailure = true;
+    }
+#endif
 
     mMaxTextureImageSize = mMaxTextureSize;
 
     if (IsSupported(GLFeature::framebuffer_multisample)) {
         fGetIntegerv(LOCAL_GL_MAX_SAMPLES, (GLint*)&mMaxSamples);
     }
 
     ////////////////////////////////////////////////////////////////////////////
@@ -2845,16 +2868,68 @@ GLContext::fDeleteFramebuffers(GLsizei n
     if (n == 1 && *names == 0) {
         // Deleting framebuffer 0 causes hangs on the DROID. See bug 623228.
     } else {
         raw_fDeleteFramebuffers(n, names);
     }
     TRACKING_CONTEXT(DeletedFramebuffers(this, n, names));
 }
 
+#ifdef MOZ_WIDGET_ANDROID
+/**
+ * Conservatively estimate whether there is enough available
+ * contiguous virtual address space to map a newly allocated texture.
+ */
+static bool
+WillTextureMapSucceed(GLsizei width, GLsizei height, GLenum format, GLenum type)
+{
+    bool willSucceed = false;
+    // Some drivers leave large gaps between textures, so require
+    // there to be double the actual size of the texture available.
+    size_t size = width * height * GetBytesPerTexel(format, type) * 2;
+
+    int fd = open("/dev/zero", O_RDONLY);
+
+    void *p = mmap(nullptr, size, PROT_NONE, MAP_SHARED, fd, 0);
+    if (p != MAP_FAILED) {
+        willSucceed = true;
+        munmap(p, size);
+    }
+
+    close(fd);
+
+    return willSucceed;
+}
+#endif // MOZ_WIDGET_ANDROID
+
+void
+GLContext::fTexImage2D(GLenum target, GLint level, GLint internalformat,
+                       GLsizei width, GLsizei height, GLint border,
+                       GLenum format, GLenum type, const GLvoid* pixels) {
+    if (!IsTextureSizeSafeToPassToDriver(target, width, height)) {
+        // pass wrong values to cause the GL to generate GL_INVALID_VALUE.
+        // See bug 737182 and the comment in IsTextureSizeSafeToPassToDriver.
+        level = -1;
+        width = -1;
+        height = -1;
+        border = -1;
+    }
+#if MOZ_WIDGET_ANDROID
+    if (mTextureAllocCrashesOnMapFailure) {
+        // We have no way of knowing whether this texture already has
+        // storage allocated for it, and therefore whether this check
+        // is necessary. We must therefore assume it does not and
+        // always perform the check.
+        if (!WillTextureMapSucceed(width, height, internalformat, type)) {
+            return;
+        }
+    }
+#endif
+    raw_fTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
+}
 
 GLuint
 GLContext::GetDrawFB()
 {
     if (mScreen)
         return mScreen->GetDrawFB();
 
     GLuint ret = 0;
@@ -2963,11 +3038,55 @@ CreateTextureForOffscreen(GLContext* aGL
         MOZ_ASSERT(unpackType == LOCAL_GL_UNSIGNED_BYTE);
         internalFormat = LOCAL_GL_BGRA_EXT;
         unpackFormat = LOCAL_GL_BGRA_EXT;
     }
 
     return CreateTexture(aGL, internalFormat, unpackFormat, unpackType, aSize);
 }
 
+uint32_t
+GetBytesPerTexel(GLenum format, GLenum type)
+{
+    // If there is no defined format or type, we're not taking up any memory
+    if (!format || !type) {
+        return 0;
+    }
+
+    if (format == LOCAL_GL_DEPTH_COMPONENT) {
+        if (type == LOCAL_GL_UNSIGNED_SHORT)
+            return 2;
+        else if (type == LOCAL_GL_UNSIGNED_INT)
+            return 4;
+    } else if (format == LOCAL_GL_DEPTH_STENCIL) {
+        if (type == LOCAL_GL_UNSIGNED_INT_24_8_EXT)
+            return 4;
+    }
+
+    if (type == LOCAL_GL_UNSIGNED_BYTE || type == LOCAL_GL_FLOAT || type == LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV) {
+        uint32_t multiplier = type == LOCAL_GL_UNSIGNED_BYTE ? 1 : 4;
+        switch (format) {
+            case LOCAL_GL_ALPHA:
+            case LOCAL_GL_LUMINANCE:
+                return 1 * multiplier;
+            case LOCAL_GL_LUMINANCE_ALPHA:
+                return 2 * multiplier;
+            case LOCAL_GL_RGB:
+                return 3 * multiplier;
+            case LOCAL_GL_RGBA:
+                return 4 * multiplier;
+            default:
+                break;
+        }
+    } else if (type == LOCAL_GL_UNSIGNED_SHORT_4_4_4_4 ||
+               type == LOCAL_GL_UNSIGNED_SHORT_5_5_5_1 ||
+               type == LOCAL_GL_UNSIGNED_SHORT_5_6_5)
+    {
+        return 2;
+    }
+
+    gfxCriticalError() << "Unknown texture type " << type << " or format " << format;
+    MOZ_CRASH();
+    return 0;
+}
 
 } /* namespace gl */
 } /* namespace mozilla */
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -165,17 +165,19 @@ enum class GLVendor {
     Other
 };
 
 enum class GLRenderer {
     Adreno200,
     Adreno205,
     AdrenoTM200,
     AdrenoTM205,
+    AdrenoTM305,
     AdrenoTM320,
+    AdrenoTM330,
     AdrenoTM420,
     Mali400MP,
     SGX530,
     SGX540,
     Tegra,
     AndroidEmulator,
     GalliumLlvmpipe,
     IntelHD3000,
@@ -1616,27 +1618,19 @@ private:
         ASSERT_NOT_PASSING_STACK_BUFFER_TO_GL(pixels);
         BEFORE_GL_CALL;
         mSymbols.fTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
         AFTER_GL_CALL;
         mHeavyGLCallsSinceLastFlush = true;
     }
 
 public:
-    void fTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels) {
-        if (!IsTextureSizeSafeToPassToDriver(target, width, height)) {
-            // pass wrong values to cause the GL to generate GL_INVALID_VALUE.
-            // See bug 737182 and the comment in IsTextureSizeSafeToPassToDriver.
-            level = -1;
-            width = -1;
-            height = -1;
-            border = -1;
-        }
-        raw_fTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
-    }
+    void fTexImage2D(GLenum target, GLint level, GLint internalformat,
+                     GLsizei width, GLsizei height, GLint border,
+                     GLenum format, GLenum type, const GLvoid* pixels);
 
     void fTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid* pixels) {
         ASSERT_NOT_PASSING_STACK_BUFFER_TO_GL(pixels);
         BEFORE_GL_CALL;
         mSymbols.fTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
         AFTER_GL_CALL;
         mHeavyGLCallsSinceLastFlush = true;
     }
@@ -3526,16 +3520,17 @@ protected:
     GLint mMaxTextureSize;
     GLint mMaxCubeMapTextureSize;
     GLint mMaxTextureImageSize;
     GLint mMaxRenderbufferSize;
     GLint mMaxViewportDims[2];
     GLsizei mMaxSamples;
     bool mNeedsTextureSizeChecks;
     bool mNeedsFlushBeforeDeleteFB;
+    bool mTextureAllocCrashesOnMapFailure;
     bool mWorkAroundDriverBugs;
 
     bool IsTextureSizeSafeToPassToDriver(GLenum target, GLsizei width, GLsizei height) const {
         if (mNeedsTextureSizeChecks) {
             // some drivers incorrectly handle some large texture sizes that are below the
             // max texture size that they report. So we check ourselves against our own values
             // (mMax[CubeMap]TextureSize).
             // see bug 737182 for Mac Intel 2D textures
@@ -3694,12 +3689,18 @@ GLuint CreateTextureForOffscreen(GLConte
  *    GL_TEXTURE_MIN_FILTER = GL_LINEAR
  *    GL_TEXTURE_MAG_FILTER = GL_LINEAR
  *    GL_TEXTURE_WRAP_S = GL_CLAMP_TO_EDGE
  *    GL_TEXTURE_WRAP_T = GL_CLAMP_TO_EDGE
  */
 GLuint CreateTexture(GLContext* aGL, GLenum aInternalFormat, GLenum aFormat,
                      GLenum aType, const gfx::IntSize& aSize, bool linear = true);
 
+/**
+ * Helper function that calculates the number of bytes required per
+ * texel for a texture from its format and type.
+ */
+uint32_t GetBytesPerTexel(GLenum format, GLenum type);
+
 } /* namespace gl */
 } /* namespace mozilla */
 
 #endif /* GLCONTEXT_H_ */
--- a/gfx/gl/GLUploadHelpers.cpp
+++ b/gfx/gl/GLUploadHelpers.cpp
@@ -376,61 +376,16 @@ TexImage2DHelper(GLContext* gl,
                         format,
                         type,
                         pixels);
         gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 0);
         gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
     }
 }
 
-static uint32_t
-GetBytesPerTexel(GLenum format, GLenum type)
-{
-    // If there is no defined format or type, we're not taking up any memory
-    if (!format || !type) {
-        return 0;
-    }
-
-    if (format == LOCAL_GL_DEPTH_COMPONENT) {
-        if (type == LOCAL_GL_UNSIGNED_SHORT)
-            return 2;
-        else if (type == LOCAL_GL_UNSIGNED_INT)
-            return 4;
-    } else if (format == LOCAL_GL_DEPTH_STENCIL) {
-        if (type == LOCAL_GL_UNSIGNED_INT_24_8_EXT)
-            return 4;
-    }
-
-    if (type == LOCAL_GL_UNSIGNED_BYTE || type == LOCAL_GL_FLOAT || type == LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV) {
-        uint32_t multiplier = type == LOCAL_GL_UNSIGNED_BYTE ? 1 : 4;
-        switch (format) {
-            case LOCAL_GL_ALPHA:
-            case LOCAL_GL_LUMINANCE:
-                return 1 * multiplier;
-            case LOCAL_GL_LUMINANCE_ALPHA:
-                return 2 * multiplier;
-            case LOCAL_GL_RGB:
-                return 3 * multiplier;
-            case LOCAL_GL_RGBA:
-                return 4 * multiplier;
-            default:
-                break;
-        }
-    } else if (type == LOCAL_GL_UNSIGNED_SHORT_4_4_4_4 ||
-               type == LOCAL_GL_UNSIGNED_SHORT_5_5_5_1 ||
-               type == LOCAL_GL_UNSIGNED_SHORT_5_6_5)
-    {
-        return 2;
-    }
-
-    gfxCriticalError() << "Unknown texture type " << type << " or format " << format;
-    MOZ_CRASH();
-    return 0;
-}
-
 SurfaceFormat
 UploadImageDataToTexture(GLContext* gl,
                          unsigned char* aData,
                          int32_t aStride,
                          SurfaceFormat aFormat,
                          const nsIntRegion& aDstRegion,
                          GLuint& aTexture,
                          size_t* aOutUploadSize,
--- 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;