Merge m-c to b2g-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 16 Oct 2014 16:16:53 +0200
changeset 210722 16a6ed2425550ca85aa3d8c6091a7ae9e30a24ca
parent 210721 eb7d8010ee1e9d48725d95cd1c42982b2af8d6f0 (current diff)
parent 210706 77f3ca1fe052ca3eced92af99221e29a7fc6c36b (diff)
child 210723 3dba8eb39239593e0ab089114096d5c51e7f1463
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
milestone36.0a1
Merge m-c to b2g-inbound
content/base/src/nsChannelPolicy.cpp
content/base/src/nsChannelPolicy.h
netwerk/base/public/nsChannelProperties.h
netwerk/base/public/nsIChannelPolicy.idl
netwerk/base/public/nsNetStrings.h
netwerk/base/src/nsNetStrings.cpp
toolkit/content/aboutWebrtc.xhtml
tools/page-loader/PageData.pm
tools/page-loader/README.txt
tools/page-loader/RegistryPrefork.pm
tools/page-loader/URLTimingDataSet.pm
tools/page-loader/URLTimingGraph.pm
tools/page-loader/dump.pl
tools/page-loader/echo.pl
tools/page-loader/graph.pl
tools/page-loader/loader.pl
tools/page-loader/report.pl
tools/page-loader/urllist.txt
tools/performance/diff-talos.py
tools/performance/pageload/base/bugzilla.mozilla.org/index.html
tools/performance/pageload/base/bugzilla.mozilla.org/res/mozilla-banner.gif
tools/performance/pageload/base/lxr.mozilla.org/index.html
tools/performance/pageload/base/lxr.mozilla.org/res/mozilla-banner.gif
tools/performance/pageload/base/vanilla-page/index.html
tools/performance/pageload/cycler.html
tools/performance/pageload/header.html
tools/performance/pageload/report.html
tools/performance/pageload/start.html
tools/performance/startup/gettime.pl
tools/performance/startup/quit.html
tools/performance/startup/quit.js
tools/performance/startup/quitForMac.html
tools/performance/startup/startup-test.html
tools/performance/startup/startup-unix.pl
tools/test-harness/jssh-driver/data/foo.html
tools/test-harness/jssh-driver/data/foo_inner_html.txt
tools/test-harness/jssh-driver/jssh_driver.py
tools/test-harness/jssh-driver/test_layout_engine.py
--- a/b2g/chrome/content/content.css
+++ b/b2g/chrome/content/content.css
@@ -173,18 +173,16 @@ input[type="submit"],
 input[type="reset"],
 button {
   border-width: 1px;
   padding: 0 7px 0 7px;
 }
 
 input[type="radio"],
 input[type="checkbox"] {
-  max-width: 14px;
-  max-height: 14px;
   border: 1px solid #a7a7a7 !important;
   padding: 2px 1px 2px 1px;
 }
 
 select > button {
   border-width: 0px !important;
   margin: 0px !important;
   padding: 0px !important;
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1375,16 +1375,23 @@ pref("devtools.debugger.ui.panes-visible
 pref("devtools.debugger.ui.variables-sorting-enabled", true);
 pref("devtools.debugger.ui.variables-only-enum-visible", false);
 pref("devtools.debugger.ui.variables-searchbox-visible", false);
 
 // Enable the Profiler and the Timeline
 pref("devtools.profiler.enabled", true);
 pref("devtools.timeline.enabled", false);
 
+// Enable perftools via build command
+#ifdef MOZ_DEVTOOLS_PERFTOOLS
+  pref("devtools.performance_dev.enabled", true);
+#else
+  pref("devtools.performance_dev.enabled", false);
+#endif
+
 // The default Profiler UI settings
 pref("devtools.profiler.ui.show-platform-data", false);
 
 // The default cache UI setting
 pref("devtools.cache.disabled", false);
 
 // Enable the Network Monitor
 pref("devtools.netmonitor.enabled", true);
--- a/browser/base/content/aboutDialog.xul
+++ b/browser/base/content/aboutDialog.xul
@@ -123,17 +123,17 @@
             <description class="text-blurb" id="communityExperimentalDesc">
               &community.exp.start;<label class="text-link" href="http://www.mozilla.org/">&community.exp.mozillaLink;</label>&community.exp.middle;<label class="text-link" href="about:credits">&community.exp.creditsLink;</label>&community.exp.end;
             </description>
           </vbox>
           <description class="text-blurb" id="communityDesc">
             &community.start2;<label class="text-link" href="http://www.mozilla.org/">&community.mozillaLink;</label>&community.middle2;<label class="text-link" href="about:credits">&community.creditsLink;</label>&community.end3;
           </description>
           <description class="text-blurb" id="contributeDesc">
-            &contribute.start;<label class="text-link" href="http://www.mozilla.org/contribute/">&contribute.getInvolvedLink;</label>&contribute.end;
+              &helpus.start;<label class="text-link" href="https://sendto.mozilla.org/page/contribute/Give-Now?source=mozillaorg_default_footer&#38;ref=firefox_about&#38;utm_campaign=firefox_about&#38;tm_source=firefox&#38;tm_medium=referral&#38;utm_content=20140929_FireFoxAbout">&helpus.donateLink;</label>&helpus.middle;<label class="text-link" href="http://www.mozilla.org/contribute/">&helpus.getInvolvedLink;</label>&helpus.end;
           </description>
         </vbox>
       </vbox>
     </hbox>
     <vbox id="bottomBox">
       <hbox pack="center">
         <label class="text-link bottom-link" href="about:license">&bottomLinks.license;</label>
         <label class="text-link bottom-link" href="about:rights">&bottomLinks.rights;</label>
--- a/browser/base/content/newtab/updater.js
+++ b/browser/base/content/newtab/updater.js
@@ -149,32 +149,29 @@ let gUpdater = {
 
   /**
    * Tries to fill empty cells with new links if available.
    * @param aLinks The array of links.
    * @param aCallback The callback to call when finished.
    */
   _fillEmptyCells: function Updater_fillEmptyCells(aLinks, aCallback) {
     let {cells, sites} = gGrid;
-    let batch = [];
 
     // Find empty cells and fill them.
-    sites.forEach(function (aSite, aIndex) {
+    Promise.all(sites.map((aSite, aIndex) => {
       if (aSite || !aLinks[aIndex])
-        return;
+        return null;
 
-      batch.push(new Promise(resolve => {
+      return new Promise(resolve => {
         // Create the new site and fade it in.
         let site = gGrid.createSite(aLinks[aIndex], cells[aIndex]);
 
         // Set the site's initial opacity to zero.
         site.node.style.opacity = 0;
 
         // Flush all style changes for the dynamically inserted site to make
         // the fade-in transition work.
         window.getComputedStyle(site.node).opacity;
         gTransformation.showSite(site, resolve);
-      }));
-    });
-
-    Promise.all(batch).then(aCallback);
+      });
+    })).then(aCallback).catch(console.exception);
   }
 };
--- a/browser/base/content/test/general/browser_notification_tab_switching.js
+++ b/browser/base/content/test/general/browser_notification_tab_switching.js
@@ -1,58 +1,95 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-"use strict";
-
-let tab;
-let notification;
-let notificationURL = "http://example.org/browser/browser/base/content/test/general/file_dom_notifications.html";
-
-function test () {
-  waitForExplicitFinish();
-
-  let pm = Services.perms;
-  registerCleanupFunction(function() {
-    pm.remove(notificationURL, "desktop-notification");
-    gBrowser.removeTab(tab);
-    window.restore();
-  });
-
-  pm.add(makeURI(notificationURL), "desktop-notification", pm.ALLOW_ACTION);
-
-  tab = gBrowser.addTab(notificationURL);
-  tab.linkedBrowser.addEventListener("load", onLoad, true);
-}
-
-function onLoad() {
-  isnot(gBrowser.selectedTab, tab, "Notification page loaded as a background tab");
-  tab.linkedBrowser.removeEventListener("load", onLoad, true);
-  let win = tab.linkedBrowser.contentWindow.wrappedJSObject;
-  notification = win.showNotification();
-  notification.addEventListener("show", onAlertShowing);
-}
-
-function onAlertShowing() {
-  info("Notification alert showing");
-  notification.removeEventListener("show", onAlertShowing);
-
-  let alertWindow = findChromeWindowByURI("chrome://global/content/alerts/alert.xul");
-  if (!alertWindow) {
-    todo(false, "Notifications don't use XUL windows on all platforms.");
-    notification.close();
-    finish();
-    return;
-  }
-  gBrowser.tabContainer.addEventListener("TabSelect", onTabSelect);
-  EventUtils.synthesizeMouseAtCenter(alertWindow.document.getElementById("alertTitleLabel"), {}, alertWindow);
-  info("Clicked on notification");
-  alertWindow.close();
-}
-
-function onTabSelect() {
-  gBrowser.tabContainer.removeEventListener("TabSelect", onTabSelect);
-  is(gBrowser.selectedTab.linkedBrowser.contentWindow.location.href, notificationURL,
-     "Notification tab should be selected.");
-
-  finish();
-}
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+"use strict";
+
+let tab;
+let notification;
+let notificationURL = "http://example.org/browser/browser/base/content/test/general/file_dom_notifications.html";
+let newWindowOpenedFromTab;
+
+function test () {
+  waitForExplicitFinish();
+
+  let pm = Services.perms;
+  registerCleanupFunction(function() {
+    pm.remove(notificationURL, "desktop-notification");
+    gBrowser.removeTab(tab);
+    window.restore();
+  });
+
+  pm.add(makeURI(notificationURL), "desktop-notification", pm.ALLOW_ACTION);
+
+  tab = gBrowser.addTab(notificationURL);
+  tab.linkedBrowser.addEventListener("load", onLoad, true);
+}
+
+function onLoad() {
+  isnot(gBrowser.selectedTab, tab, "Notification page loaded as a background tab");
+  tab.linkedBrowser.removeEventListener("load", onLoad, true);
+  let win = tab.linkedBrowser.contentWindow.wrappedJSObject;
+  win.newWindow = win.open("about:blank", "", "height=100,width=100");
+  newWindowOpenedFromTab = win.newWindow;
+  win.newWindow.addEventListener("load", function() {
+    info("new window loaded");
+    win.newWindow.addEventListener("blur", function b() {
+      info("new window got blur");
+      win.newWindow.removeEventListener("blur", b);
+      notification = win.showNotification1();
+      win.newWindow.addEventListener("focus", onNewWindowFocused);
+      notification.addEventListener("show", onAlertShowing);
+    });
+
+    function waitUntilNewWindowHasFocus() {
+      if (!win.newWindow.document.hasFocus()) {
+        setTimeout(waitUntilNewWindowHasFocus, 50);
+      } else {
+        // Focus another window so that new window gets blur event.
+        gBrowser.selectedTab.linkedBrowser.contentWindow.focus();
+      }
+    }
+    win.newWindow.focus();
+    waitUntilNewWindowHasFocus();
+  });
+}
+
+function onAlertShowing() {
+  info("Notification alert showing");
+  notification.removeEventListener("show", onAlertShowing);
+
+  let alertWindow = findChromeWindowByURI("chrome://global/content/alerts/alert.xul");
+  if (!alertWindow) {
+    todo(false, "Notifications don't use XUL windows on all platforms.");
+    notification.close();
+    newWindowOpenedFromTab.close();
+    finish();
+    return;
+  }
+  gBrowser.tabContainer.addEventListener("TabSelect", onTabSelect);
+  EventUtils.synthesizeMouseAtCenter(alertWindow.document.getElementById("alertTitleLabel"), {}, alertWindow);
+  info("Clicked on notification");
+  alertWindow.close();
+}
+
+function onNewWindowFocused(event) {
+  event.target.close();
+  isnot(gBrowser.selectedTab, tab, "Notification page loaded as a background tab");
+  // Using timeout to test that something do *not* happen!
+  setTimeout(openSecondNotification, 50);
+}
+
+function openSecondNotification() {
+  isnot(gBrowser.selectedTab, tab, "Notification page loaded as a background tab");
+  let win = tab.linkedBrowser.contentWindow.wrappedJSObject;
+  notification = win.showNotification2();
+  notification.addEventListener("show", onAlertShowing);
+}
+
+function onTabSelect() {
+  gBrowser.tabContainer.removeEventListener("TabSelect", onTabSelect);
+  is(gBrowser.selectedTab.linkedBrowser.contentWindow.location.href, notificationURL,
+     "Notification tab should be selected.");
+
+  finish();
+}
--- a/browser/base/content/test/general/file_dom_notifications.html
+++ b/browser/base/content/test/general/file_dom_notifications.html
@@ -1,23 +1,40 @@
-<html>
-<head>
-<script>
-"use strict";
-
-function showNotification() {
-  var options = {
-      dir: undefined,
-      lang: undefined,
-      body: "Test body",
-      tag: "Test tag",
-      icon: undefined,
-  };
-  return new Notification("Test title", options);
-}
-</script>
-</head>
-<body>
-<form id="notificationForm" onsubmit="showNotification();">
-  <input type="submit" value="Show notification" id="submit"/>
-</form>
-</body>
-</html>
+<html>
+<head>
+<script>
+"use strict";
+
+function showNotification1() {
+  var options = {
+      dir: undefined,
+      lang: undefined,
+      body: "Test body",
+      tag: "Test tag",
+      icon: undefined,
+  };
+  var n = new Notification("Test title", options);
+  n.addEventListener("click", function(event) {
+    event.preventDefault();
+    dump("Should focus new window.");
+    newWindow.focus();
+  });
+  return n;
+}
+
+function showNotification2() {
+  var options = {
+      dir: undefined,
+      lang: undefined,
+      body: "Test body",
+      tag: "Test tag",
+      icon: undefined,
+  };
+  return new Notification("Test title", options);
+}
+</script>
+</head>
+<body>
+<form id="notificationForm" onsubmit="showNotification();">
+  <input type="submit" value="Show notification" id="submit"/>
+</form>
+</body>
+</html>
--- a/browser/base/content/test/social/browser.ini
+++ b/browser/base/content/test/social/browser.ini
@@ -44,16 +44,17 @@ skip-if = e10s # Bug 915547 (social prov
 skip-if = e10s # Bug 915547 (social providers don't install)
 [browser_social_errorPage.js]
 [browser_social_flyout.js]
 skip-if = e10s # when we backed out bug 1047603, this test broke.
 [browser_social_isVisible.js]
 [browser_social_marks.js]
 skip-if = e10s # Bug 915547 (social providers don't install)
 [browser_social_multiprovider.js]
+skip-if = e10s # Bug 1069162 - lots of orange
 [browser_social_multiworker.js]
 [browser_social_perwindowPB.js]
 [browser_social_sidebar.js]
 [browser_social_status.js]
 skip-if = e10s # Bug 915547 (social providers don't install)
 [browser_social_window.js]
 [browser_social_workercrash.js]
 skip-if = !crashreporter
--- a/browser/components/customizableui/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/CustomizableWidgets.jsm
@@ -918,17 +918,17 @@ const CustomizableWidgets = [
     tooltiptext: "email-link-button.tooltiptext3",
     onCommand: function(aEvent) {
       let win = aEvent.view;
       win.MailIntegration.sendLinkForWindow(win.content);
     }
   }, {
     id: "loop-call-button",
     type: "custom",
-    label: "loop-call-button2.label",
+    label: "loop-call-button3.label",
     tooltiptext: "loop-call-button2.tooltiptext",
     defaultArea: CustomizableUI.AREA_NAVBAR,
     introducedInVersion: 1,
     onBuild: function(aDocument) {
       let node = aDocument.createElementNS(kNSXUL, "toolbarbutton");
       node.setAttribute("id", this.id);
       node.classList.add("toolbarbutton-1");
       node.classList.add("chromeclass-toolbar-additional");
--- a/browser/devtools/jar.mn
+++ b/browser/devtools/jar.mn
@@ -81,16 +81,20 @@ browser.jar:
     content/browser/devtools/webaudioeditor/controller.js              (webaudioeditor/controller.js)
     content/browser/devtools/webaudioeditor/views/utils.js             (webaudioeditor/views/utils.js)
     content/browser/devtools/webaudioeditor/views/context.js           (webaudioeditor/views/context.js)
     content/browser/devtools/webaudioeditor/views/inspector.js         (webaudioeditor/views/inspector.js)
     content/browser/devtools/profiler.xul                              (profiler/profiler.xul)
     content/browser/devtools/profiler.js                               (profiler/profiler.js)
     content/browser/devtools/ui-recordings.js                          (profiler/ui-recordings.js)
     content/browser/devtools/ui-profile.js                             (profiler/ui-profile.js)
+#ifdef MOZ_DEVTOOLS_PERFTOOLS
+    content/browser/devtools/performance.xul                           (performance/performance.xul)
+    content/browser/devtools/performance.js                            (performance/performance.js)
+#endif
     content/browser/devtools/responsivedesign/resize-commands.js       (responsivedesign/resize-commands.js)
     content/browser/devtools/commandline.css                           (commandline/commandline.css)
     content/browser/devtools/commandlineoutput.xhtml                   (commandline/commandlineoutput.xhtml)
     content/browser/devtools/commandlinetooltip.xhtml                  (commandline/commandlinetooltip.xhtml)
     content/browser/devtools/commandline/commands-index.js             (commandline/commands-index.js)
     content/browser/devtools/framework/toolbox-window.xul              (framework/toolbox-window.xul)
     content/browser/devtools/framework/toolbox-options.xul             (framework/toolbox-options.xul)
     content/browser/devtools/framework/toolbox-options.js              (framework/toolbox-options.js)
--- a/browser/devtools/main.js
+++ b/browser/devtools/main.js
@@ -26,16 +26,17 @@ loader.lazyGetter(this, "OptionsPanel", 
 loader.lazyGetter(this, "InspectorPanel", () => require("devtools/inspector/inspector-panel").InspectorPanel);
 loader.lazyGetter(this, "WebConsolePanel", () => require("devtools/webconsole/panel").WebConsolePanel);
 loader.lazyGetter(this, "DebuggerPanel", () => require("devtools/debugger/panel").DebuggerPanel);
 loader.lazyGetter(this, "StyleEditorPanel", () => require("devtools/styleeditor/styleeditor-panel").StyleEditorPanel);
 loader.lazyGetter(this, "ShaderEditorPanel", () => require("devtools/shadereditor/panel").ShaderEditorPanel);
 loader.lazyGetter(this, "CanvasDebuggerPanel", () => require("devtools/canvasdebugger/panel").CanvasDebuggerPanel);
 loader.lazyGetter(this, "WebAudioEditorPanel", () => require("devtools/webaudioeditor/panel").WebAudioEditorPanel);
 loader.lazyGetter(this, "ProfilerPanel", () => require("devtools/profiler/panel").ProfilerPanel);
+loader.lazyGetter(this, "PerformancePanel", () => require("devtools/performance/panel").PerformancePanel);
 loader.lazyGetter(this, "TimelinePanel", () => require("devtools/timeline/panel").TimelinePanel);
 loader.lazyGetter(this, "NetMonitorPanel", () => require("devtools/netmonitor/panel").NetMonitorPanel);
 loader.lazyGetter(this, "StoragePanel", () => require("devtools/storage/panel").StoragePanel);
 loader.lazyGetter(this, "ScratchpadPanel", () => require("devtools/scratchpad/scratchpad-panel").ScratchpadPanel);
 
 // Strings
 const toolboxProps = "chrome://browser/locale/devtools/toolbox.properties";
 const inspectorProps = "chrome://browser/locale/devtools/inspector.properties";
@@ -269,16 +270,42 @@ Tools.jsprofiler = {
     return !target.isAddon && (!target.isApp || target.form.profilerActor);
   },
 
   build: function (frame, target) {
     return new ProfilerPanel(frame, target);
   }
 };
 
+Tools.performance = {
+  id: "performance",
+  ordinal: 19,
+  icon: "chrome://browser/skin/devtools/tool-profiler.svg",
+  invertIconForLightTheme: true,
+  url: "chrome://browser/content/devtools/performance.xul",
+  // TODO bug 1082695 audit the Performance tools labels
+  label: "Performance++", //l10n("profiler.label2", profilerStrings),
+  panelLabel: "Performance++", //l10n("profiler.panelLabel2", profilerStrings),
+  tooltip: l10n("profiler.tooltip2", profilerStrings),
+  accesskey: l10n("profiler.accesskey", profilerStrings),
+  key: l10n("profiler.commandkey2", profilerStrings),
+  modifiers: "shift",
+  inMenu: true,
+
+  isTargetSupported: function (target) {
+    // Hide the profiler when debugging devices pre bug 1046394,
+    // that don't expose profiler actor in content processes.
+    return !target.isAddon && (!target.isApp || target.form.profilerActor);
+  },
+
+  build: function (frame, target) {
+    return new PerformancePanel(frame, target);
+  }
+};
+
 Tools.timeline = {
   id: "timeline",
   ordinal: 8,
   visibilityswitch: "devtools.timeline.enabled",
   icon: "chrome://browser/skin/devtools/tool-network.svg",
   invertIconForLightTheme: true,
   url: "chrome://browser/content/devtools/timeline/timeline.xul",
   label: l10n("timeline.label", timelineStrings),
@@ -399,16 +426,26 @@ let defaultTools = [
   Tools.webAudioEditor,
   Tools.jsprofiler,
   Tools.timeline,
   Tools.netMonitor,
   Tools.storage,
   Tools.scratchpad
 ];
 
+// Only enable in-development performance tools if `--enable-devtools-perf`
+// used in build, turning on `devtools.performance_dev.enabled`.
+// Add to normal `defaultTools` when ready for normal release,
+// pull out MOZ_DEVTOOLS_PERFTOOLS setting in `./configure.in`, and
+// leave config on in `./browser/app/profile/firefox.js`, and always
+// build in `./browser/devtools/moz.build`.
+if (Services.prefs.getBoolPref("devtools.performance_dev.enabled")) {
+  defaultTools.push(Tools.performance);
+}
+
 exports.defaultTools = defaultTools;
 
 for (let definition of defaultTools) {
   gDevTools.registerTool(definition);
 }
 
 Tools.darkTheme = {
   id: "dark",
--- a/browser/devtools/moz.build
+++ b/browser/devtools/moz.build
@@ -28,16 +28,19 @@ DIRS += [
     'styleinspector',
     'tilt',
     'timeline',
     'webaudioeditor',
     'webconsole',
     'webide',
 ]
 
+if CONFIG['MOZ_DEVTOOLS_PERFTOOLS']:
+  DIRS += ['performance']
+
 EXTRA_COMPONENTS += [
     'devtools-clhandler.js',
     'devtools-clhandler.manifest',
 ]
 
 JAR_MANIFESTS += ['jar.mn']
 
 EXTRA_JS_MODULES.devtools += [
new file mode 100644
--- /dev/null
+++ b/browser/devtools/performance/moz.build
@@ -0,0 +1,8 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+EXTRA_JS_MODULES.devtools.performance += [
+    'panel.js'
+]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/performance/panel.js
@@ -0,0 +1,62 @@
+/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+const {Cc, Ci, Cu, Cr} = require("chrome");
+
+Cu.import("resource://gre/modules/Task.jsm");
+
+loader.lazyRequireGetter(this, "promise");
+loader.lazyRequireGetter(this, "EventEmitter",
+  "devtools/toolkit/event-emitter");
+
+function PerformancePanel(iframeWindow, toolbox) {
+  this.panelWin = iframeWindow;
+  this._toolbox = toolbox;
+
+  EventEmitter.decorate(this);
+}
+
+exports.PerformancePanel = PerformancePanel;
+
+PerformancePanel.prototype = {
+  /**
+   * Open is effectively an asynchronous constructor.
+   *
+   * @return object
+   *         A promise that is resolved when the Profiler completes opening.
+   */
+  open: Task.async(function*() {
+    this.panelWin.gToolbox = this._toolbox;
+    this.panelWin.gTarget = this.target;
+
+    // Mock Front for now
+    let gFront = {};
+    EventEmitter.decorate(gFront);
+    this.panelWin.gFront = gFront;
+
+    yield this.panelWin.startupPerformance();
+
+    this.isReady = true;
+    this.emit("ready");
+    return this;
+  }),
+
+  // DevToolPanel API
+
+  get target() this._toolbox.target,
+
+  destroy: Task.async(function*() {
+    // Make sure this panel is not already destroyed.
+    if (this._destroyed) {
+      return;
+    }
+
+    yield this.panelWin.shutdownPerformance();
+    this.emit("destroyed");
+    this._destroyed = true;
+  })
+};
new file mode 100644
--- /dev/null
+++ b/browser/devtools/performance/performance.js
@@ -0,0 +1,97 @@
+/* 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 { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+Cu.import("resource://gre/modules/Task.jsm");
+Cu.import("resource://gre/modules/devtools/Loader.jsm");
+Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
+
+devtools.lazyRequireGetter(this, "Services");
+devtools.lazyRequireGetter(this, "promise");
+devtools.lazyRequireGetter(this, "EventEmitter",
+  "devtools/toolkit/event-emitter");
+devtools.lazyRequireGetter(this, "DevToolsUtils",
+  "devtools/toolkit/DevToolsUtils");
+
+/**
+ * The current target and the profiler connection, set by this tool's host.
+ */
+let gToolbox, gTarget, gFront;
+
+/**
+ * Initializes the profiler controller and views.
+ */
+let startupPerformance = Task.async(function*() {
+  yield promise.all([
+    PrefObserver.register(),
+    EventsHandler.initialize()
+  ]);
+});
+
+/**
+ * Destroys the profiler controller and views.
+ */
+let shutdownPerformance = Task.async(function*() {
+  yield promise.all([
+    PrefObserver.unregister(),
+    EventsHandler.destroy()
+  ]);
+});
+
+/**
+ * Observes pref changes on the devtools.profiler branch and triggers the
+ * required frontend modifications.
+ */
+let PrefObserver = {
+  register: function() {
+    this.branch = Services.prefs.getBranch("devtools.profiler.");
+    this.branch.addObserver("", this, false);
+  },
+  unregister: function() {
+    this.branch.removeObserver("", this);
+  },
+  observe: function(subject, topic, pref) {
+    Prefs.refresh();
+  }
+};
+
+/**
+ * Functions handling target-related lifetime events.
+ */
+let EventsHandler = {
+  /**
+   * Listen for events emitted by the current tab target.
+   */
+  initialize: function() {
+  },
+
+  /**
+   * Remove events emitted by the current tab target.
+   */
+  destroy: function() {
+  }
+};
+
+/**
+ * Shortcuts for accessing various profiler preferences.
+ */
+const Prefs = new ViewHelpers.Prefs("devtools.profiler", {
+});
+
+/**
+ * Convenient way of emitting events from the panel window.
+ */
+EventEmitter.decorate(this);
+
+/**
+ * DOM query helpers.
+ */
+function $(selector, target = document) {
+  return target.querySelector(selector);
+}
+function $$(selector, target = document) {
+  return target.querySelectorAll(selector);
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/performance/performance.xul
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://browser/content/devtools/widgets.css" type="text/css"?>
+<?xml-stylesheet href="chrome://browser/skin/devtools/common.css" type="text/css"?>
+<?xml-stylesheet href="chrome://browser/skin/devtools/widgets.css" type="text/css"?>
+<?xml-stylesheet href="chrome://browser/skin/devtools/performance.css" type="text/css"?>
+<!DOCTYPE window [
+  <!ENTITY % profilerDTD SYSTEM "chrome://browser/locale/devtools/profiler.dtd">
+  %profilerDTD;
+]>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script src="chrome://browser/content/devtools/theme-switching.js"/>
+  <script type="application/javascript" src="performance.js"/>
+
+  <vbox class="theme-body" flex="1">
+    <toolbar id="performance-toolbar" class="devtools-toolbar">
+      <hbox id="performance-toolbar-controls-recordings" class="devtools-toolbarbutton-group">
+        <toolbarbutton id="record-button"
+                       class="devtools-toolbarbutton"
+                       tooltiptext="&profilerUI.recordButton.tooltip;"/>
+        <toolbarbutton id="clear-button"
+                       class="devtools-toolbarbutton"
+                       label="&profilerUI.clearButton;"/>
+      </hbox>
+      <spacer flex="1"></spacer>
+      <hbox id="performance-toolbar-controls-storage" class="devtools-toolbarbutton-group">
+        <toolbarbutton id="import-button"
+                       class="devtools-toolbarbutton"
+                       label="&profilerUI.importButton;"/>
+      </hbox>
+    </toolbar>
+    <splitter class="devtools-horizontal-splitter" />
+    <box id="overview-pane"
+         class="devtools-responsive-container"
+         flex="1">
+    </box>
+    <splitter class="devtools-horizontal-splitter" />
+    <box id="details-pane"
+         class="devtools-responsive-container"
+         flex="1">
+    </box>
+  </vbox>
+</window>
--- a/browser/devtools/webaudioeditor/test/browser.ini
+++ b/browser/devtools/webaudioeditor/test/browser.ini
@@ -17,16 +17,17 @@ support-files =
 
 [browser_audionode-actor-get-param-flags.js]
 [browser_audionode-actor-get-params-01.js]
 [browser_audionode-actor-get-params-02.js]
 [browser_audionode-actor-get-set-param.js]
 [browser_audionode-actor-get-type.js]
 [browser_audionode-actor-is-source.js]
 [browser_audionode-actor-bypass.js]
+[browser_audionode-actor-connectnode-disconnect.js]
 [browser_webaudio-actor-simple.js]
 [browser_webaudio-actor-destroy-node.js]
 [browser_webaudio-actor-connect-param.js]
 
 [browser_wa_destroy-node-01.js]
 
 [browser_wa_first-run.js]
 [browser_wa_reset-01.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webaudioeditor/test/browser_audionode-actor-connectnode-disconnect.js
@@ -0,0 +1,44 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests that AudioNodeActor#connectNode() and AudioNodeActor#disconnect() work.
+ * Uses the editor front as the actors do not retain connect state.
+ */
+
+function spawnTest() {
+  let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
+  let { panelWin } = panel;
+  let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin;
+
+  reload(target);
+
+  let [actors] = yield Promise.all([
+    get3(gFront, "create-node"),
+    waitForGraphRendered(panelWin, 3, 2)
+  ]);
+
+  let [dest, osc, gain] = actors;
+
+  info("Disconnecting oscillator...");
+  osc.disconnect();
+  yield Promise.all([
+    waitForGraphRendered(panelWin, 3, 1),
+    once(gAudioNodes, "disconnect")
+  ]);
+  ok(true, "Oscillator disconnected, event emitted.");
+
+
+  info("Reconnecting oscillator...");
+  osc.connectNode(gain);
+  yield Promise.all([
+    waitForGraphRendered(panelWin, 3, 2),
+    once(gAudioNodes, "connect")
+  ]);
+  ok(true, "Oscillator reconnected.");
+
+
+  yield teardown(panel);
+  finish();
+}
+
--- a/browser/locales/en-US/chrome/browser/aboutDialog.dtd
+++ b/browser/locales/en-US/chrome/browser/aboutDialog.dtd
@@ -33,20 +33,23 @@
 <!ENTITY community.start2           "&brandShortName; is designed by ">
 <!-- LOCALIZATION NOTE (community.mozillaLink): This is a link title that links to http://www.mozilla.org/. -->
 <!ENTITY community.mozillaLink      "&vendorShortName;">
 <!ENTITY community.middle2          ", a ">
 <!-- LOCALIZATION NOTE (community.creditsLink): This is a link title that links to about:credits. -->
 <!ENTITY community.creditsLink      "global community">
 <!ENTITY community.end3             " working together to keep the Web open, public and accessible to all.">
 
-<!ENTITY contribute.start           "Sound interesting? ">
-<!-- LOCALIZATION NOTE (contribute.getInvolvedLink): This is a link title that links to http://www.mozilla.org/contribute/. -->
-<!ENTITY contribute.getInvolvedLink "Get involved!">
-<!ENTITY contribute.end             "">
+<!ENTITY helpus.start               "Want to help? ">
+<!-- LOCALIZATION NOTE (helpus.donateLink): This is a link title that links to https://sendto.mozilla.org/page/contribute/Give-Now?source=mozillaorg_default_footer&ref=firefox_about&utm_campaign=firefox_about&utm_source=firefox&utm_medium=referral&utm_content=20140929_FireFoxAbout. -->
+<!ENTITY helpus.donateLink          "Make a donation">
+<!ENTITY helpus.middle              " or ">
+<!-- LOCALIZATION NOTE (helpus.getInvolvedLink): This is a link title that links to http://www.mozilla.org/contribute/. -->
+<!ENTITY helpus.getInvolvedLink     "get involved!">
+<!ENTITY helpus.end                 "">
 
 <!-- LOCALIZATION NOTE (bottomLinks.license): This is a link title that links to about:license. -->
 <!ENTITY bottomLinks.license        "Licensing Information">
 
 <!-- LOCALIZATION NOTE (bottomLinks.rights): This is a link title that links to about:rights. -->
 <!ENTITY bottomLinks.rights         "End-User Rights">
 
 <!-- LOCALIZATION NOTE (bottomLinks.privacy): This is a link title that links to https://www.mozilla.org/legal/privacy/. -->
--- a/browser/locales/en-US/chrome/browser/customizableui/customizableWidgets.properties
+++ b/browser/locales/en-US/chrome/browser/customizableui/customizableWidgets.properties
@@ -92,17 +92,19 @@ email-link-button.tooltiptext3 = Email a
 
 # LOCALIZATION NOTE(quit-button.tooltiptext.linux2): %1$S is the brand name (e.g. Firefox),
 # %2$S is the keyboard shortcut
 quit-button.tooltiptext.linux2 = Quit %1$S (%2$S)
 # LOCALIZATION NOTE(quit-button.tooltiptext.mac): %1$S is the brand name (e.g. Firefox),
 # %2$S is the keyboard shortcut
 quit-button.tooltiptext.mac = Quit %1$S (%2$S)
 
-loop-call-button2.label = Start a conversation
+# LOCALIZATION NOTE(loop-call-button2.label2): This is a brand name, request
+# approval before you change it.
+loop-call-button3.label = Hello
 loop-call-button2.tooltiptext = Start a conversation
 
 social-share-button.label = Share This Page
 social-share-button.tooltiptext = Share This Page
 
 panic-button.label = Forget
 panic-button.tooltiptext = Forget about some browsing history
 
--- a/browser/modules/E10SUtils.jsm
+++ b/browser/modules/E10SUtils.jsm
@@ -14,17 +14,18 @@ this.E10SUtils = {
   shouldBrowserBeRemote: function(aURL) {
     // loadURI in browser.xml treats null as about:blank
     if (!aURL)
       aURL = "about:blank";
 
     if (aURL.startsWith("about:") &&
         aURL.toLowerCase() != "about:home" &&
         aURL.toLowerCase() != "about:blank" &&
-        !aURL.toLowerCase().startsWith("about:neterror")) {
+        !aURL.toLowerCase().startsWith("about:neterror") &&
+        !aURL.toLowerCase().startsWith("about:certerror")) {
       return false;
     }
 
     return true;
   },
 
   shouldLoadURI: function(aDocShell, aURI, aReferrer) {
     // about:blank is the initial document and can load anywhere
--- a/browser/modules/UITour.jsm
+++ b/browser/modules/UITour.jsm
@@ -21,16 +21,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
   "resource:///modules/CustomizableUI.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "UITelemetry",
   "resource://gre/modules/UITelemetry.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "BrowserUITelemetry",
   "resource:///modules/BrowserUITelemetry.jsm");
 
 
 const UITOUR_PERMISSION   = "uitour";
+const PREF_TEST_WHITELIST = "browser.uitour.testingOrigins";
 const PREF_SEENPAGEIDS    = "browser.uitour.seenPageIDs";
 const MAX_BUTTONS         = 4;
 
 const BUCKET_NAME         = "UITour";
 const BUCKET_TIMESTEPS    = [
   1 * 60 * 1000, // Until 1 minute after tab is closed/inactive.
   3 * 60 * 1000, // Until 3 minutes after tab is closed/inactive.
   10 * 60 * 1000, // Until 10 minutes after tab is closed/inactive.
@@ -615,30 +616,52 @@ this.UITour = {
                            .getInterface(Ci.nsIWebNavigation)
                            .QueryInterface(Ci.nsIDocShellTreeItem)
                            .rootTreeItem
                            .QueryInterface(Ci.nsIInterfaceRequestor)
                            .getInterface(Ci.nsIDOMWindow)
                            .wrappedJSObject;
   },
 
+  isTestingOrigin: function(aURI) {
+    if (Services.prefs.getPrefType(PREF_TEST_WHITELIST) != Services.prefs.PREF_STRING) {
+      return false;
+    }
+
+    // Add any testing origins (comma-seperated) to the whitelist for the session.
+    for (let origin of Services.prefs.getCharPref(PREF_TEST_WHITELIST).split(",")) {
+      try {
+        let testingURI = Services.io.newURI(origin, null, null);
+        if (aURI.prePath == testingURI.prePath) {
+          return true;
+        }
+      } catch (ex) {
+        Cu.reportError(ex);
+      }
+    }
+    return false;
+  },
+
   ensureTrustedOrigin: function(aDocument) {
     if (aDocument.defaultView.top != aDocument.defaultView)
       return false;
 
     let uri = aDocument.documentURIObject;
 
     if (uri.schemeIs("chrome"))
       return true;
 
     if (!this.isSafeScheme(uri))
       return false;
 
     let permission = Services.perms.testPermission(uri, UITOUR_PERMISSION);
-    return permission == Services.perms.ALLOW_ACTION;
+    if (permission == Services.perms.ALLOW_ACTION)
+      return true;
+
+    return this.isTestingOrigin(uri);
   },
 
   isSafeScheme: function(aURI) {
     let allowedSchemes = new Set(["https", "about"]);
     if (!Services.prefs.getBoolPref("browser.uitour.requireSecure"))
       allowedSchemes.add("http");
 
     if (!allowedSchemes.has(aURI.scheme))
--- a/browser/modules/test/browser_UITour.js
+++ b/browser/modules/test/browser_UITour.js
@@ -20,16 +20,33 @@ let tests = [
       ise(bookmarksMenu.open, false, "Bookmark menu should initially be closed");
 
       gContentAPI.showMenu("bookmarks");
       ise(bookmarksMenu.open, false, "Bookmark menu should not open on a untrusted host");
 
       done();
     }, "http://mochi.test:8888/");
   },
+  function test_testing_host(done) {
+    // Add two testing origins intentionally surrounded by whitespace to be ignored.
+    Services.prefs.setCharPref("browser.uitour.testingOrigins",
+                               "https://test1.example.com, https://test2.example.com:443 ");
+
+    registerCleanupFunction(() => {
+      Services.prefs.clearUserPref("browser.uitour.testingOrigins");
+    });
+    function callback(result) {
+      ok(result, "Callback should be called on a testing origin");
+      done();
+    }
+
+    loadUITourTestPage(function() {
+      gContentAPI.getConfiguration("appinfo", callback);
+    }, "https://test2.example.com/");
+  },
   function test_unsecure_host(done) {
     loadUITourTestPage(function() {
       let bookmarksMenu = document.getElementById("bookmarks-menu-button");
       ise(bookmarksMenu.open, false, "Bookmark menu should initially be closed");
 
       gContentAPI.showMenu("bookmarks");
       ise(bookmarksMenu.open, false, "Bookmark menu should not open on a unsecure host");
 
new file mode 100644
--- /dev/null
+++ b/browser/themes/linux/devtools/performance.css
@@ -0,0 +1,5 @@
+/* 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/devtools/performance.inc.css
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -247,16 +247,17 @@ browser.jar:
   skin/classic/browser/devtools/breadcrumbs-divider@2x.png      (../shared/devtools/images/breadcrumbs-divider@2x.png)
   skin/classic/browser/devtools/breadcrumbs-scrollbutton.png    (../shared/devtools/images/breadcrumbs-scrollbutton.png)
   skin/classic/browser/devtools/breadcrumbs-scrollbutton@2x.png (../shared/devtools/images/breadcrumbs-scrollbutton@2x.png)
 * skin/classic/browser/devtools/canvasdebugger.css    (devtools/canvasdebugger.css)
 * skin/classic/browser/devtools/debugger.css          (devtools/debugger.css)
   skin/classic/browser/devtools/eyedropper.css        (../shared/devtools/eyedropper.css)
 * skin/classic/browser/devtools/netmonitor.css        (devtools/netmonitor.css)
 * skin/classic/browser/devtools/profiler.css          (devtools/profiler.css)
+* skin/classic/browser/devtools/performance.css       (devtools/performance.css)
 * skin/classic/browser/devtools/timeline.css          (devtools/timeline.css)
 * skin/classic/browser/devtools/scratchpad.css        (devtools/scratchpad.css)
 * skin/classic/browser/devtools/shadereditor.css      (devtools/shadereditor.css)
 * skin/classic/browser/devtools/splitview.css         (../shared/devtools/splitview.css)
   skin/classic/browser/devtools/styleeditor.css       (../shared/devtools/styleeditor.css)
   skin/classic/browser/devtools/storage.css           (../shared/devtools/storage.css)
 * skin/classic/browser/devtools/webaudioeditor.css    (devtools/webaudioeditor.css)
   skin/classic/browser/devtools/magnifying-glass.png        (../shared/devtools/images/magnifying-glass.png)
new file mode 100644
--- /dev/null
+++ b/browser/themes/osx/devtools/performance.css
@@ -0,0 +1,6 @@
+/* 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.inc
+%include ../../shared/devtools/performance.inc.css
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -377,16 +377,17 @@ browser.jar:
   skin/classic/browser/devtools/breadcrumbs-divider@2x.png      (../shared/devtools/images/breadcrumbs-divider@2x.png)
   skin/classic/browser/devtools/breadcrumbs-scrollbutton.png    (../shared/devtools/images/breadcrumbs-scrollbutton.png)
   skin/classic/browser/devtools/breadcrumbs-scrollbutton@2x.png (../shared/devtools/images/breadcrumbs-scrollbutton@2x.png)
 * skin/classic/browser/devtools/canvasdebugger.css          (devtools/canvasdebugger.css)
 * skin/classic/browser/devtools/debugger.css                (devtools/debugger.css)
   skin/classic/browser/devtools/eyedropper.css              (../shared/devtools/eyedropper.css)
 * skin/classic/browser/devtools/netmonitor.css              (devtools/netmonitor.css)
 * skin/classic/browser/devtools/profiler.css                (devtools/profiler.css)
+* skin/classic/browser/devtools/performance.css             (devtools/performance.css)
 * skin/classic/browser/devtools/timeline.css                (devtools/timeline.css)
 * skin/classic/browser/devtools/scratchpad.css              (devtools/scratchpad.css)
 * skin/classic/browser/devtools/shadereditor.css            (devtools/shadereditor.css)
 * skin/classic/browser/devtools/splitview.css               (../shared/devtools/splitview.css)
   skin/classic/browser/devtools/styleeditor.css             (../shared/devtools/styleeditor.css)
   skin/classic/browser/devtools/storage.css                 (../shared/devtools/storage.css)
 * skin/classic/browser/devtools/webaudioeditor.css          (devtools/webaudioeditor.css)
   skin/classic/browser/devtools/magnifying-glass.png        (../shared/devtools/images/magnifying-glass.png)
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/devtools/performance.inc.css
@@ -0,0 +1,34 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Toolbar */
+
+#performance-toolbar {
+  -moz-border-end: 1px solid;
+}
+
+.theme-dark #performance-toolbar > tabs,
+.theme-dark #performance-toolbar {
+  -moz-border-end-color: #000; /* Splitters */
+}
+
+.theme-light #performance-toolbar > tabs,
+.theme-light #performance-toolbar {
+  -moz-border-end-color: #aaa; /* Splitters */
+}
+
+/* Overview Panel */
+
+#record-button {
+  list-style-image: url(profiler-stopwatch.svg);
+}
+
+#record-button[checked] {
+  list-style-image: url(profiler-stopwatch-checked.svg);
+}
+
+#record-button[locked] {
+  pointer-events: none;
+}
new file mode 100644
--- /dev/null
+++ b/browser/themes/windows/devtools/performance.css
@@ -0,0 +1,5 @@
+/* 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/devtools/performance.inc.css
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -284,16 +284,17 @@ browser.jar:
         skin/classic/browser/devtools/breadcrumbs-divider@2x.png    (../shared/devtools/images/breadcrumbs-divider@2x.png)
         skin/classic/browser/devtools/breadcrumbs-scrollbutton.png  (../shared/devtools/images/breadcrumbs-scrollbutton.png)
         skin/classic/browser/devtools/breadcrumbs-scrollbutton@2x.png (../shared/devtools/images/breadcrumbs-scrollbutton@2x.png)
         skin/classic/browser/devtools/eyedropper.css                (../shared/devtools/eyedropper.css)
 *       skin/classic/browser/devtools/canvasdebugger.css            (devtools/canvasdebugger.css)
 *       skin/classic/browser/devtools/debugger.css                  (devtools/debugger.css)
 *       skin/classic/browser/devtools/netmonitor.css                (devtools/netmonitor.css)
 *       skin/classic/browser/devtools/profiler.css                  (devtools/profiler.css)
+*       skin/classic/browser/devtools/performance.css               (devtools/performance.css)
 *       skin/classic/browser/devtools/timeline.css                  (devtools/timeline.css)
 *       skin/classic/browser/devtools/scratchpad.css                (devtools/scratchpad.css)
 *       skin/classic/browser/devtools/shadereditor.css              (devtools/shadereditor.css)
         skin/classic/browser/devtools/storage.css                   (../shared/devtools/storage.css)
 *       skin/classic/browser/devtools/splitview.css                 (../shared/devtools/splitview.css)
         skin/classic/browser/devtools/styleeditor.css               (../shared/devtools/styleeditor.css)
 *       skin/classic/browser/devtools/webaudioeditor.css            (devtools/webaudioeditor.css)
         skin/classic/browser/devtools/magnifying-glass.png          (../shared/devtools/images/magnifying-glass.png)
--- a/config/external/nss/nss.def
+++ b/config/external/nss/nss.def
@@ -82,16 +82,17 @@ CERT_EncodePolicyMappingExtension
 CERT_EncodeSubjectKeyID
 CERT_EncodeUserNotice
 CERT_ExtractPublicKey
 CERT_FilterCertListByCANames
 CERT_FilterCertListByUsage
 CERT_FilterCertListForUserCerts
 CERT_FindCertByDERCert
 CERT_FindCertByIssuerAndSN
+CERT_FindCertByName
 CERT_FindCertByNickname
 CERT_FindCertByNicknameOrEmailAddr
 CERT_FindCertExtension
 CERT_FindCertIssuer
 CERT_FindKeyUsageExtension
 CERT_FindUserCertByUsage
 CERT_FindUserCertsByUsage
 CERT_FinishCertificateRequestAttributes
@@ -154,16 +155,18 @@ DER_EncodeTimeChoice_Util
 DER_Encode_Util
 DER_GeneralizedTimeToTime
 DER_GeneralizedTimeToTime_Util
 DER_GetInteger
 DER_GetInteger_Util
 DER_Lengths
 DER_SetUInteger
 DER_UTCTimeToTime_Util
+DSAU_DecodeDerSigToLen
+DSAU_EncodeDerSigWithLen
 DTLS_GetHandshakeTimeout
 DTLS_ImportFD
 HASH_Begin
 HASH_Create
 HASH_Destroy
 HASH_End
 HASH_GetHashObject
 HASH_GetType
@@ -302,16 +305,17 @@ PK11_DeleteTokenCertAndKey
 PK11_DeleteTokenPrivateKey
 PK11_DeleteTokenPublicKey
 PK11_DEREncodePublicKey
 PK11_Derive
 PK11_DeriveWithTemplate
 PK11_DestroyContext
 PK11_DestroyGenericObject
 PK11_DestroyMergeLog
+PK11_DestroyObject
 PK11_DestroyTokenObject
 PK11_DigestBegin
 PK11_DigestFinal
 PK11_DigestOp
 PK11_DoesMechanism
 PK11_Encrypt
 PK11_ExportDERPrivateKeyInfo
 PK11_ExtractKeyValue
@@ -363,16 +367,17 @@ PK11_GetSlotSeries
 PK11_GetTokenInfo
 PK11_GetTokenName
 PK11_HashBuf
 PK11_HasRootCerts
 PK11_ImportCert
 PK11_ImportCertForKey
 PK11_ImportCRL
 PK11_ImportDERPrivateKeyInfoAndReturnKey
+PK11_ImportPublicKey
 PK11_ImportSymKey
 PK11_InitPin
 PK11_IsDisabled
 PK11_IsFIPS
 PK11_IsFriendly
 PK11_IsHW
 PK11_IsInternal
 PK11_IsLoggedIn
@@ -627,16 +632,17 @@ SGN_NewContext
 SGN_Update
 SSL_AuthCertificateComplete
 SSL_AuthCertificateHook
 SSL_CipherPrefGet
 SSL_CipherPrefSet
 SSL_CipherPrefSetDefault
 SSL_ClearSessionCache
 SSL_ConfigSecureServer
+SSL_ConfigSecureServerWithCertChain
 SSL_ConfigServerSessionIDCache
 SSL_ExportKeyingMaterial
 SSL_ForceHandshake
 SSL_GetChannelInfo
 SSL_GetCipherSuiteInfo
 SSL_GetClientAuthDataHook
 SSL_GetImplementedCiphers
 SSL_GetNextProto
@@ -665,11 +671,12 @@ SSL_VersionRangeGet
 SSL_VersionRangeSet
 SSL_VersionRangeSetDefault
 UTIL_SetForkState
 VFY_Begin
 VFY_CreateContext
 VFY_DestroyContext
 VFY_End
 VFY_Update
+VFY_VerifyData
 VFY_VerifyDataDirect
 VFY_VerifyDataWithAlgorithmID
 _SGN_VerifyPKCS1DigestInfo
--- a/configure.in
+++ b/configure.in
@@ -7236,19 +7236,16 @@ AC_SUBST_LIST(WIN32_CRT_LIBS)
 dnl Need to set this for make because NSS doesn't have configure
 AC_SUBST(DLLFLAGS)
 
 dnl We need to wrap dlopen and related functions on Android because we use
 dnl our own linker.
 if test "$OS_TARGET" = Android; then
     WRAP_LDFLAGS="${WRAP_LDFLAGS} -L$_objdir/dist/lib -lmozglue"
     WRAP_LDFLAGS="${WRAP_LDFLAGS} -Wl,--wrap=PR_GetEnv,--wrap=PR_SetEnv"
-    if test -z "$gonkdir"; then
-        WRAP_LDFLAGS="${WRAP_LDFLAGS} -Wl,--wrap=memccpy,--wrap=memchr,--wrap=memrchr,--wrap=memcmp,--wrap=memcpy,--wrap=memmove,--wrap=memset,--wrap=memmem,--wrap=index,--wrap=strchr,--wrap=strrchr,--wrap=strlen,--wrap=strcmp,--wrap=strcpy,--wrap=strcat,--wrap=strcasecmp,--wrap=strncasecmp,--wrap=strstr,--wrap=strcasestr,--wrap=strtok,--wrap=strtok_r,--wrap=strerror,--wrap=strerror_r,--wrap=strnlen,--wrap=strncat,--wrap=strncmp,--wrap=strncpy,--wrap=strlcat,--wrap=strlcpy,--wrap=strcspn,--wrap=strpbrk,--wrap=strsep,--wrap=strspn,--wrap=strcoll,--wrap=strxfrm"
-    fi
     if test "$MOZ_WIDGET_TOOLKIT" = gonk -a -n "$MOZ_NUWA_PROCESS"; then
         WRAP_LDFLAGS="${WRAP_LDFLAGS} -Wl,--wrap=pthread_create,--wrap=epoll_wait,--wrap=poll,--wrap=pthread_cond_timedwait,--wrap=__pthread_cond_timedwait,--wrap=pthread_cond_wait,--wrap=epoll_create,--wrap=epoll_ctl,--wrap=close,--wrap=pthread_key_create,--wrap=pthread_key_delete,--wrap=socketpair,--wrap=pthread_self,--wrap=pthread_mutex_lock,--wrap=pthread_join,--wrap=pipe,--wrap=pipe2,--wrap=tgkill"
     fi
 fi
 
 dnl ========================================================
 dnl = Use JS Call tracing
 dnl ========================================================
@@ -7705,16 +7702,29 @@ fi
 
 if test "$MOZ_CHROME_FILE_FORMAT" != "jar" &&
     test "$MOZ_CHROME_FILE_FORMAT" != "flat" &&
     test "$MOZ_CHROME_FILE_FORMAT" != "omni"; then
     AC_MSG_ERROR([--enable-chrome-format must be set to either jar, flat, or omni])
 fi
 
 dnl =========================================================
+dnl Enable support for revamped devtools Performance Tools
+dnl =========================================================
+
+MOZ_ARG_ENABLE_BOOL(devtools-perf,
+[  --enable-devtools-perf Set compile flags necessary for compiling devtools perftools],
+MOZ_DEVTOOLS_PERFTOOLS=1,
+MOZ_DEVTOOLS_PERFTOOLS= )
+if test -n "$MOZ_DEVTOOLS_PERFTOOLS"; then
+  AC_DEFINE(MOZ_DEVTOOLS_PERFTOOLS)
+fi
+AC_SUBST(MOZ_DEVTOOLS_PERFTOOLS)
+
+dnl =========================================================
 dnl Omnijar packaging (bug 552121)
 dnl =========================================================
 dnl Omnijar packaging is compatible with flat packaging.
 dnl In unpackaged builds, omnijar looks for files as if
 dnl things were flat packaged. After packaging, all files
 dnl are loaded from a single jar. MOZ_CHROME_FILE_FORMAT
 dnl is set to flat since putting files into jars is only
 dnl done during packaging with omnijar.
--- a/content/base/src/DOMMatrix.cpp
+++ b/content/base/src/DOMMatrix.cpp
@@ -430,17 +430,17 @@ DOMMatrix::TranslateSelf(double aTx,
                          double aTz)
 {
   if (aTx == 0 && aTy == 0 && aTz == 0) {
     return this;
   }
 
   if (mMatrix3D || aTz != 0) {
     Ensure3DMatrix();
-    mMatrix3D->Translate(aTx, aTy, aTz);
+    mMatrix3D->PreTranslate(aTx, aTy, aTz);
   } else {
     mMatrix2D->PreTranslate(aTx, aTy);
   }
 
   return this;
 }
 
 DOMMatrix*
--- a/content/base/src/EventSource.cpp
+++ b/content/base/src/EventSource.cpp
@@ -22,17 +22,16 @@
 #include "nsIStringBundle.h"
 #include "nsIConsoleService.h"
 #include "nsIObserverService.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsJSUtils.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsIScriptError.h"
 #include "mozilla/dom/EncodingUtils.h"
-#include "nsIChannelPolicy.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsContentUtils.h"
 #include "mozilla/Preferences.h"
 #include "xpcpublic.h"
 #include "nsCrossSiteListenerProxy.h"
 #include "nsWrapperCacheInlines.h"
 #include "mozilla/Attributes.h"
 #include "nsError.h"
@@ -732,51 +731,39 @@ EventSource::InitChannelAndRequestEventS
   if (!CheckCanRequestSrc()) {
     DispatchFailConnection();
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   nsLoadFlags loadFlags;
   loadFlags = nsIRequest::LOAD_BACKGROUND | nsIRequest::LOAD_BYPASS_CACHE;
 
-  // get Content Security Policy from principal to pass into channel
-  nsCOMPtr<nsIChannelPolicy> channelPolicy;
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  nsresult rv = mPrincipal->GetCsp(getter_AddRefs(csp));
-  NS_ENSURE_SUCCESS(rv, rv);
-  if (csp) {
-    channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
-    channelPolicy->SetContentSecurityPolicy(csp);
-    channelPolicy->SetLoadType(nsIContentPolicy::TYPE_DATAREQUEST);
-  }
-
+  nsresult rv;
   nsIScriptContext* sc = GetContextForEventHandlers(&rv);
   nsCOMPtr<nsIDocument> doc =
     nsContentUtils::GetDocumentFromScriptContext(sc);
 
   nsCOMPtr<nsIChannel> channel;
   // If we have the document, use it
   if (doc) {
     rv = NS_NewChannel(getter_AddRefs(channel),
                        mSrc,
                        doc,
                        nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
                        nsIContentPolicy::TYPE_DATAREQUEST,
-                       channelPolicy,    // aChannelPolicy
                        mLoadGroup,       // loadGroup
                        nullptr,          // aCallbacks
                        loadFlags);       // aLoadFlags
   } else {
     // otherwise use the principal
     rv = NS_NewChannel(getter_AddRefs(channel),
                        mSrc,
                        mPrincipal,
                        nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
                        nsIContentPolicy::TYPE_DATAREQUEST,
-                       channelPolicy,    // aChannelPolicy
                        mLoadGroup,       // loadGroup
                        nullptr,          // aCallbacks
                        loadFlags);       // aLoadFlags
   }
 
   NS_ENSURE_SUCCESS(rv, rv);
 
   mHttpChannel = do_QueryInterface(channel);
--- a/content/base/src/ImportManager.cpp
+++ b/content/base/src/ImportManager.cpp
@@ -7,17 +7,16 @@
 #include "ImportManager.h"
 
 #include "mozilla/EventListenerManager.h"
 #include "HTMLLinkElement.h"
 #include "nsContentPolicyUtils.h"
 #include "nsContentUtils.h"
 #include "nsCrossSiteListenerProxy.h"
 #include "nsIChannel.h"
-#include "nsIChannelPolicy.h"
 #include "nsIContentPolicy.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMEvent.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsScriptLoader.h"
@@ -480,33 +479,22 @@ ImportLoader::Open()
   }
 
   nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
   rv = secMan->CheckLoadURIWithPrincipal(principal, mURI,
                                          nsIScriptSecurityManager::STANDARD);
   NS_ENSURE_SUCCESS_VOID(rv);
 
   nsCOMPtr<nsILoadGroup> loadGroup = master->GetDocumentLoadGroup();
-  nsCOMPtr<nsIChannelPolicy> channelPolicy;
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  rv = principal->GetCsp(getter_AddRefs(csp));
-  NS_ENSURE_SUCCESS_VOID(rv);
-
-  if (csp) {
-    channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
-    channelPolicy->SetContentSecurityPolicy(csp);
-    channelPolicy->SetLoadType(nsIContentPolicy::TYPE_SUBDOCUMENT);
-  }
   nsCOMPtr<nsIChannel> channel;
   rv = NS_NewChannel(getter_AddRefs(channel),
                      mURI,
                      mImportParent,
                      nsILoadInfo::SEC_NORMAL,
                      nsIContentPolicy::TYPE_SUBDOCUMENT,
-                     channelPolicy,
                      loadGroup,
                      nullptr,  // aCallbacks
                      nsIRequest::LOAD_BACKGROUND);
 
   NS_ENSURE_SUCCESS_VOID(rv);
 
   // Init CORSListenerProxy and omit credentials.
   nsRefPtr<nsCORSListenerProxy> corsListener =
--- a/content/base/src/moz.build
+++ b/content/base/src/moz.build
@@ -107,17 +107,16 @@ UNIFIED_SOURCES += [
     'Link.cpp',
     'MultipartFileImpl.cpp',
     'NodeIterator.cpp',
     'nsAtomListUtils.cpp',
     'nsAttrAndChildArray.cpp',
     'nsAttrValue.cpp',
     'nsAttrValueOrString.cpp',
     'nsCCUncollectableMarker.cpp',
-    'nsChannelPolicy.cpp',
     'nsContentAreaDragDrop.cpp',
     'nsContentIterator.cpp',
     'nsContentList.cpp',
     'nsContentPolicy.cpp',
     'nsContentSink.cpp',
     'nsCopySupport.cpp',
     'nsCrossSiteListenerProxy.cpp',
     'nsCSPContext.cpp',
--- a/content/base/src/nsCSPContext.cpp
+++ b/content/base/src/nsCSPContext.cpp
@@ -6,36 +6,33 @@
 #include "nsCOMPtr.h"
 #include "nsContentPolicyUtils.h"
 #include "nsContentUtils.h"
 #include "nsCSPContext.h"
 #include "nsCSPParser.h"
 #include "nsCSPService.h"
 #include "nsError.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
-#include "nsIChannelPolicy.h"
 #include "nsIClassInfoImpl.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIDOMHTMLDocument.h"
 #include "nsIDOMHTMLElement.h"
 #include "nsIDOMNode.h"
 #include "nsIHttpChannel.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
-#include "nsIPropertyBag2.h"
 #include "nsIStringStream.h"
 #include "nsIUploadChannel.h"
 #include "nsIScriptError.h"
 #include "nsIWebNavigation.h"
-#include "nsIWritablePropertyBag2.h"
 #include "nsNetUtil.h"
 #include "nsNullPrincipal.h"
 #include "nsIContentPolicy.h"
 #include "nsSupportsPrimitives.h"
 #include "nsThreadUtils.h"
 #include "nsString.h"
 #include "prlog.h"
 #include "mozilla/dom/CSPReportBinding.h"
--- a/content/base/src/nsCSPService.cpp
+++ b/content/base/src/nsCSPService.cpp
@@ -7,27 +7,23 @@
 #include "nsString.h"
 #include "nsCOMPtr.h"
 #include "nsIURI.h"
 #include "nsIPrincipal.h"
 #include "nsIObserver.h"
 #include "nsIContent.h"
 #include "nsCSPService.h"
 #include "nsIContentSecurityPolicy.h"
-#include "nsIChannelPolicy.h"
-#include "nsIChannelEventSink.h"
-#include "nsIPropertyBag2.h"
-#include "nsIWritablePropertyBag2.h"
 #include "nsError.h"
-#include "nsChannelProperties.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsAsyncRedirectVerifyHelper.h"
 #include "mozilla/Preferences.h"
 #include "nsIScriptError.h"
 #include "nsContentUtils.h"
+#include "nsContentPolicyUtils.h"
 #include "nsPrincipal.h"
 
 using namespace mozilla;
 
 /* Keeps track of whether or not CSP is enabled */
 bool CSPService::sCSPEnabled = true;
 
 #ifdef PR_LOGGING
@@ -231,63 +227,66 @@ CSPService::ShouldProcess(uint32_t      
 NS_IMETHODIMP
 CSPService::AsyncOnChannelRedirect(nsIChannel *oldChannel,
                                    nsIChannel *newChannel,
                                    uint32_t flags,
                                    nsIAsyncVerifyRedirectCallback *callback)
 {
   nsAsyncRedirectAutoCallback autoCallback(callback);
 
-  // get the Content Security Policy and load type from the property bag
-  nsCOMPtr<nsISupports> policyContainer;
-  nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(oldChannel));
-  if (!props)
+  nsCOMPtr<nsILoadInfo> loadInfo;
+  nsresult rv = oldChannel->GetLoadInfo(getter_AddRefs(loadInfo));
+
+  // if no loadInfo on the channel, nothing for us to do
+  if (!loadInfo) {
     return NS_OK;
+  }
 
-  props->GetPropertyAsInterface(NS_CHANNEL_PROP_CHANNEL_POLICY,
-                                NS_GET_IID(nsISupports),
-                                getter_AddRefs(policyContainer));
+  // The loadInfo must not necessarily contain a Node, hence we try to query
+  // the CSP in the following order:
+  //   a) Get the Node, the Principal of that Node, and the CSP of that Principal
+  //   b) Get the Principal and the CSP of that Principal
 
-  // see if we have a valid nsIChannelPolicy containing CSP and load type
-  nsCOMPtr<nsIChannelPolicy> channelPolicy(do_QueryInterface(policyContainer));
-  if (!channelPolicy)
-    return NS_OK;
-
-  nsCOMPtr<nsISupports> supports;
+  nsCOMPtr<nsINode> loadingNode = loadInfo->LoadingNode();
+  nsCOMPtr<nsIPrincipal> principal = loadingNode ?
+                                     loadingNode->NodePrincipal() :
+                                     loadInfo->LoadingPrincipal();
+  NS_ASSERTION(principal, "Can not evaluate CSP without a principal");
   nsCOMPtr<nsIContentSecurityPolicy> csp;
-  channelPolicy->GetContentSecurityPolicy(getter_AddRefs(supports));
-  csp = do_QueryInterface(supports);
-  uint32_t loadType;
-  channelPolicy->GetLoadType(&loadType);
+  rv = principal->GetCsp(getter_AddRefs(csp));
+  NS_ENSURE_SUCCESS(rv, rv);
 
-  // if no CSP in the channelPolicy, nothing for us to add to the channel
-  if (!csp)
+  // if there is no CSP, nothing for us to do
+  if (!csp) {
     return NS_OK;
+  }
 
   /* Since redirecting channels don't call into nsIContentPolicy, we call our
-   * Content Policy implementation directly when redirects occur. When channels
-   * are created using NS_NewChannel(), callers can optionally pass in a
-   * nsIChannelPolicy containing a CSP object and load type, which is placed in
-   * the new channel's property bag. This container is propagated forward when
-   * channels redirect.
+   * Content Policy implementation directly when redirects occur using the
+   * information set in the LoadInfo when channels are created.
+   *
+   * We check if the CSP permits this host for this type of load, if not,
+   * we cancel the load now.
    */
 
-  // Does the CSP permit this host for this type of load?
-  // If not, cancel the load now.
   nsCOMPtr<nsIURI> newUri;
-  newChannel->GetURI(getter_AddRefs(newUri));
+  rv = newChannel->GetURI(getter_AddRefs(newUri));
+  NS_ENSURE_SUCCESS(rv, rv);
   nsCOMPtr<nsIURI> originalUri;
-  oldChannel->GetOriginalURI(getter_AddRefs(originalUri));
+  rv = oldChannel->GetOriginalURI(getter_AddRefs(originalUri));
+  NS_ENSURE_SUCCESS(rv, rv);
+  nsContentPolicyType policyType = loadInfo->GetContentPolicyType();
+
   int16_t aDecision = nsIContentPolicy::ACCEPT;
-  csp->ShouldLoad(loadType,        // load type per nsIContentPolicy (uint32_t)
-                  newUri,          // nsIURI
-                  nullptr,          // nsIURI
-                  nullptr,          // nsISupports
-                  EmptyCString(),  // ACString - MIME guess
-                  originalUri,     // nsISupports - extra
+  csp->ShouldLoad(policyType,     // load type per nsIContentPolicy (uint32_t)
+                  newUri,         // nsIURI
+                  nullptr,        // nsIURI
+                  nullptr,        // nsISupports
+                  EmptyCString(), // ACString - MIME guess
+                  originalUri,    // aMimeTypeGuess
                   &aDecision);
 
 #ifdef PR_LOGGING
   if (newUri) {
     nsAutoCString newUriSpec("None");
     newUri->GetSpec(newUriSpec);
     PR_LOG(gCspPRLog, PR_LOG_DEBUG,
            ("CSPService::AsyncOnChannelRedirect called for %s",
@@ -297,41 +296,14 @@ CSPService::AsyncOnChannelRedirect(nsICh
     PR_LOG(gCspPRLog, PR_LOG_DEBUG,
            ("CSPService::AsyncOnChannelRedirect ALLOWING request."));
   else
     PR_LOG(gCspPRLog, PR_LOG_DEBUG,
            ("CSPService::AsyncOnChannelRedirect CANCELLING request."));
 #endif
 
   // if ShouldLoad doesn't accept the load, cancel the request
-  if (aDecision != 1) {
+  if (!NS_CP_ACCEPTED(aDecision)) {
     autoCallback.DontCallback();
     return NS_BINDING_FAILED;
   }
-
-  // the redirect is permitted, so propagate the Content Security Policy
-  // and load type to the redirecting channel
-  nsresult rv;
-  nsCOMPtr<nsIWritablePropertyBag2> props2 = do_QueryInterface(newChannel);
-  if (props2) {
-    rv = props2->SetPropertyAsInterface(NS_CHANNEL_PROP_CHANNEL_POLICY,
-                                        channelPolicy);
-    if (NS_SUCCEEDED(rv)) {
-      return NS_OK;
-    }
-  }
-
-  // The redirecting channel isn't a writable property bag, we won't be able
-  // to enforce the load policy if it redirects again, so we stop it now.
-  nsAutoCString newUriSpec;
-  rv = newUri->GetSpec(newUriSpec);
-  NS_ConvertUTF8toUTF16 unicodeSpec(newUriSpec);
-  const char16_t *formatParams[] = { unicodeSpec.get() };
-  if (NS_SUCCEEDED(rv)) {
-    nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
-                                    NS_LITERAL_CSTRING("Redirect Error"), nullptr,
-                                    nsContentUtils::eDOM_PROPERTIES,
-                                    "InvalidRedirectChannelWarning",
-                                    formatParams, 1);
-  }
-
-  return NS_BINDING_FAILED;
+  return NS_OK;
 }
deleted file mode 100644
--- a/content/base/src/nsChannelPolicy.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsChannelPolicy.h"
-
-nsChannelPolicy::nsChannelPolicy()
-  : mLoadType(0)
-{
-}
-
-nsChannelPolicy::~nsChannelPolicy()
-{
-}
-
-NS_IMPL_ISUPPORTS(nsChannelPolicy, nsIChannelPolicy)
-
-NS_IMETHODIMP
-nsChannelPolicy::GetLoadType(uint32_t *aLoadType)
-{
-    *aLoadType = mLoadType;
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsChannelPolicy::SetLoadType(uint32_t aLoadType)
-{
-    mLoadType = aLoadType;
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsChannelPolicy::GetContentSecurityPolicy(nsISupports **aCSP)
-{
-    *aCSP = mCSP;
-    NS_IF_ADDREF(*aCSP);
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsChannelPolicy::SetContentSecurityPolicy(nsISupports *aCSP)
-{
-    mCSP = aCSP;
-    return NS_OK;
-}
deleted file mode 100644
--- a/content/base/src/nsChannelPolicy.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef nsChannelPolicy_h___
-#define nsChannelPolicy_h___
-
-#include "nsCOMPtr.h"
-#include "nsIChannelPolicy.h"
-
-#define NSCHANNELPOLICY_CONTRACTID "@mozilla.org/nschannelpolicy;1"
-#define NSCHANNELPOLICY_CID \
-{ 0xd396b3cd, 0xf164, 0x4ce8, \
-  { 0x93, 0xa7, 0xe3, 0x85, 0xe1, 0x46, 0x56, 0x3c } }
-
-class nsChannelPolicy : public nsIChannelPolicy
-{
-public:
-    NS_DECL_ISUPPORTS
-    NS_DECL_NSICHANNELPOLICY
-
-    nsChannelPolicy();
-
-protected:
-    virtual ~nsChannelPolicy();
-
-    /* Represents the type of content being loaded in the channel per
-     * nsIContentPolicy, e.g. TYPE_IMAGE, TYPE_SCRIPT
-     */
-    unsigned long mLoadType;
-
-    /* pointer to a Content Security Policy object if available */
-    nsCOMPtr<nsISupports> mCSP;
-};
-
-#endif /* nsChannelPolicy_h___ */
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -54,17 +54,16 @@
 #include "mozilla/dom/Selection.h"
 #include "mozilla/TextEvents.h"
 #include "nsAString.h"
 #include "nsAttrName.h"
 #include "nsAttrValue.h"
 #include "nsAttrValueInlines.h"
 #include "nsBindingManager.h"
 #include "nsCCUncollectableMarker.h"
-#include "nsChannelPolicy.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsCOMPtr.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsContentDLF.h"
 #include "nsContentList.h"
 #include "nsContentPolicyUtils.h"
 #include "nsCPrefetchService.h"
 #include "nsCRT.h"
@@ -83,17 +82,16 @@
 #include "nsGenericHTMLFrameElement.h"
 #include "nsGkAtoms.h"
 #include "nsHostObjectProtocolHandler.h"
 #include "nsHtml5Module.h"
 #include "nsHtml5StringParser.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsICategoryManager.h"
 #include "nsIChannelEventSink.h"
-#include "nsIChannelPolicy.h"
 #include "nsICharsetDetectionObserver.h"
 #include "nsIChromeRegistry.h"
 #include "nsIConsoleService.h"
 #include "nsIContent.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIContentSink.h"
 #include "nsIContentViewer.h"
 #include "nsIDocShell.h"
@@ -3002,45 +3000,30 @@ nsContentUtils::LoadImage(nsIURI* aURI, 
 
   nsCOMPtr<nsILoadGroup> loadGroup = aLoadingDocument->GetDocumentLoadGroup();
 
   nsIURI *documentURI = aLoadingDocument->GetDocumentURI();
 
   NS_ASSERTION(loadGroup || IsFontTableURI(documentURI),
                "Could not get loadgroup; onload may fire too early");
 
-  // check for a Content Security Policy to pass down to the channel that
-  // will get created to load the image
-  nsCOMPtr<nsIChannelPolicy> channelPolicy;
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  if (aLoadingPrincipal) {
-    nsresult rv = aLoadingPrincipal->GetCsp(getter_AddRefs(csp));
-    NS_ENSURE_SUCCESS(rv, rv);
-    if (csp) {
-      channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
-      channelPolicy->SetContentSecurityPolicy(csp);
-      channelPolicy->SetLoadType(nsIContentPolicy::TYPE_IMAGE);
-    }
-  }
-    
   // Make the URI immutable so people won't change it under us
   NS_TryToSetImmutable(aURI);
 
   // XXXbz using "documentURI" for the initialDocumentURI is not quite
   // right, but the best we can do here...
   return imgLoader->LoadImage(aURI,                 /* uri to load */
                               documentURI,          /* initialDocumentURI */
                               aReferrer,            /* referrer */
                               aLoadingPrincipal,    /* loading principal */
                               loadGroup,            /* loadgroup */
                               aObserver,            /* imgINotificationObserver */
                               aLoadingDocument,     /* uniquification key */
                               aLoadFlags,           /* load flags */
                               nullptr,               /* cache key */
-                              channelPolicy,        /* CSP info */
                               initiatorType,        /* the load initiator */
                               aRequest);
 }
 
 // static
 already_AddRefed<imgIContainer>
 nsContentUtils::GetImageFromContent(nsIImageLoadingContent* aContent,
                                     imgIRequest **aRequest)
--- a/content/base/src/nsCrossSiteListenerProxy.cpp
+++ b/content/base/src/nsCrossSiteListenerProxy.cpp
@@ -1117,29 +1117,27 @@ NS_StartCORSPreflight(nsIChannel* aReque
   rv = aRequestChannel->GetLoadInfo(getter_AddRefs(loadInfo));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIChannel> preflightChannel;
   if (loadInfo) {
     rv = NS_NewChannelInternal(getter_AddRefs(preflightChannel),
                                uri,
                                loadInfo,
-                               nullptr,   // aChannelPolicy
                                loadGroup,
                                nullptr,   // aCallbacks
                                loadFlags);
   }
   else {
     rv = NS_NewChannelInternal(getter_AddRefs(preflightChannel),
                                uri,
                                nullptr, // aRequestingNode,
                                nsContentUtils::GetSystemPrincipal(),
                                nsILoadInfo::SEC_NORMAL,
                                nsIContentPolicy::TYPE_OTHER,
-                               nullptr,   // aChannelPolicy
                                loadGroup,
                                nullptr,   // aCallbacks
                                loadFlags);
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIHttpChannel> preHttp = do_QueryInterface(preflightChannel);
   NS_ASSERTION(preHttp, "Failed to QI to nsIHttpChannel!");
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -1325,17 +1325,16 @@ nsExternalResourceMap::PendingLoad::Star
 
   nsCOMPtr<nsILoadGroup> loadGroup = doc->GetDocumentLoadGroup();
   nsCOMPtr<nsIChannel> channel;
   rv = NS_NewChannel(getter_AddRefs(channel),
                      aURI,
                      aRequestingNode,
                      nsILoadInfo::SEC_NORMAL,
                      nsIContentPolicy::TYPE_OTHER,
-                     nullptr, // aChannelPolicy
                      loadGroup,
                      req); // aCallbacks
 
   NS_ENSURE_SUCCESS(rv, rv);
 
   mURI = aURI;
 
   return channel->AsyncOpen(this, nullptr);
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -60,18 +60,16 @@
 #include "nsSandboxFlags.h"
 
 // Concrete classes
 #include "nsFrameLoader.h"
 
 #include "nsObjectLoadingContent.h"
 #include "mozAutoDocUpdate.h"
 #include "nsIContentSecurityPolicy.h"
-#include "nsIChannelPolicy.h"
-#include "nsChannelPolicy.h"
 #include "GeckoProfiler.h"
 #include "nsPluginFrame.h"
 #include "nsDOMClassInfo.h"
 #include "nsWrapperCacheInlines.h"
 #include "nsDOMJSUtils.h"
 
 #include "nsWidgetsCID.h"
 #include "nsContentCID.h"
@@ -2487,25 +2485,16 @@ nsObjectLoadingContent::OpenChannel()
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   rv = secMan->CheckLoadURIWithPrincipal(thisContent->NodePrincipal(), mURI, 0);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsILoadGroup> group = doc->GetDocumentLoadGroup();
   nsCOMPtr<nsIChannel> chan;
-  nsCOMPtr<nsIChannelPolicy> channelPolicy;
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  rv = doc->NodePrincipal()->GetCsp(getter_AddRefs(csp));
-  NS_ENSURE_SUCCESS(rv, rv);
-  if (csp) {
-    channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
-    channelPolicy->SetContentSecurityPolicy(csp);
-    channelPolicy->SetLoadType(nsIContentPolicy::TYPE_OBJECT);
-  }
   nsRefPtr<ObjectInterfaceRequestorShim> shim =
     new ObjectInterfaceRequestorShim(this);
 
   bool isSandBoxed = doc->GetSandboxFlags() & SANDBOXED_ORIGIN;
   bool inherit = nsContentUtils::ChannelShouldInheritPrincipal(thisContent->NodePrincipal(),
                                                                mURI,
                                                                true,   // aInheritForAboutBlank
                                                                false); // aForceInherit
@@ -2517,17 +2506,16 @@ nsObjectLoadingContent::OpenChannel()
     securityFlags |= nsILoadInfo::SEC_SANDBOXED;
   }
 
   rv = NS_NewChannel(getter_AddRefs(chan),
                      mURI,
                      thisContent,
                      securityFlags,
                      nsIContentPolicy::TYPE_OBJECT,
-                     channelPolicy,
                      group, // aLoadGroup
                      shim,  // aCallbacks
                      nsIChannel::LOAD_CALL_CONTENT_SNIFFERS |
                      nsIChannel::LOAD_CLASSIFY_URI);
 
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Referrer
--- a/content/base/src/nsScriptLoader.cpp
+++ b/content/base/src/nsScriptLoader.cpp
@@ -36,18 +36,16 @@
 #include "nsUnicharUtils.h"
 #include "nsAutoPtr.h"
 #include "nsIXPConnect.h"
 #include "nsError.h"
 #include "nsThreadUtils.h"
 #include "nsDocShellCID.h"
 #include "nsIContentSecurityPolicy.h"
 #include "prlog.h"
-#include "nsIChannelPolicy.h"
-#include "nsChannelPolicy.h"
 #include "nsCRT.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsCrossSiteListenerProxy.h"
 #include "nsSandboxFlags.h"
 #include "nsContentTypeParser.h"
 #include "nsINetworkPredictor.h"
 #include "ImportManager.h"
 #include "mozilla/dom/EncodingUtils.h"
@@ -302,35 +300,22 @@ nsScriptLoader::StartLoad(nsScriptLoadRe
 
   nsCOMPtr<nsIInterfaceRequestor> prompter(do_QueryInterface(docshell));
 
   // If this document is sandboxed without 'allow-scripts', abort.
   if (mDocument->GetSandboxFlags() & SANDBOXED_SCRIPTS) {
     return NS_OK;
   }
 
-  // check for a Content Security Policy to pass down to the channel
-  // that will be created to load the script
-  nsCOMPtr<nsIChannelPolicy> channelPolicy;
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  rv = mDocument->NodePrincipal()->GetCsp(getter_AddRefs(csp));
-  NS_ENSURE_SUCCESS(rv, rv);
-  if (csp) {
-    channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
-    channelPolicy->SetContentSecurityPolicy(csp);
-    channelPolicy->SetLoadType(nsIContentPolicy::TYPE_SCRIPT);
-  }
-
   nsCOMPtr<nsIChannel> channel;
   rv = NS_NewChannel(getter_AddRefs(channel),
                      aRequest->mURI,
                      mDocument,
                      nsILoadInfo::SEC_NORMAL,
                      nsIContentPolicy::TYPE_SCRIPT,
-                     channelPolicy,
                      loadGroup,
                      prompter,
                      nsIRequest::LOAD_NORMAL |
                      nsIChannel::LOAD_CLASSIFY_URI);
 
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsIScriptElement *script = aRequest->mElement;
--- a/content/base/src/nsSyncLoadService.cpp
+++ b/content/base/src/nsSyncLoadService.cpp
@@ -310,17 +310,16 @@ nsSyncLoadService::LoadDocument(nsIURI *
                                 nsIDOMDocument** aResult)
 {
     nsCOMPtr<nsIChannel> channel;
     nsresult rv = NS_NewChannel(getter_AddRefs(channel),
                                 aURI,
                                 aLoaderPrincipal,
                                 nsILoadInfo::SEC_NORMAL,
                                 nsIContentPolicy::TYPE_OTHER,
-                                nullptr,   // aChannelPolicy
                                 aLoadGroup);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (!aForceToXML) {
         channel->SetContentType(NS_LITERAL_CSTRING("text/xml"));
     }
 
     bool isChrome = false, isResource = false;
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -47,18 +47,16 @@
 #include "nsContentPolicyUtils.h"
 #include "nsError.h"
 #include "nsCrossSiteListenerProxy.h"
 #include "nsIHTMLDocument.h"
 #include "nsIStorageStream.h"
 #include "nsIPromptFactory.h"
 #include "nsIWindowWatcher.h"
 #include "nsIConsoleService.h"
-#include "nsIChannelPolicy.h"
-#include "nsChannelPolicy.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsAsyncRedirectVerifyHelper.h"
 #include "nsStringBuffer.h"
 #include "nsIFileChannel.h"
 #include "mozilla/Telemetry.h"
 #include "jsfriendapi.h"
 #include "GeckoProfiler.h"
 #include "mozilla/dom/EncodingUtils.h"
@@ -469,16 +467,20 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
                                                   nsXHREventTarget)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannel)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResponseXML)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCORSPreflightChannel)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXMLParserStreamListener)
 
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResponseBlob)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMFile)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNotificationCallbacks)
+
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannelEventSink)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProgressEventSink)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUpload)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsXMLHttpRequest,
                                                 nsXHREventTarget)
@@ -487,16 +489,20 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_IN
   tmp->mResultJSON = JSVAL_VOID;
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannel)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mResponseXML)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCORSPreflightChannel)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mXMLParserStreamListener)
 
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mResponseBlob)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMFile)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mNotificationCallbacks)
+
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannelEventSink)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mProgressEventSink)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mUpload)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsXMLHttpRequest,
                                                nsXHREventTarget)
@@ -1719,27 +1725,16 @@ nsXMLHttpRequest::Open(const nsACString&
   // operations will merge/override correctly.
   mAlreadySetHeaders.Clear();
 
   // When we are called from JS we can find the load group for the page,
   // and add ourselves to it. This way any pending requests
   // will be automatically aborted if the user leaves the page.
   nsCOMPtr<nsILoadGroup> loadGroup = GetLoadGroup();
 
-  // get Content Security Policy from principal to pass into channel
-  nsCOMPtr<nsIChannelPolicy> channelPolicy;
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  rv = mPrincipal->GetCsp(getter_AddRefs(csp));
-  NS_ENSURE_SUCCESS(rv, rv);
-  if (csp) {
-    channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
-    channelPolicy->SetContentSecurityPolicy(csp);
-    channelPolicy->SetLoadType(nsIContentPolicy::TYPE_XMLHTTPREQUEST);
-  }
-
   nsSecurityFlags secFlags = nsILoadInfo::SEC_NORMAL;
   if (IsSystemXHR()) {
     // Don't give this document the system principal.  We need to keep track of
     // mPrincipal being system because we use it for various security checks
     // that should be passing, but the document data shouldn't get a system
     // principal.  Hence we set the sandbox flag in loadinfo, so that 
     // GetChannelResultPrincipal will give us the nullprincipal.
     secFlags |= nsILoadInfo::SEC_SANDBOXED;
@@ -1749,28 +1744,26 @@ nsXMLHttpRequest::Open(const nsACString&
 
   // If we have the document, use it
   if (doc) {
     rv = NS_NewChannel(getter_AddRefs(mChannel),
                        uri,
                        doc,
                        secFlags,
                        nsIContentPolicy::TYPE_XMLHTTPREQUEST,
-                       channelPolicy,
                        loadGroup,
                        nullptr,   // aCallbacks
                        nsIRequest::LOAD_BACKGROUND);
   } else {
     //otherwise use the principal
     rv = NS_NewChannel(getter_AddRefs(mChannel),
                        uri,
                        mPrincipal,
                        secFlags,
                        nsIContentPolicy::TYPE_XMLHTTPREQUEST,
-                       channelPolicy,
                        loadGroup,
                        nullptr,   // aCallbacks
                        nsIRequest::LOAD_BACKGROUND);
   }
 
   if (NS_FAILED(rv)) return rv;
 
   mState &= ~(XML_HTTP_REQUEST_USE_XSITE_AC |
--- a/content/base/test/mochitest.ini
+++ b/content/base/test/mochitest.ini
@@ -286,16 +286,17 @@ skip-if = (buildapp == 'b2g' && toolkit 
 [test_bug338583.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' # b2g(https not working, bug 907770) b2g-debug(https not working, bug 907770) b2g-desktop(43 total - bug 901343, specialpowers.wrap issue createsystemxhr)
 [test_bug338679.html]
 [test_bug339494.html]
 [test_bug339494.xhtml]
 [test_bug340571.html]
 [test_bug343596.html]
 [test_bug345339.html]
+skip-if = e10s # Bug 1081453 - shutdown leaks with e10s and WebIDL dom::File
 [test_bug346485.html]
 [test_bug352728.html]
 [test_bug352728.xhtml]
 [test_bug353334.html]
 [test_bug355026.html]
 [test_bug357450.html]
 [test_bug357450.js]
 [test_bug357450.xhtml]
--- a/content/html/content/src/HTMLMediaElement.cpp
+++ b/content/html/content/src/HTMLMediaElement.cpp
@@ -94,18 +94,16 @@ static PRLogModuleInfo* gMediaElementEve
 #define LOG(type, msg) PR_LOG(gMediaElementLog, type, msg)
 #define LOG_EVENT(type, msg) PR_LOG(gMediaElementEventsLog, type, msg)
 #else
 #define LOG(type, msg)
 #define LOG_EVENT(type, msg)
 #endif
 
 #include "nsIContentSecurityPolicy.h"
-#include "nsIChannelPolicy.h"
-#include "nsChannelPolicy.h"
 
 #include "mozilla/Preferences.h"
 
 #include "nsIPermissionManager.h"
 #include "nsContentTypeParser.h"
 
 using namespace mozilla::layers;
 using mozilla::net::nsMediaFragmentURIParser;
@@ -1165,35 +1163,22 @@ nsresult HTMLMediaElement::LoadResource(
     }
     mMediaSource = source.forget();
     nsRefPtr<MediaResource> resource =
       MediaSourceDecoder::CreateResource(mMediaSource->GetPrincipal());
     return FinishDecoderSetup(decoder, resource, nullptr, nullptr);
   }
 
   nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
-
-  // check for a Content Security Policy to pass down to the channel
-  // created to load the media content
-  nsCOMPtr<nsIChannelPolicy> channelPolicy;
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  rv = NodePrincipal()->GetCsp(getter_AddRefs(csp));
-  NS_ENSURE_SUCCESS(rv,rv);
-  if (csp) {
-    channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
-    channelPolicy->SetContentSecurityPolicy(csp);
-    channelPolicy->SetLoadType(nsIContentPolicy::TYPE_MEDIA);
-  }
   nsCOMPtr<nsIChannel> channel;
   rv = NS_NewChannel(getter_AddRefs(channel),
                      mLoadingSrc,
                      static_cast<Element*>(this),
                      nsILoadInfo::SEC_NORMAL,
                      nsIContentPolicy::TYPE_MEDIA,
-                     channelPolicy,
                      loadGroup,
                      nullptr,   // aCallbacks
                      nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY |
                      nsIChannel::LOAD_MEDIA_SNIFFER_OVERRIDES_CONTENT_TYPE |
                      nsIChannel::LOAD_CALL_CONTENT_SNIFFERS);
 
   NS_ENSURE_SUCCESS(rv,rv);
 
--- a/content/html/content/src/HTMLTrackElement.cpp
+++ b/content/html/content/src/HTMLTrackElement.cpp
@@ -16,17 +16,16 @@
 #include "nsContentPolicyUtils.h"
 #include "nsContentUtils.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsGenericHTMLElement.h"
 #include "nsGkAtoms.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsICachingChannel.h"
 #include "nsIChannelEventSink.h"
-#include "nsIChannelPolicy.h"
 #include "nsIContentPolicy.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIDocument.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIDOMHTMLMediaElement.h"
 #include "nsIHttpChannel.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsILoadGroup.h"
@@ -232,38 +231,23 @@ HTMLTrackElement::LoadResource()
 
   // We may already have a TextTrack at this point if GetTrack() has already
   // been called. This happens, for instance, if script tries to get the
   // TextTrack before its mTrackElement has been bound to the DOM tree.
   if (!mTrack) {
     CreateTextTrack();
   }
 
-  // Check for a Content Security Policy to pass down to the channel
-  // created to load the media content.
-  nsCOMPtr<nsIChannelPolicy> channelPolicy;
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  rv = NodePrincipal()->GetCsp(getter_AddRefs(csp));
-  NS_ENSURE_TRUE_VOID(NS_SUCCEEDED(rv));
-  if (csp) {
-    channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
-    if (!channelPolicy) {
-      return;
-    }
-    channelPolicy->SetContentSecurityPolicy(csp);
-    channelPolicy->SetLoadType(nsIContentPolicy::TYPE_MEDIA);
-  }
   nsCOMPtr<nsIChannel> channel;
   nsCOMPtr<nsILoadGroup> loadGroup = OwnerDoc()->GetDocumentLoadGroup();
   rv = NS_NewChannel(getter_AddRefs(channel),
                      uri,
                      static_cast<Element*>(this),
                      nsILoadInfo::SEC_NORMAL,
                      nsIContentPolicy::TYPE_MEDIA,
-                     channelPolicy,
                      loadGroup);
 
   NS_ENSURE_TRUE_VOID(NS_SUCCEEDED(rv));
 
   mListener = new WebVTTListener(this);
   rv = mListener->LoadResource();
   NS_ENSURE_TRUE_VOID(NS_SUCCEEDED(rv));
   channel->SetNotificationCallbacks(mListener);
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -286,26 +286,21 @@ static const nsAttrValue::EnumTable kDir
   { "rtl", eDir_RTL },
   { "auto", eDir_Auto },
   { 0 }
 };
 
 void
 nsGenericHTMLElement::GetAccessKeyLabel(nsString& aLabel)
 {
-  //XXXsmaug We shouldn't need PresContext for this.
-  nsPresContext *presContext = GetPresContext(eForComposedDoc);
-
-  if (presContext) {
-    nsAutoString suffix;
-    GetAccessKey(suffix);
-    if (!suffix.IsEmpty() && 
-        presContext->EventStateManager()->GetAccessKeyLabelPrefix(aLabel)) {
-      aLabel.Append(suffix);
-    }
+  nsAutoString suffix;
+  GetAccessKey(suffix);
+  if (!suffix.IsEmpty()) {
+    EventStateManager::GetAccessKeyLabelPrefix(this, aLabel);
+    aLabel.Append(suffix);
   }
 }
 
 static bool IS_TABLE_CELL(nsIAtom* frameType) {
   return nsGkAtoms::tableCellFrame == frameType ||
     nsGkAtoms::bcTableCellFrame == frameType;
 }
 
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -1511,17 +1511,16 @@ nsHTMLDocument::Open(JSContext* cx,
   // So we reset the document and create a new one.
   nsCOMPtr<nsIChannel> channel;
   nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
   rv = NS_NewChannel(getter_AddRefs(channel),
                      uri,
                      callerDoc,
                      nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
                      nsIContentPolicy::TYPE_OTHER,
-                     nullptr,   // aChannelPolicy
                      group);
 
   if (rv.Failed()) {
     return nullptr;
   }
 
   // We can't depend on channels implementing property bags, so do our
   // base URI manually after reset.
--- a/content/media/GraphDriver.cpp
+++ b/content/media/GraphDriver.cpp
@@ -459,17 +459,17 @@ OfflineClockDriver::WakeUp()
   MOZ_ASSERT(false, "An offline graph should not have to wake up.");
 }
 
 AsyncCubebTask::AsyncCubebTask(AudioCallbackDriver* aDriver, AsyncCubebOperation aOperation)
   : mDriver(aDriver),
     mOperation(aOperation),
     mShutdownGrip(aDriver->GraphImpl())
 {
-  MOZ_ASSERT(mDriver->mAudioStream || aOperation == INIT, "No audio stream !");
+  NS_WARN_IF_FALSE(mDriver->mAudioStream || aOperation == INIT, "No audio stream !");
 }
 
 AsyncCubebTask::~AsyncCubebTask()
 {
 }
 
 NS_IMETHODIMP
 AsyncCubebTask::Run()
--- a/content/media/MediaResource.cpp
+++ b/content/media/MediaResource.cpp
@@ -919,17 +919,16 @@ ChannelMediaResource::RecreateChannel()
   nsCOMPtr<nsILoadGroup> loadGroup = element->GetDocumentLoadGroup();
   NS_ENSURE_TRUE(loadGroup, NS_ERROR_NULL_POINTER);
 
   nsresult rv = NS_NewChannel(getter_AddRefs(mChannel),
                               mURI,
                               element,
                               nsILoadInfo::SEC_NORMAL,
                               nsIContentPolicy::TYPE_MEDIA,
-                              nullptr,   // aChannelPolicy
                               loadGroup,
                               nullptr,  // aCallbacks
                               loadFlags);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // We have cached the Content-Type, which should not change. Give a hint to
   // the channel to avoid a sniffing failure, which would be expected because we
   // are probably seeking in the middle of the bitstream, and sniffing relies
@@ -1437,17 +1436,16 @@ already_AddRefed<MediaResource> FileMedi
 
   nsCOMPtr<nsIChannel> channel;
   nsresult rv =
     NS_NewChannel(getter_AddRefs(channel),
                   mURI,
                   element,
                   nsILoadInfo::SEC_NORMAL,
                   nsIContentPolicy::TYPE_MEDIA,
-                  nullptr,   // aChannelPolicy
                   loadGroup);
 
   if (NS_FAILED(rv))
     return nullptr;
 
   nsRefPtr<MediaResource> resource(new FileMediaResource(aDecoder, channel, mURI, GetContentType()));
   return resource.forget();
 }
--- a/content/media/mediasource/MediaSourceReader.cpp
+++ b/content/media/mediasource/MediaSourceReader.cpp
@@ -197,34 +197,36 @@ MediaSourceReader::OnDecodeError()
   MSE_DEBUG("MediaSourceReader(%p)::OnDecodeError", this);
   GetCallback()->OnDecodeError();
 }
 
 void
 MediaSourceReader::Shutdown()
 {
   MediaDecoderReader::Shutdown();
+  for (uint32_t i = 0; i < mTrackBuffers.Length(); ++i) {
+    mTrackBuffers[i]->Shutdown();
+  }
   mAudioTrack = nullptr;
   mAudioReader = nullptr;
   mVideoTrack = nullptr;
   mVideoReader = nullptr;
-  for (uint32_t i = 0; i < mTrackBuffers.Length(); ++i) {
-    mTrackBuffers[i]->Shutdown();
-  }
-  mTrackBuffers.Clear();
 }
 
 void
 MediaSourceReader::BreakCycles()
 {
   MediaDecoderReader::BreakCycles();
-  mAudioTrack = nullptr;
-  mAudioReader = nullptr;
-  mVideoTrack = nullptr;
-  mVideoReader = nullptr;
+
+  // These were cleared in Shutdown().
+  MOZ_ASSERT(!mAudioTrack);
+  MOZ_ASSERT(!mAudioReader);
+  MOZ_ASSERT(!mVideoTrack);
+  MOZ_ASSERT(!mVideoReader);
+
   for (uint32_t i = 0; i < mTrackBuffers.Length(); ++i) {
     mTrackBuffers[i]->BreakCycles();
   }
   mTrackBuffers.Clear();
 }
 
 already_AddRefed<MediaDecoderReader>
 MediaSourceReader::SelectReader(int64_t aTarget,
--- a/content/media/mediasource/SourceBuffer.cpp
+++ b/content/media/mediasource/SourceBuffer.cpp
@@ -411,18 +411,31 @@ void
 SourceBuffer::Dump(const char* aPath)
 {
   if (mTrackBuffer) {
     mTrackBuffer->Dump(aPath);
   }
 }
 #endif
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED(SourceBuffer, DOMEventTargetHelper,
-                                   mMediaSource)
+NS_IMPL_CYCLE_COLLECTION_CLASS(SourceBuffer)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SourceBuffer)
+  // Tell the TrackBuffer to end its current SourceBufferResource.
+  TrackBuffer* track = tmp->mTrackBuffer;
+  if (track) {
+    track->Detach();
+  }
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mMediaSource)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(DOMEventTargetHelper)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SourceBuffer,
+                                                  DOMEventTargetHelper)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaSource)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_ADDREF_INHERITED(SourceBuffer, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(SourceBuffer, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(SourceBuffer)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 } // namespace dom
--- a/content/media/mediasource/TrackBuffer.cpp
+++ b/content/media/mediasource/TrackBuffer.cpp
@@ -49,55 +49,44 @@ TrackBuffer::TrackBuffer(MediaSourceDeco
 TrackBuffer::~TrackBuffer()
 {
   MOZ_COUNT_DTOR(TrackBuffer);
 }
 
 class ReleaseDecoderTask : public nsRunnable {
 public:
   explicit ReleaseDecoderTask(SourceBufferDecoder* aDecoder)
+    : mDecoder(aDecoder)
   {
-    mDecoders.AppendElement(aDecoder);
-  }
-
-  explicit ReleaseDecoderTask(nsTArray<nsRefPtr<SourceBufferDecoder>>& aDecoders)
-  {
-    mDecoders.SwapElements(aDecoders);
   }
 
   NS_IMETHOD Run() MOZ_OVERRIDE MOZ_FINAL {
-    mDecoders.Clear();
+    mDecoder = nullptr;
     return NS_OK;
   }
 
 private:
-  nsTArray<nsRefPtr<SourceBufferDecoder>> mDecoders;
+  nsRefPtr<SourceBufferDecoder> mDecoder;
 };
 
 void
 TrackBuffer::Shutdown()
 {
-  // End the SourceBufferResource associated with mCurrentDecoder, which will
-  // unblock any decoder initialization in ReadMetadata().
-  DiscardDecoder();
-
   // Finish any decoder initialization, which may add to mInitializedDecoders.
   // Shutdown waits for any pending events, which may require the monitor,
   // so we must not hold the monitor during this call.
   mParentDecoder->GetReentrantMonitor().AssertNotCurrentThreadIn();
   mTaskQueue->Shutdown();
   mTaskQueue = nullptr;
 
   ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
   for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
     mDecoders[i]->GetReader()->Shutdown();
   }
   mInitializedDecoders.Clear();
-  NS_DispatchToMainThread(new ReleaseDecoderTask(mDecoders));
-  MOZ_ASSERT(mDecoders.IsEmpty());
   mParentDecoder = nullptr;
 }
 
 bool
 TrackBuffer::AppendData(const uint8_t* aData, uint32_t aLength)
 {
   MOZ_ASSERT(NS_IsMainThread());
   // TODO: Run more of the buffer append algorithm asynchronously.
@@ -394,23 +383,26 @@ TrackBuffer::ContainsTime(int64_t aTime)
   }
 
   return false;
 }
 
 void
 TrackBuffer::BreakCycles()
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
     mDecoders[i]->GetReader()->BreakCycles();
   }
-  mInitializedDecoders.Clear();
-  NS_DispatchToMainThread(new ReleaseDecoderTask(mDecoders));
-  MOZ_ASSERT(mDecoders.IsEmpty());
-  mParentDecoder = nullptr;
+  mDecoders.Clear();
+
+  // These are cleared in Shutdown()
+  MOZ_ASSERT(mInitializedDecoders.IsEmpty());
+  MOZ_ASSERT(!mParentDecoder);
 }
 
 void
 TrackBuffer::ResetDecode()
 {
   for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
     mDecoders[i]->GetReader()->ResetDecode();
   }
--- a/content/media/test/mochitest.ini
+++ b/content/media/test/mochitest.ini
@@ -455,17 +455,16 @@ skip-if = true # bug 1021673
 [test_seekLies.html]
 [test_source.html]
 [test_source_media.html]
 [test_source_null.html]
 [test_source_write.html]
 [test_standalone.html]
 [test_streams_autoplay.html]
 [test_streams_element_capture.html]
-skip-if = e10s && os == 'win' # Bug 1065881 - Crash on child process shutdown in ShadowLayerForwarder::InWorkerThread
 [test_streams_element_capture_createObjectURL.html]
 [test_streams_element_capture_playback.html]
 [test_streams_element_capture_reset.html]
 [test_streams_gc.html]
 skip-if = buildapp == 'b2g' # bug 1021682
 [test_streams_srcObject.html]
 [test_streams_tracks.html]
 [test_texttrack.html]
--- a/content/media/test/test_mediarecorder_record_immediate_stop.html
+++ b/content/media/test/test_mediarecorder_record_immediate_stop.html
@@ -92,32 +92,31 @@ function startTest(test, token) {
         ok(false, 'ondataavailable unexpectedly fired later than onstop');
         manager.finished(token);
       }
     }
   };
 
   // This handler completes a start and stop of recording and verifies
   // respective media recorder state.
-  var canPlayThrough = function() {
-    element.removeEventListener('canplaythrough', canPlayThrough, false);
-
+  element.onloadedmetadata = function () {
+    element.onloadedmetadata = null;
+    element.play();
     mediaRecorder.start();
     is(mediaRecorder.state, 'recording', 'Media recorder should be recording');
     is(mediaRecorder.stream, element.stream,
        'Media recorder stream = element stream at the start of recording');
 
     mediaRecorder.stop();
     is(mediaRecorder.state, 'inactive',
        'Media recorder is inactive after being stopped');
     is(mediaRecorder.stream, element.stream,
        'Media recorder stream = element stream post recording');
   };
 
-  element.addEventListener('canplaythrough', canPlayThrough, false);
-  element.play();
+  element.preload = "metadata";
 }
 
 manager.runTests(gMediaRecorderTests, startTest);
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_mediarecorder_record_no_timeslice.html
+++ b/content/media/test/test_mediarecorder_record_no_timeslice.html
@@ -79,33 +79,27 @@ function startTest(test, token) {
       // onstop should not have fired before ondataavailable
       if (onStopFired) {
         ok(false, 'ondataavailable unexpectedly fired later than onstop');
         manager.finished(token);
       }
     }
   };
 
-  element.oncanplaythrough = function () {
-    element.oncanplaythrough = null;
-    // If content has ended, skip the test
-    if (element.ended) {
-      ok(true, 'ended fired before canplaythrough, skipping test');
-      manager.finished(token);
-    } else {
-      // If content hasn't ended, start recording
-      mediaRecorder.start();
-      is(mediaRecorder.state, 'recording',
-         'Media recorder should be recording');
-      is(mediaRecorder.stream, element.stream,
-         'Media recorder stream = element stream at the start of recording');
-      // Recording will automatically stop when the stream ends.
-    }
+  element.preload = "metadata";
+
+  element.onloadedmetadata = function () {
+    element.onloadedmetadata = null;
+    mediaRecorder.start();
+    is(mediaRecorder.state, 'recording',
+     'Media recorder should be recording');
+    is(mediaRecorder.stream, element.stream,
+     'Media recorder stream = element stream at the start of recording');
+
+    element.play();
   }
-
-  element.play();
 }
 
 manager.runTests(gMediaRecorderTests, startTest);
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_mediarecorder_record_session.html
+++ b/content/media/test/test_mediarecorder_record_session.html
@@ -49,25 +49,27 @@ function startTest(test, token) {
   mediaRecorder.onerror = function(err) {
     ok(false, 'Unexpected error fired with:' + err);
   }
 
   mediaRecorder.onwarning = function() {
     ok(false, 'Unexpected warning fired');
   }
 
-  element.oncanplaythrough = function () {
-    element.oncanplaythrough = null;
+  element.preload = "metadata";
+
+  element.onloadedmetadata = function () {
+    element.onloadedmetadata = null;
+    element.play();
     for (var i = 0; i < mExpectStopCount; i++) {
       mediaRecorder.start(1000);
       mediaRecorder.stop();
     }
   }
 
-  element.play();
 }
 
 manager.runTests(gMediaRecorderTests, startTest);
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_timeupdate_small_files.html
+++ b/content/media/test/test_timeupdate_small_files.html
@@ -12,78 +12,75 @@ https://bugzilla.mozilla.org/show_bug.cg
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=495319">Mozilla Bug 495319</a>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 var manager = new MediaTestManager;
 
-function timeupdate(e) {
-  var v = e.target;
-  v._timeupdateCount++;
-  ok(v.gotEnded == 0, v._name + " - shouldn't get timeupdate after ended");
-}
-
 function ended(e) {
   var v = e.target;
-  ++v.gotEnded;
-  ok(v._timeupdateCount > 0, v._name + " - should see at least one timeupdate: " + v.currentTime);
-  v._finished = true;
+  ++v.counter["ended"];
+  is(v.counter["ended"], 1, v._name + " should see ended only once");
+  ok(v.counter["timeupdate"] > 0, v._name + " should see at least one timeupdate: " + v.currentTime);
+
+  // Rest event counters for we don't allow events after ended.
+  eventsToLog.forEach(function(e) {
+    v.counter[e] = 0;
+  });
+
   // Finish the test after 500ms. We shouldn't receive any timeupdate events
   // after the ended event, so this gives time for any pending timeupdate events
   // to fire so we can ensure we don't regress behaviour.
   setTimeout(
     function() {
       // Remove the event listeners before removing the video from the document.
       // We should receive a timeupdate and pause event when we remove the element
       // from the document (as the element is specified to behave as if pause() was
       // invoked when it's removed from a document), and we don't want those
       // confusing the test results.
       v.removeEventListener("ended", ended, false);
-      v.removeEventListener("timeupdate", timeupdate, false);
-      for (var i = 0; i < eventsToLog.length; ++i) {
-        v.removeEventListener(eventsToLog[i], logEvent, false);
-      }
+      eventsToLog.forEach(function(e) {
+        v.removeEventListener(e, logEvent, false);
+      });
       removeNodeAndSource(v);
       manager.finished(v.token);
     },
     500);
 }
 
 var eventsToLog = ["play", "canplay", "canplaythrough", "loadstart", "loadedmetadata",
-  "loadeddata", "playing", "progress", "timeupdate", "ended", "suspend", "error", "stalled", "emptied", "abort",
+  "loadeddata", "playing", "timeupdate", "error", "stalled", "emptied", "abort",
   "waiting", "pause"];
+
 function logEvent(event) {
-  if (event.target.gotEnded > (event.type == "ended" ? 1 : 0)) {
-    if (event.target.currentSrc.slice(-9) == "seek.webm" && event.type == "stalled") {
-      todo(false, event.target.currentSrc + " got unexpected stalled after ended (bug 760770)");
-    } else {
-      ok(false, event.target.currentSrc + " got unexpected " + event.type + " after ended");
-    }
-  } else {
-    info(event.target.currentSrc + " got " + event.type);
+  var v = event.target;
+  ++v.counter[event.type];
+  if (v.counter["ended"] > 0) {
+    is(v.counter[event.type], 0, v._name + " got unexpected " + event.type + " after ended");
   }
 }
 
 function startTest(test, token) {
   var type = getMajorMimeType(test.type);
   var v = document.createElement(type);
   v.token = token;
   manager.started(token);
   v.src = test.name;
   v._name = test.name;
-  v._timeupdateCount = 0;
-  v._finished = false;
-  v.gotEnded = 0;
+
+  // Keep how many events received for each event type.
+  v.counter = {};
+  eventsToLog.forEach(function(e) {
+    v.addEventListener(e, logEvent, false);
+    v.counter[e] = 0;
+  });
   v.addEventListener("ended", ended, false);
-  v.addEventListener("timeupdate", timeupdate, false);
-  for (var i = 0; i < eventsToLog.length; ++i) {
-    v.addEventListener(eventsToLog[i], logEvent, false);
-  }
+  v.counter["ended"] = 0;
   document.body.appendChild(v);
   v.play();
 }
 
 manager.runTests(gSmallTests, startTest);
 
 </script>
 </pre>
--- a/content/media/webspeech/synth/pico/nsPicoService.cpp
+++ b/content/media/webspeech/synth/pico/nsPicoService.cpp
@@ -406,52 +406,63 @@ NS_IMETHODIMP
 PicoCallbackRunnable::OnCancel()
 {
   mService->mCurrentTask = nullptr;
   return NS_OK;
 }
 
 NS_INTERFACE_MAP_BEGIN(nsPicoService)
   NS_INTERFACE_MAP_ENTRY(nsISpeechService)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISpeechService)
+  NS_INTERFACE_MAP_ENTRY(nsIObserver)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(nsPicoService)
 NS_IMPL_RELEASE(nsPicoService)
 
 nsPicoService::nsPicoService()
   : mInitialized(false)
   , mVoicesMonitor("nsPicoService::mVoices")
   , mCurrentTask(nullptr)
   , mPicoSystem(nullptr)
   , mPicoEngine(nullptr)
   , mSgResource(nullptr)
   , mTaResource(nullptr)
   , mPicoMemArea(nullptr)
 {
-  DebugOnly<nsresult> rv = NS_NewNamedThread("Pico Worker", getter_AddRefs(mThread));
-  MOZ_ASSERT(NS_SUCCEEDED(rv));
-  rv = mThread->Dispatch(NS_NewRunnableMethod(this, &nsPicoService::Init), NS_DISPATCH_NORMAL);
-  MOZ_ASSERT(NS_SUCCEEDED(rv));
 }
 
 nsPicoService::~nsPicoService()
 {
   // We don't worry about removing the voices because this gets
   // destructed at shutdown along with the voice registry.
   MonitorAutoLock autoLock(mVoicesMonitor);
   mVoices.Clear();
 
   if (mThread) {
     mThread->Shutdown();
   }
 
   UnloadEngine();
 }
 
+// nsIObserver
+
+NS_IMETHODIMP
+nsPicoService::Observe(nsISupports* aSubject, const char* aTopic,
+                       const char16_t* aData)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  NS_ENSURE_TRUE(!strcmp(aTopic, "profile-after-change"), NS_ERROR_UNEXPECTED);
+
+  DebugOnly<nsresult> rv = NS_NewNamedThread("Pico Worker", getter_AddRefs(mThread));
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
+  return mThread->Dispatch(
+    NS_NewRunnableMethod(this, &nsPicoService::Init), NS_DISPATCH_NORMAL);
+}
 // nsISpeechService
 
 NS_IMETHODIMP
 nsPicoService::Speak(const nsAString& aText, const nsAString& aUri,
                      float aRate, float aPitch, nsISpeechTask* aTask)
 {
   NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_AVAILABLE);
 
--- a/content/media/webspeech/synth/pico/nsPicoService.h
+++ b/content/media/webspeech/synth/pico/nsPicoService.h
@@ -5,40 +5,43 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsPicoService_h
 #define nsPicoService_h
 
 #include "mozilla/Mutex.h"
 #include "nsAutoPtr.h"
 #include "nsTArray.h"
+#include "nsIObserver.h"
 #include "nsIThread.h"
 #include "nsISpeechService.h"
 #include "nsRefPtrHashtable.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/Monitor.h"
 
 namespace mozilla {
 namespace dom {
 
 class PicoVoice;
 class PicoCallbackRunnable;
 
 typedef void* pico_System;
 typedef void* pico_Resource;
 typedef void* pico_Engine;
 
-class nsPicoService : public nsISpeechService
+class nsPicoService : public nsIObserver,
+                      public nsISpeechService
 {
   friend class PicoCallbackRunnable;
   friend class PicoInitRunnable;
 
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSISPEECHSERVICE
+  NS_DECL_NSIOBSERVER
 
   nsPicoService();
 
   static nsPicoService* GetInstance();
 
   static already_AddRefed<nsPicoService> GetInstanceForService();
 
   static void Shutdown();
--- a/content/xul/document/src/XULDocument.cpp
+++ b/content/xul/document/src/XULDocument.cpp
@@ -2697,17 +2697,16 @@ XULDocument::LoadOverlayInternal(nsIURI*
         // that the overlay's JSObjects etc end up being created
         // with the right principal and in the correct
         // compartment.
         rv = NS_NewChannel(getter_AddRefs(channel),
                            aURI,
                            NodePrincipal(),
                            nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
                            nsIContentPolicy::TYPE_OTHER,
-                           nullptr,    // aChannelPolicy
                            group);
 
         if (NS_SUCCEEDED(rv)) {
             rv = channel->AsyncOpen(listener, nullptr);
         }
 
         if (NS_FAILED(rv)) {
             // Abandon this prototype
--- a/docshell/base/nsAboutRedirector.cpp
+++ b/docshell/base/nsAboutRedirector.cpp
@@ -62,17 +62,17 @@ static RedirEntry kRedirMap[] = {
       nsIAboutModule::ALLOW_SCRIPT |
       nsIAboutModule::HIDE_FROM_ABOUTABOUT },
     { "support", "chrome://global/content/aboutSupport.xhtml",
       nsIAboutModule::ALLOW_SCRIPT },
     { "telemetry", "chrome://global/content/aboutTelemetry.xhtml",
       nsIAboutModule::ALLOW_SCRIPT },
     { "networking", "chrome://global/content/aboutNetworking.xhtml",
        nsIAboutModule::ALLOW_SCRIPT },
-    { "webrtc", "chrome://global/content/aboutWebrtc.xhtml",
+    { "webrtc", "chrome://global/content/aboutwebrtc/aboutWebrtc.xhtml",
        nsIAboutModule::ALLOW_SCRIPT },
     // about:srcdoc is unresolvable by specification.  It is included here
     // because the security manager would disallow srcdoc iframes otherwise.
     { "srcdoc", "about:blank",
       nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
       nsIAboutModule::HIDE_FROM_ABOUTABOUT }
 };
 static const int kRedirTotal = mozilla::ArrayLength(kRedirMap);
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -163,17 +163,16 @@
 #include "nsIWebBrowserChromeFocus.h"
 
 #if NS_PRINT_PREVIEW
 #include "nsIDocumentViewerPrint.h"
 #include "nsIWebBrowserPrint.h"
 #endif
 
 #include "nsContentUtils.h"
-#include "nsIChannelPolicy.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsILoadInfo.h"
 #include "nsSandboxFlags.h"
 #include "nsXULAppAPI.h"
 #include "nsDOMNavigationTiming.h"
 #include "nsISecurityUITelemetry.h"
 #include "nsIAppsService.h"
 #include "nsDSURIContentListener.h"
@@ -10153,37 +10152,17 @@ nsDocShell::DoURILoad(nsIURI * aURI,
         loadFlags |= nsIChannel::LOAD_INITIAL_DOCUMENT_URI;
     }
 
     if (mLoadType == LOAD_ERROR_PAGE) {
         // Error pages are LOAD_BACKGROUND
         loadFlags |= nsIChannel::LOAD_BACKGROUND;
     }
 
-    // check for Content Security Policy to pass along with the
-    // new channel we are creating
-    nsCOMPtr<nsIChannelPolicy> channelPolicy;
     if (IsFrame()) {
-        // check the parent docshell for a CSP
-        nsCOMPtr<nsIContentSecurityPolicy> csp;
-        nsCOMPtr<nsIDocShellTreeItem> parentItem;
-        GetSameTypeParent(getter_AddRefs(parentItem));
-        if (parentItem) {
-          nsCOMPtr<nsIDocument> doc = parentItem->GetDocument();
-          if (doc) {
-            rv = doc->NodePrincipal()->GetCsp(getter_AddRefs(csp));
-            NS_ENSURE_SUCCESS(rv, rv);
-            if (csp) {
-              channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
-              channelPolicy->SetContentSecurityPolicy(csp);
-              channelPolicy->SetLoadType(nsIContentPolicy::TYPE_SUBDOCUMENT);
-            }
-          }
-        }
-
         // Only allow view-source scheme in top-level docshells. view-source is
         // the only scheme to which this applies at the moment due to potential
         // timing attacks to read data from cross-origin iframes. If this widens
         // we should add a protocol flag for whether the scheme is allowed in
         // frames and use something like nsNetUtil::NS_URIChainHasFlags.
         nsCOMPtr<nsIURI> tempURI = aURI;
         nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(tempURI);
         while (nestedURI) {
@@ -10242,17 +10221,16 @@ nsDocShell::DoURILoad(nsIURI * aURI,
 
     if (!isSrcdoc) {
         rv = NS_NewChannelInternal(getter_AddRefs(channel),
                                    aURI,
                                    requestingNode,
                                    requestingPrincipal,
                                    securityFlags,
                                    aContentPolicyType,
-                                   channelPolicy,
                                    nullptr,   // loadGroup
                                    static_cast<nsIInterfaceRequestor*>(this),
                                    loadFlags);
 
         if (NS_FAILED(rv)) {
             if (rv == NS_ERROR_UNKNOWN_PROTOCOL) {
                 // This is a uri with a protocol scheme we don't know how
                 // to handle.  Embedders might still be interested in
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -16,17 +16,19 @@
 #include "nsIScrollable.h"
 #include "nsITextScroll.h"
 #include "nsIContentViewerContainer.h"
 #include "nsIDOMStorageManager.h"
 #include "nsDocLoader.h"
 #include "mozilla/WeakPtr.h"
 #include "mozilla/TimeStamp.h"
 #include "GeckoProfiler.h"
+#ifdef MOZ_ENABLE_PROFILER_SPS
 #include "ProfilerMarkers.h"
+#endif
 
 // Helper Classes
 #include "nsCOMPtr.h"
 #include "nsPoint.h" // mCurrent/mDefaultScrollbarPreferences
 #include "nsString.h"
 #include "nsAutoPtr.h"
 #include "nsThreadUtils.h"
 
@@ -946,16 +948,18 @@ private:
     nsTObserverArray<nsWeakPtr> mReflowObservers;
     nsTObserverArray<nsWeakPtr> mScrollObservers;
     nsCString         mOriginalUriString;
     nsWeakPtr mOpener;
     nsWeakPtr mOpenedRemote;
 
     // Storing profile timeline markers and if/when recording started
     mozilla::TimeStamp mProfileTimelineStartTime;
+
+#ifdef MOZ_ENABLE_PROFILER_SPS
     struct InternalProfileTimelineMarker
     {
       InternalProfileTimelineMarker(const char* aName,
                                     ProfilerMarkerTracing* aPayload,
                                     float aTime)
         : mName(aName)
         , mPayload(aPayload)
         , mTime(aTime)
@@ -966,16 +970,17 @@ private:
         delete mPayload;
       }
 
       const char* mName;
       ProfilerMarkerTracing* mPayload;
       float mTime;
     };
     nsTArray<InternalProfileTimelineMarker*> mProfileTimelineMarkers;
+#endif
 
     // Get the elapsed time (in millis) since the profile timeline recording
     // started
     float GetProfileTimelineDelta();
 
     // Get rid of all the timeline markers accumulated so far
     void ClearProfileTimelineMarkers();
 
--- a/dom/apps/AppsServiceChild.jsm
+++ b/dom/apps/AppsServiceChild.jsm
@@ -126,19 +126,16 @@ this.DOMApplicationRegistry = {
     // cpmm.addMessageListener causes the DOMApplicationRegistry object to
     // live forever if we don't clean up properly.
     this.webapps = null;
     this.DOMApps = null;
 
     APPS_IPC_MSG_NAMES.forEach((aMsgName) => {
       this.cpmm.removeMessageListener(aMsgName, this);
     });
-
-    this.cpmm.sendAsyncMessage("Webapps:UnregisterForMessages",
-                               APPS_IPC_MSG_NAMES)
   },
 
   receiveMessage: function receiveMessage(aMessage) {
     debug("Received " + aMessage.name + " message.");
     let msg = aMessage.data;
     switch (aMessage.name) {
       case "Webapps:AddApp":
         this.webapps[msg.id] = msg.app;
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -62,17 +62,16 @@
 #include "TimeManager.h"
 #include "DeviceStorage.h"
 #include "nsIDOMNavigatorSystemMessages.h"
 #include "nsStreamUtils.h"
 #include "nsIAppsService.h"
 #include "mozIApplication.h"
 #include "WidgetUtils.h"
 #include "mozIThirdPartyUtil.h"
-#include "nsChannelPolicy.h"
 
 #ifdef MOZ_MEDIA_NAVIGATOR
 #include "MediaManager.h"
 #endif
 #ifdef MOZ_B2G_BT
 #include "BluetoothManager.h"
 #endif
 #include "DOMCameraManager.h"
@@ -1051,36 +1050,21 @@ Navigator::SendBeacon(const nsAString& a
                                  nsContentUtils::GetSecurityManager());
   if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
     // Disallowed by content policy
     aRv.Throw(NS_ERROR_CONTENT_BLOCKED);
     return false;
   }
 
   nsCOMPtr<nsIChannel> channel;
-  nsCOMPtr<nsIChannelPolicy> channelPolicy;
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  rv = principal->GetCsp(getter_AddRefs(csp));
-  if (NS_FAILED(rv)) {
-    aRv.Throw(NS_ERROR_FAILURE);
-    return false;
-  }
-
-  if (csp) {
-    channelPolicy = do_CreateInstance(NSCHANNELPOLICY_CONTRACTID);
-    channelPolicy->SetContentSecurityPolicy(csp);
-    channelPolicy->SetLoadType(nsIContentPolicy::TYPE_BEACON);
-  }
-
   rv = NS_NewChannel(getter_AddRefs(channel),
                      uri,
                      doc,
                      nsILoadInfo::SEC_NORMAL,
-                     nsIContentPolicy::TYPE_BEACON,
-                     channelPolicy);
+                     nsIContentPolicy::TYPE_BEACON);
 
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return false;
   }
 
   nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryInterface(channel);
   if (pbChannel) {
--- a/dom/base/URLSearchParams.h
+++ b/dom/base/URLSearchParams.h
@@ -70,17 +70,17 @@ public:
   void Set(const nsAString& aName, const nsAString& aValue);
 
   void Append(const nsAString& aName, const nsAString& aValue);
 
   bool Has(const nsAString& aName);
 
   void Delete(const nsAString& aName);
 
-  void Stringify(nsString& aRetval)
+  void Stringify(nsString& aRetval) const
   {
     Serialize(aRetval);
   }
 
 private:
   void AppendInternal(const nsAString& aName, const nsAString& aValue);
 
   void DeleteAll();
--- a/dom/bindings/Errors.msg
+++ b/dom/bindings/Errors.msg
@@ -57,8 +57,9 @@ MSG_DEF(MSG_INVALID_READ_SIZE, 0, "0 (Ze
 MSG_DEF(MSG_HEADERS_IMMUTABLE, 0, "Headers are immutable and cannot be modified.")
 MSG_DEF(MSG_INVALID_HEADER_NAME, 1, "{0} is an invalid header name.")
 MSG_DEF(MSG_INVALID_HEADER_VALUE, 1, "{0} is an invalid header value.")
 MSG_DEF(MSG_INVALID_HEADER_SEQUENCE, 0, "Headers require name/value tuples when being initialized by a sequence.")
 MSG_DEF(MSG_PERMISSION_DENIED_TO_PASS_ARG, 1, "Permission denied to pass cross-origin object as {0}.")
 MSG_DEF(MSG_MISSING_REQUIRED_DICTIONARY_MEMBER, 1, "Missing required {0}.")
 MSG_DEF(MSG_INVALID_REQUEST_METHOD, 1, "Invalid request method {0}.")
 MSG_DEF(MSG_REQUEST_BODY_CONSUMED_ERROR, 0, "Request body has already been consumed.")
+MSG_DEF(MSG_RESPONSE_INVALID_STATUSTEXT_ERROR, 0, "Response statusText may not contain newline or carriage return.")
--- a/dom/browser-element/BrowserElementChildPreload.js
+++ b/dom/browser-element/BrowserElementChildPreload.js
@@ -78,29 +78,31 @@ let SEC_ERROR_UNTRUSTED_CERT = (SEC_ERRO
 let SEC_ERROR_EXPIRED_CERTIFICATE = (SEC_ERROR_BASE + 11);
 let SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED = (SEC_ERROR_BASE + 176);
 
 let SSL_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SSL_ERROR_BASE;
 let SSL_ERROR_BAD_CERT_DOMAIN = (SSL_ERROR_BASE + 12);
 
 let MOZILLA_PKIX_ERROR_BASE = Ci.nsINSSErrorsService.MOZILLA_PKIX_ERROR_BASE;
 let MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY = (MOZILLA_PKIX_ERROR_BASE + 1);
+let MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA = (MOZILLA_PKIX_ERROR_BASE + 3);
 
 function getErrorClass(errorCode) {
   let NSPRCode = -1 * NS_ERROR_GET_CODE(errorCode);
 
   switch (NSPRCode) {
     case SEC_ERROR_UNKNOWN_ISSUER:
     case SEC_ERROR_UNTRUSTED_ISSUER:
     case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
     case SEC_ERROR_UNTRUSTED_CERT:
     case SSL_ERROR_BAD_CERT_DOMAIN:
     case SEC_ERROR_EXPIRED_CERTIFICATE:
     case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED:
     case MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY:
+    case MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA:
       return Ci.nsINSSErrorsService.ERROR_CLASS_BAD_CERT;
     default:
       return Ci.nsINSSErrorsService.ERROR_CLASS_SSL_PROTOCOL;
   }
 
   return null;
 }
 
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -1017,18 +1017,20 @@ CanvasRenderingContext2D::Redraw(const m
   nsSVGEffects::InvalidateDirectRenderingObservers(mCanvasElement);
 
   mCanvasElement->InvalidateCanvasContent(&r);
 }
 
 void
 CanvasRenderingContext2D::DidRefresh()
 {
-  SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
-  if (glue) {
+  if (IsTargetValid() && SkiaGLTex()) {
+    SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
+    MOZ_ASSERT(glue);
+
     auto gl = glue->GetGLContext();
     gl->FlushIfHeavyGLCallsSinceLastFlush();
   }
 }
 
 void
 CanvasRenderingContext2D::RedrawUser(const gfxRect& r)
 {
@@ -4818,16 +4820,24 @@ CanvasRenderingContext2D::CreateImageDat
                                           ErrorResult& error)
 {
   return mozilla::dom::CreateImageData(cx, this, imagedata.Width(),
                                        imagedata.Height(), error);
 }
 
 static uint8_t g2DContextLayerUserData;
 
+
+uint32_t
+CanvasRenderingContext2D::SkiaGLTex() const
+{
+    MOZ_ASSERT(IsTargetValid());
+    return (uint32_t)(uintptr_t)mTarget->GetNativeSurface(NativeSurfaceType::OPENGL_TEXTURE);
+}
+
 already_AddRefed<CanvasLayer>
 CanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
                                          CanvasLayer *aOldLayer,
                                          LayerManager *aManager)
 {
   if (mOpaque) {
     // If we're opaque then make sure we have a surface so we paint black
     // instead of transparent.
@@ -4849,19 +4859,21 @@ CanvasRenderingContext2D::GetCanvasLayer
 
   if (!mResetLayer && aOldLayer) {
     CanvasRenderingContext2DUserData* userData =
       static_cast<CanvasRenderingContext2DUserData*>(
         aOldLayer->GetUserData(&g2DContextLayerUserData));
 
     CanvasLayer::Data data;
 
-    SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
-    GLuint skiaGLTex = (GLuint)(uintptr_t)mTarget->GetNativeSurface(NativeSurfaceType::OPENGL_TEXTURE);
-    if (glue && skiaGLTex) {
+    GLuint skiaGLTex = SkiaGLTex();
+    if (skiaGLTex) {
+      SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
+      MOZ_ASSERT(glue);
+
       data.mGLContext = glue->GetGLContext();
       data.mFrontbufferGLTex = skiaGLTex;
     } else {
       data.mDrawTarget = mTarget;
     }
 
     if (userData && userData->IsForContext(this) && aOldLayer->IsDataValid(data)) {
       nsRefPtr<CanvasLayer> ret = aOldLayer;
@@ -4893,21 +4905,24 @@ CanvasRenderingContext2D::GetCanvasLayer
   canvasLayer->SetDidTransactionCallback(
           CanvasRenderingContext2DUserData::DidTransactionCallback, userData);
   canvasLayer->SetUserData(&g2DContextLayerUserData, userData);
 
   CanvasLayer::Data data;
   data.mSize = nsIntSize(mWidth, mHeight);
   data.mHasAlpha = !mOpaque;
 
-  SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
-  GLuint skiaGLTex = (GLuint)(uintptr_t)mTarget->GetNativeSurface(NativeSurfaceType::OPENGL_TEXTURE);
-  if (glue && skiaGLTex) {
+  GLuint skiaGLTex = SkiaGLTex();
+  if (skiaGLTex) {
     canvasLayer->SetPreTransactionCallback(
             CanvasRenderingContext2DUserData::PreTransactionCallback, userData);
+
+    SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
+    MOZ_ASSERT(glue);
+
     data.mGLContext = glue->GetGLContext();
     data.mFrontbufferGLTex = skiaGLTex;
   } else {
     data.mDrawTarget = mTarget;
   }
 
   canvasLayer->Initialize(data);
   uint32_t flags = mOpaque ? Layer::CONTENT_OPAQUE : 0;
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -693,17 +693,19 @@ protected:
   /*
    * Disposes an old target and prepares to lazily create a new target.
    */
   void ClearTarget();
 
   /**
    * Check if the target is valid after calling EnsureTarget.
    */
-  bool IsTargetValid() { return mTarget != sErrorTarget && mTarget != nullptr; }
+  bool IsTargetValid() const {
+    return mTarget != sErrorTarget && mTarget != nullptr;
+  }
 
   /**
     * Returns the surface format this canvas should be allocated using. Takes
     * into account mOpaque, platform requirements, etc.
     */
   mozilla::gfx::SurfaceFormat GetSurfaceFormat() const;
 
   /**
@@ -759,16 +761,18 @@ protected:
   // If mCanvasElement is not provided, then a docshell is
   nsCOMPtr<nsIDocShell> mDocShell;
 
   // This is created lazily so it is necessary to call EnsureTarget before
   // accessing it. In the event of an error it will be equal to
   // sErrorTarget.
   mozilla::RefPtr<mozilla::gfx::DrawTarget> mTarget;
 
+  uint32_t SkiaGLTex() const;
+
   /**
     * Flag to avoid duplicate calls to InvalidateFrame. Set to true whenever
     * Redraw is called, reset to false when Render is called.
     */
   bool mIsEntireFrameInvalid;
   /**
     * When this is set, the first call to Redraw(gfxRect) should set
     * mIsEntireFrameInvalid since we expect it will be followed by
--- a/dom/canvas/WebGL2ContextTextures.cpp
+++ b/dom/canvas/WebGL2ContextTextures.cpp
@@ -346,16 +346,18 @@ WebGL2Context::CompressedTexSubImage3D(G
     MOZ_CRASH("Not Implemented.");
 }
 
 JS::Value
 WebGL2Context::GetTexParameterInternal(const TexTarget& target, GLenum pname)
 {
     switch (pname) {
         case LOCAL_GL_TEXTURE_IMMUTABLE_FORMAT:
+        case LOCAL_GL_TEXTURE_BASE_LEVEL:
+        case LOCAL_GL_TEXTURE_MAX_LEVEL:
         {
             GLint i = 0;
             gl->fGetTexParameteriv(target.get(), pname, &i);
             return JS::NumberValue(uint32_t(i));
         }
     }
     return WebGLContext::GetTexParameterInternal(target, pname);
 }
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -917,17 +917,17 @@ WebGLContext::GenerateMipmap(GLenum rawT
     WebGLTexture *tex = activeBoundTextureForTarget(target);
 
     if (!tex)
         return ErrorInvalidOperation("generateMipmap: No texture is bound to this target.");
 
     const TexImageTarget imageTarget = (target == LOCAL_GL_TEXTURE_2D)
                                                   ? LOCAL_GL_TEXTURE_2D
                                                   : LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X;
-    if (!tex->HasImageInfoAt(imageTarget, 0))
+    if (!tex->HasImageInfoAt(imageTarget, tex->GetBaseMipmapLevel()))
     {
         return ErrorInvalidOperation("generateMipmap: Level zero of texture is not defined.");
     }
 
     if (!IsWebGL2() && !tex->IsFirstImagePowerOfTwo())
         return ErrorInvalidOperation("generateMipmap: Level zero of texture does not have power-of-two width and height.");
 
     TexInternalFormat internalformat = tex->ImageInfoAt(imageTarget, 0).EffectiveInternalFormat();
@@ -1505,16 +1505,29 @@ void WebGLContext::TexParameter_base(GLe
     WebGLTexture *tex = activeBoundTextureForTarget(texTarget);
     if (!tex)
         return ErrorInvalidOperation("texParameter: no texture is bound to this target");
 
     bool pnameAndParamAreIncompatible = false;
     bool paramValueInvalid = false;
 
     switch (pname) {
+        case LOCAL_GL_TEXTURE_BASE_LEVEL:
+        case LOCAL_GL_TEXTURE_MAX_LEVEL:
+            if (!IsWebGL2())
+                return ErrorInvalidEnumInfo("texParameter: pname", pname);
+            if (intParam < 0) {
+                paramValueInvalid = true;
+                break;
+            }
+            if (pname == LOCAL_GL_TEXTURE_BASE_LEVEL)
+                tex->SetBaseMipmapLevel(intParam);
+            else
+                tex->SetMaxMipmapLevel(intParam);
+            break;
         case LOCAL_GL_TEXTURE_MIN_FILTER:
             switch (intParam) {
                 case LOCAL_GL_NEAREST:
                 case LOCAL_GL_LINEAR:
                 case LOCAL_GL_NEAREST_MIPMAP_NEAREST:
                 case LOCAL_GL_LINEAR_MIPMAP_NEAREST:
                 case LOCAL_GL_NEAREST_MIPMAP_LINEAR:
                 case LOCAL_GL_LINEAR_MIPMAP_LINEAR:
--- a/dom/canvas/WebGLContextState.cpp
+++ b/dom/canvas/WebGLContextState.cpp
@@ -181,16 +181,20 @@ WebGLContext::GetParameter(JSContext* cx
         switch (pname) {
             case LOCAL_GL_MAX_SAMPLES:
             case LOCAL_GL_MAX_UNIFORM_BLOCK_SIZE:
             case LOCAL_GL_MAX_VERTEX_UNIFORM_COMPONENTS: {
                 GLint val;
                 gl->fGetIntegerv(pname, &val);
                 return JS::NumberValue(uint32_t(val));
             }
+
+            case LOCAL_GL_TEXTURE_BINDING_3D: {
+                return WebGLObjectAsJSValue(cx, mBound3DTextures[mActiveTexture].get(), rv);
+            }
         }
     }
 
     switch (pname) {
         //
         // String params
         //
         case LOCAL_GL_VENDOR:
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -214,16 +214,23 @@ bool WebGLContext::ValidateBlendFuncEnum
 }
 
 bool WebGLContext::ValidateTextureTargetEnum(GLenum target, const char *info)
 {
     switch (target) {
         case LOCAL_GL_TEXTURE_2D:
         case LOCAL_GL_TEXTURE_CUBE_MAP:
             return true;
+        case LOCAL_GL_TEXTURE_3D: {
+            const bool isValid = IsWebGL2();
+            if (!isValid) {
+                ErrorInvalidEnumInfo(info, target);
+            }
+            return isValid;
+        }
         default:
             ErrorInvalidEnumInfo(info, target);
             return false;
     }
 }
 
 bool WebGLContext::ValidateComparisonEnum(GLenum target, const char *info)
 {
--- a/dom/canvas/WebGLTexture.cpp
+++ b/dom/canvas/WebGLTexture.cpp
@@ -28,16 +28,18 @@ WebGLTexture::WebGLTexture(WebGLContext 
     , mMinFilter(LOCAL_GL_NEAREST_MIPMAP_LINEAR)
     , mMagFilter(LOCAL_GL_LINEAR)
     , mWrapS(LOCAL_GL_REPEAT)
     , mWrapT(LOCAL_GL_REPEAT)
     , mFacesCount(0)
     , mMaxLevelWithCustomImages(0)
     , mHaveGeneratedMipmap(false)
     , mImmutable(false)
+    , mBaseMipmapLevel(0)
+    , mMaxMipmapLevel(1000)
     , mFakeBlackStatus(WebGLTextureFakeBlackStatus::IncompleteTexture)
 {
     mContext->MakeContextCurrent();
     mContext->gl->fGenTextures(1, &mGLName);
     mContext->mTextures.insertBack(this);
 }
 
 void
@@ -57,51 +59,46 @@ WebGLTexture::ImageInfo::MemoryUsage() c
 }
 
 size_t
 WebGLTexture::MemoryUsage() const {
     if (IsDeleted())
         return 0;
     size_t result = 0;
     for(size_t face = 0; face < mFacesCount; face++) {
-        if (mHaveGeneratedMipmap) {
-            size_t level0MemoryUsage = ImageInfoAtFace(face, 0).MemoryUsage();
-            // Each mipmap level is 1/(2^d) the size of the previous level,
-            // where d is 2 or 3 depending on whether the images are 2D or 3D
-            // 1 + x + x^2 + ... = 1/(1-x)
-            // for x = 1/(2^2), we get 1/(1-1/4) = 4/3
-            // for x = 1/(2^3), we get 1/(1-1/8) = 8/7
-            size_t allLevelsMemoryUsage =
-                mTarget == LOCAL_GL_TEXTURE_3D
-                ? level0MemoryUsage * 8 / 7
-                : level0MemoryUsage * 4 / 3;
-            result += allLevelsMemoryUsage;
-        } else {
-            for(size_t level = 0; level <= mMaxLevelWithCustomImages; level++)
-                result += ImageInfoAtFace(face, level).MemoryUsage();
-        }
+      for(size_t level = 0; level <= mMaxLevelWithCustomImages; level++)
+        result += ImageInfoAtFace(face, level).MemoryUsage();
     }
     return result;
 }
 
 bool
 WebGLTexture::DoesMipmapHaveAllLevelsConsistentlyDefined(TexImageTarget texImageTarget) const
 {
     if (mHaveGeneratedMipmap)
         return true;
 
+    if (GetMaxMipmapLevel() < GetBaseMipmapLevel())
+        return false;
+
     // We want a copy here so we can modify it temporarily.
-    ImageInfo expected = ImageInfoAt(texImageTarget, 0);
+    ImageInfo expected = ImageInfoAt(texImageTarget, GetBaseMipmapLevel());
 
     // checks if custom level>0 images are all defined up to the highest level defined
     // and have the expected dimensions
-    for (size_t level = 0; level <= mMaxLevelWithCustomImages; ++level) {
+    for (size_t level = GetBaseMipmapLevel(); level <= GetMaxMipmapLevel(); ++level) {
         const ImageInfo& actual = ImageInfoAt(texImageTarget, level);
         if (actual != expected)
             return false;
+
+        // Check the raw value here, not the clamped one, since we don't want
+        // to terminate early if there aren't enough levels defined.
+        if (level == mMaxMipmapLevel)
+            return true;
+
         expected.mWidth = std::max(1, expected.mWidth / 2);
         expected.mHeight = std::max(1, expected.mHeight / 2);
         expected.mDepth = std::max(1, expected.mDepth / 2);
 
         // if the current level has size 1x1, we can stop here: the spec doesn't seem to forbid the existence
         // of extra useless levels.
         if (actual.mWidth == 1 &&
             actual.mHeight == 1 &&
@@ -180,30 +177,30 @@ WebGLTexture::SetGeneratedMipmap() {
 void
 WebGLTexture::SetCustomMipmap() {
     if (mHaveGeneratedMipmap) {
         // if we were in GeneratedMipmap mode and are now switching to CustomMipmap mode,
         // we need to compute now all the mipmap image info.
 
         // since we were in GeneratedMipmap mode, we know that the level 0 images all have the same info,
         // and are power-of-two.
-        ImageInfo imageInfo = ImageInfoAtFace(0, 0);
+        ImageInfo imageInfo = ImageInfoAtFace(0, GetBaseMipmapLevel());
         NS_ASSERTION(mContext->IsWebGL2() || imageInfo.IsPowerOfTwo(),
                      "this texture is NPOT, so how could GenerateMipmap() ever accept it?");
 
         GLsizei size = std::max(std::max(imageInfo.mWidth, imageInfo.mHeight), imageInfo.mDepth);
 
         // Find floor(log2(size)). (ES 3.0.4, 3.8 - Mipmapping).
         size_t maxLevel = 0;
         for (GLsizei n = size; n > 1; n >>= 1)
             ++maxLevel;
 
         EnsureMaxLevelWithCustomImagesAtLeast(maxLevel);
 
-        for (size_t level = 1; level <= maxLevel; ++level) {
+        for (size_t level = GetBaseMipmapLevel() + 1; level <= GetMaxMipmapLevel(); ++level) {
             imageInfo.mWidth = std::max(imageInfo.mWidth / 2, 1);
             imageInfo.mHeight = std::max(imageInfo.mHeight / 2, 1);
             imageInfo.mDepth = std::max(imageInfo.mDepth / 2, 1);
             for(size_t face = 0; face < mFacesCount; ++face)
                 ImageInfoAtFace(face, level) = imageInfo;
         }
     }
     mHaveGeneratedMipmap = false;
@@ -218,17 +215,17 @@ WebGLTexture::AreAllLevel0ImageInfosEqua
     return true;
 }
 
 bool
 WebGLTexture::IsMipmapComplete() const {
     MOZ_ASSERT(mTarget == LOCAL_GL_TEXTURE_2D ||
                mTarget == LOCAL_GL_TEXTURE_3D);
 
-    if (!ImageInfoAtFace(0, 0).IsPositive())
+    if (!ImageInfoAtFace(0, GetBaseMipmapLevel()).IsPositive())
         return false;
     if (mHaveGeneratedMipmap)
         return true;
     return DoesMipmapHaveAllLevelsConsistentlyDefined(LOCAL_GL_TEXTURE_2D);
 }
 
 bool
 WebGLTexture::IsCubeComplete() const {
@@ -257,17 +254,17 @@ WebGLTexture::ResolvedFakeBlackStatus() 
     if (MOZ_LIKELY(mFakeBlackStatus != WebGLTextureFakeBlackStatus::Unknown)) {
         return mFakeBlackStatus;
     }
 
     // Determine if the texture needs to be faked as a black texture.
     // See 3.8.2 Shader Execution in the OpenGL ES 2.0.24 spec.
 
     for (size_t face = 0; face < mFacesCount; ++face) {
-        if (ImageInfoAtFace(face, 0).mImageDataStatus == WebGLImageDataStatus::NoImageData) {
+        if (ImageInfoAtFace(face, GetBaseMipmapLevel()).mImageDataStatus == WebGLImageDataStatus::NoImageData) {
             // In case of undefined texture image, we don't print any message because this is a very common
             // and often legitimate case (asynchronous texture loading).
             mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
             return mFakeBlackStatus;
         }
     }
 
     const char *msg_rendering_as_black
--- a/dom/canvas/WebGLTexture.h
+++ b/dom/canvas/WebGLTexture.h
@@ -208,16 +208,19 @@ protected:
     TexWrap mWrapS, mWrapT;
 
     size_t mFacesCount, mMaxLevelWithCustomImages;
     nsTArray<ImageInfo> mImageInfos;
 
     bool mHaveGeneratedMipmap; // set by generateMipmap
     bool mImmutable; // set by texStorage*
 
+    size_t mBaseMipmapLevel; // set by texParameter (defaults to 0)
+    size_t mMaxMipmapLevel;  // set by texParameter (defaults to 1000)
+
     WebGLTextureFakeBlackStatus mFakeBlackStatus;
 
     void EnsureMaxLevelWithCustomImagesAtLeast(size_t aMaxLevelWithCustomImages) {
         mMaxLevelWithCustomImages = std::max(mMaxLevelWithCustomImages, aMaxLevelWithCustomImages);
         mImageInfos.EnsureLengthAtLeast((mMaxLevelWithCustomImages + 1) * mFacesCount);
     }
 
     bool CheckFloatTextureFilterParams() const {
@@ -278,16 +281,27 @@ public:
 
     bool IsMipmapCubeComplete() const;
 
     void SetFakeBlackStatus(WebGLTextureFakeBlackStatus x);
 
     bool IsImmutable() const { return mImmutable; }
     void SetImmutable() { mImmutable = true; }
 
+    void SetBaseMipmapLevel(unsigned level) { mBaseMipmapLevel = level; }
+    void SetMaxMipmapLevel(unsigned level) { mMaxMipmapLevel = level; }
+    size_t GetBaseMipmapLevel() const {
+        // Clamp to [0, levels - 1]
+        return std::min(mBaseMipmapLevel, mMaxLevelWithCustomImages);
+    }
+    size_t GetMaxMipmapLevel() const {
+        // Clamp to [base, levels - 1]
+        return std::min(mMaxMipmapLevel, mMaxLevelWithCustomImages);
+    }
+
     size_t MaxLevelWithCustomImages() const { return mMaxLevelWithCustomImages; }
 
     // Returns the current fake-black-status, except if it was Unknown,
     // in which case this function resolves it first, so it never returns Unknown.
     WebGLTextureFakeBlackStatus ResolvedFakeBlackStatus();
 };
 
 inline TexImageTarget
--- a/dom/crypto/CryptoKey.cpp
+++ b/dom/crypto/CryptoKey.cpp
@@ -390,17 +390,22 @@ CryptoKey::PublicKeyFromSpki(CryptoBuffe
 
     SECStatus rv = SECITEM_CopyItem(spki->arena, &spki->algorithm.algorithm,
                                     &oidData->oid);
     if (rv != SECSuccess) {
       return nullptr;
     }
   }
 
-  return SECKEY_ExtractPublicKey(spki.get());
+  ScopedSECKEYPublicKey tmp(SECKEY_ExtractPublicKey(spki.get()));
+  if (!tmp.get() || !PublicKeyValid(tmp.get())) {
+    return nullptr;
+  }
+
+  return SECKEY_CopyPublicKey(tmp);
 }
 
 nsresult
 CryptoKey::PrivateKeyToPkcs8(SECKEYPrivateKey* aPrivKey,
                        CryptoBuffer& aRetVal,
                        const nsNSSShutDownPreventionLock& /*proofOfLock*/)
 {
   ScopedSECItem pkcs8Item(PK11_ExportDERPrivateKeyInfo(aPrivKey, nullptr));
@@ -838,16 +843,20 @@ CryptoKey::PublicKeyFromJwk(const JsonWe
 
     // Create point.
     SECItem* point = CreateECPointForCoordinates(x, y, arena.get());
     if (!point) {
       return nullptr;
     }
     key->u.ec.publicValue = *point;
 
+    if (!PublicKeyValid(key)) {
+      return nullptr;
+    }
+
     return SECKEY_CopyPublicKey(key);
   }
 
   return nullptr;
 }
 
 nsresult
 CryptoKey::PublicKeyToJwk(SECKEYPublicKey* aPubKey,
@@ -877,16 +886,36 @@ CryptoKey::PublicKeyToJwk(SECKEYPublicKe
       }
       return NS_OK;
     default:
       return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   }
 }
 
 bool
+CryptoKey::PublicKeyValid(SECKEYPublicKey* aPubKey)
+{
+  ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
+  if (!slot.get()) {
+    return false;
+  }
+
+  // This assumes that NSS checks the validity of a public key when
+  // it is imported into a PKCS#11 module, and returns CK_INVALID_HANDLE
+  // if it is invalid.
+  CK_OBJECT_HANDLE id = PK11_ImportPublicKey(slot, aPubKey, PR_FALSE);
+  if (id == CK_INVALID_HANDLE) {
+    return false;
+  }
+
+  SECStatus rv = PK11_DestroyObject(slot, id);
+  return (rv == SECSuccess);
+}
+
+bool
 CryptoKey::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const
 {
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown()) {
     return false;
   }
 
   // Write in five pieces
--- a/dom/crypto/CryptoKey.h
+++ b/dom/crypto/CryptoKey.h
@@ -166,16 +166,18 @@ public:
                                   const nsNSSShutDownPreventionLock& /*proofOfLock*/);
 
   static SECKEYPublicKey* PublicKeyFromJwk(const JsonWebKey& aKeyData,
                                            const nsNSSShutDownPreventionLock& /*proofOfLock*/);
   static nsresult PublicKeyToJwk(SECKEYPublicKey* aPrivKey,
                                  JsonWebKey& aRetVal,
                                  const nsNSSShutDownPreventionLock& /*proofOfLock*/);
 
+  static bool PublicKeyValid(SECKEYPublicKey* aPubKey);
+
   // Structured clone methods use these to clone keys
   bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const;
   bool ReadStructuredClone(JSStructuredCloneReader* aReader);
 
 private:
   ~CryptoKey();
 
   nsRefPtr<nsIGlobalObject> mGlobal;
--- a/dom/crypto/WebCryptoCommon.h
+++ b/dom/crypto/WebCryptoCommon.h
@@ -22,16 +22,17 @@
 #define WEBCRYPTO_ALG_SHA256        "SHA-256"
 #define WEBCRYPTO_ALG_SHA384        "SHA-384"
 #define WEBCRYPTO_ALG_SHA512        "SHA-512"
 #define WEBCRYPTO_ALG_HMAC          "HMAC"
 #define WEBCRYPTO_ALG_PBKDF2        "PBKDF2"
 #define WEBCRYPTO_ALG_RSASSA_PKCS1  "RSASSA-PKCS1-v1_5"
 #define WEBCRYPTO_ALG_RSA_OAEP      "RSA-OAEP"
 #define WEBCRYPTO_ALG_ECDH          "ECDH"
+#define WEBCRYPTO_ALG_ECDSA         "ECDSA"
 
 // WebCrypto key formats
 #define WEBCRYPTO_KEY_FORMAT_RAW    "raw"
 #define WEBCRYPTO_KEY_FORMAT_PKCS8  "pkcs8"
 #define WEBCRYPTO_KEY_FORMAT_SPKI   "spki"
 #define WEBCRYPTO_KEY_FORMAT_JWK    "jwk"
 
 // WebCrypto key types
@@ -79,16 +80,19 @@
 #define JWK_ALG_RS1                 "RS1"      // RSASSA-PKCS1
 #define JWK_ALG_RS256               "RS256"
 #define JWK_ALG_RS384               "RS384"
 #define JWK_ALG_RS512               "RS512"
 #define JWK_ALG_RSA_OAEP            "RSA-OAEP" // RSA-OAEP
 #define JWK_ALG_RSA_OAEP_256        "RSA-OAEP-256"
 #define JWK_ALG_RSA_OAEP_384        "RSA-OAEP-384"
 #define JWK_ALG_RSA_OAEP_512        "RSA-OAEP-512"
+#define JWK_ALG_ECDSA_P_256         "ES256"
+#define JWK_ALG_ECDSA_P_384         "ES384"
+#define JWK_ALG_ECDSA_P_521         "ES521"
 
 // JWK usages
 #define JWK_USE_ENC                 "enc"
 #define JWK_USE_SIG                 "sig"
 
 // Define an unknown mechanism type
 #define UNKNOWN_CK_MECHANISM        CKM_VENDOR_DEFINED+1
 
@@ -220,16 +224,18 @@ NormalizeToken(const nsString& aName, ns
   } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_PBKDF2)) {
     aDest.AssignLiteral(WEBCRYPTO_ALG_PBKDF2);
   } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_RSASSA_PKCS1)) {
     aDest.AssignLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1);
   } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_RSA_OAEP)) {
     aDest.AssignLiteral(WEBCRYPTO_ALG_RSA_OAEP);
   } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_ECDH)) {
     aDest.AssignLiteral(WEBCRYPTO_ALG_ECDH);
+  } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_ECDSA)) {
+    aDest.AssignLiteral(WEBCRYPTO_ALG_ECDSA);
   // Named curve values
   } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_NAMED_CURVE_P256)) {
     aDest.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P256);
   } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_NAMED_CURVE_P384)) {
     aDest.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P384);
   } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_NAMED_CURVE_P521)) {
     aDest.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P521);
   } else {
--- a/dom/crypto/WebCryptoTask.cpp
+++ b/dom/crypto/WebCryptoTask.cpp
@@ -61,17 +61,18 @@ enum TelemetryAlgorithm {
   TA_SHA_1           = 14,
   TA_SHA_224         = 15,
   TA_SHA_256         = 16,
   TA_SHA_384         = 17,
   TA_SHA_512         = 18,
   // Later additions
   TA_AES_KW          = 19,
   TA_ECDH            = 20,
-  TA_PBKDF2          = 21
+  TA_PBKDF2          = 21,
+  TA_ECDSA           = 22,
 };
 
 // Convenience functions for extracting / converting information
 
 // OOM-safe CryptoBuffer initialization, suitable for constructors
 #define ATTEMPT_BUFFER_INIT(dst, src) \
   if (!dst.Assign(src)) { \
     mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR; \
@@ -777,20 +778,19 @@ public:
       case CKM_SHA_1:
         mMgfMechanism = CKG_MGF1_SHA1; break;
       case CKM_SHA256:
         mMgfMechanism = CKG_MGF1_SHA256; break;
       case CKM_SHA384:
         mMgfMechanism = CKG_MGF1_SHA384; break;
       case CKM_SHA512:
         mMgfMechanism = CKG_MGF1_SHA512; break;
-      default: {
+      default:
         mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
         return;
-      }
     }
   }
 
 private:
   CK_MECHANISM_TYPE mHashMechanism;
   CK_MECHANISM_TYPE mMgfMechanism;
   ScopedSECKEYPrivateKey mPrivKey;
   ScopedSECKEYPublicKey mPubKey;
@@ -950,59 +950,107 @@ private:
                                    mSignature.Length());
         equal = (cmp == 0);
       }
       mResultPromise->MaybeResolve(equal);
     }
   }
 };
 
-class RsassaPkcs1Task : public WebCryptoTask
+class AsymmetricSignVerifyTask : public WebCryptoTask
 {
 public:
-  RsassaPkcs1Task(JSContext* aCx, const ObjectOrString& aAlgorithm,
-                  CryptoKey& aKey,
-                  const CryptoOperationData& aSignature,
-                  const CryptoOperationData& aData,
-                  bool aSign)
+  AsymmetricSignVerifyTask(JSContext* aCx,
+                           const ObjectOrString& aAlgorithm,
+                           CryptoKey& aKey,
+                           const CryptoOperationData& aSignature,
+                           const CryptoOperationData& aData,
+                           bool aSign)
     : mOidTag(SEC_OID_UNKNOWN)
     , mPrivKey(aKey.GetPrivateKey())
     , mPubKey(aKey.GetPublicKey())
     , mSign(aSign)
     , mVerified(false)
+    , mEcdsa(false)
   {
-    Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSASSA_PKCS1);
-
-    CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_RSASSA_PKCS1);
-
     ATTEMPT_BUFFER_INIT(mData, aData);
     if (!aSign) {
       ATTEMPT_BUFFER_INIT(mSignature, aSignature);
     }
 
-    // Look up the SECOidTag based on the KeyAlgorithm
-    // static_cast is safe because we only get here if the algorithm name
-    // is RSASSA-PKCS1-v1_5, and that only happens if we've constructed
-    // an RsaHashedKeyAlgorithm
-    CK_MECHANISM_TYPE mech;
-    mech = KeyAlgorithmProxy::GetMechanism(aKey.Algorithm().mRsa.mHash);
-
-    switch (mech) {
-      case CKM_SHA_1:
-        mOidTag = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION; break;
-      case CKM_SHA256:
-        mOidTag = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION; break;
-      case CKM_SHA384:
-        mOidTag = SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION; break;
-      case CKM_SHA512:
-        mOidTag = SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION; break;
-      default: {
-        mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+    nsString algName;
+    mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
+    if (NS_FAILED(mEarlyRv)) {
+      return;
+    }
+
+    // Look up the SECOidTag
+    if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
+      mEcdsa = false;
+      Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSASSA_PKCS1);
+      CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_RSASSA_PKCS1);
+
+      // For RSA, the hash name comes from the key algorithm
+      nsString hashName = aKey.Algorithm().mRsa.mHash.mName;
+      switch (MapAlgorithmNameToMechanism(hashName)) {
+        case CKM_SHA_1:
+          mOidTag = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION; break;
+        case CKM_SHA256:
+          mOidTag = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION; break;
+        case CKM_SHA384:
+          mOidTag = SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION; break;
+        case CKM_SHA512:
+          mOidTag = SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION; break;
+        default:
+          mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+          return;
+      }
+    } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
+      mEcdsa = true;
+      Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_ECDSA);
+      CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_ECDSA);
+
+      // For ECDSA, the hash name comes from the algorithm parameter
+      RootedDictionary<EcdsaParams> params(aCx);
+      mEarlyRv = Coerce(aCx, params, aAlgorithm);
+      if (NS_FAILED(mEarlyRv)) {
+        mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
         return;
       }
+
+      nsString hashName;
+      mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
+      if (NS_FAILED(mEarlyRv)) {
+        mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
+        return;
+      }
+
+      CK_MECHANISM_TYPE hashMechanism = MapAlgorithmNameToMechanism(hashName);
+      if (hashMechanism == UNKNOWN_CK_MECHANISM) {
+        mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
+        return;
+      }
+
+      switch (hashMechanism) {
+        case CKM_SHA_1:
+          mOidTag = SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE; break;
+        case CKM_SHA256:
+          mOidTag = SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE; break;
+        case CKM_SHA384:
+          mOidTag = SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE; break;
+        case CKM_SHA512:
+          mOidTag = SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE; break;
+        default:
+          mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+          return;
+      }
+    } else {
+      // This shouldn't happen; CreateSignVerifyTask shouldn't create
+      // one of these unless it's for the above algorithms.
+      MOZ_ASSERT(false);
     }
 
     // Check that we have the appropriate key
     if ((mSign && !mPrivKey) || (!mSign && !mPubKey)) {
       mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
       return;
     }
   }
@@ -1010,61 +1058,71 @@ public:
 private:
   SECOidTag mOidTag;
   ScopedSECKEYPrivateKey mPrivKey;
   ScopedSECKEYPublicKey mPubKey;
   CryptoBuffer mSignature;
   CryptoBuffer mData;
   bool mSign;
   bool mVerified;
+  bool mEcdsa;
 
   virtual nsresult DoCrypto() MOZ_OVERRIDE
   {
     nsresult rv;
     if (mSign) {
       ScopedSECItem signature((SECItem*) PORT_Alloc(sizeof(SECItem)));
       ScopedSGNContext ctx(SGN_NewContext(mOidTag, mPrivKey));
-      if (!ctx) {
+      if (!signature.get() || !ctx.get()) {
         return NS_ERROR_DOM_OPERATION_ERR;
       }
 
-      rv = MapSECStatus(SGN_Begin(ctx));
-      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
-
-      rv = MapSECStatus(SGN_Update(ctx, mData.Elements(), mData.Length()));
-      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
-
-      rv = MapSECStatus(SGN_End(ctx, signature));
-      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
-
-      ATTEMPT_BUFFER_ASSIGN(mSignature, signature);
-    } else {
-      ScopedSECItem signature(mSignature.ToSECItem());
-      if (!signature) {
-        return NS_ERROR_DOM_UNKNOWN_ERR;
+      rv = MapSECStatus(SEC_SignData(signature, mData.Elements(),
+                                     mData.Length(), mPrivKey, mOidTag));
+
+      if (mEcdsa) {
+        // DER-decode the signature
+        int signatureLength = PK11_SignatureLen(mPrivKey);
+        ScopedSECItem rawSignature(DSAU_DecodeDerSigToLen(signature.get(),
+                                                          signatureLength));
+        if (!rawSignature.get()) {
+          return NS_ERROR_DOM_OPERATION_ERR;
+        }
+
+        ATTEMPT_BUFFER_ASSIGN(mSignature, rawSignature);
+      } else {
+        ATTEMPT_BUFFER_ASSIGN(mSignature, signature);
       }
 
-      ScopedVFYContext ctx(VFY_CreateContext(mPubKey, signature,
-                                             mOidTag, nullptr));
-      if (!ctx) {
-        int err = PORT_GetError();
-        if (err == SEC_ERROR_BAD_SIGNATURE) {
-          mVerified = false;
-          return NS_OK;
+    } else {
+      ScopedSECItem signature;
+
+      if (mEcdsa) {
+        // DER-encode the signature
+        ScopedSECItem rawSignature(mSignature.ToSECItem());
+        if (!rawSignature.get()) {
+          return NS_ERROR_DOM_UNKNOWN_ERR;
         }
-        return NS_ERROR_DOM_OPERATION_ERR;
+
+        signature = (SECItem*) PORT_Alloc(sizeof(SECItem));
+        if (!signature.get()) {
+          return NS_ERROR_DOM_UNKNOWN_ERR;
+        }
+        rv = MapSECStatus(DSAU_EncodeDerSigWithLen(signature, rawSignature,
+                                                   rawSignature->len));
+        NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
+      } else {
+        signature = mSignature.ToSECItem();
+        if (!signature) {
+          return NS_ERROR_DOM_UNKNOWN_ERR;
+        }
       }
 
-      rv = MapSECStatus(VFY_Begin(ctx));
-      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
-
-      rv = MapSECStatus(VFY_Update(ctx, mData.Elements(), mData.Length()));
-      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
-
-      rv = MapSECStatus(VFY_End(ctx));
+      rv = MapSECStatus(VFY_VerifyData(mData.Elements(), mData.Length(),
+                                       mPubKey, signature, mOidTag, nullptr));
       mVerified = NS_SUCCEEDED(rv);
     }
 
     return NS_OK;
   }
 
   virtual void Resolve() MOZ_OVERRIDE
   {
@@ -1656,21 +1714,32 @@ private:
       }
     }
 
     return NS_OK;
   }
 
   virtual nsresult AfterCrypto() MOZ_OVERRIDE
   {
+    uint32_t privateAllowedUsages = 0, publicAllowedUsages = 0;
+    if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
+      privateAllowedUsages = CryptoKey::DERIVEBITS | CryptoKey::DERIVEKEY;
+      publicAllowedUsages = CryptoKey::DERIVEBITS | CryptoKey::DERIVEKEY;
+    } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
+      privateAllowedUsages = CryptoKey::SIGN;
+      publicAllowedUsages = CryptoKey::VERIFY;
+    }
+
     // Check permissions for the requested operation
-    if (mKey->GetKeyType() == CryptoKey::PRIVATE &&
-        mKey->HasUsageOtherThan(CryptoKey::DERIVEBITS | CryptoKey::DERIVEKEY)) {
-      return NS_ERROR_DOM_DATA_ERR;
-    }
+    if ((mKey->GetKeyType() == CryptoKey::PRIVATE &&
+         mKey->HasUsageOtherThan(privateAllowedUsages)) ||
+        (mKey->GetKeyType() == CryptoKey::PUBLIC &&
+         mKey->HasUsageOtherThan(publicAllowedUsages))) {
+       return NS_ERROR_DOM_DATA_ERR;
+     }
 
     mKey->Algorithm().MakeEc(mAlgName, mNamedCurve);
 
     if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) {
       return NS_ERROR_DOM_DATA_ERR;
     }
 
     return NS_OK;
@@ -1986,17 +2055,18 @@ public:
 
       // Set up params struct
       mRsaParams.keySizeInBits = modulusLength;
       bool converted = publicExponent.GetBigIntValue(mRsaParams.pe);
       if (!converted) {
         mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
         return;
       }
-    } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
+    } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
+               algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
       RootedDictionary<EcKeyGenParams> params(aCx);
       mEarlyRv = Coerce(aCx, params, aAlgorithm);
       if (NS_FAILED(mEarlyRv)) {
         mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
         return;
       }
 
       if (!NormalizeToken(params.mNamedCurve, mNamedCurve)) {
@@ -2009,17 +2079,18 @@ public:
       mKeyPair.mPrivateKey.get()->Algorithm().MakeEc(algName, mNamedCurve);
       mMechanism = CKM_EC_KEY_PAIR_GEN;
     } else {
       mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
       return;
     }
 
     // Set key usages.
-    if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
+    if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
+        algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
       privateAllowedUsages = CryptoKey::SIGN;
       publicAllowedUsages = CryptoKey::VERIFY;
     } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
       privateAllowedUsages = CryptoKey::DECRYPT | CryptoKey::UNWRAPKEY;
       publicAllowedUsages = CryptoKey::ENCRYPT | CryptoKey::WRAPKEY;
     } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
       privateAllowedUsages = CryptoKey::DERIVEKEY | CryptoKey::DERIVEBITS;
       publicAllowedUsages = 0;
@@ -2043,17 +2114,17 @@ public:
       mEarlyRv = mKeyPair.mPublicKey.get()->AddUsageIntersecting(aKeyUsages[i],
                                                                  publicAllowedUsages);
       if (NS_FAILED(mEarlyRv)) {
         return;
       }
     }
 
     // If no usages ended up being allowed, DataError
-    if (!mKeyPair.mPrivateKey.get()->HasAnyUsage() ||
+    if (!mKeyPair.mPublicKey.get()->HasAnyUsage() &&
         !mKeyPair.mPrivateKey.get()->HasAnyUsage()) {
       mEarlyRv = NS_ERROR_DOM_DATA_ERR;
       return;
     }
   }
 
 private:
   CryptoKeyPair mKeyPair;
@@ -2171,20 +2242,19 @@ public:
     }
 
     // Check the given hash algorithm.
     switch (MapAlgorithmNameToMechanism(hashName)) {
       case CKM_SHA_1: mHashOidTag = SEC_OID_HMAC_SHA1; break;
       case CKM_SHA256: mHashOidTag = SEC_OID_HMAC_SHA256; break;
       case CKM_SHA384: mHashOidTag = SEC_OID_HMAC_SHA384; break;
       case CKM_SHA512: mHashOidTag = SEC_OID_HMAC_SHA512; break;
-      default: {
+      default:
         mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
         return;
-      }
     }
 
     ATTEMPT_BUFFER_INIT(mSalt, params.mSalt)
     mLength = aLength >> 3; // bits to bytes
     mIterations = params.mIterations;
   }
 
 private:
@@ -2539,18 +2609,20 @@ WebCryptoTask::CreateSignVerifyTask(JSCo
   nsString algName;
   nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
   if (NS_FAILED(rv)) {
     return new FailureTask(rv);
   }
 
   if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
     return new HmacTask(aCx, aAlgorithm, aKey, aSignature, aData, aSign);
-  } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
-    return new RsassaPkcs1Task(aCx, aAlgorithm, aKey, aSignature, aData, aSign);
+  } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
+             algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
+    return new AsymmetricSignVerifyTask(aCx, aAlgorithm, aKey, aSignature,
+                                        aData, aSign);
   }
 
   return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
 }
 
 WebCryptoTask*
 WebCryptoTask::CreateDigestTask(JSContext* aCx,
                                 const ObjectOrString& aAlgorithm,
@@ -2613,17 +2685,18 @@ WebCryptoTask::CreateImportKeyTask(JSCon
       algName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2) ||
       algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
     return new ImportSymmetricKeyTask(aCx, aFormat, aKeyData, aAlgorithm,
                                       aExtractable, aKeyUsages);
   } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
              algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
     return new ImportRsaKeyTask(aCx, aFormat, aKeyData, aAlgorithm,
                                 aExtractable, aKeyUsages);
-  } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
+  } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
+             algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
     return new ImportEcKeyTask(aCx, aFormat, aKeyData, aAlgorithm,
                                aExtractable, aKeyUsages);
   } else {
     return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
   }
 }
 
 WebCryptoTask*
@@ -2689,17 +2762,18 @@ WebCryptoTask::CreateGenerateKeyTask(JSC
   if (algName.EqualsASCII(WEBCRYPTO_ALG_AES_CBC) ||
       algName.EqualsASCII(WEBCRYPTO_ALG_AES_CTR) ||
       algName.EqualsASCII(WEBCRYPTO_ALG_AES_GCM) ||
       algName.EqualsASCII(WEBCRYPTO_ALG_AES_KW) ||
       algName.EqualsASCII(WEBCRYPTO_ALG_HMAC)) {
     return new GenerateSymmetricKeyTask(aCx, aAlgorithm, aExtractable, aKeyUsages);
   } else if (algName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
              algName.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP) ||
-             algName.EqualsASCII(WEBCRYPTO_ALG_ECDH)) {
+             algName.EqualsASCII(WEBCRYPTO_ALG_ECDH) ||
+             algName.EqualsASCII(WEBCRYPTO_ALG_ECDSA)) {
     return new GenerateAsymmetricKeyTask(aCx, aAlgorithm, aExtractable, aKeyUsages);
   } else {
     return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
   }
 }
 
 WebCryptoTask*
 WebCryptoTask::CreateDeriveKeyTask(JSContext* aCx,
--- a/dom/crypto/test/test-vectors.js
+++ b/dom/crypto/test/test-vectors.js
@@ -603,9 +603,59 @@ tv = {
 
     // The Y coordinate is missing.
     jwk_missing_y: {
       kty: "EC",
       crv: "P-256",
       x: "XOe4bjsyZgQD5jcS7wmY3q4QJ_rsPBvp92-TTf61jpg",
     }
   },
+
+  // NIST ECDSA test vectors
+  // http://csrc.nist.gov/groups/STM/cavp/index.html
+  ecdsa_verify: {
+    pub_jwk: {
+      "kty": "EC",
+      "crv": "P-521",
+
+      // 0061387fd6b95914e885f912edfbb5fb274655027f216c4091ca83e19336740fd8
+      // 1aedfe047f51b42bdf68161121013e0d55b117a14e4303f926c8debb77a7fdaad1
+      "x": "AGE4f9a5WRTohfkS7fu1-ydGVQJ_IWxAkcqD4ZM2dA_Y" +
+           "Gu3-BH9RtCvfaBYRIQE-DVWxF6FOQwP5Jsjeu3en_arR",
+      // 00e7d0c75c38626e895ca21526b9f9fdf84dcecb93f2b233390550d2b1463b7ee3
+      // f58df7346435ff0434199583c97c665a97f12f706f2357da4b40288def888e59e6
+      "y": "AOfQx1w4Ym6JXKIVJrn5_fhNzsuT8rIzOQVQ0rFGO37j" +
+           "9Y33NGQ1_wQ0GZWDyXxmWpfxL3BvI1faS0Aoje-Ijlnm",
+    },
+
+    "data": util.hex2abv(
+            "9ecd500c60e701404922e58ab20cc002651fdee7cbc9336adda33e4c1088fab1" +
+            "964ecb7904dc6856865d6c8e15041ccf2d5ac302e99d346ff2f686531d255216" +
+            "78d4fd3f76bbf2c893d246cb4d7693792fe18172108146853103a51f824acc62" +
+            "1cb7311d2463c3361ea707254f2b052bc22cb8012873dcbb95bf1a5cc53ab89f"
+          ),
+    "sig": util.hex2abv(
+            "004de826ea704ad10bc0f7538af8a3843f284f55c8b946af9235af5af74f2b76e0" +
+            "99e4bc72fd79d28a380f8d4b4c919ac290d248c37983ba05aea42e2dd79fdd33e8" +
+            "0087488c859a96fea266ea13bf6d114c429b163be97a57559086edb64aed4a1859" +
+            "4b46fb9efc7fd25d8b2de8f09ca0587f54bd287299f47b2ff124aac566e8ee3b43"
+          ),
+
+    // Same as "sig", but with the last few octets set to 0
+    "sig_tampered": util.hex2abv(
+            "004de826ea704ad10bc0f7538af8a3843f284f55c8b946af9235af5af74f2b76e0" +
+            "99e4bc72fd79d28a380f8d4b4c919ac290d248c37983ba05aea42e2dd79fdd33e8" +
+            "0087488c859a96fea266ea13bf6d114c429b163be97a57559086edb64aed4a1859" +
+            "4b46fb9efc7fd25d8b2de8f09ca0587f54bd287299f47b2ff124aac56600000000"
+          )
+  },
+
+  ecdsa_bad: {
+    pub_jwk: {
+      "kty": "EC",
+      "crv": "P-521",
+      "x": "BhOH_WuVkU6IX5Eu37tfsnRlUCfyFsQJHKg-GTNnQP2B" +
+           "rt_gR_UbQr32gWESEBPg1VsRehTkMD-SbI3rt3p_2q0B",
+      "y": "AUNouOdGgHsraPNhXNeNdhpGTd15GPyN9R0iWWL98ePc" +
+           "JD4mUQD/DsEzNZ4zLkTdSa/Y5fOP6GEzVzQy0zwC+goD"
+    }
+  }
 }
--- a/dom/crypto/test/test_WebCrypto.html
+++ b/dom/crypto/test/test_WebCrypto.html
@@ -1303,16 +1303,42 @@ TestArray.addTest(
       }
 
       return crypto.subtle.generateKey(alg, false, ["sign"]).then(doSign);
     }
 
     doCheckRSASSA().then(error(that), complete(that));
   }
 );
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "Test that we reject generating keys without any usage",
+  function() {
+    var that = this;
+    var alg = {
+      name: "RSA-OAEP",
+      hash: "SHA-256",
+      modulusLength: 2048,
+      publicExponent: new Uint8Array([0x01, 0x00, 0x01])
+    };
+
+    function generateKey(usages) {
+      return crypto.subtle.generateKey(alg, false, usages);
+    }
+
+    generateKey(["encrypt", "decrypt"]).then(function () {
+      return generateKey(["encrypt"]);
+    }).then(function () {
+      return generateKey(["decrypt"]);
+    }).then(function () {
+      return generateKey(["sign"])
+    }, error(that)).then(error(that), complete(that));
+  }
+);
 /*]]>*/</script>
 </head>
 
 <body>
 
 <div id="content">
 	<div id="head">
 		<b>Web</b>Crypto<br>
new file mode 100644
--- /dev/null
+++ b/dom/crypto/test/test_WebCrypto_ECDSA.html
@@ -0,0 +1,162 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+<title>WebCrypto Test Suite</title>
+<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
+<link rel="stylesheet" href="./test_WebCrypto.css"/>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+
+<!-- Utilities for manipulating ABVs -->
+<script src="util.js"></script>
+
+<!-- A simple wrapper around IndexedDB -->
+<script src="simpledb.js"></script>
+
+<!-- Test vectors drawn from the literature -->
+<script src="./test-vectors.js"></script>
+
+<!-- General testing framework -->
+<script src="./test-array.js"></script>
+
+<script>/*<![CDATA[*/
+"use strict";
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "Generate an ECDSA key for named curve P-256",
+  function() {
+    var that = this;
+    var alg = { name: "ECDSA", namedCurve: "P-256" };
+    crypto.subtle.generateKey(alg, false, ["sign", "verify"]).then(
+      complete(that, function(x) {
+        return exists(x.publicKey) &&
+               (x.publicKey.algorithm.name == alg.name) &&
+               (x.publicKey.algorithm.namedCurve == alg.namedCurve) &&
+               (x.publicKey.type == "public") &&
+               x.publicKey.extractable &&
+               (x.publicKey.usages.length == 1) &&
+               (x.publicKey.usages[0] == "verify") &&
+               exists(x.privateKey) &&
+               (x.privateKey.algorithm.name == alg.name) &&
+               (x.privateKey.algorithm.namedCurve == alg.namedCurve) &&
+               (x.privateKey.type == "private") &&
+               !x.privateKey.extractable &&
+               (x.privateKey.usages.length == 1) &&
+               (x.privateKey.usages[0] == "sign")
+      }),
+      error(that)
+    );
+  }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "ECDSA JWK import and verify a known-good signature",
+  function() {
+    var that = this;
+    var alg = { name: "ECDSA", namedCurve: "P-256", hash: "SHA-256" };
+
+    function doVerify(x) {
+      return crypto.subtle.verify(alg, x, tv.ecdsa_verify.sig, tv.ecdsa_verify.data);
+    }
+
+    crypto.subtle.importKey("jwk", tv.ecdsa_verify.pub_jwk, alg, true, ["verify"])
+      .then(doVerify)
+      .then(complete(that), error(that))
+  }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "ECDSA JWK import and reject a known-bad signature",
+  function() {
+    var that = this;
+    var alg = { name: "ECDSA", namedCurve: "P-256", hash: "SHA-256" };
+
+    function doVerify(x) {
+      return crypto.subtle.verify(alg, x, tv.ecdsa_verify.sig_tampered,
+                                          tv.ecdsa_verify.data);
+    }
+
+    crypto.subtle.importKey("jwk", tv.ecdsa_verify.pub_jwk, alg, true, ["verify"])
+      .then(doVerify)
+      .then(complete(that, x => !x), error(that))
+  }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "ECDSA sign/verify round-trip",
+  function() {
+    var that = this;
+    var alg = { name: "ECDSA", namedCurve: "P-521", hash: "SHA-512" };
+    var pubKey;
+
+
+    function doSign(keyPair) {
+      pubKey = keyPair.publicKey;
+      return crypto.subtle.sign(alg, keyPair.privateKey, tv.ecdsa_verify.data);
+    }
+    function doVerify(sig) {
+      return crypto.subtle.verify(alg, pubKey, sig, tv.ecdsa_verify.data);
+    }
+
+    crypto.subtle.generateKey(alg, true, ["sign", "verify"])
+      .then(doSign)
+      .then(doVerify)
+      .then(complete(that), error(that))
+  }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "Verify that ECDSA import fails with a known-bad public key",
+  function() {
+    var that = this;
+    var alg = { name: "ECDSA", namedCurve: "P-256", hash: "SHA-256" };
+
+    function doVerify(x) {
+      return crypto.subtle.verify(alg, x, tv.ecdsa_verify.sig, tv.ecdsa_verify.data);
+    }
+
+    crypto.subtle.importKey("jwk", tv.ecdsa_bad.pub_jwk, alg, true, ["verify"])
+      .then(error(that), complete(that))
+  }
+);
+
+/*]]>*/</script>
+</head>
+
+<body>
+
+<div id="content">
+	<div id="head">
+		<b>Web</b>Crypto<br>
+	</div>
+
+    <div id="start" onclick="start();">RUN ALL</div>
+
+    <div id="resultDiv" class="content">
+    Summary:
+    <span class="pass"><span id="passN">0</span> passed, </span>
+    <span class="fail"><span id="failN">0</span> failed, </span>
+    <span class="pending"><span id="pendingN">0</span> pending.</span>
+    <br/>
+    <br/>
+
+    <table id="results">
+        <tr>
+            <th>Test</th>
+            <th>Result</th>
+            <th>Time</th>
+        </tr>
+    </table>
+
+    </div>
+
+    <div id="foot"></div>
+</div>
+
+</body>
+</html>
--- a/dom/events/Event.cpp
+++ b/dom/events/Event.cpp
@@ -71,16 +71,17 @@ Event::ConstructorInit(EventTarget* aOwn
   if (mIsMainThreadEvent && !sReturnHighResTimeStampIsSet) {
     Preferences::AddBoolVarCache(&sReturnHighResTimeStamp,
                                  "dom.event.highrestimestamp.enabled",
                                  sReturnHighResTimeStamp);
     sReturnHighResTimeStampIsSet = true;
   }
 
   mPrivateDataDuplicated = false;
+  mWantsPopupControlCheck = false;
 
   if (aEvent) {
     mEvent = aEvent;
     mEventIsInternal = false;
   }
   else {
     mEventIsInternal = true;
     /*
@@ -650,22 +651,30 @@ PopupAllowedForEvent(const char *eventNa
     startiter = enditer;
   }
 
   return false;
 }
 
 // static
 PopupControlState
-Event::GetEventPopupControlState(WidgetEvent* aEvent)
+Event::GetEventPopupControlState(WidgetEvent* aEvent, nsIDOMEvent* aDOMEvent)
 {
   // generally if an event handler is running, new windows are disallowed.
   // check for exceptions:
   PopupControlState abuse = openAbused;
 
+  if (aDOMEvent && aDOMEvent->InternalDOMEvent()->GetWantsPopupControlCheck()) {
+    nsAutoString type;
+    aDOMEvent->GetType(type);
+    if (PopupAllowedForEvent(NS_ConvertUTF16toUTF8(type).get())) {
+      return openAllowed;
+    }
+  }
+
   switch(aEvent->mClass) {
   case eBasicEventClass:
     // For these following events only allow popups if they're
     // triggered while handling user input. See
     // nsPresShell::HandleEventInternal() for details.
     if (EventStateManager::IsHandlingUserInput()) {
       switch(aEvent->message) {
       case NS_FORM_SELECTED :
--- a/dom/events/Event.h
+++ b/dom/events/Event.h
@@ -25,16 +25,17 @@ class nsIDOMEventTarget;
 class nsPresContext;
 
 namespace mozilla {
 namespace dom {
 
 class EventTarget;
 class ErrorEvent;
 class ProgressEvent;
+class WantsPopupControlCheck;
 
 // Dummy class so we can cast through it to get from nsISupports to
 // Event subclasses with only two non-ambiguous static casts.
 class EventBase : public nsIDOMEvent
 {
 };
 
 class Event : public EventBase,
@@ -108,17 +109,18 @@ public:
   // nsIDOMEvent Interface
   NS_DECL_NSIDOMEVENT
 
   void InitPresContextData(nsPresContext* aPresContext);
 
   // Returns true if the event should be trusted.
   bool Init(EventTarget* aGlobal);
 
-  static PopupControlState GetEventPopupControlState(WidgetEvent* aEvent);
+  static PopupControlState GetEventPopupControlState(WidgetEvent* aEvent,
+                                                     nsIDOMEvent* aDOMEvent = nullptr);
 
   static void PopupAllowedEventsChanged();
 
   static void Shutdown();
 
   static const char* GetEventName(uint32_t aEventType);
   static CSSIntPoint GetClientCoords(nsPresContext* aPresContext,
                                      WidgetEvent* aEvent,
@@ -230,29 +232,62 @@ public:
                                             nsIContent* aRelatedTarget);
 
 protected:
 
   // Internal helper functions
   void SetEventType(const nsAString& aEventTypeArg);
   already_AddRefed<nsIContent> GetTargetFromFrame();
 
+  friend class WantsPopupControlCheck;
+  void SetWantsPopupControlCheck(bool aCheck)
+  {
+    mWantsPopupControlCheck = aCheck;
+  }
+
+  bool GetWantsPopupControlCheck()
+  {
+    return IsTrusted() && mWantsPopupControlCheck;
+  }
+
   /**
    * IsChrome() returns true if aCx is chrome context or the event is created
    * in chrome's thread.  Otherwise, false.
    */
   bool IsChrome(JSContext* aCx) const;
 
   mozilla::WidgetEvent*       mEvent;
   nsRefPtr<nsPresContext>     mPresContext;
   nsCOMPtr<EventTarget>       mExplicitOriginalTarget;
   nsCOMPtr<nsPIDOMWindow>     mOwner; // nsPIDOMWindow for now.
   bool                        mEventIsInternal;
   bool                        mPrivateDataDuplicated;
   bool                        mIsMainThreadEvent;
+  // True when popup control check should rely on event.type, not
+  // WidgetEvent.message.
+  bool                        mWantsPopupControlCheck;
+};
+
+class MOZ_STACK_CLASS WantsPopupControlCheck
+{
+public:
+  WantsPopupControlCheck(nsIDOMEvent* aEvent) : mEvent(aEvent->InternalDOMEvent())
+  {
+    mOriginalWantsPopupControlCheck = mEvent->GetWantsPopupControlCheck();
+    mEvent->SetWantsPopupControlCheck(mEvent->IsTrusted());
+  }
+
+  ~WantsPopupControlCheck()
+  {
+    mEvent->SetWantsPopupControlCheck(mOriginalWantsPopupControlCheck);
+  }
+
+private:
+  Event* mEvent;
+  bool mOriginalWantsPopupControlCheck;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #define NS_FORWARD_TO_EVENT \
   NS_FORWARD_NSIDOMEVENT(Event::)
 
--- a/dom/events/EventListenerManager.cpp
+++ b/dom/events/EventListenerManager.cpp
@@ -971,17 +971,17 @@ EventListenerManager::HandleEventInterna
   //Set the value of the internal PreventDefault flag properly based on aEventStatus
   if (*aEventStatus == nsEventStatus_eConsumeNoDefault) {
     aEvent->mFlags.mDefaultPrevented = true;
   }
 
   nsAutoTObserverArray<Listener, 2>::EndLimitedIterator iter(mListeners);
   Maybe<nsAutoPopupStatePusher> popupStatePusher;
   if (mIsMainThreadELM) {
-    popupStatePusher.emplace(Event::GetEventPopupControlState(aEvent));
+    popupStatePusher.emplace(Event::GetEventPopupControlState(aEvent, *aDOMEvent));
   }
 
   bool hasListener = false;
   while (iter.HasMore()) {
     if (aEvent->mFlags.mImmediatePropagationStopped) {
       break;
     }
     Listener* listener = &iter.GetNext();
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -939,26 +939,31 @@ EventStateManager::ExecuteAccessKey(nsTA
         }
         return true;
       }
     }
   }
   return false;
 }
 
-bool
-EventStateManager::GetAccessKeyLabelPrefix(nsAString& aPrefix)
+// static
+void
+EventStateManager::GetAccessKeyLabelPrefix(Element* aElement, nsAString& aPrefix)
 {
   aPrefix.Truncate();
   nsAutoString separator, modifierText;
   nsContentUtils::GetModifierSeparatorText(separator);
 
-  nsCOMPtr<nsISupports> container = mPresContext->GetContainerWeak();
+  nsCOMPtr<nsISupports> container = aElement->OwnerDoc()->GetDocShell();
   int32_t modifierMask = GetAccessModifierMaskFor(container);
 
+  if (modifierMask == -1) {
+    return;
+  }
+
   if (modifierMask & NS_MODIFIER_CONTROL) {
     nsContentUtils::GetControlText(modifierText);
     aPrefix.Append(modifierText + separator);
   }
   if (modifierMask & NS_MODIFIER_META) {
     nsContentUtils::GetMetaText(modifierText);
     aPrefix.Append(modifierText + separator);
   }
@@ -969,17 +974,16 @@ EventStateManager::GetAccessKeyLabelPref
   if (modifierMask & NS_MODIFIER_ALT) {
     nsContentUtils::GetAltText(modifierText);
     aPrefix.Append(modifierText + separator);
   }
   if (modifierMask & NS_MODIFIER_SHIFT) {
     nsContentUtils::GetShiftText(modifierText);
     aPrefix.Append(modifierText + separator);
   }
-  return !aPrefix.IsEmpty();
 }
 
 void
 EventStateManager::HandleAccessKey(nsPresContext* aPresContext,
                                    WidgetKeyboardEvent* aEvent,
                                    nsEventStatus* aStatus,
                                    nsIDocShellTreeItem* aBubbledFrom,
                                    ProcessingAccessKeyState aAccessKeyState,
--- a/dom/events/EventStateManager.h
+++ b/dom/events/EventStateManager.h
@@ -35,16 +35,17 @@ namespace mozilla {
 class EnterLeaveDispatcher;
 class EventStates;
 class IMEContentObserver;
 class ScrollbarsForWheel;
 class WheelTransaction;
 
 namespace dom {
 class DataTransfer;
+class Element;
 class TabParent;
 } // namespace dom
 
 class OverOutElementsWrapper MOZ_FINAL : public nsISupports
 {
   ~OverOutElementsWrapper();
 
 public:
@@ -164,17 +165,17 @@ public:
   /**
    * Get accesskey registered on the given element or 0 if there is none.
    *
    * @param  aContent  the given element (must not be null)
    * @return           registered accesskey
    */
   uint32_t GetRegisteredAccessKey(nsIContent* aContent);
 
-  bool GetAccessKeyLabelPrefix(nsAString& aPrefix);
+  static void GetAccessKeyLabelPrefix(dom::Element* aElement, nsAString& aPrefix);
 
   nsresult SetCursor(int32_t aCursor, imgIContainer* aContainer,
                      bool aHaveHotspot, float aHotspotX, float aHotspotY,
                      nsIWidget* aWidget, bool aLockCursor); 
 
   static void StartHandlingUserInput()
   {
     ++sUserInputEventDepth;
--- a/dom/events/test/mochitest.ini
+++ b/dom/events/test/mochitest.ini
@@ -17,16 +17,17 @@ support-files =
 [test_addEventListenerExtraArg.html]
 [test_all_synthetic_events.html]
 [test_bug226361.xhtml]
 skip-if = buildapp == 'b2g'
 [test_bug238987.html]
 skip-if = buildapp == 'b2g'
 [test_bug288392.html]
 [test_bug299673-1.html]
+[test_bug1037990.html]
 [test_bug299673-2.html]
 [test_bug322588.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || e10s #Bug 931116, b2g desktop specific, initial triage
 [test_bug328885.html]
 [test_bug336682.js]
 [test_bug336682_1.html]
 [test_bug367781.html]
 [test_bug368835.html]
new file mode 100644
--- /dev/null
+++ b/dom/events/test/test_bug1037990.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1037990
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1037990</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1037990">Mozilla Bug 1037990</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+<script type="application/javascript">
+
+  /** Test for Bug 1037990 **/
+
+  var pre, node, detachedAccess, attachedAcess;
+
+  node = document.createElement('a');
+  node.href = 'http://example.org';
+  node.accessKey = 'e';
+  detachedAccess = node.accessKeyLabel;
+  info('[window.document] detached: ' + detachedAccess);
+  document.body.appendChild(node);
+  attachedAcess = node.accessKeyLabel;
+  info('[window.document] attached: ' + attachedAcess);
+  is(detachedAccess, attachedAcess, "Both values are same for the window.document");
+
+  var parser=new DOMParser();
+  var xmlDoc=parser.parseFromString("<root></root>","text/xml");
+  var nn = xmlDoc.createElementNS('http://www.w3.org/1999/xhtml','a');
+  nn.setAttribute('accesskey','t')
+  detachedAccess = nn.accessKeyLabel;
+  info('[xmlDoc] detached: ' + detachedAccess);
+  var root = xmlDoc.getElementsByTagName('root')[0];
+  root.appendChild(nn);
+  attachedAcess = nn.accessKeyLabel;
+  info('[xmlDoc] attached: ' + attachedAcess);
+  is(detachedAccess, attachedAcess, "Both values are same for the xmlDoc");
+
+  var myDoc = new Document();
+  var newnode = myDoc.createElementNS('http://www.w3.org/1999/xhtml','a');
+  newnode.href = 'http://example.org';
+  newnode.accessKey = 'f';
+  detachedAccess = newnode.accessKeyLabel;
+  info('[new document] detached: ' + detachedAccess);
+  myDoc.appendChild(newnode);
+  attachedAcess = newnode.accessKeyLabel;
+  info('[new document] attached: ' + attachedAcess);
+  is(detachedAccess, attachedAcess, "Both values are same for the new Document()");
+
+</script>
+</body>
+</html>
--- a/dom/fetch/Fetch.cpp
+++ b/dom/fetch/Fetch.cpp
@@ -1,100 +1,365 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "Fetch.h"
 
 #include "nsIStringStream.h"
+#include "nsIUnicodeDecoder.h"
 #include "nsIUnicodeEncoder.h"
 
+#include "nsDOMString.h"
+#include "nsNetUtil.h"
+#include "nsStreamUtils.h"
 #include "nsStringStream.h"
 
+#include "mozilla/ErrorResult.h"
 #include "mozilla/dom/EncodingUtils.h"
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/Headers.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/Request.h"
+#include "mozilla/dom/Response.h"
 #include "mozilla/dom/URLSearchParams.h"
 
 namespace mozilla {
 namespace dom {
 
+namespace {
 nsresult
-ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrScalarValueStringOrURLSearchParams& aBodyInit,
+ExtractFromArrayBuffer(const ArrayBuffer& aBuffer,
+                       nsIInputStream** aStream)
+{
+  aBuffer.ComputeLengthAndData();
+  //XXXnsm reinterpret_cast<> is used in DOMParser, should be ok.
+  return NS_NewByteInputStream(aStream,
+                               reinterpret_cast<char*>(aBuffer.Data()),
+                               aBuffer.Length(), NS_ASSIGNMENT_COPY);
+}
+
+nsresult
+ExtractFromArrayBufferView(const ArrayBufferView& aBuffer,
+                           nsIInputStream** aStream)
+{
+  aBuffer.ComputeLengthAndData();
+  //XXXnsm reinterpret_cast<> is used in DOMParser, should be ok.
+  return NS_NewByteInputStream(aStream,
+                               reinterpret_cast<char*>(aBuffer.Data()),
+                               aBuffer.Length(), NS_ASSIGNMENT_COPY);
+}
+
+nsresult
+ExtractFromBlob(const File& aFile, nsIInputStream** aStream,
+                nsCString& aContentType)
+{
+  nsRefPtr<FileImpl> impl = aFile.Impl();
+  nsresult rv = impl->GetInternalStream(aStream);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsString type;
+  impl->GetType(type);
+  aContentType = NS_ConvertUTF16toUTF8(type);
+  return NS_OK;
+}
+
+nsresult
+ExtractFromScalarValueString(const nsString& aStr,
+                             nsIInputStream** aStream,
+                             nsCString& aContentType)
+{
+  nsCOMPtr<nsIUnicodeEncoder> encoder = EncodingUtils::EncoderForEncoding("UTF-8");
+  if (!encoder) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  int32_t destBufferLen;
+  nsresult rv = encoder->GetMaxLength(aStr.get(), aStr.Length(), &destBufferLen);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCString encoded;
+  if (!encoded.SetCapacity(destBufferLen, fallible_t())) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  char* destBuffer = encoded.BeginWriting();
+  int32_t srcLen = (int32_t) aStr.Length();
+  int32_t outLen = destBufferLen;
+  rv = encoder->Convert(aStr.get(), &srcLen, destBuffer, &outLen);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  MOZ_ASSERT(outLen <= destBufferLen);
+  encoded.SetLength(outLen);
+
+  aContentType = NS_LITERAL_CSTRING("text/plain;charset=UTF-8");
+
+  return NS_NewCStringInputStream(aStream, encoded);
+}
+
+nsresult
+ExtractFromURLSearchParams(const URLSearchParams& aParams,
+                           nsIInputStream** aStream,
+                           nsCString& aContentType)
+{
+  nsAutoString serialized;
+  aParams.Stringify(serialized);
+  aContentType = NS_LITERAL_CSTRING("application/x-www-form-urlencoded;charset=UTF-8");
+  return NS_NewStringInputStream(aStream, serialized);
+}
+}
+
+nsresult
+ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrBlobOrScalarValueStringOrURLSearchParams& aBodyInit,
+                          nsIInputStream** aStream,
+                          nsCString& aContentType)
+{
+  MOZ_ASSERT(aStream);
+
+  if (aBodyInit.IsArrayBuffer()) {
+    const ArrayBuffer& buf = aBodyInit.GetAsArrayBuffer();
+    return ExtractFromArrayBuffer(buf, aStream);
+  } else if (aBodyInit.IsArrayBufferView()) {
+    const ArrayBufferView& buf = aBodyInit.GetAsArrayBufferView();
+    return ExtractFromArrayBufferView(buf, aStream);
+  } else if (aBodyInit.IsBlob()) {
+    const File& blob = aBodyInit.GetAsBlob();
+    return ExtractFromBlob(blob, aStream, aContentType);
+  } else if (aBodyInit.IsScalarValueString()) {
+    nsAutoString str;
+    str.Assign(aBodyInit.GetAsScalarValueString());
+    return ExtractFromScalarValueString(str, aStream, aContentType);
+  } else if (aBodyInit.IsURLSearchParams()) {
+    URLSearchParams& params = aBodyInit.GetAsURLSearchParams();
+    return ExtractFromURLSearchParams(params, aStream, aContentType);
+  }
+
+  NS_NOTREACHED("Should never reach here");
+  return NS_ERROR_FAILURE;
+}
+
+nsresult
+ExtractByteStreamFromBody(const ArrayBufferOrArrayBufferViewOrBlobOrScalarValueStringOrURLSearchParams& aBodyInit,
                           nsIInputStream** aStream,
                           nsCString& aContentType)
 {
   MOZ_ASSERT(aStream);
 
-  nsresult rv;
-  nsCOMPtr<nsIInputStream> byteStream;
   if (aBodyInit.IsArrayBuffer()) {
     const ArrayBuffer& buf = aBodyInit.GetAsArrayBuffer();
-    buf.ComputeLengthAndData();
-    //XXXnsm reinterpret_cast<> is used in DOMParser, should be ok.
-    rv = NS_NewByteInputStream(getter_AddRefs(byteStream),
-                               reinterpret_cast<char*>(buf.Data()),
-                               buf.Length(), NS_ASSIGNMENT_COPY);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
+    return ExtractFromArrayBuffer(buf, aStream);
   } else if (aBodyInit.IsArrayBufferView()) {
     const ArrayBufferView& buf = aBodyInit.GetAsArrayBufferView();
-    buf.ComputeLengthAndData();
-    //XXXnsm reinterpret_cast<> is used in DOMParser, should be ok.
-    rv = NS_NewByteInputStream(getter_AddRefs(byteStream),
-                               reinterpret_cast<char*>(buf.Data()),
-                               buf.Length(), NS_ASSIGNMENT_COPY);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
+    return ExtractFromArrayBufferView(buf, aStream);
+  } else if (aBodyInit.IsBlob()) {
+    const File& blob = aBodyInit.GetAsBlob();
+    return ExtractFromBlob(blob, aStream, aContentType);
   } else if (aBodyInit.IsScalarValueString()) {
-    nsString str = aBodyInit.GetAsScalarValueString();
-
-    nsCOMPtr<nsIUnicodeEncoder> encoder = EncodingUtils::EncoderForEncoding("UTF-8");
-    if (!encoder) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-
-    int32_t destBufferLen;
-    rv = encoder->GetMaxLength(str.get(), str.Length(), &destBufferLen);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    nsCString encoded;
-    if (!encoded.SetCapacity(destBufferLen, fallible_t())) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-
-    char* destBuffer = encoded.BeginWriting();
-    int32_t srcLen = (int32_t) str.Length();
-    int32_t outLen = destBufferLen;
-    rv = encoder->Convert(str.get(), &srcLen, destBuffer, &outLen);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    MOZ_ASSERT(outLen <= destBufferLen);
-    encoded.SetLength(outLen);
-    rv = NS_NewCStringInputStream(getter_AddRefs(byteStream), encoded);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    aContentType = NS_LITERAL_CSTRING("text/plain;charset=UTF-8");
+    nsAutoString str;
+    str.Assign(aBodyInit.GetAsScalarValueString());
+    return ExtractFromScalarValueString(str, aStream, aContentType);
   } else if (aBodyInit.IsURLSearchParams()) {
     URLSearchParams& params = aBodyInit.GetAsURLSearchParams();
-    nsString serialized;
-    params.Stringify(serialized);
-    rv = NS_NewStringInputStream(getter_AddRefs(byteStream), serialized);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
+    return ExtractFromURLSearchParams(params, aStream, aContentType);
+  }
+
+  NS_NOTREACHED("Should never reach here");
+  return NS_ERROR_FAILURE;
+}
+
+namespace {
+nsresult
+DecodeUTF8(const nsCString& aBuffer, nsString& aDecoded)
+{
+  nsCOMPtr<nsIUnicodeDecoder> decoder =
+    EncodingUtils::DecoderForEncoding("UTF-8");
+  if (!decoder) {
+    return NS_ERROR_FAILURE;
+  }
+
+  int32_t destBufferLen;
+  nsresult rv =
+    decoder->GetMaxLength(aBuffer.get(), aBuffer.Length(), &destBufferLen);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (!aDecoded.SetCapacity(destBufferLen, fallible_t())) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  char16_t* destBuffer = aDecoded.BeginWriting();
+  int32_t srcLen = (int32_t) aBuffer.Length();
+  int32_t outLen = destBufferLen;
+  rv = decoder->Convert(aBuffer.get(), &srcLen, destBuffer, &outLen);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  MOZ_ASSERT(outLen <= destBufferLen);
+  aDecoded.SetLength(outLen);
+  return NS_OK;
+}
+}
+
+template <class Derived>
+already_AddRefed<Promise>
+FetchBody<Derived>::ConsumeBody(ConsumeType aType, ErrorResult& aRv)
+{
+  nsRefPtr<Promise> promise = Promise::Create(DerivedClass()->GetParentObject(), aRv);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  if (BodyUsed()) {
+    aRv.ThrowTypeError(MSG_REQUEST_BODY_CONSUMED_ERROR);
+    return nullptr;
+  }
+
+  SetBodyUsed();
+
+  // While the spec says to do this asynchronously, all the body constructors
+  // right now only accept bodies whose streams are backed by an in-memory
+  // buffer that can be read without blocking. So I think this is fine.
+  nsCOMPtr<nsIInputStream> stream;
+  DerivedClass()->GetBody(getter_AddRefs(stream));
+
+  if (!stream) {
+    aRv = NS_NewByteInputStream(getter_AddRefs(stream), "", 0,
+                                NS_ASSIGNMENT_COPY);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return nullptr;
     }
-    aContentType = NS_LITERAL_CSTRING("application/x-www-form-urlencoded;charset=UTF-8");
+  }
+
+  AutoJSAPI api;
+  api.Init(DerivedClass()->GetParentObject());
+  JSContext* cx = api.cx();
+
+  // We can make this assertion because for now we only support memory backed
+  // structures for the body argument for a Request.
+  MOZ_ASSERT(NS_InputStreamIsBuffered(stream));
+  nsCString buffer;
+  uint64_t len;
+  aRv = stream->Available(&len);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  aRv = NS_ReadInputStreamToString(stream, buffer, len);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
   }
 
-  MOZ_ASSERT(byteStream);
-  byteStream.forget(aStream);
-  return NS_OK;
+  buffer.SetLength(len);
+
+  switch (aType) {
+    case CONSUME_ARRAYBUFFER: {
+      JS::Rooted<JSObject*> arrayBuffer(cx);
+      arrayBuffer =
+        ArrayBuffer::Create(cx, buffer.Length(),
+                            reinterpret_cast<const uint8_t*>(buffer.get()));
+      JS::Rooted<JS::Value> val(cx);
+      val.setObjectOrNull(arrayBuffer);
+      promise->MaybeResolve(cx, val);
+      return promise.forget();
+    }
+    case CONSUME_BLOB: {
+      // XXXnsm it is actually possible to avoid these duplicate allocations
+      // for the Blob case by having the Blob adopt the stream's memory
+      // directly, but I've not added a special case for now.
+      //
+      // FIXME(nsm): Use nsContentUtils::CreateBlobBuffer once blobs have been fixed on
+      // workers.
+      uint32_t blobLen = buffer.Length();
+      void* blobData = moz_malloc(blobLen);
+      nsRefPtr<File> blob;
+      if (blobData) {
+        memcpy(blobData, buffer.BeginReading(), blobLen);
+        blob = File::CreateMemoryFile(DerivedClass()->GetParentObject(), blobData, blobLen,
+                                      NS_ConvertUTF8toUTF16(mMimeType));
+      } else {
+        aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+        return nullptr;
+      }
+
+      promise->MaybeResolve(blob);
+      return promise.forget();
+    }
+    case CONSUME_JSON: {
+      nsAutoString decoded;
+      aRv = DecodeUTF8(buffer, decoded);
+      if (NS_WARN_IF(aRv.Failed())) {
+        return nullptr;
+      }
+
+      JS::Rooted<JS::Value> json(cx);
+      if (!JS_ParseJSON(cx, decoded.get(), decoded.Length(), &json)) {
+        JS::Rooted<JS::Value> exn(cx);
+        if (JS_GetPendingException(cx, &exn)) {
+          JS_ClearPendingException(cx);
+          promise->MaybeReject(cx, exn);
+        }
+      }
+      promise->MaybeResolve(cx, json);
+      return promise.forget();
+    }
+    case CONSUME_TEXT: {
+      nsAutoString decoded;
+      aRv = DecodeUTF8(buffer, decoded);
+      if (NS_WARN_IF(aRv.Failed())) {
+        return nullptr;
+      }
+
+      promise->MaybeResolve(decoded);
+      return promise.forget();
+    }
+  }
+
+  NS_NOTREACHED("Unexpected consume body type");
+  // Silence warnings.
+  return nullptr;
 }
 
+template
+already_AddRefed<Promise>
+FetchBody<Request>::ConsumeBody(ConsumeType aType, ErrorResult& aRv);
+
+template
+already_AddRefed<Promise>
+FetchBody<Response>::ConsumeBody(ConsumeType aType, ErrorResult& aRv);
+
+template <class Derived>
+void
+FetchBody<Derived>::SetMimeType(ErrorResult& aRv)
+{
+  // Extract mime type.
+  nsTArray<nsCString> contentTypeValues;
+  MOZ_ASSERT(DerivedClass()->GetInternalHeaders());
+  DerivedClass()->GetInternalHeaders()->GetAll(NS_LITERAL_CSTRING("Content-Type"), contentTypeValues, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return;
+  }
+
+  // HTTP ABNF states Content-Type may have only one value.
+  // This is from the "parse a header value" of the fetch spec.
+  if (contentTypeValues.Length() == 1) {
+    mMimeType = contentTypeValues[0];
+    ToLowerCase(mMimeType);
+  }
+}
+
+template
+void
+FetchBody<Request>::SetMimeType(ErrorResult& aRv);
+
+template
+void
+FetchBody<Response>::SetMimeType(ErrorResult& aRv);
 } // namespace dom
 } // namespace mozilla
--- a/dom/fetch/Fetch.h
+++ b/dom/fetch/Fetch.h
@@ -8,22 +8,99 @@
 
 #include "mozilla/dom/UnionTypes.h"
 
 class nsIInputStream;
 
 namespace mozilla {
 namespace dom {
 
+class Promise;
+
 /*
  * Creates an nsIInputStream based on the fetch specifications 'extract a byte
  * stream algorithm' - http://fetch.spec.whatwg.org/#concept-bodyinit-extract.
  * Stores content type in out param aContentType.
  */
 nsresult
-ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrScalarValueStringOrURLSearchParams& aBodyInit,
+ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrBlobOrScalarValueStringOrURLSearchParams& aBodyInit,
+                          nsIInputStream** aStream,
+                          nsCString& aContentType);
+
+/*
+ * Non-owning version.
+ */
+nsresult
+ExtractByteStreamFromBody(const ArrayBufferOrArrayBufferViewOrBlobOrScalarValueStringOrURLSearchParams& aBodyInit,
                           nsIInputStream** aStream,
                           nsCString& aContentType);
 
+template <class Derived>
+class FetchBody {
+public:
+  bool
+  BodyUsed() { return mBodyUsed; }
+
+  already_AddRefed<Promise>
+  ArrayBuffer(ErrorResult& aRv)
+  {
+    return ConsumeBody(CONSUME_ARRAYBUFFER, aRv);
+  }
+
+  already_AddRefed<Promise>
+  Blob(ErrorResult& aRv)
+  {
+    return ConsumeBody(CONSUME_BLOB, aRv);
+  }
+
+  already_AddRefed<Promise>
+  Json(ErrorResult& aRv)
+  {
+    return ConsumeBody(CONSUME_JSON, aRv);
+  }
+
+  already_AddRefed<Promise>
+  Text(ErrorResult& aRv)
+  {
+    return ConsumeBody(CONSUME_TEXT, aRv);
+  }
+
+protected:
+  FetchBody()
+    : mBodyUsed(false)
+  {
+  }
+
+  void
+  SetBodyUsed()
+  {
+    mBodyUsed = true;
+  }
+
+  void
+  SetMimeType(ErrorResult& aRv);
+
+private:
+  enum ConsumeType
+  {
+    CONSUME_ARRAYBUFFER,
+    CONSUME_BLOB,
+    // FormData not supported right now,
+    CONSUME_JSON,
+    CONSUME_TEXT,
+  };
+
+  Derived*
+  DerivedClass() const
+  {
+    return static_cast<Derived*>(const_cast<FetchBody*>(this));
+  }
+
+  already_AddRefed<Promise>
+  ConsumeBody(ConsumeType aType, ErrorResult& aRv);
+
+  bool mBodyUsed;
+  nsCString mMimeType;
+};
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_Fetch_h
--- a/dom/fetch/Headers.cpp
+++ b/dom/fetch/Headers.cpp
@@ -5,23 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/Headers.h"
 
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/Preferences.h"
 
-#include "nsCharSeparatedTokenizer.h"
-#include "nsContentUtils.h"
-#include "nsDOMString.h"
-#include "nsNetUtil.h"
-#include "nsPIDOMWindow.h"
-#include "nsReadableUtils.h"
-
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(Headers)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(Headers)
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Headers, mOwner)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Headers)
@@ -56,314 +49,65 @@ Headers::PrefEnabled(JSContext* aCx, JSO
 }
 
 // static
 already_AddRefed<Headers>
 Headers::Constructor(const GlobalObject& aGlobal,
                      const Optional<HeadersOrByteStringSequenceSequenceOrByteStringMozMap>& aInit,
                      ErrorResult& aRv)
 {
-  nsRefPtr<Headers> headers = new Headers(aGlobal.GetAsSupports());
+  nsRefPtr<InternalHeaders> ih = new InternalHeaders();
+  nsRefPtr<Headers> headers = new Headers(aGlobal.GetAsSupports(), ih);
 
   if (!aInit.WasPassed()) {
     return headers.forget();
   }
 
   if (aInit.Value().IsHeaders()) {
-    headers->Fill(aInit.Value().GetAsHeaders(), aRv);
+    ih->Fill(*aInit.Value().GetAsHeaders().mInternalHeaders, aRv);
   } else if (aInit.Value().IsByteStringSequenceSequence()) {
-    headers->Fill(aInit.Value().GetAsByteStringSequenceSequence(), aRv);
+    ih->Fill(aInit.Value().GetAsByteStringSequenceSequence(), aRv);
   } else if (aInit.Value().IsByteStringMozMap()) {
-    headers->Fill(aInit.Value().GetAsByteStringMozMap(), aRv);
+    ih->Fill(aInit.Value().GetAsByteStringMozMap(), aRv);
   }
 
   if (aRv.Failed()) {
     return nullptr;
   }
 
   return headers.forget();
 }
 
 // static
 already_AddRefed<Headers>
 Headers::Constructor(const GlobalObject& aGlobal,
                      const OwningHeadersOrByteStringSequenceSequenceOrByteStringMozMap& aInit,
                      ErrorResult& aRv)
 {
-  nsRefPtr<Headers> headers = new Headers(aGlobal.GetAsSupports());
+  nsRefPtr<InternalHeaders> ih = new InternalHeaders();
+  nsRefPtr<Headers> headers = new Headers(aGlobal.GetAsSupports(), ih);
 
   if (aInit.IsHeaders()) {
-    headers->Fill(aInit.GetAsHeaders(), aRv);
+    ih->Fill(*(aInit.GetAsHeaders().get()->mInternalHeaders), aRv);
   } else if (aInit.IsByteStringSequenceSequence()) {
-    headers->Fill(aInit.GetAsByteStringSequenceSequence(), aRv);
+    ih->Fill(aInit.GetAsByteStringSequenceSequence(), aRv);
   } else if (aInit.IsByteStringMozMap()) {
-    headers->Fill(aInit.GetAsByteStringMozMap(), aRv);
+    ih->Fill(aInit.GetAsByteStringMozMap(), aRv);
   }
 
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return headers.forget();
 }
 
-Headers::Headers(const Headers& aOther)
-  : mOwner(aOther.mOwner)
-  , mGuard(aOther.mGuard)
-{
-  ErrorResult result;
-  Fill(aOther, result);
-  MOZ_ASSERT(!result.Failed());
-}
-
-void
-Headers::Append(const nsACString& aName, const nsACString& aValue,
-                ErrorResult& aRv)
-{
-  nsAutoCString lowerName;
-  ToLowerCase(aName, lowerName);
-
-  if (IsInvalidMutableHeader(lowerName, &aValue, aRv)) {
-    return;
-  }
-
-  mList.AppendElement(Entry(lowerName, aValue));
-}
-
-void
-Headers::Delete(const nsACString& aName, ErrorResult& aRv)
-{
-  nsAutoCString lowerName;
-  ToLowerCase(aName, lowerName);
-
-  if (IsInvalidMutableHeader(lowerName, nullptr, aRv)) {
-    return;
-  }
-
-  // remove in reverse order to minimize copying
-  for (int32_t i = mList.Length() - 1; i >= 0; --i) {
-    if (lowerName == mList[i].mName) {
-      mList.RemoveElementAt(i);
-    }
-  }
-}
-
-void
-Headers::Get(const nsACString& aName, nsCString& aValue, ErrorResult& aRv) const
-{
-  nsAutoCString lowerName;
-  ToLowerCase(aName, lowerName);
-
-  if (IsInvalidName(lowerName, aRv)) {
-    return;
-  }
-
-  for (uint32_t i = 0; i < mList.Length(); ++i) {
-    if (lowerName == mList[i].mName) {
-      aValue = mList[i].mValue;
-      return;
-    }
-  }
-
-  // No value found, so return null to content
-  aValue.SetIsVoid(true);
-}
-
-void
-Headers::GetAll(const nsACString& aName, nsTArray<nsCString>& aResults,
-                ErrorResult& aRv) const
-{
-  nsAutoCString lowerName;
-  ToLowerCase(aName, lowerName);
-
-  if (IsInvalidName(lowerName, aRv)) {
-    return;
-  }
-
-  aResults.SetLength(0);
-  for (uint32_t i = 0; i < mList.Length(); ++i) {
-    if (lowerName == mList[i].mName) {
-      aResults.AppendElement(mList[i].mValue);
-    }
-  }
-}
-
-bool
-Headers::Has(const nsACString& aName, ErrorResult& aRv) const
-{
-  nsAutoCString lowerName;
-  ToLowerCase(aName, lowerName);
-
-  if (IsInvalidName(lowerName, aRv)) {
-    return false;
-  }
-
-  for (uint32_t i = 0; i < mList.Length(); ++i) {
-    if (lowerName == mList[i].mName) {
-      return true;
-    }
-  }
-  return false;
-}
-
-void
-Headers::Set(const nsACString& aName, const nsACString& aValue, ErrorResult& aRv)
-{
-  nsAutoCString lowerName;
-  ToLowerCase(aName, lowerName);
-
-  if (IsInvalidMutableHeader(lowerName, &aValue, aRv)) {
-    return;
-  }
-
-  int32_t firstIndex = INT32_MAX;
-
-  // remove in reverse order to minimize copying
-  for (int32_t i = mList.Length() - 1; i >= 0; --i) {
-    if (lowerName == mList[i].mName) {
-      firstIndex = std::min(firstIndex, i);
-      mList.RemoveElementAt(i);
-    }
-  }
-
-  if (firstIndex < INT32_MAX) {
-    Entry* entry = mList.InsertElementAt(firstIndex);
-    entry->mName = lowerName;
-    entry->mValue = aValue;
-  } else {
-    mList.AppendElement(Entry(lowerName, aValue));
-  }
-}
-
-void
-Headers::Clear()
-{
-  mList.Clear();
-}
-
-void
-Headers::SetGuard(HeadersGuardEnum aGuard, ErrorResult& aRv)
-{
-  // Rather than re-validate all current headers, just require code to set
-  // this prior to populating the Headers object.  Allow setting immutable
-  // late, though, as that is pretty much required to have a  useful, immutable
-  // headers object.
-  if (aGuard != HeadersGuardEnum::Immutable && mList.Length() > 0) {
-    aRv.Throw(NS_ERROR_FAILURE);
-  }
-  mGuard = aGuard;
-}
-
 JSObject*
 Headers::WrapObject(JSContext* aCx)
 {
   return mozilla::dom::HeadersBinding::Wrap(aCx, this);
 }
 
 Headers::~Headers()
 {
 }
-
-// static
-bool
-Headers::IsSimpleHeader(const nsACString& aName, const nsACString* aValue)
-{
-  // Note, we must allow a null content-type value here to support
-  // get("content-type"), but the IsInvalidValue() check will prevent null
-  // from being set or appended.
-  return aName.EqualsLiteral("accept") ||
-         aName.EqualsLiteral("accept-language") ||
-         aName.EqualsLiteral("content-language") ||
-         (aName.EqualsLiteral("content-type") &&
-          (!aValue || nsContentUtils::IsAllowedNonCorsContentType(*aValue)));
-}
-
-//static
-bool
-Headers::IsInvalidName(const nsACString& aName, ErrorResult& aRv)
-{
-  if (!NS_IsValidHTTPToken(aName)) {
-    NS_ConvertUTF8toUTF16 label(aName);
-    aRv.ThrowTypeError(MSG_INVALID_HEADER_NAME, &label);
-    return true;
-  }
-
-  return false;
-}
-
-// static
-bool
-Headers::IsInvalidValue(const nsACString& aValue, ErrorResult& aRv)
-{
-  if (!NS_IsReasonableHTTPHeaderValue(aValue)) {
-    NS_ConvertUTF8toUTF16 label(aValue);
-    aRv.ThrowTypeError(MSG_INVALID_HEADER_VALUE, &label);
-    return true;
-  }
-  return false;
-}
-
-bool
-Headers::IsImmutable(ErrorResult& aRv) const
-{
-  if (mGuard == HeadersGuardEnum::Immutable) {
-    aRv.ThrowTypeError(MSG_HEADERS_IMMUTABLE);
-    return true;
-  }
-  return false;
-}
-
-bool
-Headers::IsForbiddenRequestHeader(const nsACString& aName) const
-{
-  return mGuard == HeadersGuardEnum::Request &&
-         nsContentUtils::IsForbiddenRequestHeader(aName);
-}
-
-bool
-Headers::IsForbiddenRequestNoCorsHeader(const nsACString& aName,
-                                        const nsACString* aValue) const
-{
-  return mGuard == HeadersGuardEnum::Request_no_cors &&
-         !IsSimpleHeader(aName, aValue);
-}
-
-bool
-Headers::IsForbiddenResponseHeader(const nsACString& aName) const
-{
-  return mGuard == HeadersGuardEnum::Response &&
-         nsContentUtils::IsForbiddenResponseHeader(aName);
-}
-
-void
-Headers::Fill(const Headers& aInit, ErrorResult& aRv)
-{
-  const nsTArray<Entry>& list = aInit.mList;
-  for (uint32_t i = 0; i < list.Length() && !aRv.Failed(); ++i) {
-    const Entry& entry = list[i];
-    Append(entry.mName, entry.mValue, aRv);
-  }
-}
-
-void
-Headers::Fill(const Sequence<Sequence<nsCString>>& aInit, ErrorResult& aRv)
-{
-  for (uint32_t i = 0; i < aInit.Length() && !aRv.Failed(); ++i) {
-    const Sequence<nsCString>& tuple = aInit[i];
-    if (tuple.Length() != 2) {
-      aRv.ThrowTypeError(MSG_INVALID_HEADER_SEQUENCE);
-      return;
-    }
-    Append(tuple[0], tuple[1], aRv);
-  }
-}
-
-void
-Headers::Fill(const MozMap<nsCString>& aInit, ErrorResult& aRv)
-{
-  nsTArray<nsString> keys;
-  aInit.GetKeys(keys);
-  for (uint32_t i = 0; i < keys.Length() && !aRv.Failed(); ++i) {
-    Append(NS_ConvertUTF16toUTF8(keys[i]), aInit.Get(keys[i]), aRv);
-  }
-}
 } // namespace dom
 } // namespace mozilla
--- a/dom/fetch/Headers.h
+++ b/dom/fetch/Headers.h
@@ -8,121 +8,122 @@
 #define mozilla_dom_Headers_h
 
 #include "mozilla/dom/HeadersBinding.h"
 #include "mozilla/dom/UnionTypes.h"
 
 #include "nsClassHashtable.h"
 #include "nsWrapperCache.h"
 
+#include "InternalHeaders.h"
+
 class nsPIDOMWindow;
 
 namespace mozilla {
 
 class ErrorResult;
 
 namespace dom {
 
 template<typename T> class MozMap;
 class HeadersOrByteStringSequenceSequenceOrByteStringMozMap;
 
+/**
+ * This Headers class is only used to represent the content facing Headers
+ * object. It is actually backed by an InternalHeaders implementation. Gecko
+ * code should NEVER use this, except in the Request and Response
+ * implementations, where they must always be created from the backing
+ * InternalHeaders object.
+ */
 class Headers MOZ_FINAL : public nsISupports
                         , public nsWrapperCache
 {
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Headers)
 
-private:
-  struct Entry
-  {
-    Entry(const nsACString& aName, const nsACString& aValue)
-      : mName(aName)
-      , mValue(aValue)
-    { }
+  friend class Request;
+  friend class Response;
 
-    Entry() { }
-
-    nsCString mName;
-    nsCString mValue;
-  };
-
+private:
   nsCOMPtr<nsISupports> mOwner;
-  HeadersGuardEnum mGuard;
-  nsTArray<Entry> mList;
+  nsRefPtr<InternalHeaders> mInternalHeaders;
 
 public:
-  explicit Headers(nsISupports* aOwner, HeadersGuardEnum aGuard = HeadersGuardEnum::None)
+  explicit Headers(nsISupports* aOwner, InternalHeaders* aInternalHeaders)
     : mOwner(aOwner)
-    , mGuard(aGuard)
+    , mInternalHeaders(aInternalHeaders)
   {
   }
 
-  explicit Headers(const Headers& aOther);
+  explicit Headers(const Headers& aOther) MOZ_DELETE;
 
   static bool PrefEnabled(JSContext* cx, JSObject* obj);
 
   static already_AddRefed<Headers>
   Constructor(const GlobalObject& aGlobal,
               const Optional<HeadersOrByteStringSequenceSequenceOrByteStringMozMap>& aInit,
               ErrorResult& aRv);
 
   static already_AddRefed<Headers>
   Constructor(const GlobalObject& aGlobal,
               const OwningHeadersOrByteStringSequenceSequenceOrByteStringMozMap& aInit,
               ErrorResult& aRv);
 
   void Append(const nsACString& aName, const nsACString& aValue,
-              ErrorResult& aRv);
-  void Delete(const nsACString& aName, ErrorResult& aRv);
-  void Get(const nsACString& aName, nsCString& aValue, ErrorResult& aRv) const;
+              ErrorResult& aRv)
+  {
+    mInternalHeaders->Append(aName, aValue, aRv);
+  }
+
+  void Delete(const nsACString& aName, ErrorResult& aRv)
+  {
+    mInternalHeaders->Delete(aName, aRv);
+  }
+
+  void Get(const nsACString& aName, nsCString& aValue, ErrorResult& aRv) const
+  {
+    mInternalHeaders->Get(aName, aValue, aRv);
+  }
+
   void GetAll(const nsACString& aName, nsTArray<nsCString>& aResults,
-              ErrorResult& aRv) const;
-  bool Has(const nsACString& aName, ErrorResult& aRv) const;
-  void Set(const nsACString& aName, const nsACString& aValue, ErrorResult& aRv);
+              ErrorResult& aRv) const
+  {
+    mInternalHeaders->GetAll(aName, aResults, aRv);
+  }
 
-  void Clear();
+  bool Has(const nsACString& aName, ErrorResult& aRv) const
+  {
+    return mInternalHeaders->Has(aName, aRv);
+  }
+
+  void Set(const nsACString& aName, const nsACString& aValue, ErrorResult& aRv)
+  {
+    mInternalHeaders->Set(aName, aValue, aRv);
+  }
 
   // ChromeOnly
-  HeadersGuardEnum Guard() const { return mGuard; }
-  void SetGuard(HeadersGuardEnum aGuard, ErrorResult& aRv);
+  HeadersGuardEnum Guard() const
+  {
+    return mInternalHeaders->Guard();
+  }
+
+  void SetGuard(HeadersGuardEnum aGuard, ErrorResult& aRv)
+  {
+    mInternalHeaders->SetGuard(aGuard, aRv);
+  }
 
   virtual JSObject* WrapObject(JSContext* aCx);
   nsISupports* GetParentObject() const { return mOwner; }
 
-  void Fill(const Headers& aInit, ErrorResult& aRv);
 private:
-  // Since Headers is also an nsISupports, the above constructor can
-  // accidentally be invoked as new Headers(Headers*[, implied None guard]) when
-  // the intention is to use the copy constructor. Explicitly disallow it.
-  Headers(Headers* aOther) MOZ_DELETE;
-
   virtual ~Headers();
 
-  static bool IsSimpleHeader(const nsACString& aName,
-                             const nsACString* aValue = nullptr);
-  static bool IsInvalidName(const nsACString& aName, ErrorResult& aRv);
-  static bool IsInvalidValue(const nsACString& aValue, ErrorResult& aRv);
-  bool IsImmutable(ErrorResult& aRv) const;
-  bool IsForbiddenRequestHeader(const nsACString& aName) const;
-  bool IsForbiddenRequestNoCorsHeader(const nsACString& aName,
-                                      const nsACString* aValue = nullptr) const;
-  bool IsForbiddenResponseHeader(const nsACString& aName) const;
-
-  bool IsInvalidMutableHeader(const nsACString& aName,
-                              const nsACString* aValue,
-                              ErrorResult& aRv) const
+  InternalHeaders*
+  GetInternalHeaders() const
   {
-    return IsInvalidName(aName, aRv) ||
-           (aValue && IsInvalidValue(*aValue, aRv)) ||
-           IsImmutable(aRv) ||
-           IsForbiddenRequestHeader(aName) ||
-           IsForbiddenRequestNoCorsHeader(aName, aValue) ||
-           IsForbiddenResponseHeader(aName);
+    return mInternalHeaders;
   }
-
-  void Fill(const Sequence<Sequence<nsCString>>& aInit, ErrorResult& aRv);
-  void Fill(const MozMap<nsCString>& aInit, ErrorResult& aRv);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_Headers_h
new file mode 100644
--- /dev/null
+++ b/dom/fetch/InternalHeaders.cpp
@@ -0,0 +1,272 @@
+/* -*- 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 "mozilla/dom/InternalHeaders.h"
+
+#include "mozilla/ErrorResult.h"
+
+#include "nsCharSeparatedTokenizer.h"
+#include "nsContentUtils.h"
+#include "nsNetUtil.h"
+#include "nsReadableUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+void
+InternalHeaders::Append(const nsACString& aName, const nsACString& aValue,
+                        ErrorResult& aRv)
+{
+  nsAutoCString lowerName;
+  ToLowerCase(aName, lowerName);
+
+  if (IsInvalidMutableHeader(lowerName, aValue, aRv)) {
+    return;
+  }
+
+  mList.AppendElement(Entry(lowerName, aValue));
+}
+
+void
+InternalHeaders::Delete(const nsACString& aName, ErrorResult& aRv)
+{
+  nsAutoCString lowerName;
+  ToLowerCase(aName, lowerName);
+
+  if (IsInvalidMutableHeader(lowerName, aRv)) {
+    return;
+  }
+
+  // remove in reverse order to minimize copying
+  for (int32_t i = mList.Length() - 1; i >= 0; --i) {
+    if (lowerName == mList[i].mName) {
+      mList.RemoveElementAt(i);
+    }
+  }
+}
+
+void
+InternalHeaders::Get(const nsACString& aName, nsCString& aValue, ErrorResult& aRv) const
+{
+  nsAutoCString lowerName;
+  ToLowerCase(aName, lowerName);
+
+  if (IsInvalidName(lowerName, aRv)) {
+    return;
+  }
+
+  for (uint32_t i = 0; i < mList.Length(); ++i) {
+    if (lowerName == mList[i].mName) {
+      aValue = mList[i].mValue;
+      return;
+    }
+  }
+
+  // No value found, so return null to content
+  aValue.SetIsVoid(true);
+}
+
+void
+InternalHeaders::GetAll(const nsACString& aName, nsTArray<nsCString>& aResults,
+                        ErrorResult& aRv) const
+{
+  nsAutoCString lowerName;
+  ToLowerCase(aName, lowerName);
+
+  if (IsInvalidName(lowerName, aRv)) {
+    return;
+  }
+
+  aResults.SetLength(0);
+  for (uint32_t i = 0; i < mList.Length(); ++i) {
+    if (lowerName == mList[i].mName) {
+      aResults.AppendElement(mList[i].mValue);
+    }
+  }
+}
+
+bool
+InternalHeaders::Has(const nsACString& aName, ErrorResult& aRv) const
+{
+  nsAutoCString lowerName;
+  ToLowerCase(aName, lowerName);
+
+  if (IsInvalidName(lowerName, aRv)) {
+    return false;
+  }
+
+  for (uint32_t i = 0; i < mList.Length(); ++i) {
+    if (lowerName == mList[i].mName) {
+      return true;
+    }
+  }
+  return false;
+}
+
+void
+InternalHeaders::Set(const nsACString& aName, const nsACString& aValue, ErrorResult& aRv)
+{
+  nsAutoCString lowerName;
+  ToLowerCase(aName, lowerName);
+
+  if (IsInvalidMutableHeader(lowerName, aValue, aRv)) {
+    return;
+  }
+
+  int32_t firstIndex = INT32_MAX;
+
+  // remove in reverse order to minimize copying
+  for (int32_t i = mList.Length() - 1; i >= 0; --i) {
+    if (lowerName == mList[i].mName) {
+      firstIndex = std::min(firstIndex, i);
+      mList.RemoveElementAt(i);
+    }
+  }
+
+  if (firstIndex < INT32_MAX) {
+    Entry* entry = mList.InsertElementAt(firstIndex);
+    entry->mName = lowerName;
+    entry->mValue = aValue;
+  } else {
+    mList.AppendElement(Entry(lowerName, aValue));
+  }
+}
+
+void
+InternalHeaders::Clear()
+{
+  mList.Clear();
+}
+
+void
+InternalHeaders::SetGuard(HeadersGuardEnum aGuard, ErrorResult& aRv)
+{
+  // Rather than re-validate all current headers, just require code to set
+  // this prior to populating the InternalHeaders object.  Allow setting immutable
+  // late, though, as that is pretty much required to have a  useful, immutable
+  // headers object.
+  if (aGuard != HeadersGuardEnum::Immutable && mList.Length() > 0) {
+    aRv.Throw(NS_ERROR_FAILURE);
+  }
+  mGuard = aGuard;
+}
+
+InternalHeaders::~InternalHeaders()
+{
+}
+
+// static
+bool
+InternalHeaders::IsSimpleHeader(const nsACString& aName, const nsACString& aValue)
+{
+  // Note, we must allow a null content-type value here to support
+  // get("content-type"), but the IsInvalidValue() check will prevent null
+  // from being set or appended.
+  return aName.EqualsLiteral("accept") ||
+         aName.EqualsLiteral("accept-language") ||
+         aName.EqualsLiteral("content-language") ||
+         (aName.EqualsLiteral("content-type") &&
+          nsContentUtils::IsAllowedNonCorsContentType(aValue));
+}
+
+//static
+bool
+InternalHeaders::IsInvalidName(const nsACString& aName, ErrorResult& aRv)
+{
+  if (!NS_IsValidHTTPToken(aName)) {
+    NS_ConvertUTF8toUTF16 label(aName);
+    aRv.ThrowTypeError(MSG_INVALID_HEADER_NAME, &label);
+    return true;
+  }
+
+  return false;
+}
+
+// static
+bool
+InternalHeaders::IsInvalidValue(const nsACString& aValue, ErrorResult& aRv)
+{
+  if (!NS_IsReasonableHTTPHeaderValue(aValue)) {
+    NS_ConvertUTF8toUTF16 label(aValue);
+    aRv.ThrowTypeError(MSG_INVALID_HEADER_VALUE, &label);
+    return true;
+  }
+  return false;
+}
+
+bool
+InternalHeaders::IsImmutable(ErrorResult& aRv) const
+{
+  if (mGuard == HeadersGuardEnum::Immutable) {
+    aRv.ThrowTypeError(MSG_HEADERS_IMMUTABLE);
+    return true;
+  }
+  return false;
+}
+
+bool
+InternalHeaders::IsForbiddenRequestHeader(const nsACString& aName) const
+{
+  return mGuard == HeadersGuardEnum::Request &&
+         nsContentUtils::IsForbiddenRequestHeader(aName);
+}
+
+bool
+InternalHeaders::IsForbiddenRequestNoCorsHeader(const nsACString& aName) const
+{
+  return mGuard == HeadersGuardEnum::Request_no_cors &&
+         !IsSimpleHeader(aName, EmptyCString());
+}
+
+bool
+InternalHeaders::IsForbiddenRequestNoCorsHeader(const nsACString& aName,
+                                                const nsACString& aValue) const
+{
+  return mGuard == HeadersGuardEnum::Request_no_cors &&
+         !IsSimpleHeader(aName, aValue);
+}
+
+bool
+InternalHeaders::IsForbiddenResponseHeader(const nsACString& aName) const
+{
+  return mGuard == HeadersGuardEnum::Response &&
+         nsContentUtils::IsForbiddenResponseHeader(aName);
+}
+
+void
+InternalHeaders::Fill(const InternalHeaders& aInit, ErrorResult& aRv)
+{
+  const nsTArray<Entry>& list = aInit.mList;
+  for (uint32_t i = 0; i < list.Length() && !aRv.Failed(); ++i) {
+    const Entry& entry = list[i];
+    Append(entry.mName, entry.mValue, aRv);
+  }
+}
+
+void
+InternalHeaders::Fill(const Sequence<Sequence<nsCString>>& aInit, ErrorResult& aRv)
+{
+  for (uint32_t i = 0; i < aInit.Length() && !aRv.Failed(); ++i) {
+    const Sequence<nsCString>& tuple = aInit[i];
+    if (tuple.Length() != 2) {
+      aRv.ThrowTypeError(MSG_INVALID_HEADER_SEQUENCE);
+      return;
+    }
+    Append(tuple[0], tuple[1], aRv);
+  }
+}
+
+void
+InternalHeaders::Fill(const MozMap<nsCString>& aInit, ErrorResult& aRv)
+{
+  nsTArray<nsString> keys;
+  aInit.GetKeys(keys);
+  for (uint32_t i = 0; i < keys.Length() && !aRv.Failed(); ++i) {
+    Append(NS_ConvertUTF16toUTF8(keys[i]), aInit.Get(keys[i]), aRv);
+  }
+}
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/fetch/InternalHeaders.h
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_InternalHeaders_h
+#define mozilla_dom_InternalHeaders_h
+
+// needed for HeadersGuardEnum.
+#include "mozilla/dom/HeadersBinding.h"
+#include "mozilla/dom/UnionTypes.h"
+
+#include "nsClassHashtable.h"
+#include "nsWrapperCache.h"
+
+class nsPIDOMWindow;
+
+namespace mozilla {
+
+class ErrorResult;
+
+namespace dom {
+
+template<typename T> class MozMap;
+class HeadersOrByteStringSequenceSequenceOrByteStringMozMap;
+
+class InternalHeaders MOZ_FINAL
+{
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(InternalHeaders)
+
+private:
+  struct Entry
+  {
+    Entry(const nsACString& aName, const nsACString& aValue)
+      : mName(aName)
+      , mValue(aValue)
+    { }
+
+    Entry() { }
+
+    nsCString mName;
+    nsCString mValue;
+  };
+
+  HeadersGuardEnum mGuard;
+  nsTArray<Entry> mList;
+
+public:
+  explicit InternalHeaders(HeadersGuardEnum aGuard = HeadersGuardEnum::None)
+    : mGuard(aGuard)
+  {
+  }
+
+  explicit InternalHeaders(const InternalHeaders& aOther)
+    : mGuard(aOther.mGuard)
+  {
+    ErrorResult result;
+    Fill(aOther, result);
+    MOZ_ASSERT(!result.Failed());
+  }
+
+  void Append(const nsACString& aName, const nsACString& aValue,
+              ErrorResult& aRv);
+  void Delete(const nsACString& aName, ErrorResult& aRv);
+  void Get(const nsACString& aName, nsCString& aValue, ErrorResult& aRv) const;
+  void GetAll(const nsACString& aName, nsTArray<nsCString>& aResults,
+              ErrorResult& aRv) const;
+  bool Has(const nsACString& aName, ErrorResult& aRv) const;
+  void Set(const nsACString& aName, const nsACString& aValue, ErrorResult& aRv);
+
+  void Clear();
+
+  HeadersGuardEnum Guard() const { return mGuard; }
+  void SetGuard(HeadersGuardEnum aGuard, ErrorResult& aRv);
+
+  void Fill(const InternalHeaders& aInit, ErrorResult& aRv);
+  void Fill(const Sequence<Sequence<nsCString>>& aInit, ErrorResult& aRv);
+  void Fill(const MozMap<nsCString>& aInit, ErrorResult& aRv);
+private:
+  virtual ~InternalHeaders();
+
+  static bool IsSimpleHeader(const nsACString& aName,
+                             const nsACString& aValue);
+  static bool IsInvalidName(const nsACString& aName, ErrorResult& aRv);
+  static bool IsInvalidValue(const nsACString& aValue, ErrorResult& aRv);
+  bool IsImmutable(ErrorResult& aRv) const;
+  bool IsForbiddenRequestHeader(const nsACString& aName) const;
+  bool IsForbiddenRequestNoCorsHeader(const nsACString& aName) const;
+  bool IsForbiddenRequestNoCorsHeader(const nsACString& aName,
+                                      const nsACString& aValue) const;
+  bool IsForbiddenResponseHeader(const nsACString& aName) const;
+
+  bool IsInvalidMutableHeader(const nsACString& aName,
+                              ErrorResult& aRv) const
+  {
+    return IsInvalidMutableHeader(aName, EmptyCString(), aRv);
+  }
+
+  bool IsInvalidMutableHeader(const nsACString& aName,
+                              const nsACString& aValue,
+                              ErrorResult& aRv) const
+  {
+    return IsInvalidName(aName, aRv) ||
+           IsInvalidValue(aValue, aRv) ||
+           IsImmutable(aRv) ||
+           IsForbiddenRequestHeader(aName) ||
+           IsForbiddenRequestNoCorsHeader(aName, aValue) ||
+           IsForbiddenResponseHeader(aName);
+  }
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_InternalHeaders_h
--- a/dom/fetch/InternalRequest.cpp
+++ b/dom/fetch/InternalRequest.cpp
@@ -19,17 +19,17 @@ namespace dom {
 
 // The global is used to extract the principal.
 already_AddRefed<InternalRequest>
 InternalRequest::GetRequestConstructorCopy(nsIGlobalObject* aGlobal, ErrorResult& aRv) const
 {
   nsRefPtr<InternalRequest> copy = new InternalRequest();
   copy->mURL.Assign(mURL);
   copy->SetMethod(mMethod);
-  copy->mHeaders = new Headers(*mHeaders);
+  copy->mHeaders = new InternalHeaders(*mHeaders);
 
   copy->mBodyStream = mBodyStream;
   copy->mPreserveContentCodings = true;
 
   if (NS_IsMainThread()) {
     nsIPrincipal* principal = aGlobal->PrincipalOrNull();
     MOZ_ASSERT(principal);
     aRv = nsContentUtils::GetASCIIOrigin(principal, copy->mOrigin);
--- a/dom/fetch/InternalRequest.h
+++ b/dom/fetch/InternalRequest.h
@@ -1,17 +1,18 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_InternalRequest_h
 #define mozilla_dom_InternalRequest_h
 
-#include "mozilla/dom/Headers.h"
+#include "mozilla/dom/HeadersBinding.h"
+#include "mozilla/dom/InternalHeaders.h"
 #include "mozilla/dom/RequestBinding.h"
 #include "mozilla/dom/UnionTypes.h"
 
 #include "nsIContentPolicy.h"
 #include "nsIInputStream.h"
 #include "nsISupportsImpl.h"
 
 class nsIDocument;
@@ -50,17 +51,17 @@ public:
   {
     RESPONSETAINT_BASIC,
     RESPONSETAINT_CORS,
     RESPONSETAINT_OPAQUE,
   };
 
   explicit InternalRequest()
     : mMethod("GET")
-    , mHeaders(new Headers(nullptr, HeadersGuardEnum::None))
+    , mHeaders(new InternalHeaders(HeadersGuardEnum::None))
     , mContextFrameType(FRAMETYPE_NONE)
     , mReferrerType(REFERRER_CLIENT)
     , mMode(RequestMode::No_cors)
     , mCredentialsMode(RequestCredentials::Omit)
     , mResponseTainting(RESPONSETAINT_BASIC)
     , mRedirectCount(0)
     , mAuthenticationFlag(false)
     , mForceOriginHeader(false)
@@ -154,16 +155,22 @@ public:
   }
 
   bool
   IsSynchronous() const
   {
     return mSynchronous;
   }
 
+  RequestMode
+  Mode() const
+  {
+    return mMode;
+  }
+
   void
   SetMode(RequestMode aMode)
   {
     mMode = aMode;
   }
 
   void
   SetCredentialsMode(RequestCredentials aCredentialsMode)
@@ -172,18 +179,18 @@ public:
   }
 
   nsContentPolicyType
   GetContext() const
   {
     return mContext;
   }
 
-  Headers*
-  Headers_()
+  InternalHeaders*
+  Headers()
   {
     return mHeaders;
   }
 
   bool
   ForceOriginHeader()
   {
     return mForceOriginHeader;
@@ -220,17 +227,17 @@ private:
   void
   SetURL(const nsACString& aURL)
   {
     mURL.Assign(aURL);
   }
 
   nsCString mMethod;
   nsCString mURL;
-  nsRefPtr<Headers> mHeaders;
+  nsRefPtr<InternalHeaders> mHeaders;
   nsCOMPtr<nsIInputStream> mBodyStream;
 
   // nsContentPolicyType does not cover the complete set defined in the spec,
   // but it is a good start.
   nsContentPolicyType mContext;
 
   nsCString mOrigin;
 
new file mode 100644
--- /dev/null
+++ b/dom/fetch/InternalResponse.cpp
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "InternalResponse.h"
+
+#include "nsIDOMFile.h"
+
+#include "mozilla/dom/InternalHeaders.h"
+
+namespace mozilla {
+namespace dom {
+
+InternalResponse::InternalResponse(uint16_t aStatus, const nsACString& aStatusText)
+  : mType(ResponseType::Default)
+  , mStatus(aStatus)
+  , mStatusText(aStatusText)
+  , mHeaders(new InternalHeaders(HeadersGuardEnum::Response))
+{
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/fetch/InternalResponse.h
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_InternalResponse_h
+#define mozilla_dom_InternalResponse_h
+
+#include "nsIInputStream.h"
+#include "nsISupportsImpl.h"
+
+#include "mozilla/dom/ResponseBinding.h"
+
+namespace mozilla {
+namespace dom {
+
+class InternalHeaders;
+
+class InternalResponse MOZ_FINAL
+{
+  friend class FetchDriver;
+
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(InternalResponse)
+
+  InternalResponse(uint16_t aStatus, const nsACString& aStatusText);
+
+  explicit InternalResponse(const InternalResponse& aOther) MOZ_DELETE;
+
+  static already_AddRefed<InternalResponse>
+  NetworkError()
+  {
+    nsRefPtr<InternalResponse> response = new InternalResponse(0, EmptyCString());
+    response->mType = ResponseType::Error;
+    return response.forget();
+  }
+
+  ResponseType
+  Type() const
+  {
+    return mType;
+  }
+
+  bool
+  IsError() const
+  {
+    return Type() == ResponseType::Error;
+  }
+
+  // FIXME(nsm): Return with exclude fragment.
+  nsCString&
+  GetUrl()
+  {
+    return mURL;
+  }
+
+  uint16_t
+  GetStatus() const
+  {
+    return mStatus;
+  }
+
+  const nsCString&
+  GetStatusText() const
+  {
+    return mStatusText;
+  }
+
+  InternalHeaders*
+  Headers()
+  {
+    return mHeaders;
+  }
+
+  void
+  GetBody(nsIInputStream** aStream)
+  {
+    nsCOMPtr<nsIInputStream> stream = mBody;
+    stream.forget(aStream);
+  }
+
+  void
+  SetBody(nsIInputStream* aBody)
+  {
+    mBody = aBody;
+  }
+
+private:
+  ~InternalResponse()
+  { }
+
+  ResponseType mType;
+  nsCString mTerminationReason;
+  nsCString mURL;
+  const uint16_t mStatus;
+  const nsCString mStatusText;
+  nsRefPtr<InternalHeaders> mHeaders;
+  nsCOMPtr<nsIInputStream> mBody;
+  nsCString mContentType;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_InternalResponse_h
--- a/dom/fetch/Request.cpp
+++ b/dom/fetch/Request.cpp
@@ -1,52 +1,43 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "Request.h"
 
-#include "nsIUnicodeDecoder.h"
 #include "nsIURI.h"
-
-#include "nsDOMString.h"
-#include "nsNetUtil.h"
 #include "nsPIDOMWindow.h"
-#include "nsStreamUtils.h"
-#include "nsStringStream.h"
 
 #include "mozilla/ErrorResult.h"
-#include "mozilla/dom/EncodingUtils.h"
-#include "mozilla/dom/File.h"
 #include "mozilla/dom/Headers.h"
 #include "mozilla/dom/Fetch.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/URL.h"
 #include "mozilla/dom/workers/bindings/URL.h"
 
-// dom/workers
 #include "WorkerPrivate.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(Request)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(Request)
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Request, mOwner)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Request, mOwner, mHeaders)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Request)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 Request::Request(nsIGlobalObject* aOwner, InternalRequest* aRequest)
-  : mOwner(aOwner)
+  : FetchBody<Request>()
+  , mOwner(aOwner)
   , mRequest(aRequest)
-  , mBodyUsed(false)
 {
 }
 
 Request::~Request()
 {
 }
 
 already_AddRefed<InternalRequest>
@@ -159,274 +150,95 @@ Request::Constructor(const GlobalObject&
       aRv.ThrowTypeError(MSG_INVALID_REQUEST_METHOD, &label);
       return nullptr;
     }
 
     ToUpperCase(method);
     request->SetMethod(method);
   }
 
-  nsRefPtr<Request> domRequest = new Request(global, request);
-  nsRefPtr<Headers> domRequestHeaders = domRequest->Headers_();
+  nsRefPtr<InternalHeaders> requestHeaders = request->Headers();
 
-  nsRefPtr<Headers> headers;
+  nsRefPtr<InternalHeaders> headers;
   if (aInit.mHeaders.WasPassed()) {
-    headers = Headers::Constructor(aGlobal, aInit.mHeaders.Value(), aRv);
+    nsRefPtr<Headers> h = Headers::Constructor(aGlobal, aInit.mHeaders.Value(), aRv);
     if (aRv.Failed()) {
       return nullptr;
     }
+    headers = h->GetInternalHeaders();
   } else {
-    headers = new Headers(*domRequestHeaders);
+    headers = new InternalHeaders(*requestHeaders);
   }
 
-  domRequestHeaders->Clear();
+  requestHeaders->Clear();
 
-  if (domRequest->Mode() == RequestMode::No_cors) {
+  if (request->Mode() == RequestMode::No_cors) {
     nsCString method;
-    domRequest->GetMethod(method);
+    request->GetMethod(method);
     ToLowerCase(method);
     if (!method.EqualsASCII("get") &&
         !method.EqualsASCII("head") &&
         !method.EqualsASCII("post")) {
       NS_ConvertUTF8toUTF16 label(method);
       aRv.ThrowTypeError(MSG_INVALID_REQUEST_METHOD, &label);
       return nullptr;
     }
 
-    domRequestHeaders->SetGuard(HeadersGuardEnum::Request_no_cors, aRv);
+    requestHeaders->SetGuard(HeadersGuardEnum::Request_no_cors, aRv);
     if (aRv.Failed()) {
       return nullptr;
     }
   }
 
-  domRequestHeaders->Fill(*headers, aRv);
+  requestHeaders->Fill(*headers, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
   if (aInit.mBody.WasPassed()) {
-    const OwningArrayBufferOrArrayBufferViewOrScalarValueStringOrURLSearchParams& bodyInit = aInit.mBody.Value();
+    const OwningArrayBufferOrArrayBufferViewOrBlobOrScalarValueStringOrURLSearchParams& bodyInit = aInit.mBody.Value();
     nsCOMPtr<nsIInputStream> stream;
     nsCString contentType;
     aRv = ExtractByteStreamFromBody(bodyInit,
                                     getter_AddRefs(stream), contentType);
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
     }
     request->SetBody(stream);
 
     if (!contentType.IsVoid() &&
-        !domRequestHeaders->Has(NS_LITERAL_CSTRING("Content-Type"), aRv)) {
-      domRequestHeaders->Append(NS_LITERAL_CSTRING("Content-Type"),
-                                contentType, aRv);
+        !requestHeaders->Has(NS_LITERAL_CSTRING("Content-Type"), aRv)) {
+      requestHeaders->Append(NS_LITERAL_CSTRING("Content-Type"),
+                             contentType, aRv);
     }
 
     if (aRv.Failed()) {
       return nullptr;
     }
   }
 
-  // Extract mime type.
-  nsTArray<nsCString> contentTypeValues;
-  domRequestHeaders->GetAll(NS_LITERAL_CSTRING("Content-Type"),
-                            contentTypeValues, aRv);
-  if (aRv.Failed()) {
-    return nullptr;
-  }
-
-  // HTTP ABNF states Content-Type may have only one value.
-  // This is from the "parse a header value" of the fetch spec.
-  if (contentTypeValues.Length() == 1) {
-    domRequest->mMimeType = contentTypeValues[0];
-    ToLowerCase(domRequest->mMimeType);
-  }
-
+  nsRefPtr<Request> domRequest = new Request(global, request);
+  domRequest->SetMimeType(aRv);
   return domRequest.forget();
 }
 
 already_AddRefed<Request>
 Request::Clone() const
 {
   // FIXME(nsm): Bug 1073231. This is incorrect, but the clone method isn't
   // well defined yet.
   nsRefPtr<Request> request = new Request(mOwner,
                                           new InternalRequest(*mRequest));
   return request.forget();
 }
 
-namespace {
-nsresult
-DecodeUTF8(const nsCString& aBuffer, nsString& aDecoded)
-{
-  nsCOMPtr<nsIUnicodeDecoder> decoder =
-    EncodingUtils::DecoderForEncoding("UTF-8");
-  if (!decoder) {
-    return NS_ERROR_FAILURE;
-  }
-
-  int32_t destBufferLen;
-  nsresult rv =
-    decoder->GetMaxLength(aBuffer.get(), aBuffer.Length(), &destBufferLen);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  if (!aDecoded.SetCapacity(destBufferLen, fallible_t())) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  char16_t* destBuffer = aDecoded.BeginWriting();
-  int32_t srcLen = (int32_t) aBuffer.Length();
-  int32_t outLen = destBufferLen;
-  rv = decoder->Convert(aBuffer.get(), &srcLen, destBuffer, &outLen);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  MOZ_ASSERT(outLen <= destBufferLen);
-  aDecoded.SetLength(outLen);
-  return NS_OK;
-}
-}
-
-already_AddRefed<Promise>
-Request::ConsumeBody(ConsumeType aType, ErrorResult& aRv)
+Headers*
+Request::Headers_()
 {
-  nsRefPtr<Promise> promise = Promise::Create(mOwner, aRv);
-  if (aRv.Failed()) {
-    return nullptr;
-  }
-
-  if (BodyUsed()) {
-    aRv.ThrowTypeError(MSG_REQUEST_BODY_CONSUMED_ERROR);
-    return nullptr;
-  }
-
-  SetBodyUsed();
-
-  // While the spec says to do this asynchronously, all the body constructors
-  // right now only accept bodies whose streams are backed by an in-memory
-  // buffer that can be read without blocking. So I think this is fine.
-  nsCOMPtr<nsIInputStream> stream;
-  mRequest->GetBody(getter_AddRefs(stream));
-
-  if (!stream) {
-    aRv = NS_NewByteInputStream(getter_AddRefs(stream), "", 0,
-                                NS_ASSIGNMENT_COPY);
-    if (aRv.Failed()) {
-      return nullptr;
-    }
-  }
-
-  AutoJSAPI api;
-  api.Init(mOwner);
-  JSContext* cx = api.cx();
-
-  // We can make this assertion because for now we only support memory backed
-  // structures for the body argument for a Request.
-  MOZ_ASSERT(NS_InputStreamIsBuffered(stream));
-  nsCString buffer;
-  uint64_t len;
-  aRv = stream->Available(&len);
-  if (aRv.Failed()) {
-    return nullptr;
-  }
-
-  aRv = NS_ReadInputStreamToString(stream, buffer, len);
-  if (aRv.Failed()) {
-    return nullptr;
+  if (!mHeaders) {
+    mHeaders = new Headers(mOwner, mRequest->Headers());
   }
 
-  buffer.SetLength(len);
-
-  switch (aType) {
-    case CONSUME_ARRAYBUFFER: {
-      JS::Rooted<JSObject*> arrayBuffer(cx);
-      arrayBuffer =
-        ArrayBuffer::Create(cx, buffer.Length(),
-                            reinterpret_cast<const uint8_t*>(buffer.get()));
-      JS::Rooted<JS::Value> val(cx);
-      val.setObjectOrNull(arrayBuffer);
-      promise->MaybeResolve(cx, val);
-      return promise.forget();
-    }
-    case CONSUME_BLOB: {
-      // XXXnsm it is actually possible to avoid these duplicate allocations
-      // for the Blob case by having the Blob adopt the stream's memory
-      // directly, but I've not added a special case for now.
-      //
-      // This is similar to nsContentUtils::CreateBlobBuffer, but also deals
-      // with worker wrapping.
-      uint32_t blobLen = buffer.Length();
-      void* blobData = moz_malloc(blobLen);
-      nsRefPtr<File> blob;
-      if (blobData) {
-        memcpy(blobData, buffer.BeginReading(), blobLen);
-        blob = File::CreateMemoryFile(GetParentObject(), blobData, blobLen,
-                                      NS_ConvertUTF8toUTF16(mMimeType));
-      } else {
-        aRv = NS_ERROR_OUT_OF_MEMORY;
-        return nullptr;
-      }
-
-      promise->MaybeResolve(blob);
-      return promise.forget();
-    }
-    case CONSUME_JSON: {
-      nsString decoded;
-      aRv = DecodeUTF8(buffer, decoded);
-      if (aRv.Failed()) {
-        return nullptr;
-      }
-
-      JS::Rooted<JS::Value> json(cx);
-      if (!JS_ParseJSON(cx, decoded.get(), decoded.Length(), &json)) {
-        JS::Rooted<JS::Value> exn(cx);
-        if (JS_GetPendingException(cx, &exn)) {
-          JS_ClearPendingException(cx);
-          promise->MaybeReject(cx, exn);
-        }
-      }
-      promise->MaybeResolve(cx, json);
-      return promise.forget();
-    }
-    case CONSUME_TEXT: {
-      nsString decoded;
-      aRv = DecodeUTF8(buffer, decoded);
-      if (aRv.Failed()) {
-        return nullptr;
-      }
-
-      promise->MaybeResolve(decoded);
-      return promise.forget();
-    }
-  }
-
-  NS_NOTREACHED("Unexpected consume body type");
-  // Silence warnings.
-  return nullptr;
+  return mHeaders;
 }
 
-already_AddRefed<Promise>
-Request::ArrayBuffer(ErrorResult& aRv)
-{
-  return ConsumeBody(CONSUME_ARRAYBUFFER, aRv);
-}
-
-already_AddRefed<Promise>
-Request::Blob(ErrorResult& aRv)
-{
-  return ConsumeBody(CONSUME_BLOB, aRv);
-}
-
-already_AddRefed<Promise>
-Request::Json(ErrorResult& aRv)
-{
-  return ConsumeBody(CONSUME_JSON, aRv);
-}
-
-already_AddRefed<Promise>
-Request::Text(ErrorResult& aRv)
-{
-  return ConsumeBody(CONSUME_TEXT, aRv);
-}
 } // namespace dom
 } // namespace mozilla
--- a/dom/fetch/Request.h
+++ b/dom/fetch/Request.h
@@ -4,32 +4,35 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_Request_h
 #define mozilla_dom_Request_h
 
 #include "nsISupportsImpl.h"
 #include "nsWrapperCache.h"
 
+#include "mozilla/dom/Fetch.h"
 #include "mozilla/dom/InternalRequest.h"
 // Required here due to certain WebIDL enums/classes being declared in both
 // files.
 #include "mozilla/dom/RequestBinding.h"
 #include "mozilla/dom/UnionTypes.h"
 
 class nsPIDOMWindow;
 
 namespace mozilla {
 namespace dom {
 
 class Headers;
+class InternalHeaders;
 class Promise;
 
 class Request MOZ_FINAL : public nsISupports
                         , public nsWrapperCache
+                        , public FetchBody<Request>
 {
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Request)
 
 public:
   Request(nsIGlobalObject* aOwner, InternalRequest* aRequest);
 
   JSObject*
@@ -69,73 +72,46 @@ public:
       aReferrer.AsAString() = EmptyString();
       return;
     }
 
     // FIXME(nsm): Spec doesn't say what to do if referrer is client.
     aReferrer.AsAString() = NS_ConvertUTF8toUTF16(mRequest->mReferrerURL);
   }
 
-  Headers* Headers_() const { return mRequest->Headers_(); }
+  InternalHeaders*
+  GetInternalHeaders() const
+  {
+    return mRequest->Headers();
+  }
+
+  Headers* Headers_();
+
+  void
+  GetBody(nsIInputStream** aStream) { return mRequest->GetBody(aStream); }
 
   static already_AddRefed<Request>
   Constructor(const GlobalObject& aGlobal, const RequestOrScalarValueString& aInput,
               const RequestInit& aInit, ErrorResult& rv);
 
-  nsISupports* GetParentObject() const
+  nsIGlobalObject* GetParentObject() const
   {
     return mOwner;
   }
 
   already_AddRefed<Request>
   Clone() const;
 
-  already_AddRefed<Promise>
-  ArrayBuffer(ErrorResult& aRv);
-
-  already_AddRefed<Promise>
-  Blob(ErrorResult& aRv);
-
-  already_AddRefed<Promise>
-  Json(ErrorResult& aRv);
-
-  already_AddRefed<Promise>
-  Text(ErrorResult& aRv);
-
-  bool
-  BodyUsed() const
-  {
-    return mBodyUsed;
-  }
-
   already_AddRefed<InternalRequest>
   GetInternalRequest();
 private:
-  enum ConsumeType
-  {
-    CONSUME_ARRAYBUFFER,
-    CONSUME_BLOB,
-    // FormData not supported right now,
-    CONSUME_JSON,
-    CONSUME_TEXT,
-  };
-
   ~Request();
 
-  already_AddRefed<Promise>
-  ConsumeBody(ConsumeType aType, ErrorResult& aRv);
-
-  void
-  SetBodyUsed()
-  {
-    mBodyUsed = true;
-  }
-
   nsCOMPtr<nsIGlobalObject> mOwner;
   nsRefPtr<InternalRequest> mRequest;
-  bool mBodyUsed;
-  nsCString mMimeType;
+  // Lazily created.
+  nsRefPtr<Headers> mHeaders;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_Request_h
--- a/dom/fetch/Response.cpp
+++ b/dom/fetch/Response.cpp
@@ -1,138 +1,163 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "Response.h"
-#include "nsDOMString.h"
+
+#include "nsISupportsImpl.h"
+#include "nsIURI.h"
 #include "nsPIDOMWindow.h"
-#include "nsIURI.h"
-#include "nsISupportsImpl.h"
 
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/Headers.h"
 #include "mozilla/dom/Promise.h"
 
+#include "nsDOMString.h"
+
+#include "InternalResponse.h"
+
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(Response)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(Response)
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Response, mOwner)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Response, mOwner, mHeaders)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Response)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
-Response::Response(nsISupports* aOwner)
-  : mOwner(aOwner)
-  , mHeaders(new Headers(aOwner))
+Response::Response(nsIGlobalObject* aGlobal, InternalResponse* aInternalResponse)
+  : FetchBody<Response>()
+  , mOwner(aGlobal)
+  , mInternalResponse(aInternalResponse)
 {
 }
 
 Response::~Response()
 {
 }
 
 /* static */ already_AddRefed<Response>
 Response::Error(const GlobalObject& aGlobal)
 {
-  ErrorResult result;
-  ResponseInit init;
-  init.mStatus = 0;
-  Optional<ArrayBufferOrArrayBufferViewOrScalarValueStringOrURLSearchParams> body;
-  nsRefPtr<Response> r = Response::Constructor(aGlobal, body, init, result);
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
+  nsRefPtr<InternalResponse> error = InternalResponse::NetworkError();
+  nsRefPtr<Response> r = new Response(global, error);
   return r.forget();
 }
 
 /* static */ already_AddRefed<Response>
 Response::Redirect(const GlobalObject& aGlobal, const nsAString& aUrl,
                    uint16_t aStatus)
 {
   ErrorResult result;
   ResponseInit init;
-  Optional<ArrayBufferOrArrayBufferViewOrScalarValueStringOrURLSearchParams> body;
+  Optional<ArrayBufferOrArrayBufferViewOrBlobOrScalarValueStringOrURLSearchParams> body;
   nsRefPtr<Response> r = Response::Constructor(aGlobal, body, init, result);
   return r.forget();
 }
 
 /*static*/ already_AddRefed<Response>
-Response::Constructor(const GlobalObject& global,
-                      const Optional<ArrayBufferOrArrayBufferViewOrScalarValueStringOrURLSearchParams>& aBody,
-                      const ResponseInit& aInit, ErrorResult& rv)
-{
-  nsRefPtr<Response> response = new Response(global.GetAsSupports());
-  return response.forget();
-}
-
-already_AddRefed<Response>
-Response::Clone()
-{
-  nsRefPtr<Response> response = new Response(mOwner);
-  return response.forget();
-}
-
-already_AddRefed<Promise>
-Response::ArrayBuffer(ErrorResult& aRv)
+Response::Constructor(const GlobalObject& aGlobal,
+                      const Optional<ArrayBufferOrArrayBufferViewOrBlobOrScalarValueStringOrURLSearchParams>& aBody,
+                      const ResponseInit& aInit, ErrorResult& aRv)
 {
-  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
-  MOZ_ASSERT(global);
-  nsRefPtr<Promise> promise = Promise::Create(global, aRv);
-  if (aRv.Failed()) {
-    return nullptr;
-  }
-
-  promise->MaybeReject(NS_ERROR_NOT_AVAILABLE);
-  return promise.forget();
-}
-
-already_AddRefed<Promise>
-Response::Blob(ErrorResult& aRv)
-{
-  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
-  MOZ_ASSERT(global);
-  nsRefPtr<Promise> promise = Promise::Create(global, aRv);
-  if (aRv.Failed()) {
+  if (aInit.mStatus < 200 || aInit.mStatus > 599) {
+    aRv.Throw(NS_ERROR_RANGE_ERR);
     return nullptr;
   }
 
-  promise->MaybeReject(NS_ERROR_NOT_AVAILABLE);
-  return promise.forget();
-}
-
-already_AddRefed<Promise>
-Response::Json(ErrorResult& aRv)
-{
-  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
-  MOZ_ASSERT(global);
-  nsRefPtr<Promise> promise = Promise::Create(global, aRv);
-  if (aRv.Failed()) {
-    return nullptr;
+  nsCString statusText;
+  if (aInit.mStatusText.WasPassed()) {
+    statusText = aInit.mStatusText.Value();
+    nsACString::const_iterator start, end;
+    statusText.BeginReading(start);
+    statusText.EndReading(end);
+    if (FindCharInReadable('\r', start, end)) {
+      aRv.ThrowTypeError(MSG_RESPONSE_INVALID_STATUSTEXT_ERROR);
+      return nullptr;
+    }
+    // Reset iterator since FindCharInReadable advances it.
+    statusText.BeginReading(start);
+    if (FindCharInReadable('\n', start, end)) {
+      aRv.ThrowTypeError(MSG_RESPONSE_INVALID_STATUSTEXT_ERROR);
+      return nullptr;
+    }
+  } else {
+    // Since we don't support default values for ByteString.
+    statusText = NS_LITERAL_CSTRING("OK");
   }
 
-  promise->MaybeReject(NS_ERROR_NOT_AVAILABLE);
-  return promise.forget();
+  nsRefPtr<InternalResponse> internalResponse =
+    new InternalResponse(aInit.mStatus, statusText);
+
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
+  nsRefPtr<Response> r = new Response(global, internalResponse);
+
+  if (aInit.mHeaders.WasPassed()) {
+    internalResponse->Headers()->Clear();
+
+    // Instead of using Fill, create an object to allow the constructor to
+    // unwrap the HeadersInit.
+    nsRefPtr<Headers> headers =
+      Headers::Constructor(aGlobal, aInit.mHeaders.Value(), aRv);
+    if (aRv.Failed()) {
+      return nullptr;
+    }
+
+    internalResponse->Headers()->Fill(*headers->GetInternalHeaders(), aRv);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return nullptr;
+    }
+  }
+
+  if (aBody.WasPassed()) {
+    nsCOMPtr<nsIInputStream> bodyStream;
+    nsCString contentType;
+    aRv = ExtractByteStreamFromBody(aBody.Value(), getter_AddRefs(bodyStream), contentType);
+    internalResponse->SetBody(bodyStream);
+
+    if (!contentType.IsVoid() &&
+        !internalResponse->Headers()->Has(NS_LITERAL_CSTRING("Content-Type"), aRv)) {
+      internalResponse->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"), contentType, aRv);
+    }
+
+    if (aRv.Failed()) {
+      return nullptr;
+    }
+  }
+
+  r->SetMimeType(aRv);
+  return r.forget();
 }
 
-already_AddRefed<Promise>
-Response::Text(ErrorResult& aRv)
+// FIXME(nsm): Bug 1073231: This is currently unspecced!
+already_AddRefed<Response>
+Response::Clone()
 {
-  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
-  MOZ_ASSERT(global);
-  nsRefPtr<Promise> promise = Promise::Create(global, aRv);
-  if (aRv.Failed()) {
-    return nullptr;
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mOwner);
+  nsRefPtr<Response> response = new Response(global, mInternalResponse);
+  return response.forget();
+}
+
+void
+Response::SetBody(nsIInputStream* aBody)
+{
+  // FIXME(nsm): Do we flip bodyUsed here?
+  mInternalResponse->SetBody(aBody);
+}
+
+Headers*
+Response::Headers_()
+{
+  if (!mHeaders) {
+    mHeaders = new Headers(mOwner, mInternalResponse->Headers());
   }
 
-  promise->MaybeReject(NS_ERROR_NOT_AVAILABLE);
-  return promise.forget();
-}
-
-bool
-Response::BodyUsed()
-{
-  return false;
+  return mHeaders;
 }
 } // namespace dom
 } // namespace mozilla
--- a/dom/fetch/Response.h
+++ b/dom/fetch/Response.h
@@ -4,105 +4,110 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_Response_h
 #define mozilla_dom_Response_h
 
 #include "nsWrapperCache.h"
 #include "nsISupportsImpl.h"
 
+#include "mozilla/dom/Fetch.h"
 #include "mozilla/dom/ResponseBinding.h"
 #include "mozilla/dom/UnionTypes.h"
 
+#include "InternalResponse.h"
+
 class nsPIDOMWindow;
 
 namespace mozilla {
 namespace dom {
 
 class Headers;
+class InternalHeaders;
 class Promise;
 
 class Response MOZ_FINAL : public nsISupports
                          , public nsWrapperCache
+                         , public FetchBody<Response>
 {
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Response)
 
 public:
-  explicit Response(nsISupports* aOwner);
+  Response(nsIGlobalObject* aGlobal, InternalResponse* aInternalResponse);
+
+  Response(const Response& aOther) MOZ_DELETE;
 
   JSObject*
   WrapObject(JSContext* aCx)
   {
     return ResponseBinding::Wrap(aCx, this);
   }
 
   ResponseType
   Type() const
   {
-    return ResponseType::Error;
+    return mInternalResponse->Type();
   }
 
   void
   GetUrl(DOMString& aUrl) const
   {
-    aUrl.AsAString() = EmptyString();
+    aUrl.AsAString() = NS_ConvertUTF8toUTF16(mInternalResponse->GetUrl());
   }
 
   uint16_t
   Status() const
   {
-    return 400;
+    return mInternalResponse->GetStatus();
   }
 
   void
   GetStatusText(nsCString& aStatusText) const
   {
-    aStatusText = EmptyCString();
+    aStatusText = mInternalResponse->GetStatusText();
   }
 
-  Headers*
-  Headers_() const { return mHeaders; }
+  InternalHeaders*
+  GetInternalHeaders() const
+  {
+    return mInternalResponse->Headers();
+  }
+
+  Headers* Headers_();
+
+  void
+  GetBody(nsIInputStream** aStream) { return mInternalResponse->GetBody(aStream); }
 
   static already_AddRefed<Response>
   Error(const GlobalObject& aGlobal);
 
   static already_AddRefed<Response>
   Redirect(const GlobalObject& aGlobal, const nsAString& aUrl, uint16_t aStatus);
 
   static already_AddRefed<Response>
   Constructor(const GlobalObject& aGlobal,
-              const Optional<ArrayBufferOrArrayBufferViewOrScalarValueStringOrURLSearchParams>& aBody,
+              const Optional<ArrayBufferOrArrayBufferViewOrBlobOrScalarValueStringOrURLSearchParams>& aBody,
               const ResponseInit& aInit, ErrorResult& rv);
 
-  nsISupports* GetParentObject() const
+  nsIGlobalObject* GetParentObject() const
   {
     return mOwner;
   }
 
   already_AddRefed<Response>
   Clone();
 
-  already_AddRefed<Promise>
-  ArrayBuffer(ErrorResult& aRv);
-
-  already_AddRefed<Promise>
-  Blob(ErrorResult& aRv);
-
-  already_AddRefed<Promise>
-  Json(ErrorResult& aRv);
-
-  already_AddRefed<Promise>
-  Text(ErrorResult& aRv);
-
-  bool
-  BodyUsed();
+  void
+  SetBody(nsIInputStream* aBody);
 private:
   ~Response();
 
-  nsCOMPtr<nsISupports> mOwner;
+  nsCOMPtr<nsIGlobalObject> mOwner;
+  nsRefPtr<InternalResponse> mInternalResponse;
+  // Lazily created
   nsRefPtr<Headers> mHeaders;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_Response_h
--- a/dom/fetch/moz.build
+++ b/dom/fetch/moz.build
@@ -2,25 +2,29 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXPORTS.mozilla.dom += [
     'Fetch.h',
     'Headers.h',
+    'InternalHeaders.h',
     'InternalRequest.h',
+    'InternalResponse.h',
     'Request.h',
     'Response.h',
 ]
 
 UNIFIED_SOURCES += [
     'Fetch.cpp',
     'Headers.cpp',
+    'InternalHeaders.cpp',
     'InternalRequest.cpp',
+    'InternalResponse.cpp',
     'Request.cpp',
     'Response.cpp',
 ]
 
 LOCAL_INCLUDES += [
     '../workers',
 ]
 
--- a/dom/notification/Notification.cpp
+++ b/dom/notification/Notification.cpp
@@ -19,16 +19,17 @@
 #include "nsServiceManagerUtils.h"
 #include "nsStructuredCloneContainer.h"
 #include "nsToolkitCompsCID.h"
 #include "nsGlobalWindow.h"
 #include "nsDOMJSUtils.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIXPConnect.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
+#include "mozilla/dom/Event.h"
 #include "mozilla/Services.h"
 #include "nsContentPermissionHelper.h"
 #ifdef MOZ_B2G
 #include "nsIDOMDesktopNotification.h"
 #endif
 
 namespace mozilla {
 namespace dom {
@@ -355,29 +356,41 @@ NotificationTask::Run()
 
 NS_IMPL_ISUPPORTS(NotificationObserver, nsIObserver)
 
 NS_IMETHODIMP
 NotificationObserver::Observe(nsISupports* aSubject, const char* aTopic,
                               const char16_t* aData)
 {
   nsCOMPtr<nsPIDOMWindow> window = mNotification->GetOwner();
-  if (!window) {
+  if (!window || !window->IsCurrentInnerWindow()) {
     // Window has been closed, this observer is not valid anymore
     return NS_ERROR_FAILURE;
   }
 
   if (!strcmp("alertclickcallback", aTopic)) {
-    nsIDocument* doc = window ? window->GetExtantDoc() : nullptr;
-    if (doc) {
-      nsContentUtils::DispatchChromeEvent(doc, window,
-                                          NS_LITERAL_STRING("DOMWebNotificationClicked"),
-                                          true, true);
+
+    nsCOMPtr<nsIDOMEvent> event;
+    NS_NewDOMEvent(getter_AddRefs(event), mNotification, nullptr, nullptr);
+    nsresult rv = event->InitEvent(NS_LITERAL_STRING("click"), false, true);
+    NS_ENSURE_SUCCESS(rv, rv);
+    event->SetTrusted(true);
+    WantsPopupControlCheck popupControlCheck(event);
+    bool doDefaultAction = true;
+    mNotification->DispatchEvent(event, &doDefaultAction);
+    if (doDefaultAction) {
+      nsIDocument* doc = window ? window->GetExtantDoc() : nullptr;
+      if (doc) {
+        // Browser UI may use DOMWebNotificationClicked to focus the tab
+        // from which the event was dispatched.
+        nsContentUtils::DispatchChromeEvent(doc, window->GetOuterWindow(),
+                                            NS_LITERAL_STRING("DOMWebNotificationClicked"),
+                                            true, true);
+      }
     }
-    mNotification->DispatchTrustedEvent(NS_LITERAL_STRING("click"));
   } else if (!strcmp("alertfinished", aTopic)) {
     nsCOMPtr<nsINotificationStorage> notificationStorage =
       do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID);
     if (notificationStorage && mNotification->IsStored()) {
       nsString origin;
       nsresult rv = Notification::GetOrigin(mNotification->GetOwner(), origin);
       if (NS_SUCCEEDED(rv)) {
         nsString id;
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -2852,17 +2852,16 @@ nsresult nsPluginHost::NewPluginURLStrea
   // form |nsDocShell::OnLinkClickSync| bug 166613
   nsCOMPtr<nsIChannel> channel;
   rv = NS_NewChannelInternal(getter_AddRefs(channel),
                              url,
                              doc,
                              principal,
                              nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
                              nsIContentPolicy::TYPE_OBJECT_SUBREQUEST,
-                             nullptr,  // aChannelPolicy
                              nullptr,  // aLoadGroup 
                              listenerPeer);
 
   if (NS_FAILED(rv))
     return rv;
 
   if (doc) {
     // And if it's a script allow it to execute against the
--- a/dom/plugins/base/nsPluginStreamListenerPeer.cpp
+++ b/dom/plugins/base/nsPluginStreamListenerPeer.cpp
@@ -657,17 +657,16 @@ nsPluginStreamListenerPeer::RequestRead(
 
   nsCOMPtr<nsIChannel> channel;
   rv = NS_NewChannelInternal(getter_AddRefs(channel),
                              mURL,
                              doc,
                              principal,
                              nsILoadInfo::SEC_NORMAL,
                              nsIContentPolicy::TYPE_OTHER,
-                             nullptr,   // aChannelPolicy
                              loadGroup,
                              callbacks);
 
   if (NS_FAILED(rv))
     return rv;
 
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
   if (!httpChannel)
--- a/dom/system/gonk/AudioManager.cpp
+++ b/dom/system/gonk/AudioManager.cpp
@@ -830,16 +830,21 @@ AudioManager::SetStreamVolumeIndex(int32
   status += AudioSystem::setStreamVolumeIndex(
               static_cast<audio_stream_type_t>(aStream),
               aIndex,
               AUDIO_DEVICE_OUT_WIRED_HEADPHONE);
   status += AudioSystem::setStreamVolumeIndex(
               static_cast<audio_stream_type_t>(aStream),
               aIndex,
               AUDIO_DEVICE_OUT_EARPIECE);
+  status += AudioSystem::setStreamVolumeIndex(
+              static_cast<audio_stream_type_t>(aStream),
+              aIndex,
+              AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET);
+
   return status ? NS_ERROR_FAILURE : NS_OK;
 #endif
 }
 
 nsresult
 AudioManager::GetStreamVolumeIndex(int32_t aStream, int32_t *aIndex) {
   if (!aIndex) {
     return NS_ERROR_INVALID_ARG;
--- a/dom/webidl/Fetch.webidl
+++ b/dom/webidl/Fetch.webidl
@@ -3,20 +3,19 @@
  * 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/.
  *
  * The origin of this IDL file is
  * http://fetch.spec.whatwg.org/
  */
 
 typedef object JSON;
-// FIXME(nsm): Bug 1071290: Blobs can't be passed as unions in workers.
 // FIXME(nsm): Bug 739173: FormData is not available in workers.
 // typedef (ArrayBuffer or ArrayBufferView or Blob or FormData or ScalarValueString or URLSearchParams) BodyInit;
-typedef (ArrayBuffer or ArrayBufferView or ScalarValueString or URLSearchParams) BodyInit;
+typedef (ArrayBuffer or ArrayBufferView or Blob or ScalarValueString or URLSearchParams) BodyInit;
 
 [NoInterfaceObject, Exposed=(Window,Worker)]
 interface Body {
   readonly attribute boolean bodyUsed;
   [Throws]
   Promise<ArrayBuffer> arrayBuffer();
   [Throws]
   Promise<Blob> blob();
--- a/dom/webidl/SubtleCrypto.webidl
+++ b/dom/webidl/SubtleCrypto.webidl
@@ -82,16 +82,19 @@ dictionary AesDerivedKeyParams : Algorit
 dictionary HmacDerivedKeyParams : HmacImportParams {
   [EnforceRange] unsigned long length;
 };
 
 dictionary EcdhKeyDeriveParams : Algorithm {
   required CryptoKey public;
 };
 
+dictionary EcdsaParams : Algorithm {
+  required AlgorithmIdentifier hash;
+};
 
 /***** JWK *****/
 
 dictionary RsaOtherPrimesInfo {
   // The following fields are defined in Section 6.3.2.7 of JSON Web Algorithms
   required DOMString r;
   required DOMString d;
   required DOMString t;
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -1,28 +1,26 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
 /* 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 "ScriptLoader.h"
 
 #include "nsIChannel.h"
-#include "nsIChannelPolicy.h"
 #include "nsIContentPolicy.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIHttpChannel.h"
 #include "nsIIOService.h"
 #include "nsIProtocolHandler.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIStreamLoader.h"
 #include "nsIURI.h"
 
 #include "jsapi.h"
-#include "nsChannelPolicy.h"
 #include "nsError.h"
 #include "nsContentPolicyUtils.h"
 #include "nsContentUtils.h"
 #include "nsDocShellCID.h"
 #include "nsISupportsPrimitives.h"
 #include "nsNetUtil.h"
 #include "nsScriptLoader.h"
 #include "nsString.h"
@@ -99,61 +97,42 @@ ChannelFromScriptURL(nsIPrincipal* princ
     rv = principal->CheckMayLoad(uri, false, true);
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SECURITY_ERR);
   }
   else {
     rv = secMan->CheckLoadURIWithPrincipal(principal, uri, 0);
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SECURITY_ERR);
   }
 
-  // Get Content Security Policy from parent document to pass into channel.
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  rv = principal->GetCsp(getter_AddRefs(csp));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsIChannelPolicy> channelPolicy;
-  if (csp) {
-    channelPolicy = do_CreateInstance(NSCHANNELPOLICY_CONTRACTID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = channelPolicy->SetContentSecurityPolicy(csp);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = channelPolicy->SetLoadType(nsIContentPolicy::TYPE_SCRIPT);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
   uint32_t flags = nsIRequest::LOAD_NORMAL | nsIChannel::LOAD_CLASSIFY_URI;
 
   nsCOMPtr<nsIChannel> channel;
   // If we have the document, use it
   if (parentDoc) {
     rv = NS_NewChannel(getter_AddRefs(channel),
                        uri,
                        parentDoc,
                        nsILoadInfo::SEC_NORMAL,
                        nsIContentPolicy::TYPE_SCRIPT,
-                       channelPolicy,
                        loadGroup,
                        nullptr, // aCallbacks
                        flags,
                        ios);
   } else {
     // we should use 'principal' here; needs to be fixed before
     // we move security checks to AsyncOpen. We use nullPrincipal
     // for now, because the loadGroup is null and hence causes
     // GetChannelUriPrincipal to return the wrong principal.
     nsCOMPtr<nsIPrincipal> nullPrincipal =
       do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
     rv = NS_NewChannel(getter_AddRefs(channel),
                        uri,
                        nullPrincipal,
                        nsILoadInfo::SEC_NORMAL,
                        nsIContentPolicy::TYPE_SCRIPT,
-                       channelPolicy,
                        loadGroup,
                        nullptr, // aCallbacks
                        flags,
                        ios);
   }
 
   NS_ENSURE_SUCCESS(rv, rv);
 
--- a/dom/workers/test/fetch/mochitest.ini
+++ b/dom/workers/test/fetch/mochitest.ini
@@ -1,7 +1,9 @@
 [DEFAULT]
 support-files =
   worker_interfaces.js
   worker_test_request.js
+  worker_test_response.js
 
 [test_interfaces.html]
 [test_request.html]
+[test_response.html]
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/fetch/test_response.html
@@ -0,0 +1,48 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 1039846 - Test Response object in worker</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+  function runTest() {
+    var worker = new Worker("worker_test_response.js");
+    worker.onmessage = function(event) {
+
+      if (event.data.type == 'finish') {
+        SimpleTest.finish();
+      } else if (event.data.type == 'status') {
+        ok(event.data.status, event.data.msg);
+      }
+    }
+
+    worker.onerror = function(event) {
+      ok(false, "Worker had an error: " + event.message + " at " + event.lineno);
+      SimpleTest.finish();
+    };
+
+    worker.postMessage(true);
+  }
+
+  SimpleTest.waitForExplicitFinish();
+
+  SpecialPowers.pushPrefEnv({"set": [
+    ["dom.fetch.enabled", true]
+  ]}, function() {
+    runTest();
+  });
+</script>
+</pre>
+</body>
+</html>
+
--- a/dom/workers/test/fetch/worker_test_request.js
+++ b/dom/workers/test/fetch/worker_test_request.js
@@ -126,17 +126,16 @@ function testBodyUsed() {
     return req.blob().then((v) => {
       ok(false, "Attempting to read body again should fail.");
     }, (e) => {
       ok(true, "Attempting to read body again should fail.");
     })
   });
 }
 
-// FIXME(nsm): Bug 1071290: We can't use Blobs as the body yet.
 function testBodyCreation() {
   var text = "κόσμε";
   var req1 = new Request("", { body: text });
   var p1 = req1.text().then(function(v) {
     ok(typeof v === "string", "Should resolve to string");
     is(text, v, "Extracted string should match");
   });
 
@@ -145,29 +144,34 @@ function testBodyCreation() {
     is("Hello", v, "Extracted string should match");
   });
 
   var req2b = new Request("", { body: (new Uint8Array([72, 101, 108, 108, 111])).buffer });
   var p2b = req2b.text().then(function(v) {
     is("Hello", v, "Extracted string should match");
   });
 
+  var reqblob = new Request("", { body: new Blob([text]) });
+  var pblob = reqblob.text().then(function(v) {
+    is(v, text, "Extracted string should match");
+  });
+
   var params = new URLSearchParams();
   params.append("item", "Geckos");
   params.append("feature", "stickyfeet");
   params.append("quantity", "700");
   var req3 = new Request("", { body: params });
   var p3 = req3.text().then(function(v) {
     var extracted = new URLSearchParams(v);
     is(extracted.get("item"), "Geckos", "Param should match");
     is(extracted.get("feature"), "stickyfeet", "Param should match");
     is(extracted.get("quantity"), "700", "Param should match");
   });
 
-  return Promise.all([p1, p2, p2b, p3]);
+  return Promise.all([p1, p2, p2b, pblob, p3]);
 }
 
 function testBodyExtraction() {
   var text = "κόσμε";
   var newReq = function() { return new Request("", { body: text }); }
   return newReq().text().then(function(v) {
     ok(typeof v === "string", "Should resolve to string");
     is(text, v, "Extracted string should match");
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/fetch/worker_test_response.js
@@ -0,0 +1,128 @@
+function ok(a, msg) {
+  dump("OK: " + !!a + "  =>  " + a + " " + msg + "\n");
+  postMessage({type: 'status', status: !!a, msg: a + ": " + msg });
+}
+
+function is(a, b, msg) {
+  dump("IS: " + (a===b) + "  =>  " + a + " | " + b + " " + msg + "\n");
+  postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg });
+}
+
+function testDefaultCtor() {
+  var res = new Response();
+  is(res.type, "default", "Default Response type is default");
+  ok(res.headers instanceof Headers, "Response should have non-null Headers object");
+  is(res.url, "", "URL should be empty string");
+  is(res.status, 200, "Default status is 200");
+  is(res.statusText, "OK", "Default statusText is OK");
+}
+
+function testClone() {
+  var res = (new Response("This is a body", {
+              status: 404,
+              statusText: "Not Found",
+              headers: { "Content-Length": 5 },
+            })).clone();
+  is(res.status, 404, "Response status is 404");
+  is(res.statusText, "Not Found", "Response statusText is POST");
+  ok(res.headers instanceof Headers, "Response should have non-null Headers object");
+  is(res.headers.get('content-length'), "5", "Response content-length should be 5.");
+}
+
+function testBodyUsed() {
+  var res = new Response("Sample body");
+  ok(!res.bodyUsed, "bodyUsed is initially false.");
+  return res.text().then((v) => {
+    is(v, "Sample body", "Body should match");
+    ok(res.bodyUsed, "After reading body, bodyUsed should be true.");
+  }).then(() => {
+    return res.blob().then((v) => {
+      ok(false, "Attempting to read body again should fail.");
+    }, (e) => {
+      ok(true, "Attempting to read body again should fail.");
+    })
+  });
+}
+
+function testBodyCreation() {
+  var text = "κόσμε";
+  var res1 = new Response(text);
+  var p1 = res1.text().then(function(v) {
+    ok(typeof v === "string", "Should resolve to string");
+    is(text, v, "Extracted string should match");
+  });
+
+  var res2 = new Response(new Uint8Array([72, 101, 108, 108, 111]));
+  var p2 = res2.text().then(function(v) {
+    is("Hello", v, "Extracted string should match");
+  });
+
+  var res2b = new Response((new Uint8Array([72, 101, 108, 108, 111])).buffer);
+  var p2b = res2b.text().then(function(v) {
+    is("Hello", v, "Extracted string should match");
+  });
+
+  var resblob = new Response(new Blob([text]));
+  var pblob = resblob.text().then(function(v) {
+    is(v, text, "Extracted string should match");
+  });
+
+  var params = new URLSearchParams();
+  params.append("item", "Geckos");
+  params.append("feature", "stickyfeet");
+  params.append("quantity", "700");
+  var res3 = new Response(params);
+  var p3 = res3.text().then(function(v) {
+    var extracted = new URLSearchParams(v);
+    is(extracted.get("item"), "Geckos", "Param should match");
+    is(extracted.get("feature"), "stickyfeet", "Param should match");
+    is(extracted.get("quantity"), "700", "Param should match");
+  });
+
+  return Promise.all([p1, p2, p2b, pblob, p3]);
+}
+
+function testBodyExtraction() {
+  var text = "κόσμε";
+  var newRes = function() { return new Response(text); }
+  return newRes().text().then(function(v) {
+    ok(typeof v === "string", "Should resolve to string");
+    is(text, v, "Extracted string should match");
+  }).then(function() {
+    return newRes().blob().then(function(v) {
+      ok(v instanceof Blob, "Should resolve to Blob");
+      var fs = new FileReaderSync();
+      is(fs.readAsText(v), text, "Decoded Blob should match original");
+    });
+  }).then(function() {
+    return newRes().json().then(function(v) {
+      ok(false, "Invalid json should reject");
+    }, function(e) {
+      ok(true, "Invalid json should reject");
+    })
+  }).then(function() {
+    return newRes().arrayBuffer().then(function(v) {
+      ok(v instanceof ArrayBuffer, "Should resolve to ArrayBuffer");
+      var dec = new TextDecoder();
+      is(dec.decode(new Uint8Array(v)), text, "UTF-8 decoded ArrayBuffer should match original");
+    });
+  })
+}
+
+onmessage = function() {
+  var done = function() { postMessage({ type: 'finish' }) }
+
+  testDefaultCtor();
+  testClone();
+
+  Promise.resolve()
+    .then(testBodyCreation)
+    .then(testBodyUsed)
+    .then(testBodyExtraction)
+    // Put more promise based tests here.
+    .then(done)
+    .catch(function(e) {
+      ok(false, "Some Response tests failed " + e);
+      done();
+    })
+}
--- a/dom/xbl/nsXBLService.cpp
+++ b/dom/xbl/nsXBLService.cpp
@@ -1071,17 +1071,16 @@ nsXBLService::FetchBindingDocument(nsICo
   // Note that we are calling NS_NewChannelInternal here with both a node and a principal.
   // This is because the principal and node could be different.
   rv = NS_NewChannelInternal(getter_AddRefs(channel),
                              aDocumentURI,
                              aBoundDocument,
                              requestingPrincipal,
                              nsILoadInfo::SEC_NORMAL,
                              nsIContentPolicy::TYPE_OTHER,
-                             nullptr,   // aChannelPolicy
                              loadGroup);
 
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIInterfaceRequestor> sameOriginChecker = nsContentUtils::GetSameOriginChecker();
   NS_ENSURE_TRUE(sameOriginChecker, NS_ERROR_OUT_OF_MEMORY);
 
   channel->SetNotificationCallbacks(sameOriginChecker);
--- a/dom/xml/XMLDocument.cpp
+++ b/dom/xml/XMLDocument.cpp
@@ -444,17 +444,16 @@ XMLDocument::Load(const nsAString& aUrl,
   // nsIRequest::LOAD_BACKGROUND prevents throbber from becoming active,
   // which in turn keeps STOP button from becoming active  
   rv = NS_NewChannel(getter_AddRefs(channel),
                      uri,
                      callingDoc ? callingDoc.get() :
                                   static_cast<nsIDocument*>(this),
                      nsILoadInfo::SEC_NORMAL,
                      nsIContentPolicy::TYPE_XMLHTTPREQUEST,
-                     nullptr,   // aChannelPolicy
                      loadGroup,
                      req,
                      nsIRequest::LOAD_BACKGROUND);
 
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return false;
   }
--- a/dom/xslt/base/txURIUtils.cpp
+++ b/dom/xslt/base/txURIUtils.cpp
@@ -59,17 +59,16 @@ URIUtils::ResetWithSource(nsIDocument *a
     nsCOMPtr<nsIChannel> channel = sourceDoc->GetChannel();
     if (!channel) {
         // Need to synthesize one
         nsresult rv = NS_NewChannel(getter_AddRefs(channel),
                                     sourceDoc->GetDocumentURI(),
                                     sourceDoc,
                                     nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
                                     nsIContentPolicy::TYPE_OTHER,
-                                    nullptr,   // aChannelPolicy
                                     loadGroup);
 
         if (NS_FAILED(rv)) {
             return;
         }
     }
     aNewDoc->Reset(channel, loadGroup);
     aNewDoc->SetPrincipal(sourcePrincipal);
--- a/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp
+++ b/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp
@@ -460,17 +460,16 @@ txCompileObserver::startLoad(nsIURI* aUr
     }
 
     nsCOMPtr<nsIChannel> channel;
     nsresult rv = NS_NewChannel(getter_AddRefs(channel),
                                 aUri,
                                 aReferrerPrincipal,
                                 nsILoadInfo::SEC_NORMAL,
                                 nsIContentPolicy::TYPE_STYLESHEET,
-                                nullptr,
                                 loadGroup);
 
     NS_ENSURE_SUCCESS(rv, rv);
 
     channel->SetContentType(NS_LITERAL_CSTRING("text/xml"));
 
     nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
     if (httpChannel) {
--- a/embedding/browser/nsContextMenuInfo.cpp
+++ b/embedding/browser/nsContextMenuInfo.cpp
@@ -18,17 +18,16 @@
 #include "nsIDOMWindow.h"
 #include "nsIDOMCSSStyleDeclaration.h"
 #include "nsIDOMCSSValue.h"
 #include "nsIDOMCSSPrimitiveValue.h"
 #include "nsNetUtil.h"
 #include "nsUnicharUtils.h"
 #include "nsIDocument.h"
 #include "nsIPrincipal.h"
-#include "nsIChannelPolicy.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIContentPolicy.h"
 #include "nsAutoPtr.h"
 #include "imgRequestProxy.h"
 
 
 //*****************************************************************************
 // class nsContextMenuInfo
@@ -263,32 +262,19 @@ nsContextMenuInfo::GetBackgroundImageReq
 
   nsCOMPtr<nsIDOMWindow> window;
   document->GetDefaultView(getter_AddRefs(window));
   NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
 
   nsCOMPtr<nsIDOMCSSPrimitiveValue> primitiveValue;
   nsAutoString bgStringValue;
 
-  // get Content Security Policy to pass to LoadImage
   nsCOMPtr<nsIDocument> doc(do_QueryInterface(document));
-  nsCOMPtr<nsIPrincipal> principal;
-  nsCOMPtr<nsIChannelPolicy> channelPolicy;
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  if (doc) {
-    principal = doc->NodePrincipal();
-    nsresult rv = principal->GetCsp(getter_AddRefs(csp));
-    NS_ENSURE_SUCCESS(rv, rv);
-    if (csp) {
-      channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
-      channelPolicy->SetContentSecurityPolicy(csp);
-      channelPolicy->SetLoadType(nsIContentPolicy::TYPE_IMAGE);
-    }
-  }
-  
+  nsCOMPtr<nsIPrincipal> principal = doc ? doc->NodePrincipal() : nullptr;
+
   while (true) {
     nsCOMPtr<nsIDOMElement> domElement(do_QueryInterface(domNode));
     // bail for the parent node of the root element or null argument
     if (!domElement)
       break;
     
     nsCOMPtr<nsIDOMCSSStyleDeclaration> computedStyle;
     window->GetComputedStyle(domElement, EmptyString(),
@@ -305,17 +291,17 @@ nsContextMenuInfo::GetBackgroundImageReq
           NS_NewURI(getter_AddRefs(bgUri), bgStringValue);
           NS_ENSURE_TRUE(bgUri, NS_ERROR_FAILURE);
 
           nsRefPtr<imgLoader> il = imgLoader::GetInstance();
           NS_ENSURE_TRUE(il, NS_ERROR_FAILURE);
 
           return il->LoadImage(bgUri, nullptr, nullptr, principal, nullptr,
                                nullptr, nullptr, nsIRequest::LOAD_NORMAL,
-                               nullptr, channelPolicy, EmptyString(), aRequest);
+                               nullptr, EmptyString(), aRequest);
         }
       }
 
       // bail if we encounter non-transparent background-color
       computedStyle->GetPropertyCSSValue(NS_LITERAL_STRING("background-color"),
                                          getter_AddRefs(cssValue));
       primitiveValue = do_QueryInterface(cssValue);
       if (primitiveValue) {
--- a/embedding/components/webbrowserpersist/nsWebBrowserPersist.cpp
+++ b/embedding/components/webbrowserpersist/nsWebBrowserPersist.cpp
@@ -1200,17 +1200,16 @@ nsresult nsWebBrowserPersist::SaveURIInt
 
     // Open a channel to the URI
     nsCOMPtr<nsIChannel> inputChannel;
     rv = NS_NewChannel(getter_AddRefs(inputChannel),
                        aURI,
                        nsContentUtils::GetSystemPrincipal(),
                        nsILoadInfo::SEC_NORMAL,
                        nsIContentPolicy::TYPE_OTHER,
-                       nullptr,  // aChannelPolicy
                        nullptr,  // aLoadGroup
                        static_cast<nsIInterfaceRequestor*>(this),
                        loadFlags);
 
     nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryInterface(inputChannel);
     if (pbChannel)
     {
         pbChannel->SetPrivate(aIsPrivate);
--- a/extensions/pref/autoconfig/src/nsAutoConfig.cpp
+++ b/extensions/pref/autoconfig/src/nsAutoConfig.cpp
@@ -276,17 +276,16 @@ nsresult nsAutoConfig::downloadAutoConfi
 
     PR_LOG(MCD, PR_LOG_DEBUG, ("running MCD url %s\n", mConfigURL.get()));
     // open a channel for the url
     rv = NS_NewChannel(getter_AddRefs(channel),
                        url,
                        nsContentUtils::GetSystemPrincipal(),
                        nsILoadInfo::SEC_NORMAL,
                        nsIContentPolicy::TYPE_OTHER,
-                       nullptr,  // aChannelPolicy
                        nullptr,  // loadGroup
                        nullptr,  // aCallbacks
                        nsIRequest::INHIBIT_PERSISTENT_CACHING |
                        nsIRequest::LOAD_BYPASS_CACHE);
 
     if (NS_FAILED(rv)) 
         return rv;
 
--- a/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -1270,17 +1270,24 @@ DestroyPixmap(void *data)
   delete static_cast<DestroyPixmapClosure*>(data);
 }
 #endif
 
 TemporaryRef<SourceSurface>
 DrawTargetCairo::OptimizeSourceSurface(SourceSurface *aSurface) const
 {
 #ifdef CAIRO_HAS_XLIB_SURFACE
-  if (cairo_surface_get_type(mSurface) != CAIRO_SURFACE_TYPE_XLIB) {
+  cairo_surface_type_t ctype = cairo_surface_get_type(mSurface);
+  if (aSurface->GetType() == SurfaceType::CAIRO &&
+      cairo_surface_get_type(
+        static_cast<SourceSurfaceCairo*>(aSurface)->GetSurface()) == ctype) {
+    return aSurface;
+  }
+
+  if (ctype != CAIRO_SURFACE_TYPE_XLIB) {
     return aSurface;
   }
 
   IntSize size = aSurface->GetSize();
   if (!size.width || !size.height) {
     return aSurface;
   }
 
--- a/gfx/2d/Matrix.h
+++ b/gfx/2d/Matrix.h
@@ -553,17 +553,17 @@ public:
    *   Matrix4x4::Translation(x, y) * this
    *
    * (Note that in performance critical code multiplying by the result of a
    * Translation()/Scaling() call is not recommended since that results in a
    * full matrix multiply involving 64 floating-point multiplications. Calling
    * this method would be preferred since it only involves 12 floating-point
    * multiplications.)
    */
-  Matrix4x4 &Translate(Float aX, Float aY, Float aZ)
+  Matrix4x4 &PreTranslate(Float aX, Float aY, Float aZ)
   {
     _41 += aX * _11 + aY * _21 + aZ * _31;
     _42 += aX * _12 + aY * _22 + aZ * _32;
     _43 += aX * _13 + aY * _23 + aZ * _33;
     _44 += aX * _14 + aY * _24 + aZ * _34;
 
     return *this;
   }
@@ -604,17 +604,17 @@ public:
                      0.0f, aScaleY, 0.0f, 0.0f,
                      0.0f, 0.0f, aScaleZ, 0.0f,
                      0.0f, 0.0f, 0.0f, 1.0f);
   }
 
   /**
    * Similar to PreTranslate, but applies a scale instead of a translation.
    */
-  Matrix4x4 &Scale(Float aX, Float aY, Float aZ)
+  Matrix4x4 &PreScale(Float aX, Float aY, Float aZ)
   {
     _11 *= aX;
     _12 *= aX;
     _13 *= aX;
     _21 *= aY;
     _22 *= aY;
     _23 *= aY;
     _31 *= aZ;
@@ -658,17 +658,17 @@ public:
   void SkewYZ(Float aSkew)
   {
       (*this)[2] += (*this)[1] * aSkew;
   }
 
   Matrix4x4 &ChangeBasis(Float aX, Float aY, Float aZ)
   {
     // Translate to the origin before applying this matrix
-    Translate(-aX, -aY, -aZ);
+    PreTranslate(-aX, -aY, -aZ);
 
     // Translate back into position after applying this matrix
     PostTranslate(aX, aY, aZ);
 
     return *this;
   }
 
   bool operator==(const Matrix4x4& o) const
@@ -772,52 +772,16 @@ public:
   {
       for (int i = 0; i < 4; i++) {
           for (int j = 0; j < 4; j++) {
               (*this)[i][j] /= (*this)[3][3];
          }
       }
   }
 
-  void ScalePost(Float aX, Float aY, Float aZ)
-  {
-    _11 *= aX;
-    _21 *= aX;
-    _31 *= aX;
-    _41 *= aX;
-
-    _12 *= aY;
-    _22 *= aY;
-    _32 *= aY;
-    _42 *= aY;
-
-    _13 *= aZ;
-    _23 *= aZ;
-    _33 *= aZ;
-    _43 *= aZ;
-  }
-
-  void TranslatePost(Float aX, Float aY, Float aZ)
-  {
-      _11 += _14 * aX;
-      _21 += _24 * aX;
-      _31 += _34 * aX;
-      _41 += _44 * aX;
-
-      _12 += _14 * aY;
-      _22 += _24 * aY;
-      _32 += _34 * aY;
-      _42 += _44 * aY;
-
-      _13 += _14 * aZ;
-      _23 += _24 * aZ;
-      _33 += _34 * aZ;
-      _43 += _44 * aZ;
-  }
-
   bool FuzzyEqual(const Matrix4x4& o) const
   {
     return gfx::FuzzyEqual(_11, o._11) && gfx::FuzzyEqual(_12, o._12) &&
            gfx::FuzzyEqual(_13, o._13) && gfx::FuzzyEqual(_14, o._14) &&
            gfx::FuzzyEqual(_21, o._21) && gfx::FuzzyEqual(_22, o._22) &&
            gfx::FuzzyEqual(_23, o._23) && gfx::FuzzyEqual(_24, o._24) &&
            gfx::FuzzyEqual(_31, o._31) && gfx::FuzzyEqual(_32, o._32) &&
            gfx::FuzzyEqual(_33, o._33) && gfx::FuzzyEqual(_34, o._34) &&
--- a/gfx/2d/PathHelpers.cpp
+++ b/gfx/2d/PathHelpers.cpp
@@ -157,11 +157,70 @@ AppendEllipseToPath(PathBuilder* aPathBu
 {
   Size halfDim = aDimensions / 2.0;
   Rect rect(aCenter - Point(halfDim.width, halfDim.height), aDimensions);
   Size radii[] = { halfDim, halfDim, halfDim, halfDim };
 
   AppendRoundedRectToPath(aPathBuilder, rect, radii);
 }
 
+bool
+SnapLineToDevicePixelsForStroking(Point& aP1, Point& aP2,
+                                  const DrawTarget& aDrawTarget)
+{
+  Matrix mat = aDrawTarget.GetTransform();
+  if (mat.HasNonTranslation()) {
+    return false;
+  }
+  if (aP1.x != aP2.x && aP1.y != aP2.y) {
+    return false; // not a horizontal or vertical line
+  }
+  Point p1 = aP1 + mat.GetTranslation(); // into device space
+  Point p2 = aP2 + mat.GetTranslation();
+  p1.Round();
+  p2.Round();
+  p1 -= mat.GetTranslation(); // back into user space
+  p2 -= mat.GetTranslation();
+  if (aP1.x == aP2.x) {
+    // snap vertical line, adding 0.5 to align it to be mid-pixel:
+    aP1 = p1 + Point(0.5, 0);
+    aP2 = p2 + Point(0.5, 0);
+  } else {
+    // snap horizontal line, adding 0.5 to align it to be mid-pixel:
+    aP1 = p1 + Point(0, 0.5);
+    aP2 = p2 + Point(0, 0.5);
+  }
+  return true;
+}
+
+void
+StrokeSnappedEdgesOfRect(const Rect& aRect, DrawTarget& aDrawTarget,
+                        const ColorPattern& aColor,
+                        const StrokeOptions& aStrokeOptions)
+{
+  if (aRect.IsEmpty()) {
+    return;
+  }
+
+  Point p1 = aRect.TopLeft();
+  Point p2 = aRect.BottomLeft();
+  SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget);
+  aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions);
+
+  p1 = aRect.BottomLeft();
+  p2 = aRect.BottomRight();
+  SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget);
+  aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions);
+
+  p1 = aRect.TopLeft();
+  p2 = aRect.TopRight();
+  SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget);
+  aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions);
+
+  p1 = aRect.TopRight();
+  p2 = aRect.BottomRight();
+  SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget);
+  aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions);
+}
+
 } // namespace gfx
 } // namespace mozilla
 
--- a/gfx/2d/PathHelpers.h
+++ b/gfx/2d/PathHelpers.h
@@ -151,16 +151,41 @@ GFX2D_API void AppendRoundedRectToPath(P
  *
  * The ellipse extends aDimensions.width / 2.0 in the horizontal direction
  * from aCenter, and aDimensions.height / 2.0 in the vertical direction.
  */
 GFX2D_API void AppendEllipseToPath(PathBuilder* aPathBuilder,
                                    const Point& aCenter,
                                    const Size& aDimensions);
 
+/**
+ * If aDrawTarget's transform only contains a translation, and if this line is
+ * a horizontal or vertical line, this function will snap the line's vertices
+ * to align with the device pixel grid so that stroking the line with a one
+ * pixel wide stroke will result in a crisp line that is not antialiased over
+ * two pixels across its width.
+ *
+ * @return Returns true if this function snaps aRect's vertices, else returns
+ *   false.
+ */
+GFX2D_API bool SnapLineToDevicePixelsForStroking(Point& aP1, Point& aP2,
+                                                 const DrawTarget& aDrawTarget);
+
+/**
+ * This function paints each edge of aRect separately, snapping the edges using
+ * SnapLineToDevicePixelsForStroking. Stroking the edges as separate paths
+ * helps ensure not only that the stroke spans a single row of device pixels if
+ * possible, but also that the ends of stroke dashes start and end on device
+ * pixels too.
+ */
+GFX2D_API void StrokeSnappedEdgesOfRect(const Rect& aRect,
+                                        DrawTarget& aDrawTarget,
+                                        const ColorPattern& aColor,
+                                        const StrokeOptions& aStrokeOptions);
+
 static inline bool
 UserToDevicePixelSnapped(Rect& aRect, const Matrix& aTransform)
 {
   Point p1 = aTransform * aRect.TopLeft();
   Point p2 = aTransform * aRect.TopRight();
   Point p3 = aTransform * aRect.BottomRight();
 
   // Check that the rectangle is axis-aligned. For an axis-aligned rectangle,
--- a/gfx/layers/ImageLayers.cpp
+++ b/gfx/layers/ImageLayers.cpp
@@ -33,18 +33,18 @@ void ImageLayer::ComputeEffectiveTransfo
   // Snap image edges to pixel boundaries
   gfxRect sourceRect(0, 0, 0, 0);
   if (mContainer) {
     sourceRect.SizeTo(gfx::ThebesIntSize(mContainer->GetCurrentSize()));
     if (mScaleMode != ScaleMode::SCALE_NONE &&
         sourceRect.width != 0.0 && sourceRect.height != 0.0) {
       NS_ASSERTION(mScaleMode == ScaleMode::STRETCH,
                    "No other scalemodes than stretch and none supported yet.");
-      local.Scale(mScaleToSize.width / sourceRect.width,
-                  mScaleToSize.height / sourceRect.height, 1.0);
+      local.PreScale(mScaleToSize.width / sourceRect.width,
+                     mScaleToSize.height / sourceRect.height, 1.0);
     }
   }
   // Snap our local transform first, and snap the inherited transform as well.
   // This makes our snapping equivalent to what would happen if our content
   // was drawn into a PaintedLayer (gfxContext would snap using the local
   // transform, then we'd snap again when compositing the PaintedLayer).
   mEffectiveTransform =
       SnapTransform(local, sourceRect, nullptr) *
--- a/gfx/layers/LayerTreeInvalidation.cpp
+++ b/gfx/layers/LayerTreeInvalidation.cpp
@@ -462,13 +462,13 @@ LayerPropertiesBase::ComputeDifferences(
     }
     return invalid;
   }
 }
 
 void
 LayerPropertiesBase::MoveBy(const nsIntPoint& aOffset)
 {
-  mTransform.TranslatePost(aOffset.x, aOffset.y, 0);
+  mTransform.PostTranslate(aOffset.x, aOffset.y, 0);
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -712,33 +712,33 @@ Layer::IsScrollInfoLayer() const
 }
 
 const Matrix4x4
 Layer::GetTransform() const
 {
   Matrix4x4 transform = mTransform;
   transform.PostScale(mPostXScale, mPostYScale, 1.0f);
   if (const ContainerLayer* c = AsContainerLayer()) {
-    transform.Scale(c->GetPreXScale(), c->GetPreYScale(), 1.0f);
+    transform.PreScale(c->GetPreXScale(), c->GetPreYScale(), 1.0f);
   }
   return transform;
 }
 
 const Matrix4x4
 Layer::GetLocalTransform()
 {
   Matrix4x4 transform;
   if (LayerComposite* shadow = AsLayerComposite())
     transform = shadow->GetShadowTransform();
   else
     transform = mTransform;
 
   transform.PostScale(mPostXScale, mPostYScale, 1.0f);
   if (ContainerLayer* c = AsContainerLayer()) {
-    transform.Scale(c->GetPreXScale(), c->GetPreYScale(), 1.0f);
+    transform.PreScale(c->GetPreXScale(), c->GetPreYScale(), 1.0f);
   }
 
   return transform;
 }
 
 void
 Layer::ApplyPendingUpdatesForThisTransaction()
 {
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -169,19 +169,19 @@ TranslateShadowLayer2D(Layer* aLayer,
   layerTransform._31 += aTranslation.x;
   layerTransform._32 += aTranslation.y;
 
   // The transform already takes the resolution scale into account.  Since we
   // will apply the resolution scale again when computing the effective
   // transform, we must apply the inverse resolution scale here.
   Matrix4x4 layerTransform3D = Matrix4x4::From2D(layerTransform);
   if (ContainerLayer* c = aLayer->AsContainerLayer()) {
-    layerTransform3D.Scale(1.0f/c->GetPreXScale(),
-                           1.0f/c->GetPreYScale(),
-                           1);
+    layerTransform3D.PreScale(1.0f/c->GetPreXScale(),
+                              1.0f/c->GetPreYScale(),
+                              1);
   }
   layerTransform3D.PostScale(1.0f/aLayer->GetPostXScale(),
                              1.0f/aLayer->GetPostYScale(),
                              1);
 
   LayerComposite* layerComposite = aLayer->AsLayerComposite();
   layerComposite->SetShadowTransform(layerTransform3D);
   layerComposite->SetShadowTransformSetByAnimation(false);
@@ -615,36 +615,36 @@ AsyncCompositionManager::ApplyAsyncConte
 
   if (hasAsyncTransform) {
     Matrix4x4 transform = AdjustAndCombineWithCSSTransform(combinedAsyncTransform, aLayer);
 
     // GetTransform already takes the pre- and post-scale into account.  Since we
     // will apply the pre- and post-scale again when computing the effective
     // transform, we must apply the inverses here.
     if (ContainerLayer* container = aLayer->AsContainerLayer()) {
-      transform.Scale(1.0f/container->GetPreXScale(),
-                      1.0f/container->GetPreYScale(),
-                      1);
+      transform.PreScale(1.0f/container->GetPreXScale(),
+                         1.0f/container->GetPreYScale(),
+                         1);
     }
     transform.PostScale(1.0f/aLayer->GetPostXScale(),
                         1.0f/aLayer->GetPostYScale(),
                         1);
     layerComposite->SetShadowTransform(transform);
     NS_ASSERTION(!layerComposite->GetShadowTransformSetByAnimation(),
                  "overwriting animated transform!");
 
     const FrameMetrics& bottom = LayerMetricsWrapper::BottommostScrollableMetrics(aLayer);
     MOZ_ASSERT(bottom.IsScrollable());  // must be true because hasAsyncTransform is true
 
     // Apply resolution scaling to the old transform - the layer tree as it is
     // doesn't have the necessary transform to display correctly. We use the
     // bottom-most scrollable metrics because that should have the most accurate
     // cumulative resolution for aLayer.
     LayoutDeviceToLayerScale resolution = bottom.mCumulativeResolution;
-    oldTransform.Scale(resolution.scale, resolution.scale, 1);
+    oldTransform.PreScale(resolution.scale, resolution.scale, 1);
 
     // For the purpose of aligning fixed and sticky layers, we disregard
     // the overscroll transform when computing the 'aCurrentTransformForRoot'
     // parameter. This ensures that the overscroll transform is not unapplied,
     // and therefore that the visual effect applies to fixed and sticky layers.
     Matrix4x4 transformWithoutOverscroll = AdjustAndCombineWithCSSTransform(
         combinedAsyncTransformWithoutOverscroll, aLayer);
     // Since fixed/sticky layers are relative to their nearest scrolling ancestor,
@@ -762,19 +762,19 @@ ApplyAsyncTransformToScrollbarForContent
       TransformClipRect(ancestor, compensation);
     }
   }
 
   // GetTransform already takes the pre- and post-scale into account.  Since we
   // will apply the pre- and post-scale again when computing the effective
   // transform, we must apply the inverses here.
   if (ContainerLayer* container = aScrollbar->AsContainerLayer()) {
-    transform.Scale(1.0f/container->GetPreXScale(),
-                    1.0f/container->GetPreYScale(),
-                    1);
+    transform.PreScale(1.0f/container->GetPreXScale(),
+                       1.0f/container->GetPreYScale(),
+                        1);
   }
   transform.PostScale(1.0f/aScrollbar->GetPostXScale(),
                       1.0f/aScrollbar->GetPostYScale(),
                       1);
   aScrollbar->AsLayerComposite()->SetShadowTransform(transform);
 }
 
 static LayerMetricsWrapper
@@ -909,30 +909,30 @@ AsyncCompositionManager::TransformScroll
   ScreenPoint translation = userScroll - geckoScroll;
   Matrix4x4 treeTransform = ViewTransform(scale, -translation);
 
   // The transform already takes the resolution scale into account.  Since we
   // will apply the resolution scale again when computing the effective
   // transform, we must apply the inverse resolution scale here.
   Matrix4x4 computedTransform = oldTransform * treeTransform;
   if (ContainerLayer* container = aLayer->AsContainerLayer()) {
-    computedTransform.Scale(1.0f/container->GetPreXScale(),
-                            1.0f/container->GetPreYScale(),
-                            1);
+    computedTransform.PreScale(1.0f/container->GetPreXScale(),
+                               1.0f/container->GetPreYScale(),
+                               1);
   }
-  computedTransform.ScalePost(1.0f/aLayer->GetPostXScale(),
+  computedTransform.PostScale(1.0f/aLayer->GetPostXScale(),
                               1.0f/aLayer->GetPostYScale(),
                               1);
   layerComposite->SetShadowTransform(computedTransform);
   NS_ASSERTION(!layerComposite->GetShadowTransformSetByAnimation(),
                "overwriting animated transform!");
 
   // Apply resolution scaling to the old transform - the layer tree as it is
   // doesn't have the necessary transform to display correctly.
-  oldTransform.Scale(metrics.mResolution.scale, metrics.mResolution.scale, 1);
+  oldTransform.PreScale(metrics.mResolution.scale, metrics.mResolution.scale, 1);
 
   // Make sure that overscroll and under-zoom are represented in the old
   // transform so that fixed position content moves and scales accordingly.
   // These calculations will effectively scale and offset fixed position layers
   // in screen space when the compensatory transform is performed in
   // AlignFixedAndStickyLayers.
   ScreenRect contentScreenRect = mContentRect * userZoom;
   Point3D overscrollTranslation;
@@ -943,30 +943,30 @@ AsyncCompositionManager::TransformScroll
       (userScroll.x + metrics.mCompositionBounds.width);
   }
   if (userScroll.y < contentScreenRect.y) {
     overscrollTranslation.y = contentScreenRect.y - userScroll.y;
   } else if (userScroll.y + metrics.mCompositionBounds.height > contentScreenRect.YMost()) {
     overscrollTranslation.y = contentScreenRect.YMost() -
       (userScroll.y + metrics.mCompositionBounds.height);
   }
-  oldTransform.Translate(overscrollTranslation.x,
-                         overscrollTranslation.y,
-                         overscrollTranslation.z);
+  oldTransform.PreTranslate(overscrollTranslation.x,
+                            overscrollTranslation.y,
+                            overscrollTranslation.z);
 
   gfx::Size underZoomScale(1.0f, 1.0f);
   if (mContentRect.width * userZoom.scale < metrics.mCompositionBounds.width) {
     underZoomScale.width = (mContentRect.width * userZoom.scale) /
       metrics.mCompositionBounds.width;
   }
   if (mContentRect.height * userZoom.scale < metrics.mCompositionBounds.height) {
     underZoomScale.height = (mContentRect.height * userZoom.scale) /
       metrics.mCompositionBounds.height;
   }
-  oldTransform.Scale(underZoomScale.width, underZoomScale.height, 1);
+  oldTransform.PreScale(underZoomScale.width, underZoomScale.height, 1);
 
   // Make sure fixed position layers don't move away from their anchor points
   // when we're asynchronously panning or zooming
   AlignFixedAndStickyLayers(aLayer, aLayer, metrics.GetScrollId(), oldTransform,
                             aLayer->GetLocalTransform(), fixedLayerMargins);
 }
 
 void
--- a/gfx/layers/composite/FPSCounter.cpp
+++ b/gfx/layers/composite/FPSCounter.cpp
@@ -378,17 +378,17 @@ static void DrawDigits(unsigned int aVal
   if (aValue > 999) {
     aValue = 999;
   }
 
   unsigned int divisor = 100;
   float textureWidth = FontWidth * 10;
   gfx::Float opacity = 1;
   gfx::Matrix4x4 transform;
-  transform.Scale(FontScaleX, FontScaleY, 1);
+  transform.PreScale(FontScaleX, FontScaleY, 1);
 
   for (size_t n = 0; n < 3; ++n) {
     unsigned int digit = aValue % (divisor * 10) / divisor;
     divisor /= 10;
 
     RefPtr<TexturedEffect> texturedEffect = static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
     texturedEffect->mTextureCoords = Rect(float(digit * FontWidth) / textureWidth, 0, FontWidth / textureWidth, 1.0f);
 
--- a/gfx/layers/composite/ImageLayerComposite.cpp
+++ b/gfx/layers/composite/ImageLayerComposite.cpp
@@ -124,18 +124,18 @@ ImageLayerComposite::ComputeEffectiveTra
       mImageHost->IsAttached() &&
       mImageHost->GetAsTextureHost()) {
     IntSize size = mImageHost->GetAsTextureHost()->GetSize();
     sourceRect.SizeTo(size.width, size.height);
     if (mScaleMode != ScaleMode::SCALE_NONE &&
         sourceRect.width != 0.0 && sourceRect.height != 0.0) {
       NS_ASSERTION(mScaleMode == ScaleMode::STRETCH,
                    "No other scalemodes than stretch and none supported yet.");
-      local.Scale(mScaleToSize.width / sourceRect.width,
-                  mScaleToSize.height / sourceRect.height, 1.0);
+      local.PreScale(mScaleToSize.width / sourceRect.width,
+                     mScaleToSize.height / sourceRect.height, 1.0);
     }
   }
   // Snap our local transform first, and snap the inherited transform as well.
   // This makes our snapping equivalent to what would happen if our content
   // was drawn into a PaintedLayer (gfxContext would snap using the local
   // transform, then we'd snap again when compositing the PaintedLayer).
   mEffectiveTransform =
       SnapTransform(local, sourceRect, nullptr) *
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -850,17 +850,17 @@ LayerManagerComposite::ComputeRenderInte
   nsTArray<Layer*> rootScrollableLayers;
   GetRootScrollableLayers(rootScrollableLayers);
   if (rootScrollableLayers.Length() > 0) {
     // This is derived from the code in
     // AsyncCompositionManager::TransformScrollableLayer
     Layer* rootScrollable = rootScrollableLayers[0];
     const FrameMetrics& metrics = LayerMetricsWrapper::TopmostScrollableMetrics(rootScrollable);
     Matrix4x4 transform = rootScrollable->GetEffectiveTransform();
-    transform.ScalePost(metrics.mResolution.scale, metrics.mResolution.scale, 1);
+    transform.PostScale(metrics.mResolution.scale, metrics.mResolution.scale, 1);
 
     // Clip the screen rect to the document bounds
     Rect documentBounds =
       transform.TransformBounds(Rect(metrics.mScrollableRect.x - metrics.GetScrollOffset().x,
                                      metrics.mScrollableRect.y - metrics.GetScrollOffset().y,
                                      metrics.mScrollableRect.width,
                                      metrics.mScrollableRect.height));
     documentBounds.RoundOut();
--- a/gfx/layers/composite/TextRenderer.cpp
+++ b/gfx/layers/composite/TextRenderer.cpp
@@ -129,17 +129,17 @@ TextRenderer::RenderText(const string& a
     return;
   }
 
   RefPtr<EffectRGB> effect = new EffectRGB(src, true, Filter::LINEAR);
   EffectChain chain;
   chain.mPrimaryEffect = effect;
 
   Matrix4x4 transform = aTransform;
-  transform.Scale(scaleFactor, scaleFactor, 1.0f);
+  transform.PreScale(scaleFactor, scaleFactor, 1.0f);
   mCompositor->DrawQuad(Rect(aOrigin.x, aOrigin.y, maxWidth, numLines * 16),
                         Rect(-10000, -10000, 20000, 20000), chain, 1.0f, transform);
 }
 
 void
 TextRenderer::EnsureInitialized()
 {
   if (mGlyphBitmaps) {
--- a/gfx/layers/composite/TiledContentHost.cpp
+++ b/gfx/layers/composite/TiledContentHost.cpp
@@ -551,18 +551,18 @@ TiledContentHost::RenderLayerBuffer(Tile
     maskRegion = mTiledBuffer.GetValidRegion();
     // XXX This should be ScaleRoundIn, but there is no such function on
     //     nsIntRegion.
     maskRegion.ScaleRoundOut(layerScale.width, layerScale.height);
   }
 
   // Make sure the resolution and difference in frame resolution are accounted
   // for in the layer transform.
-  aTransform.Scale(1/(resolution * layerScale.width),
-                   1/(resolution * layerScale.height), 1);
+  aTransform.PreScale(1/(resolution * layerScale.width),
+                      1/(resolution * layerScale.height), 1);
 
   uint32_t rowCount = 0;
   uint32_t tileX = 0;
   nsIntRect visibleRect = aVisibleRegion.GetBounds();
   gfx::IntSize scaledTileSize = aLayerBuffer.GetScaledTileSize();
   for (int32_t x = visibleRect.x; x < visibleRect.x + visibleRect.width;) {
     rowCount++;
     int32_t tileStartX = aLayerBuffer.GetTileStart(x, scaledTileSize.width);
--- a/gfx/layers/ipc/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -665,17 +665,17 @@ LayerTransactionParent::RecvGetAnimation
 
   // The following code recovers the untranslated transform
   // from the shadow transform by undoing the translations in
   // AsyncCompositionManager::SampleValue.
 
   Matrix4x4 transform = layer->AsLayerComposite()->GetShadowTransform();
   if (ContainerLayer* c = layer->AsContainerLayer()) {
     // Undo the scale transform applied by AsyncCompositionManager::SampleValue
-    transform.ScalePost(1.0f/c->GetInheritedXScale(),
+    transform.PostScale(1.0f/c->GetInheritedXScale(),
                         1.0f/c->GetInheritedYScale(),
                         1.0f);
   }
   float scale = 1;
   Point3D scaledOrigin;
   Point3D transformOrigin;
   for (uint32_t i=0; i < layer->GetAnimations().Length(); i++) {
     if (layer->GetAnimations()[i].data().type() == AnimationData::TTransformData) {
@@ -689,17 +689,17 @@ LayerTransactionParent::RecvGetAnimation
         double(nsDeviceContext::AppUnitsPerCSSPixel()) / double(scale);
       transformOrigin = data.transformOrigin() * cssPerDev;
       break;
     }
   }
 
   // Undo the translation to the origin of the reference frame applied by
   // AsyncCompositionManager::SampleValue
-  transform.Translate(-scaledOrigin.x, -scaledOrigin.y, -scaledOrigin.z);
+  transform.PreTranslate(-scaledOrigin.x, -scaledOrigin.y, -scaledOrigin.z);
 
   // Undo the rebasing applied by
   // nsDisplayTransform::GetResultingTransformMatrixInternal
   Point3D basis = -scaledOrigin - transformOrigin;
   transform.ChangeBasis(basis.x, basis.y, basis.z);
 
   // Convert to CSS pixels (this undoes the operations performed by
   // nsStyleTransformMatrix::ProcessTranslatePart which is called from
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -489,17 +489,17 @@ ShadowLayerForwarder::RemoveTextureFromC
   }
   CompositableClient::HoldUntilComplete(aCompositable->GetIPDLActor(),
                                         aAsyncTransactionTracker);
 }
 
 bool
 ShadowLayerForwarder::InWorkerThread()
 {
-  return GetMessageLoop()->id() == MessageLoop::current()->id();
+  return MessageLoop::current() && (GetMessageLoop()->id() == MessageLoop::current()->id());
 }
 
 static void RemoveTextureWorker(TextureClient* aTexture, ReentrantMonitor* aBarrier, bool* aDone)
 {
   aTexture->ForceRemove();
 
   ReentrantMonitorAutoEnter autoMon(*aBarrier);
   *aDone = true;
--- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp
+++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp
@@ -693,18 +693,18 @@ TEST_F(APZCBasicTester, ComplexTransform
   nsIntRegion layerVisibleRegion[] = {
     nsIntRegion(nsIntRect(0, 0, 300, 300)),
     nsIntRegion(nsIntRect(0, 0, 150, 300)),
   };
   Matrix4x4 transforms[] = {
     Matrix4x4(),
     Matrix4x4(),
   };
-  transforms[0].ScalePost(0.5f, 0.5f, 1.0f); // this results from the 2.0 resolution on the root layer
-  transforms[1].ScalePost(2.0f, 1.0f, 1.0f); // this is the 2.0 x-axis CSS transform on the child layer
+  transforms[0].PostScale(0.5f, 0.5f, 1.0f); // this results from the 2.0 resolution on the root layer
+  transforms[1].PostScale(2.0f, 1.0f, 1.0f); // this is the 2.0 x-axis CSS transform on the child layer
 
   nsTArray<nsRefPtr<Layer> > layers;
   nsRefPtr<LayerManager> lm;
   nsRefPtr<Layer> root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, transforms, lm, layers);
 
   FrameMetrics metrics;
   metrics.mCompositionBounds = ParentLayerRect(0, 0, 24, 24);
   metrics.mDisplayPort = CSSRect(-1, -1, 6, 6);
--- a/gfx/thebes/gfxContext.cpp
+++ b/gfx/thebes/gfxContext.cpp
@@ -83,17 +83,16 @@ private:
   gfxContext *mContext;
   Pattern *mPattern;
 };
 
 gfxContext::gfxContext(DrawTarget *aTarget, const Point& aDeviceOffset)
   : mPathIsRect(false)
   , mTransformChanged(false)
   , mRefCairo(nullptr)
-  , mSurface(nullptr)
   , mFlags(0)
   , mDT(aTarget)
   , mOriginalDT(aTarget)
 {
   MOZ_ASSERT(aTarget, "Don't create a gfxContext without a DrawTarget");
 
   MOZ_COUNT_CTOR(gfxContext);
 
@@ -125,34 +124,16 @@ gfxContext::~gfxContext()
     if (mStateStack[i].clipWasReset) {
       break;
     }
   }
   mDT->Flush();
   MOZ_COUNT_DTOR(gfxContext);
 }
 
-gfxASurface *
-gfxContext::OriginalSurface()
-{
-    if (mSurface) {
-        return mSurface;
-    }
-
-    if (mOriginalDT && mOriginalDT->GetBackendType() == BackendType::CAIRO) {
-        cairo_surface_t *s =
-            (cairo_surface_t*)mOriginalDT->GetNativeSurface(NativeSurfaceType::CAIRO_SURFACE);
-        if (s) {
-            mSurface = gfxASurface::Wrap(s);
-            return mSurface;
-        }
-    }
-    return nullptr;
-}
-
 already_AddRefed<gfxASurface>
 gfxContext::CurrentSurface(gfxFloat *dx, gfxFloat *dy)
 {
   if (mDT->GetBackendType() == BackendType::CAIRO) {
     cairo_surface_t *s =
     (cairo_surface_t*)mDT->GetNativeSurface(NativeSurfaceType::CAIRO_SURFACE);
     if (s) {
       if (dx && dy) {
--- a/gfx/thebes/gfxContext.h
+++ b/gfx/thebes/gfxContext.h
@@ -56,21 +56,16 @@ public:
     /**
      * Create a new gfxContext wrapping aTarget and preserving aTarget's
      * transform. Note that the transform is moved from aTarget to the resulting
      * gfxContext, aTarget will no longer have its transform.
      */
     static already_AddRefed<gfxContext> ContextForDrawTarget(mozilla::gfx::DrawTarget* aTarget);
 
     /**
-     * Return the surface that this gfxContext was created with
-     */
-    gfxASurface *OriginalSurface();
-
-    /**
      * Return the current transparency group target, if any, along
      * with its device offsets from the top.  If no group is
      * active, returns the surface the gfxContext was created with,
      * and 0,0 in dx,dy.
      */
     already_AddRefed<gfxASurface> CurrentSurface(gfxFloat *dx, gfxFloat *dy);
     already_AddRefed<gfxASurface> CurrentSurface() {
         return CurrentSurface(nullptr, nullptr);
@@ -512,20 +507,16 @@ public:
         OPERATOR_COLOR,
         OPERATOR_LUMINOSITY
     };
     /**
      * Sets the operator used for all further drawing. The operator affects
      * how drawing something will modify the destination. For example, the
      * OVER operator will do alpha blending of source and destination, while
      * SOURCE will replace the destination with the source.
-     *
-     * Note that if the flag FLAG_SIMPLIFY_OPERATORS is set on this
-     * gfxContext, the actual operator set might change for optimization
-     * purposes.  Check the comments below around that flag.
      */
     void SetOperator(GraphicsOperator op);
     GraphicsOperator CurrentOperator() const;
 
     void SetAntialiasMode(mozilla::gfx::AntialiasMode mode);
     mozilla::gfx::AntialiasMode CurrentAntialiasMode() const;
 
     /**
@@ -588,42 +579,22 @@ public:
     already_AddRefed<gfxPattern> PopGroup();
     void PopGroupToSource();
 
     mozilla::TemporaryRef<mozilla::gfx::SourceSurface>
     PopGroupToSurface(mozilla::gfx::Matrix* aMatrix);
 
     mozilla::gfx::Point GetDeviceOffset() const;
 
-    /**
-     ** Flags
-     **/
-
     enum {
-        /* If this flag is set, operators other than CLEAR, SOURCE, or
-         * OVER will be converted to OVER before being sent to cairo.
-         *
-         * This is most useful with a printing surface, where
-         * operators such as ADD are used to avoid seams for on-screen
-         * display, but where such errors aren't noticeable in print.
-         * This approach is currently used in border rendering.
-         *
-         * However, when printing complex renderings such as SVG,
-         * care should be taken to clear this flag.
-         */
-        FLAG_SIMPLIFY_OPERATORS = (1 << 0),
-        /**
+         /**
          * When this flag is set, snapping to device pixels is disabled.
          * It simply never does anything.
          */
         FLAG_DISABLE_SNAPPING = (1 << 1),
-        /**
-         * Disable copying of backgrounds in PushGroupAndCopyBackground.
-         */
-        FLAG_DISABLE_COPY_BACKGROUND = (1 << 2)
     };
 
     void SetFlag(int32_t aFlag) { mFlags |= aFlag; }
     void ClearFlag(int32_t aFlag) { mFlags &= ~aFlag; }
     int32_t GetFlags() const { return mFlags; }
 
     // Work out whether cairo will snap inter-glyph spacing to pixels.
     void GetRoundOffsetsToPixels(bool *aRoundX, bool *aRoundY);
@@ -727,17 +698,16 @@ private:
   mozilla::RefPtr<Path> mPath;
   Matrix mTransform;
   nsTArray<AzureState> mStateStack;
 
   AzureState &CurrentState() { return mStateStack[mStateStack.Length() - 1]; }
   const AzureState &CurrentState() const { return mStateStack[mStateStack.Length() - 1]; }
 
   cairo_t *mRefCairo;
-  nsRefPtr<gfxASurface> mSurface;
   int32_t mFlags;
 
   mozilla::RefPtr<DrawTarget> mDT;
   mozilla::RefPtr<DrawTarget> mOriginalDT;
 };
 
 /**
  * Sentry helper class for functions with multiple return points that need to
@@ -901,22 +871,19 @@ public:
         if (aDisable) {
             mDT = aContext->GetDrawTarget();
             mSubpixelAntialiasingEnabled = mDT->GetPermitSubpixelAA();
             mDT->SetPermitSubpixelAA(false);
         }
     }
     ~gfxContextAutoDisableSubpixelAntialiasing()
     {
-        if (mSurface) {
-            mSurface->SetSubpixelAntialiasingEnabled(mSubpixelAntialiasingEnabled);
-        } else if (mDT) {
+        if (mDT) {
             mDT->SetPermitSubpixelAA(mSubpixelAntialiasingEnabled);
         }
     }
 
 private:
-    nsRefPtr<gfxASurface> mSurface;
     mozilla::RefPtr<mozilla::gfx::DrawTarget> mDT;
     bool mSubpixelAntialiasingEnabled;
 };
 
 #endif /* GFX_CONTEXT_H */
--- a/image/public/imgILoader.idl
+++ b/image/public/imgILoader.idl
@@ -11,28 +11,27 @@ interface imgIRequest;
 
 interface nsIChannel;
 interface nsILoadGroup;
 interface nsIPrincipal;
 interface nsIStreamListener;
 interface nsIURI;
 
 interface nsISimpleEnumerator;
-interface nsIChannelPolicy;
 
 #include "nsIRequest.idl" // for nsLoadFlags
 
 /**
  * imgILoader interface
  *
  * @author Stuart Parmenter <pavlov@netscape.com>
  * @version 0.3
  * @see imagelib2
  */
-[scriptable, builtinclass, uuid(c8126129-8dac-43cd-b1ba-3896fba2dd01)]
+[scriptable, builtinclass, uuid(046d5fa6-ac59-489d-b51e-0ffe57e8df59)]
 interface imgILoader : nsISupports
 {
   // Extra flags to pass to loadImage if you want a load to use CORS
   // validation.
   const unsigned long LOAD_CORS_ANONYMOUS = 1 << 16;
   const unsigned long LOAD_CORS_USE_CREDENTIALS = 1 << 17;
 
   /**
@@ -57,18 +56,17 @@ interface imgILoader : nsISupports
   imgIRequest loadImageXPCOM(in nsIURI aURI,
                              in nsIURI aInitialDocumentURL,
                              in nsIURI aReferrerURI,
                              in nsIPrincipal aLoadingPrincipal,
                              in nsILoadGroup aLoadGroup,
                              in imgINotificationObserver aObserver,
                              in nsISupports aCX,
                              in nsLoadFlags aLoadFlags,
-                             in nsISupports cacheKey,
-                             in nsIChannelPolicy channelPolicy);
+                             in nsISupports cacheKey);
 
   /**
    * Start the load and decode of an image.
    * @param aChannel the channel to load the image from.  This must
    *                 already be opened before ths method is called, and there
    *                 must have been no OnDataAvailable calls for it yet.   
    * @param aObserver the observer (may be null)
    * @param cx some random data
--- a/image/src/Decoder.cpp
+++ b/image/src/Decoder.cpp
@@ -15,16 +15,17 @@ namespace mozilla {
 namespace image {
 
 Decoder::Decoder(RasterImage &aImage)
   : mImage(aImage)
   , mCurrentFrame(nullptr)
   , mImageData(nullptr)
   , mColormap(nullptr)
   , mDecodeFlags(0)
+  , mBytesDecoded(0)
   , mDecodeDone(false)
   , mDataError(false)
   , mFrameCount(0)
   , mFailCode(NS_OK)
   , mNeedsNewFrame(false)
   , mInitialized(false)
   , mSizeDecode(false)
   , mInFrame(false)
@@ -88,18 +89,21 @@ void
 Decoder::Write(const char* aBuffer, uint32_t aCount, DecodeStrategy aStrategy)
 {
   PROFILER_LABEL("ImageDecoder", "Write",
     js::ProfileEntry::Category::GRAPHICS);
 
   MOZ_ASSERT(NS_IsMainThread() || aStrategy == DECODE_ASYNC);
 
   // We're strict about decoder errors
-  NS_ABORT_IF_FALSE(!HasDecoderError(),
-                    "Not allowed to make more decoder calls after error!");
+  MOZ_ASSERT(!HasDecoderError(),
+             "Not allowed to make more decoder calls after error!");
+
+  // Keep track of the total number of bytes written.
+  mBytesDecoded += aCount;
 
   // If a data error occured, just ignore future data
   if (HasDataError())
     return;
 
   if (IsSizeDecode() && HasSize()) {
     // More data came in since we found the size. We have nothing to do here.
     return;
--- a/image/src/Decoder.h
+++ b/image/src/Decoder.h
@@ -96,16 +96,18 @@ public:
   }
 
   void SetObserver(imgDecoderObserver* aObserver)
   {
     MOZ_ASSERT(aObserver);
     mObserver = aObserver;
   }
 
+  size_t BytesDecoded() const { return mBytesDecoded; }
+
   // The number of frames we have, including anything in-progress. Thus, this
   // is only 0 if we haven't begun any frames.
   uint32_t GetFrameCount() { return mFrameCount; }
 
   // The number of complete frames we have (ie, not including anything in-progress).
   uint32_t GetCompleteFrameCount() { return mInFrame ? mFrameCount - 1 : mFrameCount; }
 
   // Error tracking
@@ -229,16 +231,17 @@ protected:
   ImageMetadata mImageMetadata;
 
   uint8_t* mImageData;       // Pointer to image data in either Cairo or 8bit format
   uint32_t mImageDataLength;
   uint32_t* mColormap;       // Current colormap to be used in Cairo format
   uint32_t mColormapSize;
 
   uint32_t mDecodeFlags;
+  size_t mBytesDecoded;
   bool mDecodeDone;
   bool mDataError;
 
 private:
   uint32_t mFrameCount; // Number of frames, including anything in-progress
 
   nsIntRect mInvalidRect; // Tracks an invalidation region in the current frame.
 
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -57,17 +57,19 @@
 #endif
 
 namespace mozilla {
 
 using namespace gfx;
 using namespace layers;
 
 namespace image {
+
 using std::ceil;
+using std::min;
 
 // a mask for flags that will affect the decoding
 #define DECODE_FLAGS_MASK (imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA | imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION)
 #define DECODE_FLAGS_DEFAULT 0
 
 static uint32_t
 DecodeFlags(uint32_t aFlags)
 {
@@ -314,17 +316,16 @@ RasterImage::RasterImage(imgStatusTracke
   mLockCount(0),
   mDecodeCount(0),
   mRequestedSampleSize(0),
 #ifdef DEBUG
   mFramesNotified(0),
 #endif
   mDecodingMonitor("RasterImage Decoding Monitor"),
   mDecoder(nullptr),
-  mBytesDecoded(0),
   mInDecoder(false),
   mStatusDiff(ImageStatusDiff::NoChange()),
   mNotifying(false),
   mHasSize(false),
   mDecodeOnDraw(false),
   mMultipart(false),
   mDiscardable(false),
   mHasSourceData(false),
@@ -1530,17 +1531,17 @@ RasterImage::AddSourceData(const char *a
   // be extra garbage data at the end of a file.
   if (mDecoded) {
     return NS_OK;
   }
 
   // Starting a new part's frames, let's clean up before we add any
   // This needs to happen just before we start getting EnsureFrame() call(s),
   // so that there's no gap for anything to miss us.
-  if (mMultipart && mBytesDecoded == 0) {
+  if (mMultipart && (!mDecoder || mDecoder->BytesDecoded() == 0)) {
     // Our previous state may have been animated, so let's clean up
     if (mAnimating)
       StopAnimation();
     mAnimationFinished = false;
     if (mAnim) {
       mAnim = nullptr;
     }
     // If there's only one frame, this could cause flickering
@@ -2090,18 +2091,16 @@ RasterImage::ShutdownDecoder(eShutdownIn
   }
 
   // If we finished a full decode, and we're not meant to be storing source
   // data, stop storing it.
   if (!wasSizeDecode && !StoringSourceData()) {
     mSourceData.Clear();
   }
 
-  mBytesDecoded = 0;
-
   return NS_OK;
 }
 
 // Writes the data to the decoder, updating the total number of bytes written.
 nsresult
 RasterImage::WriteToDecoder(const char *aBuffer, uint32_t aCount, DecodeStrategy aStrategy)
 {
   mDecodingMonitor.AssertCurrentThreadIn();
@@ -2112,20 +2111,16 @@ RasterImage::WriteToDecoder(const char *
   // Write
   nsRefPtr<Decoder> kungFuDeathGrip = mDecoder;
   mInDecoder = true;
   mDecoder->Write(aBuffer, aCount, aStrategy);
   mInDecoder = false;
 
   CONTAINER_ENSURE_SUCCESS(mDecoder->GetDecoderError());
 
-  // Keep track of the total number of bytes written over the lifetime of the
-  // decoder
-  mBytesDecoded += aCount;
-
   return NS_OK;
 }
 
 // This function is called in situations where it's clear that we want the
 // frames in decoded form (Draw, LookupFrame, etc).  If we're completely decoded,
 // this method resets the discard timer (if we're discardable), since wanting
 // the frames now is a good indicator of wanting them again soon. If we're not
 // decoded, this method kicks off asynchronous decoding to generate the frames.
@@ -2247,28 +2242,29 @@ RasterImage::RequestDecodeCore(RequestDe
   // synchronous decode if we're already waiting on a full decode).
   if (mDecoded) {
     return NS_OK;
   }
 
   // If we've already got a full decoder running, and have already decoded
   // some bytes, we have nothing to do if we haven't been asked to do some
   // sync decoding
-  if (mDecoder && !mDecoder->IsSizeDecode() && mBytesDecoded &&
+  if (mDecoder && !mDecoder->IsSizeDecode() && mDecoder->BytesDecoded() > 0 &&
       aDecodeType != SYNCHRONOUS_NOTIFY_AND_SOME_DECODE) {
     return NS_OK;
   }
 
   ReentrantMonitorAutoEnter lock(mDecodingMonitor);
 
   // If we don't have any bytes to flush to the decoder, we can't do anything.
-  // mBytesDecoded can be bigger than mSourceData.Length() if we're not storing
-  // the source data.
-  if (mBytesDecoded > mSourceData.Length())
+  // mDecoder->BytesDecoded() can be bigger than mSourceData.Length() if we're
+  // not storing the source data.
+  if (mDecoder && mDecoder->BytesDecoded() > mSourceData.Length()) {
     return NS_OK;
+  }
 
   // After acquiring the lock we may have finished some more decoding, so
   // we need to repeat the following three checks after getting the lock.
 
   // If the image is waiting for decode work to be notified, go ahead and do that.
   if (mDecodeRequest &&
       mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_WORK_DONE &&
       aDecodeType != ASYNCHRONOUS) {
@@ -2280,18 +2276,18 @@ RasterImage::RequestDecodeCore(RequestDe
   // DecodeUntilSizeAvailable and FinishedSomeDecoding because they can result
   // in us finishing an in-progress decode (or kicking off and finishing a
   // synchronous decode if we're already waiting on a full decode).
   if (mDecoded) {
     return NS_OK;
   }
 
   // If we've already got a full decoder running, and have already
-  // decoded some bytes, we have nothing to do
-  if (mDecoder && !mDecoder->IsSizeDecode() && mBytesDecoded) {
+  // decoded some bytes, we have nothing to do.
+  if (mDecoder && !mDecoder->IsSizeDecode() && mDecoder->BytesDecoded() > 0) {
     return NS_OK;
   }
 
   // If we have a size decode open, interrupt it and shut it down; or if
   // the decoder has different flags than what we need
   if (mDecoder && mDecoder->GetDecodeFlags() != mFrameDecodeFlags) {
     nsresult rv = FinishedSomeDecoding(eShutdownIntent_NotNeeded);
     CONTAINER_ENSURE_SUCCESS(rv);
@@ -2299,23 +2295,24 @@ RasterImage::RequestDecodeCore(RequestDe
 
   // If we don't have a decoder, create one
   if (!mDecoder) {
     rv = InitDecoder(/* aDoSizeDecode = */ false);
     CONTAINER_ENSURE_SUCCESS(rv);
 
     rv = FinishedSomeDecoding();
     CONTAINER_ENSURE_SUCCESS(rv);
-
-    MOZ_ASSERT(mDecoder);
   }
 
+  MOZ_ASSERT(mDecoder);
+
   // If we've read all the data we have, we're done
-  if (mHasSourceData && mBytesDecoded == mSourceData.Length())
+  if (mHasSourceData && mDecoder->BytesDecoded() == mSourceData.Length()) {
     return NS_OK;
+  }
 
   // If we can do decoding now, do so.  Small images will decode completely,
   // large images will decode a bit and post themselves to the event loop
   // to finish decoding.
   if (!mDecoded && !mInDecoder && mHasSourceData && aDecodeType == SYNCHRONOUS_NOTIFY_AND_SOME_DECODE) {
     PROFILER_LABEL_PRINTF("RasterImage", "DecodeABitOf",
       js::ProfileEntry::Category::GRAPHICS, "%s", GetURIString().get());
 
@@ -2372,20 +2369,21 @@ RasterImage::SyncDecode()
   nsresult rv;
 
   // If we're decoded already, or decoding until the size was available
   // finished us as a side-effect, no worries
   if (mDecoded)
     return NS_OK;
 
   // If we don't have any bytes to flush to the decoder, we can't do anything.
-  // mBytesDecoded can be bigger than mSourceData.Length() if we're not storing
-  // the source data.
-  if (mBytesDecoded > mSourceData.Length())
+  // mDecoder->BytesDecoded() can be bigger than mSourceData.Length() if we're
+  // not storing the source data.
+  if (mDecoder && mDecoder->BytesDecoded() > mSourceData.Length()) {
     return NS_OK;
+  }
 
   // If we have a decoder open with different flags than what we need, shut it
   // down
   if (mDecoder && mDecoder->GetDecodeFlags() != mFrameDecodeFlags) {
     nsresult rv = FinishedSomeDecoding(eShutdownIntent_NotNeeded);
     CONTAINER_ENSURE_SUCCESS(rv);
 
     if (mDecoded) {
@@ -2406,18 +2404,21 @@ RasterImage::SyncDecode()
   }
 
   // If we don't have a decoder, create one
   if (!mDecoder) {
     rv = InitDecoder(/* aDoSizeDecode = */ false);
     CONTAINER_ENSURE_SUCCESS(rv);
   }
 
+  MOZ_ASSERT(mDecoder);
+
   // Write everything we have
-  rv = DecodeSomeData(mSourceData.Length() - mBytesDecoded, DECODE_SYNC);
+  rv = DecodeSomeData(mSourceData.Length() - mDecoder->BytesDecoded(),
+                      DECODE_SYNC);
   CONTAINER_ENSURE_SUCCESS(rv);
 
   // When we're doing a sync decode, we want to get as much information from the
   // image as possible. We've send the decoder all of our data, so now's a good
   // time  to flush any invalidations (in case we don't have all the data and what
   // we got left us mid-frame).
   nsRefPtr<Decoder> kungFuDeathGrip = mDecoder;
   mInDecoder = true;
@@ -2767,58 +2768,57 @@ RasterImage::RequestDiscard()
 
   return NS_OK;
 }
 
 // Flushes up to aMaxBytes to the decoder.
 nsresult
 RasterImage::DecodeSomeData(size_t aMaxBytes, DecodeStrategy aStrategy)
 {
-  // We should have a decoder if we get here
-  NS_ABORT_IF_FALSE(mDecoder, "trying to decode without decoder!");
+  MOZ_ASSERT(mDecoder, "Should have a decoder");
 
   mDecodingMonitor.AssertCurrentThreadIn();
 
   // First, if we've just been called because we allocated a frame on the main
   // thread, let the decoder deal with the data it set aside at that time by
   // passing it a null buffer.
   if (mDecodeRequest->mAllocatedNewFrame) {
     mDecodeRequest->mAllocatedNewFrame = false;
     nsresult rv = WriteToDecoder(nullptr, 0, aStrategy);
     if (NS_FAILED(rv) || mDecoder->NeedsNewFrame()) {
       return rv;
     }
   }
 
-  // If we have nothing else to decode, return
-  if (mBytesDecoded == mSourceData.Length())
+  // If we have nothing else to decode, return.
+  if (mDecoder->BytesDecoded() == mSourceData.Length()) {
     return NS_OK;
-
-  MOZ_ASSERT(mBytesDecoded < mSourceData.Length());
+  }
+
+  MOZ_ASSERT(mDecoder->BytesDecoded() < mSourceData.Length());
 
   // write the proper amount of data
-  size_t bytesToDecode = std::min(aMaxBytes,
-                                  mSourceData.Length() - mBytesDecoded);
-  nsresult rv = WriteToDecoder(mSourceData.Elements() + mBytesDecoded,
-                               bytesToDecode,
-                               aStrategy);
-
-  return rv;
+  size_t bytesToDecode = min(aMaxBytes,
+                             mSourceData.Length() - mDecoder->BytesDecoded());
+  return WriteToDecoder(mSourceData.Elements() + mDecoder->BytesDecoded(),
+                        bytesToDecode,
+                        aStrategy);
+
 }
 
 // There are various indicators that tell us we're finished with the decode
 // task at hand and can shut down the decoder.
 //
 // This method may not be called if there is no decoder.
 bool
 RasterImage::IsDecodeFinished()
 {
   // Precondition
   mDecodingMonitor.AssertCurrentThreadIn();
-  NS_ABORT_IF_FALSE(mDecoder, "Can't call IsDecodeFinished() without decoder!");
+  MOZ_ASSERT(mDecoder, "Should have a decoder");
 
   // The decode is complete if we got what we wanted.
   if (mDecoder->IsSizeDecode()) {
     if (mDecoder->HasSize()) {
       return true;
     }
   } else if (mDecoder->GetDecodeDone()) {
     return true;
@@ -2833,17 +2833,17 @@ RasterImage::IsDecodeFinished()
   }
 
   // Otherwise, if we have all the source data and wrote all the source data,
   // we're done.
   //
   // (NB - This can be the case even for non-erroneous images because
   // Decoder::GetDecodeDone() might not return true until after we call
   // Decoder::Finish() in ShutdownDecoder())
-  if (mHasSourceData && (mBytesDecoded == mSourceData.Length())) {
+  if (mHasSourceData && (mDecoder->BytesDecoded() == mSourceData.Length())) {
     return true;
   }
 
   // If we get here, assume it's not finished.
   return false;
 }
 
 // Indempotent error flagging routine. If a decoder is open, shuts it down.
@@ -3026,17 +3026,17 @@ RasterImage::FinishedSomeDecoding(eShutd
       if (request && !wasSize) {
         Telemetry::Accumulate(Telemetry::IMAGE_DECODE_TIME,
                               int32_t(request->mDecodeTime.ToMicroseconds()));
 
         // We record the speed for only some decoders. The rest have
         // SpeedHistogram return HistogramCount.
         Telemetry::ID id = decoder->SpeedHistogram();
         if (id < Telemetry::HistogramCount) {
-          int32_t KBps = int32_t(request->mImage->mBytesDecoded /
+          int32_t KBps = int32_t(decoder->BytesDecoded() /
                                  (1024 * request->mDecodeTime.ToSeconds()));
           Telemetry::Accumulate(id, KBps);
         }
       }
 
       // We need to shut down the decoder first, in order to ensure all
       // decoding routines have been finished.
       rv = image->ShutdownDecoder(aIntent);
@@ -3202,17 +3202,18 @@ RasterImage::DecodePool::RequestDecode(R
   MOZ_ASSERT(aImg->mDecoder);
   aImg->mDecodingMonitor.AssertCurrentThreadIn();
 
   // If we're currently waiting on a new frame for this image, we can't do any
   // decoding.
   if (!aImg->mDecoder->NeedsNewFrame()) {
     // No matter whether this is currently being decoded, we need to update the
     // number of bytes we want it to decode.
-    aImg->mDecodeRequest->mBytesToDecode = aImg->mSourceData.Length() - aImg->mBytesDecoded;
+    aImg->mDecodeRequest->mBytesToDecode =
+      aImg->mSourceData.Length() - aImg->mDecoder->BytesDecoded();
 
     if (aImg->mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_PENDING ||
         aImg->mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_ACTIVE) {
       // The image is already in our list of images to decode, or currently being
       // decoded, so we don't have to do anything else.
       return;
     }
 
@@ -3250,17 +3251,17 @@ RasterImage::DecodePool::DecodeABitOf(Ra
   if (aImg->mDecoder && aImg->mDecoder->NeedsNewFrame()) {
     FrameNeededWorker::GetNewFrame(aImg);
   } else {
     // If we aren't yet finished decoding and we have more data in hand, add
     // this request to the back of the priority list.
     if (aImg->mDecoder &&
         !aImg->mError &&
         !aImg->IsDecodeFinished() &&
-        aImg->mSourceData.Length() > aImg->mBytesDecoded) {
+        aImg->mSourceData.Length() > aImg->mDecoder->BytesDecoded()) {
       RequestDecode(aImg);
     }
   }
 }
 
 /* static */ void
 RasterImage::DecodePool::StopDecoding(RasterImage* aImg)
 {
@@ -3294,29 +3295,29 @@ RasterImage::DecodePool::DecodeJob::Run(
   // still needs a new frame, we can't do anything. Wait until the
   // FrameNeededWorker enqueues another frame.
   if (mImage->mDecoder->NeedsNewFrame()) {
     return NS_OK;
   }
 
   mRequest->mRequestStatus = DecodeRequest::REQUEST_ACTIVE;
 
-  size_t oldByteCount = mImage->mBytesDecoded;
+  size_t oldByteCount = mImage->mDecoder->BytesDecoded();
 
   DecodeType type = DECODE_TYPE_UNTIL_DONE_BYTES;
 
   // Multithreaded decoding can be disabled. If we've done so, we don't want to
   // monopolize the main thread, and will allow a timeout in DecodeSomeOfImage.
   if (NS_IsMainThread()) {
     type = DECODE_TYPE_UNTIL_TIME;
   }
 
   DecodePool::Singleton()->DecodeSomeOfImage(mImage, DECODE_ASYNC, type, mRequest->mBytesToDecode);
 
-  size_t bytesDecoded = mImage->mBytesDecoded - oldByteCount;
+  size_t bytesDecoded = mImage->mDecoder->BytesDecoded() - oldByteCount;
 
   mRequest->mRequestStatus = DecodeRequest::REQUEST_WORK_DONE;
 
   // If the decoder needs a new frame, enqueue an event to get it; that event
   // will enqueue another decode request when it's done.
   if (mImage->mDecoder && mImage->mDecoder->NeedsNewFrame()) {
     FrameNeededWorker::GetNewFrame(mImage);
   }
@@ -3437,31 +3438,31 @@ RasterImage::DecodePool::DecodeSomeOfIma
   } else {
     // We're only guaranteed to decode this many bytes, so in particular,
     // gfxPrefs::ImageMemDecodeBytesAtATime should be set high enough for us
     // to read the size from most images.
     maxBytes = gfxPrefs::ImageMemDecodeBytesAtATime();
   }
 
   if (bytesToDecode == 0) {
-    bytesToDecode = aImg->mSourceData.Length() - aImg->mBytesDecoded;
+    bytesToDecode = aImg->mSourceData.Length() - aImg->mDecoder->BytesDecoded();
   }
 
   int32_t chunkCount = 0;
   TimeStamp start = TimeStamp::Now();
   TimeStamp deadline = start + TimeDuration::FromMilliseconds(gfxPrefs::ImageMemMaxMSBeforeYield());
 
   // We keep decoding chunks until:
   //  * we don't have any data left to decode,
   //  * the decode completes,
   //  * we're an UNTIL_SIZE decode and we get the size, or
   //  * we run out of time.
   // We also try to decode at least one "chunk" if we've allocated a new frame,
   // even if we have no more data to send to the decoder.
-  while ((aImg->mSourceData.Length() > aImg->mBytesDecoded &&
+  while ((aImg->mSourceData.Length() > aImg->mDecoder->BytesDecoded() &&
           bytesToDecode > 0 &&
           !aImg->IsDecodeFinished() &&
           !(aDecodeType == DECODE_TYPE_UNTIL_SIZE && aImg->mHasSize) &&
           !aImg->mDecoder->NeedsNewFrame()) ||
          (aImg->mDecodeRequest && aImg->mDecodeRequest->mAllocatedNewFrame)) {
     chunkCount++;
     uint32_t chunkSize = std::min(bytesToDecode, maxBytes);
     nsresult rv = aImg->DecodeSomeData(chunkSize, aStrategy);
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -651,17 +651,16 @@ private: // data
   // BEGIN LOCKED MEMBER VARIABLES
   ReentrantMonitor           mDecodingMonitor;
 
   FallibleTArray<char>       mSourceData;
 
   // Decoder and friends
   nsRefPtr<Decoder>          mDecoder;
   nsRefPtr<DecodeRequest>    mDecodeRequest;
-  size_t                     mBytesDecoded;
 
   bool                       mInDecoder;
   // END LOCKED MEMBER VARIABLES
 
   // Notification state. Used to avoid recursive notifications.
   ImageStatusDiff            mStatusDiff;
   bool                       mNotifying:1;
 
--- a/image/src/imgLoader.cpp
+++ b/image/src/imgLoader.cpp
@@ -630,17 +630,16 @@ static nsresult NewImageChannel(nsIChann
                                 // aLoadingPrincipal and false otherwise.
                                 bool *aForcePrincipalCheckForCacheEntry,
                                 nsIURI *aURI,
                                 nsIURI *aInitialDocumentURI,
                                 nsIURI *aReferringURI,
                                 nsILoadGroup *aLoadGroup,
                                 const nsCString& aAcceptHeader,
                                 nsLoadFlags aLoadFlags,
-                                nsIChannelPolicy *aPolicy,
                                 nsIPrincipal *aLoadingPrincipal,
                                 nsISupports *aRequestingContext)
 {
   nsresult rv;
   nsCOMPtr<nsIHttpChannel> newHttpChannel;
 
   nsCOMPtr<nsIInterfaceRequestor> callbacks;
 
@@ -686,17 +685,16 @@ static nsresult NewImageChannel(nsIChann
   // stylesheets, where the document is being styled, but the principal is that
   // of the user stylesheet.
   rv = NS_NewChannelInternal(aResult,
                              aURI,
                              requestingNode,
                              requestingPrincipal,
                              securityFlags,
                              nsIContentPolicy::TYPE_IMAGE,
-                             aPolicy,
                              nullptr,   // loadGroup
                              callbacks,
                              aLoadFlags);
 
   if (NS_FAILED(rv))
     return rv;
 
   *aForcePrincipalCheckForCacheEntry = inherit && !isSandBoxed;
@@ -1440,17 +1438,16 @@ bool imgLoader::ValidateRequestWithNewCh
                                                 nsIURI *aURI,
                                                 nsIURI *aInitialDocumentURI,
                                                 nsIURI *aReferrerURI,
                                                 nsILoadGroup *aLoadGroup,
                                                 imgINotificationObserver *aObserver,
                                                 nsISupports *aCX,
                                                 nsLoadFlags aLoadFlags,
                                                 imgRequestProxy **aProxyRequest,
-                                                nsIChannelPolicy *aPolicy,
                                                 nsIPrincipal* aLoadingPrincipal,
                                                 int32_t aCORSMode)
 {
   // now we need to insert a new channel request object inbetween the real
   // request and the proxy that basically delays loading the image until it
   // gets a 304 or figures out that this needs to be a new request
 
   nsresult rv;
@@ -1488,17 +1485,16 @@ bool imgLoader::ValidateRequestWithNewCh
     rv = NewImageChannel(getter_AddRefs(newChannel),
                          &forcePrincipalCheck,
                          aURI,
                          aInitialDocumentURI,
                          aReferrerURI,
                          aLoadGroup,
                          mAcceptHeader,
                          aLoadFlags,
-                         aPolicy,
                          aLoadingPrincipal,
                          aCX);
     if (NS_FAILED(rv)) {
       return false;
     }
 
     nsRefPtr<imgRequestProxy> req;
     rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver,
@@ -1568,17 +1564,16 @@ bool imgLoader::ValidateEntry(imgCacheEn
                                 nsIURI *aInitialDocumentURI,
                                 nsIURI *aReferrerURI,
                                 nsILoadGroup *aLoadGroup,
                                 imgINotificationObserver *aObserver,
                                 nsISupports *aCX,
                                 nsLoadFlags aLoadFlags,
                                 bool aCanMakeNewChannel,
                                 imgRequestProxy **aProxyRequest,
-                                nsIChannelPolicy *aPolicy,
                                 nsIPrincipal* aLoadingPrincipal,
                                 int32_t aCORSMode)
 {
   LOG_SCOPE(GetImgLog(), "imgLoader::ValidateEntry");
 
   bool hasExpired;
   uint32_t expirationTime = aEntry->GetExpiryTime();
   if (expirationTime <= SecondsFromPRTime(PR_Now())) {
@@ -1673,17 +1668,17 @@ bool imgLoader::ValidateEntry(imgCacheEn
     return false;
   }
 
   if (validateRequest && aCanMakeNewChannel) {
     LOG_SCOPE(GetImgLog(), "imgLoader::ValidateRequest |cache hit| must validate");
 
     return ValidateRequestWithNewChannel(request, aURI, aInitialDocumentURI,
                                          aReferrerURI, aLoadGroup, aObserver,
-                                         aCX, aLoadFlags, aProxyRequest, aPolicy,
+                                         aCX, aLoadFlags, aProxyRequest,
                                          aLoadingPrincipal, aCORSMode);
   }
 
   return !validateRequest;
 }
 
 bool imgLoader::RemoveFromCache(nsIURI *aKey)
 {
@@ -1848,52 +1843,54 @@ NS_IMETHODIMP imgLoader::LoadImageXPCOM(
                                    nsIURI *aInitialDocumentURI,
                                    nsIURI *aReferrerURI,
                                    nsIPrincipal* aLoadingPrincipal,
                                    nsILoadGroup *aLoadGroup,
                                    imgINotificationObserver *aObserver,
                                    nsISupports *aCX,
                                    nsLoadFlags aLoadFlags,
                                    nsISupports *aCacheKey,
-                                   nsIChannelPolicy *aPolicy,
                                    imgIRequest **_retval)
 {
     imgRequestProxy *proxy;
     nsresult result = LoadImage(aURI,
                                 aInitialDocumentURI,
                                 aReferrerURI,
                                 aLoadingPrincipal,
                                 aLoadGroup,
                                 aObserver,
                                 aCX,
                                 aLoadFlags,
                                 aCacheKey,
-                                aPolicy,
                                 EmptyString(),
                                 &proxy);
     *_retval = proxy;
     return result;
 }
 
-
-
-/* imgIRequest loadImage(in nsIURI aURI, in nsIURI aInitialDocumentURL, in nsIURI aReferrerURI, in nsIPrincipal aLoadingPrincipal, in nsILoadGroup aLoadGroup, in imgINotificationObserver aObserver, in nsISupports aCX, in nsLoadFlags aLoadFlags, in nsISupports cacheKey, in nsIChannelPolicy channelPolicy); */
-
+// imgIRequest loadImage(in nsIURI aURI,
+//                       in nsIURI aInitialDocumentURL,
+//                       in nsIURI aReferrerURI,
+//                       in nsIPrincipal aLoadingPrincipal,
+//                       in nsILoadGroup aLoadGroup,
+//                       in imgINotificationObserver aObserver,
+//                       in nsISupports aCX,
+//                       in nsLoadFlags aLoadFlags,
+//                       in nsISupports cacheKey);
 nsresult imgLoader::LoadImage(nsIURI *aURI,
-			      nsIURI *aInitialDocumentURI,
-			      nsIURI *aReferrerURI,
-			      nsIPrincipal* aLoadingPrincipal,
-			      nsILoadGroup *aLoadGroup,
-			      imgINotificationObserver *aObserver,
-			      nsISupports *aCX,
-			      nsLoadFlags aLoadFlags,
-			      nsISupports *aCacheKey,
-			      nsIChannelPolicy *aPolicy,
-			      const nsAString& initiatorType,
-			      imgRequestProxy **_retval)
+                              nsIURI *aInitialDocumentURI,
+                              nsIURI *aReferrerURI,
+                              nsIPrincipal* aLoadingPrincipal,
+                              nsILoadGroup *aLoadGroup,
+                              imgINotificationObserver *aObserver,
+                              nsISupports *aCX,
+                              nsLoadFlags aLoadFlags,
+                              nsISupports *aCacheKey,
+                              const nsAString& initiatorType,
+                              imgRequestProxy **_retval)
 {
 	VerifyCacheSizes();
 
   NS_ASSERTION(aURI, "imgLoader::LoadImage -- NULL URI pointer");
 
   if (!aURI)
     return NS_ERROR_NULL_POINTER;
 
@@ -1961,17 +1958,17 @@ nsresult imgLoader::LoadImage(nsIURI *aU
   // XXX For now ignore aCacheKey. We will need it in the future
   // for correctly dealing with image load requests that are a result
   // of post data.
   imgCacheTable &cache = GetCache(aURI);
 
   if (cache.Get(spec, getter_AddRefs(entry)) && entry) {
     if (ValidateEntry(entry, aURI, aInitialDocumentURI, aReferrerURI,
                       aLoadGroup, aObserver, aCX, requestFlags, true,
-                      _retval, aPolicy, aLoadingPrincipal, corsmode)) {
+                      _retval, aLoadingPrincipal, corsmode)) {
       request = entry->GetRequest();
 
       // If this entry has no proxies, its request has no reference to the entry.
       if (entry->HasNoProxies()) {
         LOG_FUNC_WITH_PARAM(GetImgLog(), "imgLoader::LoadImage() adding proxyless entry", "uri", spec.get());
         NS_ABORT_IF_FALSE(!request->HasCacheEntry(), "Proxyless entry's request has cache entry!");
         request->SetCacheEntry(entry);
 
@@ -2003,17 +2000,16 @@ nsresult imgLoader::LoadImage(nsIURI *aU
     rv = NewImageChannel(getter_AddRefs(newChannel),
                          &forcePrincipalCheck,
                          aURI,
                          aInitialDocumentURI,
                          aReferrerURI,
                          aLoadGroup,
                          mAcceptHeader,
                          requestFlags,
-                         aPolicy,
                          aLoadingPrincipal,
                          aCX);
     if (NS_FAILED(rv))
       return NS_ERROR_FAILURE;
 
     MOZ_ASSERT(NS_UsePrivateBrowsing(newChannel) == mRespectPrivacy);
 
     NewRequestAndEntry(forcePrincipalCheck, this, getter_AddRefs(request), getter_AddRefs(entry));
@@ -2187,17 +2183,17 @@ nsresult imgLoader::LoadImageWithChannel
       // We don't want to kick off another network load. So we ask
       // ValidateEntry to only do validation without creating a new proxy. If
       // it says that the entry isn't valid any more, we'll only use the entry
       // we're getting if the channel is loading from the cache anyways.
       //
       // XXX -- should this be changed? it's pretty much verbatim from the old
       // code, but seems nonsensical.
       if (ValidateEntry(entry, uri, nullptr, nullptr, nullptr, aObserver, aCX,
-                        requestFlags, false, nullptr, nullptr, nullptr,
+                        requestFlags, false, nullptr, nullptr,
                         imgIRequest::CORS_NONE)) {
         request = entry->GetRequest();
       } else {
         nsCOMPtr<nsICachingChannel> cacheChan(do_QueryInterface(channel));
         bool bUseCacheCopy;
 
         if (cacheChan)
           cacheChan->IsFromCache(&bUseCacheCopy);
--- a/image/src/imgLoader.h
+++ b/image/src/imgLoader.h
@@ -24,17 +24,16 @@
 #include "imgIRequest.h"
 
 class imgLoader;
 class imgRequestProxy;
 class imgINotificationObserver;
 class nsILoadGroup;
 class imgCacheExpirationTracker;
 class imgMemoryReporter;
-class nsIChannelPolicy;
 
 namespace mozilla {
 namespace image {
 class ImageURL;
 }
 }
 
 class imgCacheEntry
@@ -253,17 +252,16 @@ public:
                      nsIURI *aInitialDocumentURI,
                      nsIURI *aReferrerURI,
                      nsIPrincipal* aLoadingPrincipal,
                      nsILoadGroup *aLoadGroup,
                      imgINotificationObserver *aObserver,
                      nsISupports *aCX,
                      nsLoadFlags aLoadFlags,
                      nsISupports *aCacheKey,
-                     nsIChannelPolicy *aPolicy,
                      const nsAString& initiatorType,
                      imgRequestProxy **_retval);
   nsresult LoadImageWithChannel(nsIChannel *channel,
                                 imgINotificationObserver *aObserver,
                                 nsISupports *aCX,
                                 nsIStreamListener **listener,
                                 imgRequestProxy **_retval);
 
@@ -335,28 +333,26 @@ public:
 private: // methods
 
   bool ValidateEntry(imgCacheEntry *aEntry, nsIURI *aKey,
                        nsIURI *aInitialDocumentURI, nsIURI *aReferrerURI,
                        nsILoadGroup *aLoadGroup,
                        imgINotificationObserver *aObserver, nsISupports *aCX,
                        nsLoadFlags aLoadFlags, bool aCanMakeNewChannel,
                        imgRequestProxy **aProxyRequest,
-                       nsIChannelPolicy *aPolicy,
                        nsIPrincipal* aLoadingPrincipal,
                        int32_t aCORSMode);
 
   bool ValidateRequestWithNewChannel(imgRequest *request, nsIURI *aURI,
                                        nsIURI *aInitialDocumentURI,
                                        nsIURI *aReferrerURI,
                                        nsILoadGroup *aLoadGroup,
                                        imgINotificationObserver *aObserver,
                                        nsISupports *aCX, nsLoadFlags aLoadFlags,
                                        imgRequestProxy **aProxyRequest,
-                                       nsIChannelPolicy *aPolicy,
                                        nsIPrincipal* aLoadingPrincipal,
                                        int32_t aCORSMode);
 
   nsresult CreateNewProxyForRequest(imgRequest *aRequest, nsILoadGroup *aLoadGroup,
                                     imgINotificationObserver *aObserver,
                                     nsLoadFlags aLoadFlags, imgRequestProxy **_retval);
 
   void ReadAcceptHeaderPref();
--- a/image/test/unit/async_load_tests.js
+++ b/image/test/unit/async_load_tests.js
@@ -91,17 +91,17 @@ function secondLoadDone(oldlistener, aRe
 // therefore would be at most risk of being served synchronously.
 function checkSecondLoad()
 {
   do_test_pending();
 
   var listener = new ImageListener(checkClone, secondLoadDone);
   var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
                 .createScriptedObserver(listener);
-  requests.push(gCurrentLoader.loadImageXPCOM(uri, null, null, null, null, outer, null, 0, null, null));
+  requests.push(gCurrentLoader.loadImageXPCOM(uri, null, null, null, null, outer, null, 0, null));
   listener.synchronous = false;
 }
 
 function firstLoadDone(oldlistener, aRequest)
 {
   checkSecondLoad(uri);
 
   do_test_finished();
@@ -189,17 +189,17 @@ function startImageCallback(otherCb)
 {
   return function(listener, request)
   {
     // Make sure we can load the same image immediately out of the cache.
     do_test_pending();
     var listener2 = new ImageListener(null, function(foo, bar) { do_test_finished(); });
     var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
                   .createScriptedObserver(listener2);
-    requests.push(gCurrentLoader.loadImageXPCOM(uri, null, null, null, null, outer, null, 0, null, null));
+    requests.push(gCurrentLoader.loadImageXPCOM(uri, null, null, null, null, outer, null, 0, null));
     listener2.synchronous = false;
 
     // Now that we've started another load, chain to the callback.
     otherCb(listener, request);
   }
 }
 
 var gCurrentLoader;
@@ -216,16 +216,16 @@ function run_test()
   do_register_cleanup(cleanup);
 
   gCurrentLoader = Cc["@mozilla.org/image/loader;1"].createInstance(Ci.imgILoader);
 
   do_test_pending();
   var listener = new ImageListener(startImageCallback(checkClone), firstLoadDone);
   var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
                 .createScriptedObserver(listener);
-  var req = gCurrentLoader.loadImageXPCOM(uri, null, null, null, null, outer, null, 0, null, null);
+  var req = gCurrentLoader.loadImageXPCOM(uri, null, null, null, null, outer, null, 0, null);
   requests.push(req);
 
   // Ensure that we don't cause any mayhem when we lock an image.
   req.lockImage();
 
   listener.synchronous = false;
 }
--- a/image/test/unit/test_private_channel.js
+++ b/image/test/unit/test_private_channel.js
@@ -72,17 +72,17 @@ function setup_chan(path, isPrivate, cal
 function loadImage(isPrivate, callback) {
   var listener = new ImageListener(null, callback);
   var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
                 .createScriptedObserver(listener);
   var uri = gIoService.newURI(gImgPath, null, null);
   var loadGroup = Cc["@mozilla.org/network/load-group;1"].createInstance(Ci.nsILoadGroup);
   loadGroup.notificationCallbacks = new NotificationCallbacks(isPrivate);
   var loader = isPrivate ? gPrivateLoader : gPublicLoader;
-  requests.push(loader.loadImageXPCOM(uri, null, null, null, loadGroup, outer, null, 0, null, null));
+  requests.push(loader.loadImageXPCOM(uri, null, null, null, loadGroup, outer, null, 0, null));
   listener.synchronous = false;
 }
 
 function run_loadImage_tests() {
   function observer() {
     Services.obs.removeObserver(observer, "cacheservice:empty-cache");
     gHits = 0;
     loadImage(false, function() {
--- a/js/public/UbiNodeTraverse.h
+++ b/js/public/UbiNodeTraverse.h
@@ -90,16 +90,25 @@ struct BreadthFirst {
 
     // Initialize this traversal object. Return false on OOM.
     bool init() { return visited.init(); }
 
     // Add |node| as a starting point for the traversal. You may add
     // as many starting points as you like. Return false on OOM.
     bool addStart(Node node) { return pending.append(node); }
 
+    // Add |node| as a starting point for the traversal (see addStart) and also
+    // add it to the |visited| set. Return false on OOM.
+    bool addStartVisited(Node node) {
+        typename NodeMap::AddPtr ptr = visited.lookupForAdd(node);
+        if (!ptr && !visited.add(ptr, node, typename Handler::NodeData()))
+            return false;
+        return addStart(node);
+    }
+
     // True if the handler wants us to compute edge names; doing so can be
     // expensive in time and memory. True by default.
     bool wantNames;
 
     // Traverse the graph in breadth-first order, starting at the given
     // start nodes, applying |handler::operator()| for each edge traversed
     // as described above.
     //
--- a/js/src/asmjs/AsmJSLink.cpp
+++ b/js/src/asmjs/AsmJSLink.cpp
@@ -467,16 +467,24 @@ LinkModuleToHeap(JSContext *cx, AsmJSMod
         ScopedJSFreePtr<char> msg(
             JS_smprintf("ArrayBuffer byteLength of 0x%x is less than 0x%x (the size implied "
                         "by const heap accesses and/or change-heap minimum-length requirements).",
                         heapLength,
                         module.minHeapLength()));
         return LinkFail(cx, msg.get());
     }
 
+    if (heapLength > module.maxHeapLength()) {
+        ScopedJSFreePtr<char> msg(
+            JS_smprintf("ArrayBuffer byteLength 0x%x is greater than maximum length of 0x%x",
+                        heapLength,
+                        module.maxHeapLength()));
+        return LinkFail(cx, msg.get());
+    }
+
     // If we've generated the code with signal handlers in mind (for bounds
     // checks on x64 and for interrupt callback requesting on all platforms),
     // we need to be able to use signals at runtime. In particular, a module
     // can have been created using signals and cached, and executed without
     // signals activated.
     if (module.usesSignalHandlersForInterrupt() && !cx->canUseSignalHandlers())
         return LinkFail(cx, "Code generated with signal handlers but signals are deactivated");
 
@@ -488,17 +496,17 @@ LinkModuleToHeap(JSContext *cx, AsmJSMod
 
     module.initHeap(heap, cx);
     return true;
 }
 
 static bool
 DynamicallyLinkModule(JSContext *cx, CallArgs args, AsmJSModule &module)
 {
-    module.setIsDynamicallyLinked();
+    module.setIsDynamicallyLinked(cx->runtime());
 
     HandleValue globalVal = args.get(0);
     HandleValue importVal = args.get(1);
     HandleValue bufferVal = args.get(2);
 
     Rooted<ArrayBufferObjectMaybeShared *> heap(cx);
     if (module.hasArrayView()) {
         if (IsArrayBuffer(bufferVal) || IsSharedArrayBuffer(bufferVal))
@@ -566,21 +574,29 @@ ChangeHeap(JSContext *cx, AsmJSModule &m
     HandleValue bufferArg = args.get(0);
     if (!IsArrayBuffer(bufferArg)) {
         ReportIncompatible(cx, args);
         return false;
     }
 
     Rooted<ArrayBufferObject*> newBuffer(cx, &bufferArg.toObject().as<ArrayBufferObject>());
     uint32_t heapLength = newBuffer->byteLength();
-    if (heapLength & module.heapLengthMask() || heapLength < module.minHeapLength()) {
+    if (heapLength & module.heapLengthMask() ||
+        heapLength < module.minHeapLength() ||
+        heapLength > module.maxHeapLength())
+    {
         args.rval().set(BooleanValue(false));
         return true;
     }
 
+    if (!module.hasArrayView()) {
+        args.rval().set(BooleanValue(true));
+        return true;
+    }
+
     MOZ_ASSERT(IsValidAsmJSHeapLength(heapLength));
     MOZ_ASSERT(!IsDeprecatedAsmJSHeapLength(heapLength));
 
     if (!ArrayBufferObject::prepareForAsmJS(cx, newBuffer, module.usesSignalHandlersForOOB()))
         return false;
 
     args.rval().set(BooleanValue(module.changeHeap(newBuffer, cx)));
     return true;
@@ -673,25 +689,24 @@ CallAsmJS(JSContext *cx, unsigned argc, 
             if (!ToSimdConstant<Float32x4>(cx, v, &simd))
                 return false;
             memcpy(&coercedArgs[i], simd.asFloat32x4(), Simd128DataSize);
             break;
           }
         }
     }
 
-    // An asm.js module is specialized to its heap's base address and length
-    // which is normally immutable except for the neuter operation that occurs
-    // when an ArrayBuffer is transfered. Throw an internal error if we're
-    // about to run with a neutered heap.
-    if (module.maybeHeapBufferObject() &&
-        module.maybeHeapBufferObject()->is<ArrayBufferObject>() &&
-        module.maybeHeapBufferObject()->as<ArrayBufferObject>().isNeutered())
-    {
-        js_ReportOverRecursed(cx);
+    // The correct way to handle this situation would be to allocate a new range
+    // of PROT_NONE memory and module.changeHeap to this memory. That would
+    // cause every access to take the out-of-bounds signal-handler path which
+    // does the right thing. For now, just throw an out-of-memory exception
+    // since these can technically pop out anywhere and the full fix may
+    // actually OOM when trying to allocate the PROT_NONE memory.
+    if (module.hasDetachedHeap()) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_OUT_OF_MEMORY);
         return false;
     }
 
     {
         // Push an AsmJSActivation to describe the asm.js frames we're about to
         // push when running this module. Additionally, push a JitActivation so
         // that the optimized asm.js-to-Ion FFI call path (which we want to be
         // very fast) can avoid doing so. The JitActivation is marked as
--- a/js/src/asmjs/AsmJSModule.cpp
+++ b/js/src/asmjs/AsmJSModule.cpp
@@ -49,16 +49,46 @@ using namespace js;
 using namespace jit;
 using namespace frontend;
 using mozilla::BinarySearch;
 using mozilla::PodCopy;
 using mozilla::PodEqual;
 using mozilla::Compression::LZ4;
 using mozilla::Swap;
 
+// At any time, the executable code of an asm.js module can be protected (as
+// part of RequestInterruptForAsmJSCode). When we touch the executable outside
+// of executing it (which the AsmJSFaultHandler will correctly handle), we need
+// to guard against this by unprotecting the code (if it has been protected) and
+// preventing it from being protected while we are touching it.
+class AutoUnprotectCode
+{
+    JSRuntime *rt_;
+    JSRuntime::AutoLockForInterrupt lock_;
+    const AsmJSModule &module_;
+    const bool protectedBefore_;
+
+  public:
+    AutoUnprotectCode(JSContext *cx, const AsmJSModule &module)
+      : rt_(cx->runtime()),
+        lock_(rt_),
+        module_(module),
+        protectedBefore_(module_.codeIsProtected(rt_))
+    {
+        if (protectedBefore_)
+            module_.unprotectCode(rt_);
+    }
+
+    ~AutoUnprotectCode()
+    {
+        if (protectedBefore_)
+            module_.protectCode(rt_);
+    }
+};
+
 static uint8_t *
 AllocateExecutableMemory(ExclusiveContext *cx, size_t bytes)
 {
 #ifdef XP_WIN
     unsigned permissions = PAGE_EXECUTE_READWRITE;
 #else
     unsigned permissions = PROT_READ | PROT_WRITE | PROT_EXEC;
 #endif
@@ -73,26 +103,29 @@ AsmJSModule::AsmJSModule(ScriptSource *s
   : srcStart_(srcStart),
     srcBodyStart_(srcBodyStart),
     scriptSource_(scriptSource),
     globalArgumentName_(nullptr),
     importArgumentName_(nullptr),
     bufferArgumentName_(nullptr),
     code_(nullptr),
     interruptExit_(nullptr),
+    prevLinked_(nullptr),
+    nextLinked_(nullptr),
     dynamicallyLinked_(false),
     loadedFromCache_(false),
     profilingEnabled_(false),
     interrupted_(false),
     codeIsProtected_(false)
 {
     mozilla::PodZero(&pod);
     pod.funcPtrTableAndExitBytes_ = SIZE_MAX;
     pod.functionBytes_ = UINT32_MAX;
     pod.minHeapLength_ = RoundUpToNextValidAsmJSHeapLength(0);
+    pod.maxHeapLength_ = 0x80000000;
     pod.strict_ = strict;
     pod.usesSignalHandlers_ = canUseSignalHandlers;
 
     scriptSource_->incref();
 }
 
 AsmJSModule::~AsmJSModule()
 {
@@ -110,16 +143,21 @@ AsmJSModule::~AsmJSModule()
             exitDatum.ionScript->removeDependentAsmJSModule(exit);
         }
 
         DeallocateExecutableMemory(code_, pod.totalBytes_, AsmJSPageSize);
     }
 
     for (size_t i = 0; i < numFunctionCounts(); i++)
         js_delete(functionCounts(i));
+
+    if (prevLinked_)
+        *prevLinked_ = nextLinked_;
+    if (nextLinked_)
+        nextLinked_->prevLinked_ = prevLinked_;
 }
 
 void
 AsmJSModule::trace(JSTracer *trc)
 {
     for (unsigned i = 0; i < globals_.length(); i++)
         globals_[i].trace(trc);
     for (unsigned i = 0; i < exits_.length(); i++) {
@@ -441,16 +479,24 @@ AsmJSModule::setAutoFlushICacheRange()
 
 static void
 AsmJSReportOverRecursed()
 {
     JSContext *cx = PerThreadData::innermostAsmJSActivation()->cx();
     js_ReportOverRecursed(cx);
 }
 
+static void
+OnDetached()
+{
+    // See hasDetachedHeap comment in LinkAsmJS.
+    JSContext *cx = PerThreadData::innermostAsmJSActivation()->cx();
+    JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_OUT_OF_MEMORY);
+}
+
 static bool
 AsmJSHandleExecutionInterrupt()
 {
     AsmJSActivation *act = PerThreadData::innermostAsmJSActivation();
     act->module().setInterrupted(true);
     bool ret = HandleExecutionInterrupt(act->cx());
     act->module().setInterrupted(false);
     return ret;
@@ -624,16 +670,18 @@ AddressOf(AsmJSImmKind kind, ExclusiveCo
       case AsmJSImm_Runtime:
         return cx->runtimeAddressForJit();
       case AsmJSImm_RuntimeInterrupt:
         return cx->runtimeAddressOfInterrupt();
       case AsmJSImm_StackLimit:
         return cx->stackLimitAddressForJitCode(StackForUntrustedScript);
       case AsmJSImm_ReportOverRecursed:
         return RedirectCall(FuncCast(AsmJSReportOverRecursed), Args_General0);
+      case AsmJSImm_OnDetached:
+        return RedirectCall(FuncCast(OnDetached), Args_General0);
       case AsmJSImm_HandleExecutionInterrupt:
         return RedirectCall(FuncCast(AsmJSHandleExecutionInterrupt), Args_General0);
       case AsmJSImm_InvokeFromAsmJS_Ignore:
         return RedirectCall(FuncCast(InvokeFromAsmJS_Ignore), Args_General3);
       case AsmJSImm_InvokeFromAsmJS_ToInt32:
         return RedirectCall(FuncCast(InvokeFromAsmJS_ToInt32), Args_General3);
       case AsmJSImm_InvokeFromAsmJS_ToNumber:
         return RedirectCall(FuncCast(InvokeFromAsmJS_ToNumber), Args_General3);
@@ -831,16 +879,53 @@ AsmJSModule::restoreToInitialState(Array
                                                PatchedImmPtr(originalValue));
         }
     }
 #endif
 
     restoreHeapToInitialState(maybePrevBuffer);
 }
 
+bool
+AsmJSModule::detachHeap(JSContext *cx)
+{
+    MOZ_ASSERT(isDynamicallyLinked());
+    MOZ_ASSERT(maybeHeap_);
+
+    // Content JS should not be able to run (and detach heap) from within an
+    // interrupt callback, but in case it does, fail. Otherwise, the heap can
+    // change at an arbitrary instruction and break the assumption below.
+    if (interrupted_) {
+        JS_ReportError(cx, "attempt to detach from inside interrupt handler");
+        return false;
+    }
+
+    // Even if this->active(), to reach here, the activation must have called
+    // out via an FFI stub. FFI stubs check if heapDatum() is null on reentry
+    // and throw an exception if so.
+    MOZ_ASSERT_IF(active(), activation()->exitReason() == AsmJSExit::Reason_IonFFI ||
+                            activation()->exitReason() == AsmJSExit::Reason_SlowFFI);
+
+    AutoUnprotectCode auc(cx, *this);
+    restoreHeapToInitialState(maybeHeap_);
+
+    MOZ_ASSERT(hasDetachedHeap());
+    return true;
+}
+
+bool
+js::OnDetachAsmJSArrayBuffer(JSContext *cx, Handle<ArrayBufferObject*> buffer)
+{
+    for (AsmJSModule *m = cx->runtime()->linkedAsmJSModules; m; m = m->nextLinked()) {
+        if (buffer == m->maybeHeapBufferObject() && !m->detachHeap(cx))
+            return false;
+    }
+    return true;
+}
+
 static void
 AsmJSModuleObject_finalize(FreeOp *fop, JSObject *obj)
 {
     fop->delete_(&obj->as<AsmJSModuleObject>().module());
 }
 
 static void
 AsmJSModuleObject_trace(JSTracer *trc, JSObject *obj)
@@ -1472,46 +1557,16 @@ AsmJSModule::deserialize(ExclusiveContex
 #endif
     (cursor = staticLinkData_.deserialize(cx, cursor));
 
     loadedFromCache_ = true;
 
     return cursor;
 }
 
-// At any time, the executable code of an asm.js module can be protected (as
-// part of RequestInterruptForAsmJSCode). When we touch the executable outside
-// of executing it (which the AsmJSFaultHandler will correctly handle), we need
-// to guard against this by unprotecting the code (if it has been protected) and
-// preventing it from being protected while we are touching it.
-class AutoUnprotectCode
-{
-    JSRuntime *rt_;
-    JSRuntime::AutoLockForInterrupt lock_;
-    const AsmJSModule &module_;
-    const bool protectedBefore_;
-
-  public:
-    AutoUnprotectCode(JSContext *cx, const AsmJSModule &module)
-      : rt_(cx->runtime()),
-        lock_(rt_),
-        module_(module),
-        protectedBefore_(module_.codeIsProtected(rt_))
-    {
-        if (protectedBefore_)
-            module_.unprotectCode(rt_);
-    }
-
-    ~AutoUnprotectCode()
-    {
-        if (protectedBefore_)
-            module_.protectCode(rt_);
-    }
-};
-
 bool
 AsmJSModule::clone(JSContext *cx, ScopedJSDeletePtr<AsmJSModule> *moduleOut) const
 {
     AutoUnprotectCode auc(cx, *this);
 
     *moduleOut = cx->new_<AsmJSModule>(scriptSource_, srcStart_, srcBodyStart_, pod.strict_,
                                        pod.usesSignalHandlers_);
     if (!*moduleOut)
@@ -1556,16 +1611,18 @@ AsmJSModule::clone(JSContext *cx, Scoped