merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 07 Dec 2015 14:15:43 +0100
changeset 309978 59bc3c7a83de7ffb611203912a7da6ad84535a5a
parent 309826 528ea05671e9bd9ccb33d1558a20691a72c85f98 (current diff)
parent 309977 0e03498265086330fc1cad3c047105b46d4c6068 (diff)
child 309982 1009be1b37ec484d73361487a11ec7cf05d5fb35
push id5513
push userraliiev@mozilla.com
push dateMon, 25 Jan 2016 13:55:34 +0000
treeherdermozilla-beta@5ee97dd05b5c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone45.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
accessible/xpcom/Makefile.in
js/src/jit-test/tests/TypedObject/bug976697.js
testing/taskcluster/tasks/tests/fx_unittest_base.yml
testing/web-platform/meta/XMLHttpRequest/send-redirect-to-cors.htm.ini
--- a/Makefile.in
+++ b/Makefile.in
@@ -131,17 +131,17 @@ endif
 .PHONY: $(addprefix install-,$(install_manifests))
 $(addprefix install-,$(filter dist/%,$(install_manifests))): install-dist/%: $(install_manifest_depends)
 	$(call py_action,process_install_manifest,$(if $(NO_REMOVE),--no-remove )$(DIST)/$* _build_manifests/install/dist_$*)
 
 # Dummy wrapper rule to allow the faster backend to piggy back
 install-dist_%: install-dist/% ;
 
 install-_tests: $(install_manifest_depends)
-	$(call py_action,process_install_manifest,$(if $(NO_REMOVE),--no-remove )_tests _build_manifests/install/tests)
+	$(call py_action,process_install_manifest,$(if $(NO_REMOVE),--no-remove )_tests _build_manifests/install/_tests)
 
 # For compatibility
 .PHONY: install-tests
 install-tests: install-_tests
 
 include $(topsrcdir)/build/moz-automation.mk
 
 # dist and _tests should be purged during cleaning. However, we don't want them
deleted file mode 100644
--- a/accessible/xpcom/Makefile.in
+++ /dev/null
@@ -1,10 +0,0 @@
-# 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/.
-
-# We'd like this to be defined in a future GENERATED_EXPORTS list.
-# Bug 1160185 has a few proposals for this.
-INSTALL_TARGETS += xpcaccevents
-xpcaccevents_FILES := xpcAccEvents.h
-xpcaccevents_DEST = $(DIST)/include
-xpcaccevents_TARGET := export
--- a/accessible/xpcom/moz.build
+++ b/accessible/xpcom/moz.build
@@ -19,16 +19,20 @@ UNIFIED_SOURCES += [
     'xpcAccessibleTextRange.cpp',
     'xpcAccessibleValue.cpp',
 ]
 
 SOURCES += [
     '!xpcAccEvents.cpp',
 ]
 
+EXPORTS += [
+    '!xpcAccEvents.h',
+]
+
 LOCAL_INCLUDES += [
     '/accessible/base',
     '/accessible/generic',
 ]
 
 if CONFIG['MOZ_ENABLE_GTK']:
     LOCAL_INCLUDES += [
         '/accessible/atk',
--- a/browser/components/extensions/ext-tabs.js
+++ b/browser/components/extensions/ext-tabs.js
@@ -104,17 +104,17 @@ global.currentWindow = function(context)
   if (pageData) {
     return pageData.parentWindow;
   }
   return WindowManager.topWindow;
 };
 
 // TODO: activeTab permission
 
-extensions.registerAPI((extension, context) => {
+extensions.registerSchemaAPI("tabs", null, (extension, context) => {
   let self = {
     tabs: {
       onActivated: new WindowEventManager(context, "tabs.onActivated", "TabSelect", (fire, event) => {
         let tab = event.originalTarget;
         let tabId = TabManager.getId(tab);
         let windowId = WindowManager.getId(tab.ownerDocument.defaultView);
         fire({tabId, windowId});
       }).api(),
@@ -291,17 +291,17 @@ extensions.registerAPI((extension, conte
             window.gBrowser.pinTab(tab);
           }
 
           if (callback) {
             runSafe(context, callback, TabManager.convert(extension, tab));
           }
         }
 
-        let window = "windowId" in createProperties ?
+        let window = createProperties.windowId !== null ?
           WindowManager.getWindow(createProperties.windowId) :
           WindowManager.topWindow;
         if (!window.gBrowser) {
           let obs = (finishedWindow, topic, data) => {
             if (finishedWindow != window) {
               return;
             }
             Services.obs.removeObserver(obs, "browser-delayed-startup-finished");
@@ -323,52 +323,45 @@ extensions.registerAPI((extension, conte
           tab.ownerDocument.defaultView.gBrowser.removeTab(tab);
         }
 
         if (callback) {
           runSafe(context, callback);
         }
       },
 
-      update: function(...args) {
-        let tabId, updateProperties, callback;
-        if (args.length == 1) {
-          updateProperties = args[0];
-        } else {
-          [tabId, updateProperties, callback] = args;
-        }
-
-        let tab = tabId ? TabManager.getTab(tabId) : TabManager.activeTab;
+      update: function(tabId, updateProperties, callback) {
+        let tab = tabId !== null ? TabManager.getTab(tabId) : TabManager.activeTab;
         let tabbrowser = tab.ownerDocument.defaultView.gBrowser;
-        if ("url" in updateProperties) {
+        if (updateProperties.url !== null) {
           tab.linkedBrowser.loadURI(updateProperties.url);
         }
-        if ("active" in updateProperties) {
+        if (updateProperties.active !== null) {
           if (updateProperties.active) {
             tabbrowser.selectedTab = tab;
           } else {
             // Not sure what to do here? Which tab should we select?
           }
         }
-        if ("pinned" in updateProperties) {
+        if (updateProperties.pinned !== null) {
           if (updateProperties.pinned) {
             tabbrowser.pinTab(tab);
           } else {
             tabbrowser.unpinTab(tab);
           }
         }
-        // FIXME: highlighted/selected, openerTabId
+        // FIXME: highlighted/selected, muted, openerTabId
 
         if (callback) {
           runSafe(context, callback, TabManager.convert(extension, tab));
         }
       },
 
       reload: function(tabId, reloadProperties, callback) {
-        let tab = tabId ? TabManager.getTab(tabId) : TabManager.activeTab;
+        let tab = tabId !== null ? TabManager.getTab(tabId) : TabManager.activeTab;
         let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
         if (reloadProperties && reloadProperties.bypassCache) {
           flags |= Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE;
         }
         tab.linkedBrowser.reloadWithFlags(flags);
 
         if (callback) {
           runSafe(context, callback);
@@ -383,71 +376,59 @@ extensions.registerAPI((extension, conte
       getCurrent(callback) {
         let tab;
         if (context.tabId) {
           tab = TabManager.convert(extension, TabManager.getTab(context.tabId));
         }
         runSafe(context, callback, tab);
       },
 
-      getAllInWindow: function(...args) {
-        let window, callback;
-        if (args.length == 1) {
-          callback = args[0];
-        } else {
-          window = WindowManager.getWindow(args[0]);
-          callback = args[1];
+      getAllInWindow: function(windowId, callback) {
+        if (windowId === null) {
+          windowId = WindowManager.topWindow.windowId;
         }
 
-        if (!window) {
-          window = WindowManager.topWindow;
-        }
-
-        return self.tabs.query({windowId: WindowManager.getId(window)}, callback);
+        return self.tabs.query({windowId}, callback);
       },
 
       query: function(queryInfo, callback) {
-        if (!queryInfo) {
-          queryInfo = {};
-        }
-
         let pattern = null;
-        if (queryInfo.url) {
+        if (queryInfo.url !== null) {
           pattern = new MatchPattern(queryInfo.url);
         }
 
         function matches(window, tab) {
           let props = ["active", "pinned", "highlighted", "status", "title", "index"];
           for (let prop of props) {
-            if (prop in queryInfo && queryInfo[prop] != tab[prop]) {
+            if (queryInfo[prop] !== null && queryInfo[prop] != tab[prop]) {
               return false;
             }
           }
 
           let lastFocused = window == WindowManager.topWindow;
-          if ("lastFocusedWindow" in queryInfo && queryInfo.lastFocusedWindow != lastFocused) {
+          if (queryInfo.lastFocusedWindow !== null && queryInfo.lastFocusedWindow != lastFocused) {
             return false;
           }
 
           let windowType = WindowManager.windowType(window);
-          if ("windowType" in queryInfo && queryInfo.windowType != windowType) {
+          if (queryInfo.windowType !== null && queryInfo.windowType != windowType) {
             return false;
           }
 
-          if ("windowId" in queryInfo) {
+          if (queryInfo.windowId !== null) {
             if (queryInfo.windowId == WindowManager.WINDOW_ID_CURRENT) {
               if (currentWindow(context) != window) {
                 return false;
               }
             } else if (queryInfo.windowId != tab.windowId) {
               return false;
             }
           }
 
-          if ("currentWindow" in queryInfo) {
+          if (queryInfo.currentWindow !== null) {
             let eq = window == currentWindow(context);
             if (queryInfo.currentWindow != eq) {
               return false;
             }
           }
 
           if (pattern && !pattern.matches(Services.io.newURI(tab.url, null, null))) {
             return false;
@@ -464,17 +445,17 @@ extensions.registerAPI((extension, conte
               result.push(tab);
             }
           }
         }
         runSafe(context, callback, result);
       },
 
       _execute: function(tabId, details, kind, callback) {
-        let tab = tabId ? TabManager.getTab(tabId) : TabManager.activeTab;
+        let tab = tabId !== null ? TabManager.getTab(tabId) : TabManager.activeTab;
         let mm = tab.linkedBrowser.messageManager;
 
         let options = {
           js: [],
           css: [],
 
           // We need to send the inner window ID to make sure we only
           // execute the script if the window is currently navigated to
@@ -489,80 +470,75 @@ extensions.registerAPI((extension, conte
         if (TabManager.for(extension).hasActiveTabPermission(tab)) {
           // If we have the "activeTab" permission for this tab, ignore
           // the host whitelist.
           options.matchesHost = ["<all_urls>"];
         } else {
           options.matchesHost = extension.whiteListedHosts.serialize();
         }
 
-        if (details.code) {
+        if (details.code !== null) {
           options[kind + "Code"] = details.code;
         }
-        if (details.file) {
+        if (details.file !== null) {
           let url = context.uri.resolve(details.file);
           if (extension.isExtensionURL(url)) {
             // We should really set |lastError| here, and go straight to
             // the callback, but we don't have |lastError| yet.
             options[kind].push(url);
           }
         }
         if (details.allFrames) {
           options.all_frames = details.allFrames;
         }
         if (details.matchAboutBlank) {
           options.match_about_blank = details.matchAboutBlank;
         }
-        if (details.runAt) {
+        if (details.runAt !== null) {
           options.run_at = details.runAt;
         }
         mm.sendAsyncMessage("Extension:Execute",
                             {extensionId: extension.id, options});
 
         // TODO: Call the callback with the result (which is what???).
       },
 
-      executeScript: function(...args) {
-        if (args.length == 1) {
-          self.tabs._execute(undefined, args[0], "js", undefined);
-        } else {
-          self.tabs._execute(args[0], args[1], "js", args[2]);
-        }
+      executeScript: function(tabId, details, callback) {
+        self.tabs._execute(tabId, details, 'js', callback);
       },
 
-      insertCss: function(...args) {
-        if (args.length == 1) {
-          self.tabs._execute(undefined, args[0], "css", undefined);
-        } else {
-          self.tabs._execute(args[0], args[1], "css", args[2]);
-        }
+      insertCss: function(tabId, details, callback) {
+        self.tabs._execute(tabId, details, 'css', callback);
       },
 
       connect: function(tabId, connectInfo) {
         let tab = TabManager.getTab(tabId);
         let mm = tab.linkedBrowser.messageManager;
 
-        let name = connectInfo.name || "";
+        let name = "";
+        if (connectInfo && connectInfo.name !== null) {
+          name = connectInfo.name;
+        }
         let recipient = {extensionId: extension.id};
-        if ("frameId" in connectInfo) {
+        if (connectInfo && connectInfo.frameId !== null) {
           recipient.frameId = connectInfo.frameId;
         }
         return context.messenger.connect(mm, name, recipient);
       },
 
       sendMessage: function(tabId, message, options, responseCallback) {
         let tab = TabManager.getTab(tabId);
         if (!tab) {
           // ignore sendMessage to non existent tab id
           return;
         }
         let mm = tab.linkedBrowser.messageManager;
 
         let recipient = {extensionId: extension.id};
-        if (options && "frameId" in options) {
+        if (options && options.frameId !== null) {
           recipient.frameId = options.frameId;
         }
         return context.messenger.sendMessage(mm, message, recipient, responseCallback);
       },
     },
   };
   return self;
 });
--- a/browser/components/extensions/ext-utils.js
+++ b/browser/components/extensions/ext-utils.js
@@ -425,16 +425,17 @@ extensions.on("shutdown", (type, extensi
 });
 /* eslint-enable mozilla/balanced-listeners */
 
 // Manages mapping between XUL windows and extension window IDs.
 global.WindowManager = {
   _windows: new WeakMap(),
   _nextId: 0,
 
+  // Note: These must match the values in windows.json.
   WINDOW_ID_NONE: -1,
   WINDOW_ID_CURRENT: -2,
 
   get topWindow() {
     return Services.wm.getMostRecentWindow("navigator:browser");
   },
 
   windowType(window) {
--- a/browser/components/extensions/ext-windows.js
+++ b/browser/components/extensions/ext-windows.js
@@ -9,22 +9,19 @@ XPCOMUtils.defineLazyServiceGetter(this,
 /* globals aboutNewTabService */
 
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 var {
   EventManager,
   runSafe,
 } = ExtensionUtils;
 
-extensions.registerAPI((extension, context) => {
+extensions.registerSchemaAPI("windows", null, (extension, context) => {
   return {
     windows: {
-      WINDOW_ID_CURRENT: WindowManager.WINDOW_ID_CURRENT,
-      WINDOW_ID_NONE: WindowManager.WINDOW_ID_NONE,
-
       onCreated:
       new WindowEventManager(context, "windows.onCreated", "domwindowopened", (fire, window) => {
         fire(WindowManager.convert(extension, window));
       }).api(),
 
       onRemoved:
       new WindowEventManager(context, "windows.onRemoved", "domwindowclosed", (fire, window) => {
         fire(WindowManager.getId(window));
@@ -46,31 +43,21 @@ extensions.registerAPI((extension, conte
       }).api(),
 
       get: function(windowId, getInfo, callback) {
         let window = WindowManager.getWindow(windowId);
         runSafe(context, callback, WindowManager.convert(extension, window, getInfo));
       },
 
       getCurrent: function(getInfo, callback) {
-        if (!callback) {
-          callback = getInfo;
-          getInfo = {};
-        }
         let window = currentWindow(context);
         runSafe(context, callback, WindowManager.convert(extension, window, getInfo));
       },
 
-      getLastFocused: function(...args) {
-        let getInfo, callback;
-        if (args.length == 1) {
-          callback = args[0];
-        } else {
-          [getInfo, callback] = args;
-        }
+      getLastFocused: function(getInfo, callback) {
         let window = WindowManager.topWindow;
         runSafe(context, callback, WindowManager.convert(extension, window, getInfo));
       },
 
       getAll: function(getInfo, callback) {
         let e = Services.wm.getEnumerator("navigator:browser");
         let windows = [];
         while (e.hasMoreElements()) {
@@ -85,50 +72,50 @@ extensions.registerAPI((extension, conte
       create: function(createData, callback) {
         function mkstr(s) {
           let result = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
           result.data = s;
           return result;
         }
 
         let args = Cc["@mozilla.org/supports-array;1"].createInstance(Ci.nsISupportsArray);
-        if ("url" in createData) {
+        if (createData.url !== null) {
           if (Array.isArray(createData.url)) {
             let array = Cc["@mozilla.org/supports-array;1"].createInstance(Ci.nsISupportsArray);
             for (let url of createData.url) {
               array.AppendElement(mkstr(url));
             }
             args.AppendElement(array);
           } else {
             args.AppendElement(mkstr(createData.url));
           }
         } else {
           args.AppendElement(mkstr(aboutNewTabService.newTabURL));
         }
 
         let extraFeatures = "";
-        if ("incognito" in createData) {
+        if (createData.incognito !== null) {
           if (createData.incognito) {
             extraFeatures += ",private";
           } else {
             extraFeatures += ",non-private";
           }
         }
 
         let window = Services.ww.openWindow(null, "chrome://browser/content/browser.xul", "_blank",
                                             "chrome,dialog=no,all" + extraFeatures, args);
 
-        if ("left" in createData || "top" in createData) {
-          let left = "left" in createData ? createData.left : window.screenX;
-          let top = "top" in createData ? createData.top : window.screenY;
+        if (createData.left !== null || createData.top !== null) {
+          let left = createData.left !== null ? createData.left : window.screenX;
+          let top = createData.top !== null ? createData.top : window.screenY;
           window.moveTo(left, top);
         }
-        if ("width" in createData || "height" in createData) {
-          let width = "width" in createData ? createData.width : window.outerWidth;
-          let height = "height" in createData ? createData.height : window.outerHeight;
+        if (createData.width !== null || createData.height !== null) {
+          let width = createData.width !== null ? createData.width : window.outerWidth;
+          let height = createData.height !== null ? createData.height : window.outerHeight;
           window.resizeTo(width, height);
         }
 
         // TODO: focused, type, state
 
         window.addEventListener("load", function listener() {
           window.removeEventListener("load", listener);
           if (callback) {
--- a/browser/components/extensions/moz.build
+++ b/browser/components/extensions/moz.build
@@ -1,9 +1,11 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 JAR_MANIFESTS += ['jar.mn']
 
+DIRS += ['schemas']
+
 BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini']
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/schemas/LICENSE
@@ -0,0 +1,27 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/schemas/jar.mn
@@ -0,0 +1,7 @@
+# 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/.
+
+browser.jar:
+    content/browser/schemas/tabs.json
+    content/browser/schemas/windows.json
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/schemas/moz.build
@@ -0,0 +1,7 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+JAR_MANIFESTS += ['jar.mn']
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/schemas/tabs.json
@@ -0,0 +1,1190 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+[
+  {
+    "namespace": "tabs",
+    "description": "Use the <code>browser.tabs</code> API to interact with the browser's tab system. You can use this API to create, modify, and rearrange tabs in the browser.",
+    "types": [
+      { "id": "MutedInfoReason",
+        "type": "string",
+        "description": "An event that caused a muted state change.",
+        "enum": [
+          {"name": "user", "description": "A user input action has set/overridden the muted state."},
+          {"name": "capture", "description": "Tab capture started, forcing a muted state change."},
+          {"name": "extension", "description": "An extension, identified by the extensionId field, set the muted state."}
+        ]
+      },
+      {
+        "id": "MutedInfo",
+        "type": "object",
+        "description": "Tab muted state and the reason for the last state change.",
+        "properties": {
+          "muted": {
+            "type": "boolean",
+            "description": "Whether the tab is prevented from playing sound (but hasn't necessarily recently produced sound). Equivalent to whether the muted audio indicator is showing."
+          },
+          "reason": {
+            "$ref": "MutedInfoReason",
+            "optional": true,
+            "description": "The reason the tab was muted or unmuted. Not set if the tab's mute state has never been changed."
+          },
+          "extensionId": {
+            "type": "string",
+            "optional": true,
+            "description": "The ID of the extension that changed the muted state. Not set if an extension was not the reason the muted state last changed."
+          }
+        }
+      },
+      {
+        "id": "Tab",
+        "type": "object",
+        "properties": {
+          "id": {"type": "integer", "minimum": -1, "optional": true, "description": "The ID of the tab. Tab IDs are unique within a browser session. Under some circumstances a Tab may not be assigned an ID, for example when querying foreign tabs using the $(ref:sessions) API, in which case a session ID may be present. Tab ID can also be set to $(ref:tabs.TAB_ID_NONE) for apps and devtools windows."},
+          "index": {"type": "integer", "minimum": -1, "description": "The zero-based index of the tab within its window."},
+          "windowId": {"type": "integer", "minimum": 0, "description": "The ID of the window the tab is contained within."},
+          "openerTabId": {"unsupported": true, "type": "integer", "minimum": 0, "optional": true, "description": "The ID of the tab that opened this tab, if any. This property is only present if the opener tab still exists."},
+          "selected": {"type": "boolean", "description": "Whether the tab is selected.", "deprecated": "Please use $(ref:tabs.Tab.highlighted).", "unsupported": true},
+          "highlighted": {"type": "boolean", "description": "Whether the tab is highlighted."},
+          "active": {"type": "boolean", "description": "Whether the tab is active in its window. (Does not necessarily mean the window is focused.)"},
+          "pinned": {"type": "boolean", "description": "Whether the tab is pinned."},
+          "audible": {"unsupported": true, "type": "boolean", "optional": true, "description": "Whether the tab has produced sound over the past couple of seconds (but it might not be heard if also muted). Equivalent to whether the speaker audio indicator is showing."},
+          "mutedInfo": {"unsupported": true, "$ref": "MutedInfo", "optional": true, "description": "Current tab muted state and the reason for the last state change."},
+          "url": {"type": "string", "optional": true, "description": "The URL the tab is displaying. This property is only present if the extension's manifest includes the <code>\"tabs\"</code> permission."},
+          "title": {"type": "string", "optional": true, "description": "The title of the tab. This property is only present if the extension's manifest includes the <code>\"tabs\"</code> permission."},
+          "favIconUrl": {"type": "string", "optional": true, "description": "The URL of the tab's favicon. This property is only present if the extension's manifest includes the <code>\"tabs\"</code> permission. It may also be an empty string if the tab is loading."},
+          "status": {"type": "string", "optional": true, "description": "Either <em>loading</em> or <em>complete</em>."},
+          "incognito": {"type": "boolean", "description": "Whether the tab is in an incognito window."},
+          "width": {"type": "integer", "optional": true, "description": "The width of the tab in pixels."},
+          "height": {"type": "integer", "optional": true, "description": "The height of the tab in pixels."},
+          "sessionId": {"unsupported": true, "type": "string", "optional": true, "description": "The session ID used to uniquely identify a Tab obtained from the $(ref:sessions) API."}
+        }
+      },
+      {
+        "id": "ZoomSettingsMode",
+        "type": "string",
+        "description": "Defines how zoom changes are handled, i.e. which entity is responsible for the actual scaling of the page; defaults to <code>automatic</code>.",
+        "enum": [
+          {
+            "name": "automatic",
+            "description": "Zoom changes are handled automatically by the browser."
+          },
+          {
+            "name": "manual",
+            "description": "Overrides the automatic handling of zoom changes. The <code>onZoomChange</code> event will still be dispatched, and it is the responsibility of the extension to listen for this event and manually scale the page. This mode does not support <code>per-origin</code> zooming, and will thus ignore the <code>scope</code> zoom setting and assume <code>per-tab</code>."
+          },
+          {
+            "name": "disabled",
+            "description": "Disables all zooming in the tab. The tab will revert to the default zoom level, and all attempted zoom changes will be ignored."
+          }
+        ]
+      },
+      {
+        "id": "ZoomSettingsScope",
+        "type": "string",
+        "description": "Defines whether zoom changes will persist for the page's origin, or only take effect in this tab; defaults to <code>per-origin</code> when in <code>automatic</code> mode, and <code>per-tab</code> otherwise.",
+        "enum": [
+          {
+            "name": "per-origin",
+            "description": "Zoom changes will persist in the zoomed page's origin, i.e. all other tabs navigated to that same origin will be zoomed as well. Moreover, <code>per-origin</code> zoom changes are saved with the origin, meaning that when navigating to other pages in the same origin, they will all be zoomed to the same zoom factor. The <code>per-origin</code> scope is only available in the <code>automatic</code> mode."
+          },
+          {
+            "name": "per-tab",
+            "description": "Zoom changes only take effect in this tab, and zoom changes in other tabs will not affect the zooming of this tab. Also, <code>per-tab</code> zoom changes are reset on navigation; navigating a tab will always load pages with their <code>per-origin</code> zoom factors."
+          }
+        ]
+      },
+      {
+        "id": "ZoomSettings",
+        "type": "object",
+        "description": "Defines how zoom changes in a tab are handled and at what scope.",
+        "properties": {
+          "mode": {
+            "$ref": "ZoomSettingsMode",
+            "description": "Defines how zoom changes are handled, i.e. which entity is responsible for the actual scaling of the page; defaults to <code>automatic</code>.",
+            "optional": true
+          },
+          "scope": {
+            "$ref": "ZoomSettingsScope",
+            "description": "Defines whether zoom changes will persist for the page's origin, or only take effect in this tab; defaults to <code>per-origin</code> when in <code>automatic</code> mode, and <code>per-tab</code> otherwise.",
+            "optional": true
+          },
+          "defaultZoomFactor": {
+            "type": "number",
+            "optional": true,
+            "description": "Used to return the default zoom level for the current tab in calls to tabs.getZoomSettings."
+          }
+        }
+      },
+      {
+        "id": "TabStatus",
+        "type": "string",
+        "enum": ["loading", "complete"],
+        "description": "Whether the tabs have completed loading."
+      },
+      {
+        "id": "WindowType",
+        "type": "string",
+        "enum": ["normal", "popup", "panel", "app", "devtools"],
+        "description": "The type of window."
+      }
+    ],
+    "properties": {
+      "TAB_ID_NONE": {
+        "value": -1,
+        "description": "An ID which represents the absence of a browser tab."
+      }
+    },
+    "functions": [
+      {
+        "name": "get",
+        "type": "function",
+        "description": "Retrieves details about the specified tab.",
+        "parameters": [
+          {
+            "type": "integer",
+            "name": "tabId",
+            "minimum": 0
+          },
+          {
+            "type": "function",
+            "name": "callback",
+            "parameters": [
+              {"name": "tab", "$ref": "Tab"}
+            ]
+          }
+        ]
+      },
+      {
+        "name": "getCurrent",
+        "type": "function",
+        "description": "Gets the tab that this script call is being made from. May be undefined if called from a non-tab context (for example: a background page or popup view).",
+        "parameters": [
+          {
+            "type": "function",
+            "name": "callback",
+            "parameters": [
+              {
+                "name": "tab",
+                "$ref": "Tab",
+                "optional": true
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "connect",
+        "type": "function",
+        "description": "Connects to the content script(s) in the specified tab. The $(ref:runtime.onConnect) event is fired in each content script running in the specified tab for the current extension. For more details, see $(topic:messaging)[Content Script Messaging].",
+        "parameters": [
+          {
+            "type": "integer",
+            "name": "tabId",
+            "minimum": 0
+          },
+          {
+            "type": "object",
+            "name": "connectInfo",
+            "properties": {
+              "name": { "type": "string", "optional": true, "description": "Will be passed into onConnect for content scripts that are listening for the connection event." },
+              "frameId": {
+                "type": "integer",
+                "optional": true,
+                "minimum": 0,
+                "description": "Open a port to a specific $(topic:frame_ids)[frame] identified by <code>frameId</code> instead of all frames in the tab."
+              }
+            },
+            "optional": true
+          }
+        ],
+        "returns": {
+          "$ref": "runtime.Port",
+          "description": "A port that can be used to communicate with the content scripts running in the specified tab. The port's $(ref:runtime.Port) event is fired if the tab closes or does not exist. "
+        }
+      },
+      {
+        "name": "sendRequest",
+        "deprecated": "Please use $(ref:runtime.sendMessage).",
+        "unsupported": true,
+        "type": "function",
+        "description": "Sends a single request to the content script(s) in the specified tab, with an optional callback to run when a response is sent back.  The $(ref:extension.onRequest) event is fired in each content script running in the specified tab for the current extension.",
+        "parameters": [
+          {
+            "type": "integer",
+            "name": "tabId",
+            "minimum": 0
+          },
+          {
+            "type": "any",
+            "name": "request"
+          },
+          {
+            "type": "function",
+            "name": "responseCallback",
+            "optional": true,
+            "parameters": [
+              {
+                "name": "response",
+                "type": "any",
+                "description": "The JSON response object sent by the handler of the request. If an error occurs while connecting to the specified tab, the callback will be called with no arguments and $(ref:runtime.lastError) will be set to the error message."
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "sendMessage",
+        "type": "function",
+        "description": "Sends a single message to the content script(s) in the specified tab, with an optional callback to run when a response is sent back.  The $(ref:runtime.onMessage) event is fired in each content script running in the specified tab for the current extension.",
+        "parameters": [
+          {
+            "type": "integer",
+            "name": "tabId",
+            "minimum": 0
+          },
+          {
+            "type": "any",
+            "name": "message"
+          },
+          {
+            "type": "object",
+            "name": "options",
+            "properties": {
+              "frameId": {
+                "type": "integer",
+                "optional": true,
+                "minimum": 0,
+                "description": "Send a message to a specific $(topic:frame_ids)[frame] identified by <code>frameId</code> instead of all frames in the tab."
+              }
+            },
+            "optional": true
+          },
+          {
+            "type": "function",
+            "name": "responseCallback",
+            "optional": true,
+            "parameters": [
+              {
+                "name": "response",
+                "type": "any",
+                "description": "The JSON response object sent by the handler of the message. If an error occurs while connecting to the specified tab, the callback will be called with no arguments and $(ref:runtime.lastError) will be set to the error message."
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "getSelected",
+        "deprecated": "Please use $(ref:tabs.query) <code>{active: true}</code>.",
+        "unsupported": true,
+        "type": "function",
+        "description": "Gets the tab that is selected in the specified window.",
+        "parameters": [
+          {
+            "type": "integer",
+            "name": "windowId",
+            "minimum": -2,
+            "optional": true,
+            "description": "Defaults to the $(topic:current-window)[current window]."
+          },
+          {
+            "type": "function",
+            "name": "callback",
+            "parameters": [
+              {"name": "tab", "$ref": "Tab"}
+            ]
+          }
+        ]
+      },
+      {
+        "name": "getAllInWindow",
+        "type": "function",
+        "deprecated": "Please use $(ref:tabs.query) <code>{windowId: windowId}</code>.",
+        "description": "Gets details about all tabs in the specified window.",
+        "parameters": [
+          {
+            "type": "integer",
+            "name": "windowId",
+            "minimum": -2,
+            "optional": true,
+            "description": "Defaults to the $(topic:current-window)[current window]."
+            },
+          {
+            "type": "function",
+            "name": "callback",
+            "parameters": [
+              {"name": "tabs", "type": "array", "items": { "$ref": "Tab" } }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "create",
+        "type": "function",
+        "description": "Creates a new tab.",
+        "parameters": [
+          {
+            "type": "object",
+            "name": "createProperties",
+            "properties": {
+              "windowId": {
+                "type": "integer",
+                "minimum": -2,
+                "optional": true,
+                "description": "The window to create the new tab in. Defaults to the $(topic:current-window)[current window]."
+              },
+              "index": {
+                "type": "integer",
+                "minimum": 0,
+                "optional": true,
+                "description": "The position the tab should take in the window. The provided value will be clamped to between zero and the number of tabs in the window."
+              },
+              "url": {
+                "type": "string",
+                "optional": true,
+                "description": "The URL to navigate the tab to initially. Fully-qualified URLs must include a scheme (i.e. 'http://www.google.com', not 'www.google.com'). Relative URLs will be relative to the current page within the extension. Defaults to the New Tab Page."
+              },
+              "active": {
+                "type": "boolean",
+                "optional": true,
+                "description": "Whether the tab should become the active tab in the window. Does not affect whether the window is focused (see $(ref:windows.update)). Defaults to <var>true</var>."
+              },
+              "selected": {
+                "deprecated": "Please use <em>active</em>.",
+                "unsupported": true,
+                "type": "boolean",
+                "optional": true,
+                "description": "Whether the tab should become the selected tab in the window. Defaults to <var>true</var>"
+              },
+              "pinned": {
+                "type": "boolean",
+                "optional": true,
+                "description": "Whether the tab should be pinned. Defaults to <var>false</var>"
+              },
+              "openerTabId": {
+                "unsupported": true,
+                "type": "integer",
+                "minimum": 0,
+                "optional": true,
+                "description": "The ID of the tab that opened this tab. If specified, the opener tab must be in the same window as the newly created tab."
+              }
+            }
+          },
+          {
+            "type": "function",
+            "name": "callback",
+            "optional": true,
+            "parameters": [
+              {
+                "name": "tab",
+                "$ref": "Tab",
+                "description": "Details about the created tab. Will contain the ID of the new tab."
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "duplicate",
+        "type": "function",
+        "description": "Duplicates a tab.",
+        "parameters": [
+          {
+            "type": "integer",
+            "name": "tabId",
+            "minimum": 0,
+            "description": "The ID of the tab which is to be duplicated."
+          },
+          {
+            "type": "function",
+            "name": "callback",
+            "optional": true,
+            "parameters": [
+              {
+                "name": "tab",
+                "optional": true,
+                "description": "Details about the duplicated tab. The $(ref:tabs.Tab) object doesn't contain <code>url</code>, <code>title</code> and <code>favIconUrl</code> if the <code>\"tabs\"</code> permission has not been requested.",
+                "$ref": "Tab"
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "query",
+        "type": "function",
+        "description": "Gets all tabs that have the specified properties, or all tabs if no properties are specified.",
+        "parameters": [
+          {
+            "type": "object",
+            "name": "queryInfo",
+            "properties": {
+              "active": {
+                "type": "boolean",
+                "optional": true,
+                "description": "Whether the tabs are active in their windows."
+              },
+              "pinned": {
+                "type": "boolean",
+                "optional": true,
+                "description": "Whether the tabs are pinned."
+              },
+              "audible": {
+                "unsupported": true,
+                "type": "boolean",
+                "optional": true,
+                "description": "Whether the tabs are audible."
+              },
+              "muted": {
+                "unsupported": true,
+                "type": "boolean",
+                "optional": true,
+                "description": "Whether the tabs are muted."
+              },
+              "highlighted": {
+                "type": "boolean",
+                "optional": true,
+                "description": "Whether the tabs are highlighted."
+              },
+              "currentWindow": {
+                "type": "boolean",
+                "optional": true,
+                "description": "Whether the tabs are in the $(topic:current-window)[current window]."
+              },
+              "lastFocusedWindow": {
+                "type": "boolean",
+                "optional": true,
+                "description": "Whether the tabs are in the last focused window."
+              },
+              "status": {
+                "$ref": "TabStatus",
+                "optional": true,
+                "description": "Whether the tabs have completed loading."
+              },
+              "title": {
+                "type": "string",
+                "optional": true,
+                "description": "Match page titles against a pattern."
+              },
+              "url": {
+                "choices": [
+                  {"type": "string"},
+                  {"type": "array", "items": {"type": "string"}}
+                ],
+                "optional": true,
+                "description": "Match tabs against one or more $(topic:match_patterns)[URL patterns]. Note that fragment identifiers are not matched."
+              },
+              "windowId": {
+                "type": "integer",
+                "optional": true,
+                "minimum": -2,
+                "description": "The ID of the parent window, or $(ref:windows.WINDOW_ID_CURRENT) for the $(topic:current-window)[current window]."
+              },
+              "windowType": {
+                "$ref": "WindowType",
+                "optional": true,
+                "description": "The type of window the tabs are in."
+              },
+              "index": {
+                "type": "integer",
+                "optional": true,
+                "minimum": 0,
+                "description": "The position of the tabs within their windows."
+              }
+            }
+          },
+          {
+            "type": "function",
+            "name": "callback",
+            "parameters": [
+              {
+                "name": "result",
+                "type": "array",
+                "items": {
+                  "$ref": "Tab"
+                }
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "highlight",
+        "type": "function",
+        "description": "Highlights the given tabs.",
+        "parameters": [
+          {
+            "type": "object",
+            "name": "highlightInfo",
+            "properties": {
+               "windowId": {
+                 "type": "integer",
+                 "optional": true,
+                 "description": "The window that contains the tabs.",
+                 "minimum": -2
+               },
+               "tabs": {
+                 "description": "One or more tab indices to highlight.",
+                 "choices": [
+                   {"type": "array", "items": {"type": "integer", "minimum": 0}},
+                   {"type": "integer"}
+                 ]
+               }
+             }
+           },
+           {
+             "type": "function",
+             "name": "callback",
+             "optional": true,
+             "parameters": [
+               {
+                 "name": "window",
+                 "$ref": "windows.Window",
+                 "description": "Contains details about the window whose tabs were highlighted."
+               }
+             ]
+           }
+        ]
+      },
+      {
+        "name": "update",
+        "type": "function",
+        "description": "Modifies the properties of a tab. Properties that are not specified in <var>updateProperties</var> are not modified.",
+        "parameters": [
+          {
+            "type": "integer",
+            "name": "tabId",
+            "minimum": 0,
+            "optional": true,
+            "description": "Defaults to the selected tab of the $(topic:current-window)[current window]."
+          },
+          {
+            "type": "object",
+            "name": "updateProperties",
+            "properties": {
+              "url": {
+                "type": "string",
+                "optional": true,
+                "description": "A URL to navigate the tab to."
+              },
+              "active": {
+                "type": "boolean",
+                "optional": true,
+                "description": "Whether the tab should be active. Does not affect whether the window is focused (see $(ref:windows.update))."
+              },
+              "highlighted": {
+                "unsupported": true,
+                "type": "boolean",
+                "optional": true,
+                "description": "Adds or removes the tab from the current selection."
+              },
+              "selected": {
+                "unsupported": true,
+                "deprecated": "Please use <em>highlighted</em>.",
+                "type": "boolean",
+                "optional": true,
+                "description": "Whether the tab should be selected."
+              },
+              "pinned": {
+                "type": "boolean",
+                "optional": true,
+                "description": "Whether the tab should be pinned."
+              },
+              "muted": {
+                "unsupported": true,
+                "type": "boolean",
+                "optional": true,
+                "description": "Whether the tab should be muted."
+              },
+              "openerTabId": {
+                "unsupported": true,
+                "type": "integer",
+                "minimum": 0,
+                "optional": true,
+                "description": "The ID of the tab that opened this tab. If specified, the opener tab must be in the same window as this tab."
+              }
+            }
+          },
+          {
+            "type": "function",
+            "name": "callback",
+            "optional": true,
+            "parameters": [
+              {
+                "name": "tab",
+                "$ref": "Tab",
+                "optional": true,
+                "description": "Details about the updated tab. The $(ref:tabs.Tab) object doesn't contain <code>url</code>, <code>title</code> and <code>favIconUrl</code> if the <code>\"tabs\"</code> permission has not been requested."
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "move",
+        "type": "function",
+        "description": "Moves one or more tabs to a new position within its window, or to a new window. Note that tabs can only be moved to and from normal (window.type === \"normal\") windows.",
+        "parameters": [
+          {
+            "name": "tabIds",
+            "description": "The tab or list of tabs to move.",
+            "choices": [
+              {"type": "integer", "minimum": 0},
+              {"type": "array", "items": {"type": "integer", "minimum": 0}}
+            ]
+          },
+          {
+            "type": "object",
+            "name": "moveProperties",
+            "properties": {
+              "windowId": {
+                "type": "integer",
+                "minimum": -2,
+                "optional": true,
+                "description": "Defaults to the window the tab is currently in."
+              },
+              "index": {
+                "type": "integer",
+                "minimum": -1,
+                "description": "The position to move the window to. -1 will place the tab at the end of the window."
+              }
+            }
+          },
+          {
+            "type": "function",
+            "name": "callback",
+            "optional": true,
+            "parameters": [
+              {
+                "name": "tabs",
+                "description": "Details about the moved tabs.",
+                "choices": [
+                  {"$ref": "Tab"},
+                  {"type": "array", "items": {"$ref": "Tab"}}
+                ]
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "reload",
+        "type": "function",
+        "description": "Reload a tab.",
+        "parameters": [
+          {"type": "integer", "name": "tabId", "minimum": 0, "optional": true, "description": "The ID of the tab to reload; defaults to the selected tab of the current window."},
+          {
+            "type": "object",
+            "name": "reloadProperties",
+            "optional": true,
+            "properties": {
+              "bypassCache": {
+                "type": "boolean",
+                "optional": true,
+                "description": "Whether using any local cache. Default is false."
+              }
+            }
+          },
+          {"type": "function", "name": "callback", "optional": true, "parameters": []}
+        ]
+      },
+      {
+        "name": "remove",
+        "type": "function",
+        "description": "Closes one or more tabs.",
+        "parameters": [
+          {
+            "name": "tabIds",
+            "description": "The tab or list of tabs to close.",
+            "choices": [
+              {"type": "integer", "minimum": 0},
+              {"type": "array", "items": {"type": "integer", "minimum": 0}}
+            ]
+          },
+          {"type": "function", "name": "callback", "optional": true, "parameters": []}
+        ]
+      },
+      {
+        "name": "detectLanguage",
+        "type": "function",
+        "description": "Detects the primary language of the content in a tab.",
+        "parameters": [
+          {
+            "type": "integer",
+            "name": "tabId",
+            "minimum": 0,
+            "optional": true,
+            "description": "Defaults to the active tab of the $(topic:current-window)[current window]."
+          },
+          {
+            "type": "function",
+            "name": "callback",
+            "parameters": [
+              {
+                "type": "string",
+                "name": "language",
+                "description": "An ISO language code such as <code>en</code> or <code>fr</code>. For a complete list of languages supported by this method, see <a href='http://src.chromium.org/viewvc/chrome/trunk/src/third_party/cld/languages/internal/languages.cc'>kLanguageInfoTable</a>. The 2nd to 4th columns will be checked and the first non-NULL value will be returned except for Simplified Chinese for which zh-CN will be returned. For an unknown language, <code>und</code> will be returned."
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "captureVisibleTab",
+        "type": "function",
+        "description": "Captures the visible area of the currently active tab in the specified window. You must have $(topic:declare_permissions)[&lt;all_urls&gt;] permission to use this method.",
+        "parameters": [
+          {
+            "type": "integer",
+            "name": "windowId",
+            "minimum": -2,
+            "optional": true,
+            "description": "The target window. Defaults to the $(topic:current-window)[current window]."
+          },
+          {
+            "$ref": "extensionTypes.ImageDetails",
+            "name": "options",
+            "optional": true
+          },
+          {
+            "type": "function", "name": "callback", "parameters": [
+              {"type": "string", "name": "dataUrl", "description": "A data URL which encodes an image of the visible area of the captured tab. May be assigned to the 'src' property of an HTML Image element for display."}
+            ]
+          }
+        ]
+      },
+      {
+        "name": "executeScript",
+        "type": "function",
+        "description": "Injects JavaScript code into a page. For details, see the $(topic:content_scripts)[programmatic injection] section of the content scripts doc.",
+        "parameters": [
+          {"type": "integer", "name": "tabId", "minimum": 0, "optional": true, "description": "The ID of the tab in which to run the script; defaults to the active tab of the current window."},
+          {
+            "$ref": "extensionTypes.InjectDetails",
+            "name": "details",
+            "description": "Details of the script to run."
+          },
+          {
+            "type": "function",
+            "name": "callback",
+            "optional": true,
+            "description": "Called after all the JavaScript has been executed.",
+            "parameters": [
+              {
+                "name": "result",
+                "optional": true,
+                "type": "array",
+                "items": {"type": "any", "minimum": 0},
+                "description": "The result of the script in every injected frame."
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "insertCSS",
+        "type": "function",
+        "description": "Injects CSS into a page. For details, see the $(topic:content_scripts)[programmatic injection] section of the content scripts doc.",
+        "parameters": [
+          {"type": "integer", "name": "tabId", "minimum": 0, "optional": true, "description": "The ID of the tab in which to insert the CSS; defaults to the active tab of the current window."},
+          {
+            "$ref": "extensionTypes.InjectDetails",
+            "name": "details",
+            "description": "Details of the CSS text to insert."
+          },
+          {
+            "type": "function",
+            "name": "callback",
+            "optional": true,
+            "description": "Called when all the CSS has been inserted.",
+            "parameters": []
+          }
+        ]
+      },
+      {
+        "name": "setZoom",
+        "type": "function",
+        "description": "Zooms a specified tab.",
+        "parameters": [
+          {
+            "type": "integer",
+            "name": "tabId",
+            "minimum": 0,
+            "optional": true,
+            "description": "The ID of the tab to zoom; defaults to the active tab of the current window."
+          },
+          {
+            "type": "number",
+            "name": "zoomFactor",
+            "description": "The new zoom factor. Use a value of 0 here to set the tab to its current default zoom factor. Values greater than zero specify a (possibly non-default) zoom factor for the tab."
+          },
+          {
+            "type": "function",
+            "name": "callback",
+            "optional": true,
+            "description": "Called after the zoom factor has been changed.",
+            "parameters": []
+          }
+        ]
+      },
+      {
+        "name": "getZoom",
+        "type": "function",
+        "description": "Gets the current zoom factor of a specified tab.",
+        "parameters": [
+          {
+            "type": "integer",
+            "name": "tabId",
+            "minimum": 0,
+            "optional": true,
+            "description": "The ID of the tab to get the current zoom factor from; defaults to the active tab of the current window."
+          },
+          {
+            "type": "function",
+            "name": "callback",
+            "description": "Called with the tab's current zoom factor after it has been fetched.",
+            "parameters": [
+              {
+                "type": "number",
+                "name": "zoomFactor",
+                "description": "The tab's current zoom factor."
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "setZoomSettings",
+        "type": "function",
+        "description": "Sets the zoom settings for a specified tab, which define how zoom changes are handled. These settings are reset to defaults upon navigating the tab.",
+        "parameters": [
+          {
+            "type": "integer",
+            "name": "tabId",
+            "optional": true,
+            "minimum": 0,
+            "description": "The ID of the tab to change the zoom settings for; defaults to the active tab of the current window."
+          },
+          {
+            "$ref": "ZoomSettings",
+            "name": "zoomSettings",
+            "description": "Defines how zoom changes are handled and at what scope."
+          },
+          {
+            "type": "function",
+            "name": "callback",
+            "optional": true,
+            "description": "Called after the zoom settings have been changed.",
+            "parameters": []
+          }
+        ]
+      },
+      {
+        "name": "getZoomSettings",
+        "type": "function",
+        "description": "Gets the current zoom settings of a specified tab.",
+        "parameters": [
+          {
+            "type": "integer",
+            "name": "tabId",
+            "optional": true,
+            "minimum": 0,
+            "description": "The ID of the tab to get the current zoom settings from; defaults to the active tab of the current window."
+          },
+          {
+            "type": "function",
+            "name": "callback",
+            "description": "Called with the tab's current zoom settings.",
+            "parameters": [
+              {
+                "$ref": "ZoomSettings",
+                "name": "zoomSettings",
+                "description": "The tab's current zoom settings."
+              }
+            ]
+          }
+        ]
+      }
+    ],
+    "events": [
+      {
+        "name": "onCreated",
+        "type": "function",
+        "description": "Fired when a tab is created. Note that the tab's URL may not be set at the time this event fired, but you can listen to onUpdated events to be notified when a URL is set.",
+        "parameters": [
+          {
+            "$ref": "Tab",
+            "name": "tab",
+            "description": "Details of the tab that was created."
+          }
+        ]
+      },
+      {
+        "name": "onUpdated",
+        "type": "function",
+        "description": "Fired when a tab is updated.",
+        "parameters": [
+          {"type": "integer", "name": "tabId", "minimum": 0},
+          {
+            "type": "object",
+            "name": "changeInfo",
+            "description": "Lists the changes to the state of the tab that was updated.",
+            "properties": {
+              "status": {
+                "type": "string",
+                "optional": true,
+                "description": "The status of the tab. Can be either <em>loading</em> or <em>complete</em>."
+              },
+              "url": {
+                "type": "string",
+                "optional": true,
+                "description": "The tab's URL if it has changed."
+              },
+              "pinned": {
+                "type": "boolean",
+                "optional": true,
+                "description": "The tab's new pinned state."
+              },
+              "audible": {
+                "unsupported": true,
+                "type": "boolean",
+                "optional": true,
+                "description": "The tab's new audible state."
+              },
+              "mutedInfo": {
+                "unsupported": true,
+                "$ref": "MutedInfo",
+                "optional": true,
+                "description": "The tab's new muted state and the reason for the change."
+              },
+              "favIconUrl": {
+                "type": "string",
+                "optional": true,
+                "description": "The tab's new favicon URL."
+              }
+            }
+          },
+          {
+            "$ref": "Tab",
+            "name": "tab",
+            "description": "Gives the state of the tab that was updated."
+          }
+        ]
+      },
+      {
+        "name": "onMoved",
+        "type": "function",
+        "description": "Fired when a tab is moved within a window. Only one move event is fired, representing the tab the user directly moved. Move events are not fired for the other tabs that must move in response. This event is not fired when a tab is moved between windows. For that, see $(ref:tabs.onDetached).",
+        "parameters": [
+          {"type": "integer", "name": "tabId", "minimum": 0},
+          {
+            "type": "object",
+            "name": "moveInfo",
+            "properties": {
+              "windowId": {"type": "integer", "minimum": 0},
+              "fromIndex": {"type": "integer", "minimum": 0},
+              "toIndex": {"type": "integer", "minimum": 0}
+            }
+          }
+        ]
+      },
+      {
+        "name": "onSelectionChanged",
+        "deprecated": "Please use $(ref:tabs.onActivated).",
+        "unsupported": true,
+        "type": "function",
+        "description": "Fires when the selected tab in a window changes.",
+        "parameters": [
+          {
+            "type": "integer",
+            "name": "tabId",
+            "minimum": 0,
+            "description": "The ID of the tab that has become active."
+          },
+          {
+            "type": "object",
+            "name": "selectInfo",
+            "properties": {
+              "windowId": {
+                "type": "integer",
+                "minimum": 0,
+                "description": "The ID of the window the selected tab changed inside of."
+              }
+            }
+          }
+        ]
+      },
+      {
+        "name": "onActiveChanged",
+        "deprecated": "Please use $(ref:tabs.onActivated).",
+        "unsupported": true,
+        "type": "function",
+        "description": "Fires when the selected tab in a window changes. Note that the tab's URL may not be set at the time this event fired, but you can listen to $(ref:tabs.onUpdated) events to be notified when a URL is set.",
+        "parameters": [
+          {
+            "type": "integer",
+            "name": "tabId",
+            "minimum": 0,
+            "description": "The ID of the tab that has become active."
+          },
+          {
+            "type": "object",
+            "name": "selectInfo",
+            "properties": {
+              "windowId": {
+                "type": "integer",
+                "minimum": 0,
+                "description": "The ID of the window the selected tab changed inside of."
+              }
+            }
+          }
+        ]
+      },
+      {
+        "name": "onActivated",
+        "type": "function",
+        "description": "Fires when the active tab in a window changes. Note that the tab's URL may not be set at the time this event fired, but you can listen to onUpdated events to be notified when a URL is set.",
+        "parameters": [
+          {
+            "type": "object",
+            "name": "activeInfo",
+            "properties": {
+              "tabId": {
+                "type": "integer",
+                "minimum": 0,
+                "description": "The ID of the tab that has become active."
+              },
+              "windowId": {
+                "type": "integer",
+                "minimum": 0,
+                "description": "The ID of the window the active tab changed inside of."
+              }
+            }
+          }
+        ]
+      },
+      {
+        "name": "onHighlightChanged",
+        "deprecated": "Please use $(ref:tabs.onHighlighted).",
+        "unsupported": true,
+        "type": "function",
+        "description": "Fired when the highlighted or selected tabs in a window changes.",
+        "parameters": [
+          {
+            "type": "object",
+            "name": "selectInfo",
+            "properties": {
+              "windowId": {
+                "type": "integer",
+                "minimum": 0,
+                "description": "The window whose tabs changed."
+              },
+              "tabIds": {
+                "type": "array",
+                "items": {"type": "integer", "minimum": 0},
+                "description": "All highlighted tabs in the window."
+              }
+            }
+          }
+        ]
+      },
+      {
+        "name": "onHighlighted",
+        "type": "function",
+        "description": "Fired when the highlighted or selected tabs in a window changes.",
+        "parameters": [
+          {
+            "type": "object",
+            "name": "highlightInfo",
+            "properties": {
+              "windowId": {
+                "type": "integer",
+                "minimum": 0,
+                "description": "The window whose tabs changed."
+              },
+              "tabIds": {
+                "type": "array",
+                "items": {"type": "integer", "minimum": 0},
+                "description": "All highlighted tabs in the window."
+              }
+            }
+          }
+        ]
+      },
+      {
+        "name": "onDetached",
+        "type": "function",
+        "description": "Fired when a tab is detached from a window, for example because it is being moved between windows.",
+        "parameters": [
+          {"type": "integer", "name": "tabId", "minimum": 0},
+          {
+            "type": "object",
+            "name": "detachInfo",
+            "properties": {
+              "oldWindowId": {"type": "integer", "minimum": 0},
+              "oldPosition": {"type": "integer", "minimum": 0}
+            }
+          }
+        ]
+      },
+      {
+        "name": "onAttached",
+        "type": "function",
+        "description": "Fired when a tab is attached to a window, for example because it was moved between windows.",
+        "parameters": [
+          {"type": "integer", "name": "tabId", "minimum": 0},
+          {
+            "type": "object",
+            "name": "attachInfo",
+            "properties": {
+              "newWindowId": {"type": "integer", "minimum": 0},
+              "newPosition": {"type": "integer", "minimum": 0}
+            }
+          }
+        ]
+      },
+      {
+        "name": "onRemoved",
+        "type": "function",
+        "description": "Fired when a tab is closed.",
+        "parameters": [
+          {"type": "integer", "name": "tabId", "minimum": 0},
+          {
+            "type": "object",
+            "name": "removeInfo",
+            "properties": {
+              "windowId": {"type": "integer", "minimum": 0, "description": "The window whose tab is closed." },
+              "isWindowClosing": {"type": "boolean", "description": "True when the tab is being closed because its window is being closed." }
+            }
+          }
+        ]
+      },
+      {
+        "name": "onReplaced",
+        "type": "function",
+        "description": "Fired when a tab is replaced with another tab due to prerendering or instant.",
+        "parameters": [
+          {"type": "integer", "name": "addedTabId", "minimum": 0},
+          {"type": "integer", "name": "removedTabId", "minimum": 0}
+        ]
+      },
+      {
+        "name": "onZoomChange",
+        "type": "function",
+        "description": "Fired when a tab is zoomed.",
+        "parameters": [{
+          "type": "object",
+          "name": "ZoomChangeInfo",
+          "properties": {
+            "tabId": {"type": "integer", "minimum": 0},
+            "oldZoomFactor": {"type": "number"},
+            "newZoomFactor": {"type": "number"},
+            "zoomSettings": {"$ref": "ZoomSettings"}
+          }
+        }]
+      }
+    ]
+  }
+]
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/schemas/windows.json
@@ -0,0 +1,324 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+[
+  {
+    "namespace": "windows",
+    "description": "Use the <code>browser.windows</code> API to interact with browser windows. You can use this API to create, modify, and rearrange windows in the browser.",
+    "types": [
+      {
+        "id": "WindowType",
+        "type": "string",
+        "description": "The type of browser window this is. Under some circumstances a Window may not be assigned type property, for example when querying closed windows from the $(ref:sessions) API.",
+        "enum": ["normal", "popup", "panel", "app", "devtools"]
+      },
+      {
+        "id": "WindowState",
+        "type": "string",
+        "description": "The state of this browser window. Under some circumstances a Window may not be assigned state property, for example when querying closed windows from the $(ref:sessions) API.",
+        "enum": ["normal", "minimized", "maximized", "fullscreen", "docked"]
+      },
+      {
+        "id": "Window",
+        "type": "object",
+        "properties": {
+          "id": {"type": "integer", "optional": true, "minimum": 0, "description": "The ID of the window. Window IDs are unique within a browser session. Under some circumstances a Window may not be assigned an ID, for example when querying windows using the $(ref:sessions) API, in which case a session ID may be present."},
+          "focused": {"type": "boolean", "description": "Whether the window is currently the focused window."},
+          "top": {"type": "integer", "optional": true, "description": "The offset of the window from the top edge of the screen in pixels. Under some circumstances a Window may not be assigned top property, for example when querying closed windows from the $(ref:sessions) API."},
+          "left": {"type": "integer", "optional": true, "description": "The offset of the window from the left edge of the screen in pixels. Under some circumstances a Window may not be assigned left property, for example when querying closed windows from the $(ref:sessions) API."},
+          "width": {"type": "integer", "optional": true, "description": "The width of the window, including the frame, in pixels. Under some circumstances a Window may not be assigned width property, for example when querying closed windows from the $(ref:sessions) API."},
+          "height": {"type": "integer", "optional": true, "description": "The height of the window, including the frame, in pixels. Under some circumstances a Window may not be assigned height property, for example when querying closed windows from the $(ref:sessions) API."},
+          "tabs": {"type": "array", "items": { "$ref": "tabs.Tab" }, "optional": true, "description": "Array of $(ref:tabs.Tab) objects representing the current tabs in the window."},
+          "incognito": {"type": "boolean", "description": "Whether the window is incognito."},
+          "type": {
+            "$ref": "WindowType",
+            "optional": true,
+            "description": "The type of browser window this is."
+          },
+          "state": {
+            "$ref": "WindowState",
+            "optional": true,
+            "description": "The state of this browser window."
+          },
+          "alwaysOnTop": {"unsupported": true, "type": "boolean", "description": "Whether the window is set to be always on top."},
+          "sessionId": {"unsupported": true, "type": "string", "optional": true, "description": "The session ID used to uniquely identify a Window obtained from the $(ref:sessions) API."}
+        }
+      },
+      {
+        "id": "CreateType",
+        "type": "string",
+        "description": "Specifies what type of browser window to create. The 'panel' and 'detached_panel' types create a popup unless the '--enable-panels' flag is set.",
+        "enum": ["normal", "popup", "panel", "detached_panel"]
+      }
+    ],
+    "properties": {
+      "WINDOW_ID_NONE": {
+        "value": -1,
+        "description": "The windowId value that represents the absence of a browser window."
+      },
+      "WINDOW_ID_CURRENT": {
+        "value": -2,
+        "description": "The windowId value that represents the $(topic:current-window)[current window]."
+      }
+    },
+    "functions": [
+      {
+        "name": "get",
+        "type": "function",
+        "description": "Gets details about a window.",
+        "parameters": [
+          {"type": "integer", "name": "windowId", "minimum": -2},
+          {
+            "type": "object",
+            "name": "getInfo",
+            "optional": true,
+            "description": "",
+            "properties": {
+              "populate": {"type": "boolean", "optional": true, "description": "If true, the $(ref:windows.Window) object will have a <var>tabs</var> property that contains a list of the $(ref:tabs.Tab) objects. The <code>Tab</code> objects only contain the <code>url</code>, <code>title</code> and <code>favIconUrl</code> properties if the extension's manifest file includes the <code>\"tabs\"</code> permission." },
+              "windowTypes": {"type": "array", "items": { "$ref": "WindowType" }, "optional": true, "description": "If set, the $(ref:windows.Window) returned will be filtered based on its type. If unset the default filter is set to <code>['app', 'normal', 'panel', 'popup']</code>, with <code>'app'</code> and <code>'panel'</code> window types limited to the extension's own windows." }
+            }
+          },
+          {
+            "type": "function",
+            "name": "callback",
+            "parameters": [
+              {
+                "name": "window", "$ref": "Window"
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "getCurrent",
+        "type": "function",
+        "description": "Gets the $(topic:current-window)[current window].",
+        "parameters": [
+          {
+            "type": "object",
+            "name": "getInfo",
+            "optional": true,
+            "description": "",
+            "properties": {
+              "populate": {"type": "boolean", "optional": true, "description": "If true, the $(ref:windows.Window) object will have a <var>tabs</var> property that contains a list of the $(ref:tabs.Tab) objects. The <code>Tab</code> objects only contain the <code>url</code>, <code>title</code> and <code>favIconUrl</code> properties if the extension's manifest file includes the <code>\"tabs\"</code> permission." },
+              "windowTypes": {"type": "array", "items": { "$ref": "WindowType" }, "optional": true, "description": "If set, the $(ref:windows.Window) returned will be filtered based on its type. If unset the default filter is set to <code>['app', 'normal', 'panel', 'popup']</code>, with <code>'app'</code> and <code>'panel'</code> window types limited to the extension's own windows." }
+            }
+          },
+          {
+            "type": "function",
+            "name": "callback",
+            "parameters": [
+              {
+                "name": "window", "$ref": "Window"
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "getLastFocused",
+        "type": "function",
+        "description": "Gets the window that was most recently focused &mdash; typically the window 'on top'.",
+        "parameters": [
+          {
+            "type": "object",
+            "name": "getInfo",
+            "optional": true,
+            "description": "",
+            "properties": {
+              "populate": {"type": "boolean", "optional": true, "description": "If true, the $(ref:windows.Window) object will have a <var>tabs</var> property that contains a list of the $(ref:tabs.Tab) objects. The <code>Tab</code> objects only contain the <code>url</code>, <code>title</code> and <code>favIconUrl</code> properties if the extension's manifest file includes the <code>\"tabs\"</code> permission." },
+              "windowTypes": {"type": "array", "items": { "$ref": "WindowType" }, "optional": true, "description": "If set, the $(ref:windows.Window) returned will be filtered based on its type. If unset the default filter is set to <code>['app', 'normal', 'panel', 'popup']</code>, with <code>'app'</code> and <code>'panel'</code> window types limited to the extension's own windows." }
+            }
+          },
+          {
+            "type": "function",
+            "name": "callback",
+            "parameters": [
+              {
+                "name": "window", "$ref": "Window"
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "getAll",
+        "type": "function",
+        "description": "Gets all windows.",
+        "parameters": [
+          {
+            "type": "object",
+            "name": "getInfo",
+            "optional": true,
+            "description": "",
+            "properties": {
+              "populate": {"type": "boolean", "optional": true, "description": "If true, each $(ref:windows.Window) object will have a <var>tabs</var> property that contains a list of the $(ref:tabs.Tab) objects for that window. The <code>Tab</code> objects only contain the <code>url</code>, <code>title</code> and <code>favIconUrl</code> properties if the extension's manifest file includes the <code>\"tabs\"</code> permission." },
+              "windowTypes": {"type": "array", "items": { "$ref": "WindowType" }, "optional": true, "description": "If set, the $(ref:windows.Window) returned will be filtered based on its type. If unset the default filter is set to <code>['app', 'normal', 'panel', 'popup']</code>, with <code>'app'</code> and <code>'panel'</code> window types limited to the extension's own windows." }
+            }
+          },
+          {
+            "type": "function",
+            "name": "callback",
+            "parameters": [
+              {
+                "name": "windows", "type": "array", "items": { "$ref": "Window" }
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "create",
+        "type": "function",
+        "description": "Creates (opens) a new browser with any optional sizing, position or default URL provided.",
+        "parameters": [
+          {
+            "type": "object",
+            "name": "createData",
+            "properties": {
+              "url": {
+                "description": "A URL or array of URLs to open as tabs in the window. Fully-qualified URLs must include a scheme (i.e. 'http://www.google.com', not 'www.google.com'). Relative URLs will be relative to the current page within the extension. Defaults to the New Tab Page.",
+                "optional": true,
+                "choices": [
+                  {"type": "string"},
+                  {"type": "array", "items": {"type": "string"}}
+                ]
+              },
+              "tabId": {"type": "integer", "minimum": 0, "optional": true, "description": "The id of the tab for which you want to adopt to the new window."},
+              "left": {"type": "integer", "optional": true, "description": "The number of pixels to position the new window from the left edge of the screen. If not specified, the new window is offset naturally from the last focused window. This value is ignored for panels."},
+              "top": {"type": "integer", "optional": true, "description": "The number of pixels to position the new window from the top edge of the screen. If not specified, the new window is offset naturally from the last focused window. This value is ignored for panels."},
+              "width": {"type": "integer", "minimum": 0, "optional": true, "description": "The width in pixels of the new window, including the frame. If not specified defaults to a natural width."},
+              "height": {"type": "integer", "minimum": 0, "optional": true, "description": "The height in pixels of the new window, including the frame. If not specified defaults to a natural height."},
+              "focused": {"unsupported": true, "type": "boolean", "optional": true, "description": "If true, opens an active window. If false, opens an inactive window."},
+              "incognito": {"type": "boolean", "optional": true, "description": "Whether the new window should be an incognito window."},
+              "type": {
+                "unsupported": true,
+                "$ref": "CreateType",
+                "optional": true,
+                "description": "Specifies what type of browser window to create. The 'panel' and 'detached_panel' types create a popup unless the '--enable-panels' flag is set."
+              },
+              "state": {
+                "unsupported": true,
+                "$ref": "WindowState",
+                "optional": true,
+                "description": "The initial state of the window. The 'minimized', 'maximized' and 'fullscreen' states cannot be combined with 'left', 'top', 'width' or 'height'."
+              }
+            },
+            "optional": true
+          },
+          {
+            "type": "function",
+            "name": "callback",
+            "optional": true,
+            "parameters": [
+              {
+                "name": "window", "$ref": "Window", "description": "Contains details about the created window.",
+                "optional": true
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "update",
+        "type": "function",
+        "description": "Updates the properties of a window. Specify only the properties that you want to change; unspecified properties will be left unchanged.",
+        "parameters": [
+          {"type": "integer", "name": "windowId", "minimum": -2},
+          {
+            "type": "object",
+            "name": "updateInfo",
+            "properties": {
+              "left": {"unsupported": true, "type": "integer", "optional": true, "description": "The offset from the left edge of the screen to move the window to in pixels. This value is ignored for panels."},
+              "top": {"unsupported": true, "type": "integer", "optional": true, "description": "The offset from the top edge of the screen to move the window to in pixels. This value is ignored for panels."},
+              "width": {"unsupported": true, "type": "integer", "minimum": 0, "optional": true, "description": "The width to resize the window to in pixels. This value is ignored for panels."},
+              "height": {"unsupported": true, "type": "integer", "minimum": 0, "optional": true, "description": "The height to resize the window to in pixels. This value is ignored for panels."},
+              "focused": {"type": "boolean", "optional": true, "description": "If true, brings the window to the front. If false, brings the next window in the z-order to the front."},
+              "drawAttention": {"unsupported": true, "type": "boolean", "optional": true, "description": "If true, causes the window to be displayed in a manner that draws the user's attention to the window, without changing the focused window. The effect lasts until the user changes focus to the window. This option has no effect if the window already has focus. Set to false to cancel a previous draw attention request."},
+              "state": {
+                "unsupported": true,
+                "$ref": "WindowState",
+                "optional": true,
+                "description": "The new state of the window. The 'minimized', 'maximized' and 'fullscreen' states cannot be combined with 'left', 'top', 'width' or 'height'."
+              }
+            }
+          },
+          {
+            "type": "function",
+            "name": "callback",
+            "optional": true,
+            "parameters": [
+              {
+                "name": "window", "$ref": "Window"
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "remove",
+        "type": "function",
+        "description": "Removes (closes) a window, and all the tabs inside it.",
+        "parameters": [
+          {"type": "integer", "name": "windowId", "minimum": 0},
+          {"type": "function", "name": "callback", "optional": true, "parameters": []}
+        ]
+      }
+    ],
+    "events": [
+      {
+        "name": "onCreated",
+        "type": "function",
+        "description": "Fired when a window is created.",
+        "filters": [
+          {
+            "name": "windowTypes",
+            "type": "array",
+            "items": { "$ref": "WindowType" },
+            "description": "Conditions that the window's type being created must satisfy. By default it will satisfy <code>['app', 'normal', 'panel', 'popup']</code>, with <code>'app'</code> and <code>'panel'</code> window types limited to the extension's own windows."
+          }
+        ],
+        "parameters": [
+          {
+            "$ref": "Window",
+            "name": "window",
+            "description": "Details of the window that was created."
+          }
+        ]
+      },
+      {
+        "name": "onRemoved",
+        "type": "function",
+        "description": "Fired when a window is removed (closed).",
+        "filters": [
+          {
+            "name": "windowTypes",
+            "type": "array",
+            "items": { "$ref": "WindowType" },
+            "description": "Conditions that the window's type being removed must satisfy. By default it will satisfy <code>['app', 'normal', 'panel', 'popup']</code>, with <code>'app'</code> and <code>'panel'</code> window types limited to the extension's own windows."
+          }
+        ],
+        "parameters": [
+          {"type": "integer", "name": "windowId", "minimum": 0, "description": "ID of the removed window."}
+        ]
+      },
+      {
+        "name": "onFocusChanged",
+        "type": "function",
+        "description": "Fired when the currently focused window changes. Will be $(ref:windows.WINDOW_ID_NONE) if all browser windows have lost focus. Note: On some Linux window managers, WINDOW_ID_NONE will always be sent immediately preceding a switch from one browser window to another.",
+        "filters": [
+          {
+            "name": "windowTypes",
+            "type": "array",
+            "items": { "$ref": "WindowType" },
+            "description": "Conditions that the window's type being removed must satisfy. By default it will satisfy <code>['app', 'normal', 'panel', 'popup']</code>, with <code>'app'</code> and <code>'panel'</code> window types limited to the extension's own windows."
+          }
+        ],
+        "parameters": [
+          {"type": "integer", "name": "windowId", "minimum": -1, "description": "ID of the newly focused window."}
+        ]
+      }
+    ]
+  }
+]
--- a/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_bad.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_bad.js
@@ -9,17 +9,17 @@ function* testHasNoPermission(params) {
     browser.runtime.onMessage.addListener((msg, sender) => {
       browser.test.assertEq(msg, "second script ran", "second script ran");
       browser.test.notifyPass("executeScript");
     });
 
     browser.test.onMessage.addListener(msg => {
       browser.test.assertEq(msg, "execute-script");
 
-      browser.tabs.query({ activeWindow: true }, tabs => {
+      browser.tabs.query({ currentWindow: true }, tabs => {
         browser.tabs.executeScript({
           file: "script.js",
         });
 
         // Execute a script we know we have permissions for in the
         // second tab, in the hopes that it will execute after the
         // first one. This has intermittent failure written all over
         // it, but it's just about the best we can do until we
--- a/browser/components/extensions/test/browser/browser_ext_tabs_onUpdated.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_onUpdated.js
@@ -102,17 +102,17 @@ function* do_test_update(background) {
   yield extension.unload();
 
   yield BrowserTestUtils.closeWindow(win1);
 }
 
 add_task(function* test_pinned() {
   yield do_test_update(function background() {
     // Create a new tab for testing update.
-    browser.tabs.create(null, function(tab) {
+    browser.tabs.create({}, function(tab) {
       browser.tabs.onUpdated.addListener(function onUpdated(tabId, changeInfo) {
         // Check callback
         browser.test.assertEq(tabId, tab.id, "Check tab id");
         browser.test.log("onUpdate: " + JSON.stringify(changeInfo));
         if ("pinned" in changeInfo) {
           browser.test.assertTrue(changeInfo.pinned, "Check changeInfo.pinned");
           browser.tabs.onUpdated.removeListener(onUpdated);
           // Remove created tab.
@@ -146,17 +146,17 @@ add_task(function* test_unpinned() {
       browser.tabs.update(tab.id, {pinned: false});
     });
   });
 });
 
 add_task(function* test_url() {
   yield do_test_update(function background() {
     // Create a new tab for testing update.
-    browser.tabs.create(null, function(tab) {
+    browser.tabs.create({}, function(tab) {
       browser.tabs.onUpdated.addListener(function onUpdated(tabId, changeInfo) {
         // Check callback
         browser.test.assertEq(tabId, tab.id, "Check tab id");
         browser.test.log("onUpdate: " + JSON.stringify(changeInfo));
         if ("url" in changeInfo) {
           browser.test.assertEq("about:preferences", changeInfo.url,
                                 "Check changeInfo.url");
           browser.tabs.onUpdated.removeListener(onUpdated);
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -618,16 +618,19 @@ BrowserGlue.prototype = {
     ExtensionManagement.registerScript("chrome://browser/content/ext-utils.js");
     ExtensionManagement.registerScript("chrome://browser/content/ext-browserAction.js");
     ExtensionManagement.registerScript("chrome://browser/content/ext-pageAction.js");
     ExtensionManagement.registerScript("chrome://browser/content/ext-contextMenus.js");
     ExtensionManagement.registerScript("chrome://browser/content/ext-tabs.js");
     ExtensionManagement.registerScript("chrome://browser/content/ext-windows.js");
     ExtensionManagement.registerScript("chrome://browser/content/ext-bookmarks.js");
 
+    ExtensionManagement.registerSchema("chrome://browser/content/schemas/tabs.json");
+    ExtensionManagement.registerSchema("chrome://browser/content/schemas/windows.json");
+
     this._flashHangCount = 0;
     this._firstWindowReady = new Promise(resolve => this._firstWindowLoaded = resolve);
   },
 
   // cleanup (called on application shutdown)
   _dispose: function BG__dispose() {
     let os = Services.obs;
     os.removeObserver(this, "notifications-open-settings");
--- a/build/mach_bootstrap.py
+++ b/build/mach_bootstrap.py
@@ -68,16 +68,17 @@ SEARCH_PATHS = [
     'other-licenses/ply',
     'xpcom/idl-parser',
     'testing',
     'testing/tools/autotry',
     'testing/taskcluster',
     'testing/xpcshell',
     'testing/web-platform',
     'testing/web-platform/harness',
+    'testing/web-platform/tests/tools/wptserve',
     'testing/marionette/client',
     'testing/marionette/client/marionette/runner/mixins/browsermob-proxy-py',
     'testing/marionette/transport',
     'testing/marionette/driver',
     'testing/luciddream',
     'testing/mozbase/mozcrash',
     'testing/mozbase/mozdebug',
     'testing/mozbase/mozdevice',
--- a/build/virtualenv_packages.txt
+++ b/build/virtualenv_packages.txt
@@ -1,11 +1,12 @@
 marionette_transport.pth:testing/marionette/transport
 marionette_driver.pth:testing/marionette/driver
 browsermobproxy.pth:testing/marionette/client/marionette/runner/mixins/browsermob-proxy-py
+wptserve.pth:testing/web-platform/tests/tools/wptserve
 marionette.pth:testing/marionette/client
 blessings.pth:python/blessings
 configobj.pth:python/configobj
 jsmin.pth:python/jsmin
 mach.pth:python/mach
 mozbuild.pth:python/mozbuild
 pymake.pth:build/pymake
 optional:setup.py:python/psutil:build_ext:--inplace
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -426,17 +426,16 @@ nsScriptSecurityManager::IsSystemPrincip
 // nsScriptSecurityManager //
 /////////////////////////////
 
 ////////////////////////////////////
 // Methods implementing ISupports //
 ////////////////////////////////////
 NS_IMPL_ISUPPORTS(nsScriptSecurityManager,
                   nsIScriptSecurityManager,
-                  nsIChannelEventSink,
                   nsIObserver)
 
 ///////////////////////////////////////////////////
 // Methods implementing nsIScriptSecurityManager //
 ///////////////////////////////////////////////////
 
 ///////////////// Security Checks /////////////////
 
@@ -1231,51 +1230,16 @@ nsScriptSecurityManager::CanGetService(J
     nsAutoCString errorMsg("Permission denied to get service. CID=");
     char cidStr[NSID_LENGTH];
     aCID.ToProvidedString(cidStr);
     errorMsg.Append(cidStr);
     SetPendingException(cx, errorMsg.get());
     return NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED;
 }
 
-/////////////////////////////////////////////
-// Method implementing nsIChannelEventSink //
-/////////////////////////////////////////////
-NS_IMETHODIMP
-nsScriptSecurityManager::AsyncOnChannelRedirect(nsIChannel* oldChannel, 
-                                                nsIChannel* newChannel,
-                                                uint32_t redirFlags,
-                                                nsIAsyncVerifyRedirectCallback *cb)
-{
-    nsCOMPtr<nsIPrincipal> oldPrincipal;
-    GetChannelResultPrincipal(oldChannel, getter_AddRefs(oldPrincipal));
-
-    nsCOMPtr<nsIURI> newURI;
-    newChannel->GetURI(getter_AddRefs(newURI));
-    nsCOMPtr<nsIURI> newOriginalURI;
-    newChannel->GetOriginalURI(getter_AddRefs(newOriginalURI));
-
-    NS_ENSURE_STATE(oldPrincipal && newURI && newOriginalURI);
-
-    const uint32_t flags =
-        nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT |
-        nsIScriptSecurityManager::DISALLOW_SCRIPT;
-    nsresult rv = CheckLoadURIWithPrincipal(oldPrincipal, newURI, flags);
-    if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) {
-        rv = CheckLoadURIWithPrincipal(oldPrincipal, newOriginalURI, flags);
-    }
-
-    if (NS_FAILED(rv))
-        return rv;
-
-    cb->OnRedirectVerifyCallback(NS_OK);
-    return NS_OK;
-}
-
-
 /////////////////////////////////////
 // Method implementing nsIObserver //
 /////////////////////////////////////
 const char sJSEnabledPrefName[] = "javascript.enabled";
 const char sFileOriginPolicyPrefName[] =
     "security.fileuri.strict_origin_policy";
 
 static const char* kObservedPrefs[] = {
--- a/caps/nsScriptSecurityManager.h
+++ b/caps/nsScriptSecurityManager.h
@@ -9,17 +9,16 @@
 
 #include "nsIScriptSecurityManager.h"
 
 #include "nsIAddonPolicyService.h"
 #include "mozilla/Maybe.h"
 #include "nsIAddonPolicyService.h"
 #include "nsIPrincipal.h"
 #include "nsCOMPtr.h"
-#include "nsIChannelEventSink.h"
 #include "nsIObserver.h"
 #include "nsServiceManagerUtils.h"
 #include "plstr.h"
 #include "js/TypeDecls.h"
 
 #include <stdint.h>
 
 class nsCString;
@@ -34,27 +33,25 @@ class PrincipalOriginAttributes;
 /////////////////////////////
 // nsScriptSecurityManager //
 /////////////////////////////
 #define NS_SCRIPTSECURITYMANAGER_CID \
 { 0x7ee2a4c0, 0x4b93, 0x17d3, \
 { 0xba, 0x18, 0x00, 0x60, 0xb0, 0xf1, 0x99, 0xa2 }}
 
 class nsScriptSecurityManager final : public nsIScriptSecurityManager,
-                                      public nsIChannelEventSink,
                                       public nsIObserver
 {
 public:
     static void Shutdown();
 
     NS_DEFINE_STATIC_CID_ACCESSOR(NS_SCRIPTSECURITYMANAGER_CID)
 
     NS_DECL_ISUPPORTS
     NS_DECL_NSISCRIPTSECURITYMANAGER
-    NS_DECL_NSICHANNELEVENTSINK
     NS_DECL_NSIOBSERVER
 
     static nsScriptSecurityManager*
     GetScriptSecurityManager();
 
     // Invoked exactly once, by XPConnect.
     static void InitStatics();
 
--- a/config/faster/rules.mk
+++ b/config/faster/rules.mk
@@ -100,18 +100,17 @@ ACDEFINES += -DBUILD_FASTER
 # slashes replaced with underscores, and prefixed with `install_`. That is,
 # the install manifest for `dist/bin` would be `install_dist_bin`.
 $(addprefix install-,$(INSTALL_MANIFESTS)): install-%: $(TOPOBJDIR)/config/buildid
 	@# For now, force preprocessed files to be reprocessed every time.
 	@# The overhead is not that big, and this avoids waiting for proper
 	@# support for defines tracking in process_install_manifest.
 	@touch install_$(subst /,_,$*)
 	$(PYTHON) -m mozbuild.action.process_install_manifest \
-		--no-remove \
-		--no-remove-empty-directories \
+		--track install_$(subst /,_,$*).track \
 		$(TOPOBJDIR)/$* \
 		-DAB_CD=en-US \
 		-DMOZ_APP_BUILDID=$(shell cat $(TOPOBJDIR)/config/buildid) \
 		$(ACDEFINES) \
 		$(MOZ_DEBUG_DEFINES) \
 		install_$(subst /,_,$*)
 
 # Create some chrome manifests
--- a/dom/base/BlobSet.h
+++ b/dom/base/BlobSet.h
@@ -27,17 +27,18 @@ public:
   nsresult AppendVoidPtr(const void* aData, uint32_t aLength);
   nsresult AppendString(const nsAString& aString, bool nativeEOL, JSContext* aCx);
   nsresult AppendBlobImpl(BlobImpl* aBlobImpl);
   nsresult AppendBlobImpls(const nsTArray<RefPtr<BlobImpl>>& aBlobImpls);
 
   nsTArray<RefPtr<BlobImpl>>& GetBlobImpls() { Flush(); return mBlobImpls; }
 
   already_AddRefed<Blob> GetBlobInternal(nsISupports* aParent,
-                                         const nsACString& aContentType);
+                                         const nsACString& aContentType,
+                                         ErrorResult& aRv);
 
 protected:
   bool ExpandBufferSize(uint64_t aSize)
   {
     using mozilla::CheckedUint32;
 
     if (mDataBufferLen >= mDataLen + aSize) {
       mDataLen += aSize;
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -1228,19 +1228,21 @@ Element::GetAttributeNode(const nsAStrin
 {
   OwnerDoc()->WarnOnceAbout(nsIDocument::eGetAttributeNode);
   return Attributes()->GetNamedItem(aName);
 }
 
 already_AddRefed<Attr>
 Element::SetAttributeNode(Attr& aNewAttr, ErrorResult& aError)
 {
+  // XXXbz can we just remove this warning and the one in setAttributeNodeNS and
+  // alias setAttributeNode to setAttributeNodeNS?
   OwnerDoc()->WarnOnceAbout(nsIDocument::eSetAttributeNode);
 
-  return Attributes()->SetNamedItem(aNewAttr, aError);
+  return Attributes()->SetNamedItemNS(aNewAttr, aError);
 }
 
 already_AddRefed<Attr>
 Element::RemoveAttributeNode(Attr& aAttribute,
                              ErrorResult& aError)
 {
   Element *elem = aAttribute.GetElement();
   if (elem != this) {
--- a/dom/base/EventSource.cpp
+++ b/dom/base/EventSource.cpp
@@ -673,17 +673,17 @@ EventSource::InitChannelAndRequestEventS
   nsIScriptContext* sc = GetContextForEventHandlers(&rv);
   nsCOMPtr<nsIDocument> doc =
     nsContentUtils::GetDocumentFromScriptContext(sc);
 
   nsSecurityFlags securityFlags =
     nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS;
 
   if (mWithCredentials) {
-    securityFlags |= nsILoadInfo::SEC_REQUIRE_CORS_WITH_CREDENTIALS;
+    securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
   }
 
   nsCOMPtr<nsIChannel> channel;
   // If we have the document, use it
   if (doc) {
     rv = NS_NewChannel(getter_AddRefs(channel),
                        mSrc,
                        doc,
--- a/dom/base/File.cpp
+++ b/dom/base/File.cpp
@@ -252,26 +252,29 @@ Blob::ToFile()
   } else {
     file = new File(mParent, mImpl);
   }
 
   return file.forget();
 }
 
 already_AddRefed<File>
-Blob::ToFile(const nsAString& aName) const
+Blob::ToFile(const nsAString& aName, ErrorResult& aRv) const
 {
   nsAutoTArray<RefPtr<BlobImpl>, 1> blobImpls;
   blobImpls.AppendElement(mImpl);
 
   nsAutoString contentType;
   mImpl->GetType(contentType);
 
   RefPtr<MultipartBlobImpl> impl =
-    new MultipartBlobImpl(blobImpls, aName, contentType);
+    MultipartBlobImpl::Create(blobImpls, aName, contentType, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
 
   RefPtr<File> file = new File(mParent, impl);
   return file.forget();
 }
 
 already_AddRefed<Blob>
 Blob::CreateSlice(uint64_t aStart, uint64_t aLength,
                   const nsAString& aContentType,
@@ -342,17 +345,21 @@ Blob::WrapObject(JSContext* aCx, JS::Han
   return BlobBinding::Wrap(aCx, this, aGivenProto);
 }
 
 /* static */ already_AddRefed<Blob>
 Blob::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
 {
   RefPtr<MultipartBlobImpl> impl = new MultipartBlobImpl();
 
-  impl->InitializeBlob();
+  impl->InitializeBlob(aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
   MOZ_ASSERT(!impl->IsFile());
 
   RefPtr<Blob> blob = Blob::Create(aGlobal.GetAsSupports(), impl);
   return blob.forget();
 }
 
 /* static */ already_AddRefed<Blob>
 Blob::Constructor(
@@ -846,17 +853,17 @@ BlobImplFile::GetMozFullPathInternal(nsA
 {
   NS_ASSERTION(mIsFile, "Should only be called on files");
   aRv = mFile->GetPath(aFilename);
 }
 
 uint64_t
 BlobImplFile::GetSize(ErrorResult& aRv)
 {
-  if (IsSizeUnknown()) {
+  if (BlobImplBase::IsSizeUnknown()) {
     NS_ASSERTION(mWholeFile,
                  "Should only use lazy size when using the whole file");
     int64_t fileSize;
     aRv = mFile->GetFileSize(&fileSize);
     if (NS_WARN_IF(aRv.Failed())) {
       return 0;
     }
 
@@ -897,17 +904,17 @@ BlobImplFile::GetType(nsAString& aType)
 
   aType = mContentType;
 }
 
 int64_t
 BlobImplFile::GetLastModified(ErrorResult& aRv)
 {
   NS_ASSERTION(mIsFile, "Should only be called on files");
-  if (IsDateUnknown()) {
+  if (BlobImplBase::IsDateUnknown()) {
     PRTime msecs;
     aRv = mFile->GetLastModifiedTime(&msecs);
     if (NS_WARN_IF(aRv.Failed())) {
       return 0;
     }
 
     mLastModificationDate = msecs;
   }
@@ -1116,21 +1123,29 @@ BlobImplTemporaryBlob::GetInternalStream
     new nsTemporaryFileInputStream(mFileDescOwner, mStartPos, mStartPos + mLength);
   stream.forget(aStream);
 }
 
 ////////////////////////////////////////////////////////////////////////////
 // BlobSet implementation
 
 already_AddRefed<Blob>
-BlobSet::GetBlobInternal(nsISupports* aParent, const nsACString& aContentType)
+BlobSet::GetBlobInternal(nsISupports* aParent,
+                         const nsACString& aContentType,
+                         ErrorResult& aRv)
 {
-  RefPtr<Blob> blob = Blob::Create(aParent,
-    new MultipartBlobImpl(GetBlobImpls(),
-                          NS_ConvertASCIItoUTF16(aContentType)));
+  RefPtr<BlobImpl> blobImpl =
+    MultipartBlobImpl::Create(GetBlobImpls(),
+                              NS_ConvertASCIItoUTF16(aContentType),
+                              aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  RefPtr<Blob> blob = Blob::Create(aParent, blobImpl);
   return blob.forget();
 }
 
 nsresult
 BlobSet::AppendVoidPtr(const void* aData, uint32_t aLength)
 {
   NS_ENSURE_ARG_POINTER(aData);
 
--- a/dom/base/File.h
+++ b/dom/base/File.h
@@ -113,17 +113,18 @@ public:
   // otherwise it returns a new File object with the same BlobImpl.
   already_AddRefed<File> ToFile();
 
   // XXXjwatt Consider having a ToDirectory() method. The need for a FileSystem
   // object complicates that though.
 
   // This method creates a new File object with the given name and the same
   // BlobImpl.
-  already_AddRefed<File> ToFile(const nsAString& aName) const;
+  already_AddRefed<File> ToFile(const nsAString& aName,
+                                ErrorResult& aRv) const;
 
   already_AddRefed<Blob>
   CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType,
               ErrorResult& aRv);
 
   void
   GetInternalStream(nsIInputStream** aStream, ErrorResult& aRv);
 
@@ -793,16 +794,20 @@ public:
                                       ErrorResult& aRv) const override;
   virtual void GetInternalStream(nsIInputStream** aInputStream,
                                  ErrorResult& aRv) override;
 
   void SetPath(const nsAString& aFullPath);
 
   virtual void LookupAndCacheIsDirectory() override;
 
+  // We always have size and date for this kind of blob.
+  virtual bool IsSizeUnknown() const override { return false; }
+  virtual bool IsDateUnknown() const override { return false; }
+
 protected:
   virtual ~BlobImplFile() {
     if (mFile && mIsTemporary) {
       NS_WARNING("In temporary ~BlobImplFile");
       // Ignore errors if any, not much we can do. Clean-up will be done by
       // https://mxr.mozilla.org/mozilla-central/source/xpcom/io/nsAnonymousTemporaryFile.cpp?rev=6c1c7e45c902#127
 #ifdef DEBUG
       nsresult rv =
--- a/dom/base/MultipartBlobImpl.cpp
+++ b/dom/base/MultipartBlobImpl.cpp
@@ -20,16 +20,47 @@
 #include "nsIXPConnect.h"
 #include <algorithm>
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 NS_IMPL_ISUPPORTS_INHERITED0(MultipartBlobImpl, BlobImpl)
 
+/* static */ already_AddRefed<MultipartBlobImpl>
+MultipartBlobImpl::Create(const nsTArray<RefPtr<BlobImpl>>& aBlobImpls,
+                          const nsAString& aName,
+                          const nsAString& aContentType,
+                          ErrorResult& aRv)
+{
+  RefPtr<MultipartBlobImpl> blobImpl =
+    new MultipartBlobImpl(aBlobImpls, aName, aContentType);
+  blobImpl->SetLengthAndModifiedDate(aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  return blobImpl.forget();
+}
+
+/* static */ already_AddRefed<MultipartBlobImpl>
+MultipartBlobImpl::Create(const nsTArray<RefPtr<BlobImpl>>& aBlobImpls,
+                          const nsAString& aContentType,
+                          ErrorResult& aRv)
+{
+  RefPtr<MultipartBlobImpl> blobImpl =
+    new MultipartBlobImpl(aBlobImpls, aContentType);
+  blobImpl->SetLengthAndModifiedDate(aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  return blobImpl.forget();
+}
+
 void
 MultipartBlobImpl::GetInternalStream(nsIInputStream** aStream,
                                      ErrorResult& aRv)
 {
   *aStream = nullptr;
 
   nsCOMPtr<nsIMultiplexInputStream> stream =
     do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1");
@@ -120,25 +151,29 @@ MultipartBlobImpl::CreateSlice(uint64_t 
       blobImpls.AppendElement(lastBlobImpl);
     } else {
       blobImpls.AppendElement(blobImpl);
     }
     length -= std::min<uint64_t>(l, length);
   }
 
   // we can create our blob now
-  RefPtr<BlobImpl> impl =
-    new MultipartBlobImpl(blobImpls, aContentType);
+  RefPtr<BlobImpl> impl = Create(blobImpls, aContentType, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
   return impl.forget();
 }
 
 void
-MultipartBlobImpl::InitializeBlob()
+MultipartBlobImpl::InitializeBlob(ErrorResult& aRv)
 {
-  SetLengthAndModifiedDate();
+  SetLengthAndModifiedDate(aRv);
+  NS_WARN_IF(aRv.Failed());
 }
 
 void
 MultipartBlobImpl::InitializeBlob(
        JSContext* aCx,
        const Sequence<OwningArrayBufferOrArrayBufferViewOrBlobOrString>& aData,
        const nsAString& aContentType,
        bool aNativeEOL,
@@ -182,47 +217,51 @@ MultipartBlobImpl::InitializeBlob(
 
     else {
       MOZ_CRASH("Impossible blob data type.");
     }
   }
 
 
   mBlobImpls = blobSet.GetBlobImpls();
-  SetLengthAndModifiedDate();
+  SetLengthAndModifiedDate(aRv);
+  NS_WARN_IF(aRv.Failed());
 }
 
 void
-MultipartBlobImpl::SetLengthAndModifiedDate()
+MultipartBlobImpl::SetLengthAndModifiedDate(ErrorResult& aRv)
 {
   MOZ_ASSERT(mLength == UINT64_MAX);
   MOZ_ASSERT(mLastModificationDate == INT64_MAX);
 
   uint64_t totalLength = 0;
   int64_t lastModified = 0;
   bool lastModifiedSet = false;
 
   for (uint32_t index = 0, count = mBlobImpls.Length(); index < count; index++) {
     RefPtr<BlobImpl>& blob = mBlobImpls[index];
 
 #ifdef DEBUG
     MOZ_ASSERT(!blob->IsSizeUnknown());
     MOZ_ASSERT(!blob->IsDateUnknown());
 #endif
 
-    ErrorResult error;
-    uint64_t subBlobLength = blob->GetSize(error);
-    MOZ_ALWAYS_TRUE(!error.Failed());
+    uint64_t subBlobLength = blob->GetSize(aRv);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return;
+    }
 
     MOZ_ASSERT(UINT64_MAX - subBlobLength >= totalLength);
     totalLength += subBlobLength;
 
     if (blob->IsFile()) {
-      int64_t partLastModified = blob->GetLastModified(error);
-      MOZ_ALWAYS_TRUE(!error.Failed());
+      int64_t partLastModified = blob->GetLastModified(aRv);
+      if (NS_WARN_IF(aRv.Failed())) {
+        return;
+      }
 
       if (lastModified < partLastModified) {
         lastModified = partLastModified;
         lastModifiedSet = true;
       }
     }
   }
 
@@ -309,17 +348,18 @@ MultipartBlobImpl::InitializeChromeFile(
     aBlob.GetType(mContentType);
   }
 
 
   BlobSet blobSet;
   blobSet.AppendBlobImpl(aBlob.Impl());
   mBlobImpls = blobSet.GetBlobImpls();
 
-  SetLengthAndModifiedDate();
+  SetLengthAndModifiedDate(aRv);
+  NS_WARN_IF(aRv.Failed());
 }
 
 void
 MultipartBlobImpl::InitializeChromeFile(nsPIDOMWindow* aWindow,
                                         nsIFile* aFile,
                                         const ChromeFilePropertyBag& aBag,
                                         bool aIsFromNsIFile,
                                         ErrorResult& aRv)
@@ -380,17 +420,18 @@ MultipartBlobImpl::InitializeChromeFile(
   if (mContentType.IsEmpty()) {
     blob->GetType(mContentType);
   }
 
   BlobSet blobSet;
   blobSet.AppendBlobImpl(static_cast<File*>(blob.get())->Impl());
   mBlobImpls = blobSet.GetBlobImpls();
 
-  SetLengthAndModifiedDate();
+  SetLengthAndModifiedDate(aRv);
+  NS_WARN_IF(aRv.Failed());
 }
 
 void
 MultipartBlobImpl::InitializeChromeFile(nsPIDOMWindow* aWindow,
                                         const nsAString& aData,
                                         const ChromeFilePropertyBag& aBag,
                                         ErrorResult& aRv)
 {
--- a/dom/base/MultipartBlobImpl.h
+++ b/dom/base/MultipartBlobImpl.h
@@ -20,51 +20,43 @@ using namespace mozilla;
 using namespace mozilla::dom;
 
 class MultipartBlobImpl final : public BlobImplBase
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
   // Create as a file
-  MultipartBlobImpl(const nsTArray<RefPtr<BlobImpl>>& aBlobImpls,
-                    const nsAString& aName,
-                    const nsAString& aContentType)
-    : BlobImplBase(aName, aContentType, UINT64_MAX),
-      mBlobImpls(aBlobImpls),
-      mIsFromNsIFile(false)
-  {
-    SetLengthAndModifiedDate();
-  }
+  static already_AddRefed<MultipartBlobImpl>
+  Create(const nsTArray<RefPtr<BlobImpl>>& aBlobImpls,
+         const nsAString& aName,
+         const nsAString& aContentType,
+         ErrorResult& aRv);
 
   // Create as a blob
-  MultipartBlobImpl(const nsTArray<RefPtr<BlobImpl>>& aBlobImpls,
-                    const nsAString& aContentType)
-    : BlobImplBase(aContentType, UINT64_MAX),
-      mBlobImpls(aBlobImpls),
-      mIsFromNsIFile(false)
-  {
-    SetLengthAndModifiedDate();
-  }
+  static already_AddRefed<MultipartBlobImpl>
+  Create(const nsTArray<RefPtr<BlobImpl>>& aBlobImpls,
+         const nsAString& aContentType,
+         ErrorResult& aRv);
 
   // Create as a file to be later initialized
   explicit MultipartBlobImpl(const nsAString& aName)
     : BlobImplBase(aName, EmptyString(), UINT64_MAX),
       mIsFromNsIFile(false)
   {
   }
 
   // Create as a blob to be later initialized
   MultipartBlobImpl()
     : BlobImplBase(EmptyString(), UINT64_MAX),
       mIsFromNsIFile(false)
   {
   }
 
-  void InitializeBlob();
+  void InitializeBlob(ErrorResult& aRv);
 
   void InitializeBlob(
        JSContext* aCx,
        const Sequence<OwningArrayBufferOrArrayBufferViewOrBlobOrString>& aData,
        const nsAString& aContentType,
        bool aNativeEOL,
        ErrorResult& aRv);
 
@@ -115,17 +107,34 @@ public:
   void SetFromNsIFile(bool aValue)
   {
     mIsFromNsIFile = aValue;
   }
 
   virtual bool MayBeClonedToOtherThreads() const override;
 
 protected:
+  MultipartBlobImpl(const nsTArray<RefPtr<BlobImpl>>& aBlobImpls,
+                    const nsAString& aName,
+                    const nsAString& aContentType)
+    : BlobImplBase(aName, aContentType, UINT64_MAX),
+      mBlobImpls(aBlobImpls),
+      mIsFromNsIFile(false)
+  {
+  }
+
+  MultipartBlobImpl(const nsTArray<RefPtr<BlobImpl>>& aBlobImpls,
+                    const nsAString& aContentType)
+    : BlobImplBase(aContentType, UINT64_MAX),
+      mBlobImpls(aBlobImpls),
+      mIsFromNsIFile(false)
+  {
+  }
+
   virtual ~MultipartBlobImpl() {}
 
-  void SetLengthAndModifiedDate();
+  void SetLengthAndModifiedDate(ErrorResult& aRv);
 
   nsTArray<RefPtr<BlobImpl>> mBlobImpls;
   bool mIsFromNsIFile;
 };
 
 #endif // mozilla_dom_MultipartBlobImpl_h
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -1199,17 +1199,18 @@ Navigator::SendBeacon(const nsAString& a
     aRv.Throw(NS_ERROR_CONTENT_BLOCKED);
     return false;
   }
 
   nsCOMPtr<nsIChannel> channel;
   rv = NS_NewChannel(getter_AddRefs(channel),
                      uri,
                      doc,
-                     nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS,
+                     nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS |
+                       nsILoadInfo::SEC_COOKIES_INCLUDE,
                      nsIContentPolicy::TYPE_BEACON);
 
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return false;
   }
 
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
@@ -1310,51 +1311,16 @@ Navigator::SendBeacon(const nsAString& a
   // cancel the channel and any redirected channels it may create.
   nsCOMPtr<nsILoadGroup> loadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
   nsCOMPtr<nsIInterfaceRequestor> callbacks =
     do_QueryInterface(mWindow->GetDocShell());
   loadGroup->SetNotificationCallbacks(callbacks);
   channel->SetLoadGroup(loadGroup);
 
   RefPtr<BeaconStreamListener> beaconListener = new BeaconStreamListener();
-
-  // Start a preflight if cross-origin and content type is not whitelisted
-  nsCOMPtr<nsIScriptSecurityManager> secMan = nsContentUtils::GetSecurityManager();
-  rv = secMan->CheckSameOriginURI(documentURI, uri, false);
-  bool crossOrigin = NS_FAILED(rv);
-  nsAutoCString contentType, parsedCharset;
-  rv = NS_ParseRequestContentType(mimeType, contentType, parsedCharset);
-  if (crossOrigin &&
-      mimeType.Length() > 0 &&
-      !contentType.Equals(APPLICATION_WWW_FORM_URLENCODED) &&
-      !contentType.Equals(MULTIPART_FORM_DATA) &&
-      !contentType.Equals(TEXT_PLAIN)) {
-
-    // we need to set the sameOriginChecker as a notificationCallback
-    // so we can tell the channel not to follow redirects
-    nsCOMPtr<nsIInterfaceRequestor> soc = nsContentUtils::SameOriginChecker();
-    channel->SetNotificationCallbacks(soc);
-
-    nsCOMPtr<nsIHttpChannelInternal> internalChannel =
-      do_QueryInterface(channel);
-    if (!internalChannel) {
-      aRv.Throw(NS_ERROR_FAILURE);
-      return false;
-    }
-    nsTArray<nsCString> unsafeHeaders;
-    unsafeHeaders.AppendElement(NS_LITERAL_CSTRING("Content-Type"));
-    rv = internalChannel->SetCorsPreflightParameters(unsafeHeaders,
-                                                     true,
-                                                     doc->NodePrincipal());
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      aRv.Throw(rv);
-      return false;
-    }
-  }
-
   rv = channel->AsyncOpen2(beaconListener);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return false;
   }
   // make the beaconListener hold a strong reference to the loadgroup
   // which is released in ::OnStartRequest
   beaconListener->SetLoadGroup(loadGroup);
--- a/dom/base/StructuredCloneHolder.cpp
+++ b/dom/base/StructuredCloneHolder.cpp
@@ -550,17 +550,18 @@ StructuredCloneHolder::WriteFullySeriali
   return false;
 }
 
 namespace {
 
 // Recursive!
 already_AddRefed<BlobImpl>
 EnsureBlobForBackgroundManager(BlobImpl* aBlobImpl,
-                               PBackgroundChild* aManager = nullptr)
+                               PBackgroundChild* aManager,
+                               ErrorResult& aRv)
 {
   MOZ_ASSERT(aBlobImpl);
   RefPtr<BlobImpl> blobImpl = aBlobImpl;
 
   if (!aManager) {
     aManager = BackgroundChild::GetForCurrentThread();
     if (!aManager) {
       return blobImpl.forget();
@@ -599,35 +600,44 @@ EnsureBlobForBackgroundManager(BlobImpl*
   bool newBlobImplNeeded = false;
 
   for (uint32_t index = 0; index < subBlobCount; index++) {
     const RefPtr<BlobImpl>& subBlobImpl = subBlobImpls->ElementAt(index);
     MOZ_ASSERT(subBlobImpl);
 
     RefPtr<BlobImpl>& newSubBlobImpl = newSubBlobImpls[index];
 
-    newSubBlobImpl = EnsureBlobForBackgroundManager(subBlobImpl, aManager);
+    newSubBlobImpl = EnsureBlobForBackgroundManager(subBlobImpl, aManager, aRv);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return nullptr;
+    }
+
     MOZ_ASSERT(newSubBlobImpl);
 
     if (subBlobImpl != newSubBlobImpl) {
       newBlobImplNeeded = true;
     }
   }
 
   if (newBlobImplNeeded) {
     nsString contentType;
     blobImpl->GetType(contentType);
 
     if (blobImpl->IsFile()) {
       nsString name;
       blobImpl->GetName(name);
 
-      blobImpl = new MultipartBlobImpl(newSubBlobImpls, name, contentType);
+      blobImpl = MultipartBlobImpl::Create(newSubBlobImpls, name,
+                                           contentType, aRv);
     } else {
-      blobImpl = new MultipartBlobImpl(newSubBlobImpls, contentType);
+      blobImpl = MultipartBlobImpl::Create(newSubBlobImpls, contentType, aRv);
+    }
+
+    if (NS_WARN_IF(aRv.Failed())) {
+      return nullptr;
     }
 
     MOZ_ALWAYS_TRUE(NS_SUCCEEDED(blobImpl->SetMutable(false)));
   }
 
   return blobImpl.forget();
 }
 
@@ -635,17 +645,23 @@ JSObject*
 ReadBlob(JSContext* aCx,
          uint32_t aIndex,
          StructuredCloneHolder* aHolder)
 {
   MOZ_ASSERT(aHolder);
   MOZ_ASSERT(aIndex < aHolder->BlobImpls().Length());
   RefPtr<BlobImpl> blobImpl = aHolder->BlobImpls()[aIndex];
 
-  blobImpl = EnsureBlobForBackgroundManager(blobImpl);
+  ErrorResult rv;
+  blobImpl = EnsureBlobForBackgroundManager(blobImpl, nullptr, rv);
+  if (NS_WARN_IF(rv.Failed())) {
+    rv.SuppressException();
+    return nullptr;
+  }
+
   MOZ_ASSERT(blobImpl);
 
   // RefPtr<File> needs to go out of scope before toObjectOrNull() is
   // called because the static analysis thinks dereferencing XPCOM objects
   // can GC (because in some cases it can!), and a return statement with a
   // JSObject* type means that JSObject* is on the stack as a raw pointer
   // while destructors are running.
   JS::Rooted<JS::Value> val(aCx);
@@ -663,17 +679,24 @@ bool
 WriteBlob(JSStructuredCloneWriter* aWriter,
           Blob* aBlob,
           StructuredCloneHolder* aHolder)
 {
   MOZ_ASSERT(aWriter);
   MOZ_ASSERT(aBlob);
   MOZ_ASSERT(aHolder);
 
-  RefPtr<BlobImpl> blobImpl = EnsureBlobForBackgroundManager(aBlob->Impl());
+  ErrorResult rv;
+  RefPtr<BlobImpl> blobImpl =
+    EnsureBlobForBackgroundManager(aBlob->Impl(), nullptr, rv);
+  if (NS_WARN_IF(rv.Failed())) {
+    rv.SuppressException();
+    return false;
+  }
+
   MOZ_ASSERT(blobImpl);
 
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(blobImpl->SetMutable(false)));
 
   // We store the position of the blobImpl in the array as index.
   if (JS_WriteUint32Pair(aWriter, SCTAG_DOM_BLOB,
                          aHolder->BlobImpls().Length())) {
     aHolder->BlobImpls().AppendElement(blobImpl);
@@ -709,17 +732,23 @@ ReadFileList(JSContext* aCx,
     // |aCount| is the number of BlobImpls to use from the |offset|.
     for (uint32_t i = 0; i < aCount; ++i) {
       uint32_t index = offset + i;
       MOZ_ASSERT(index < aHolder->BlobImpls().Length());
 
       RefPtr<BlobImpl> blobImpl = aHolder->BlobImpls()[index];
       MOZ_ASSERT(blobImpl->IsFile());
 
-      blobImpl = EnsureBlobForBackgroundManager(blobImpl);
+      ErrorResult rv;
+      blobImpl = EnsureBlobForBackgroundManager(blobImpl, nullptr, rv);
+      if (NS_WARN_IF(rv.Failed())) {
+        rv.SuppressException();
+        return nullptr;
+      }
+
       MOZ_ASSERT(blobImpl);
 
       RefPtr<File> file = File::Create(aHolder->ParentDuringRead(), blobImpl);
       if (!fileList->Append(file)) {
         return nullptr;
       }
     }
 
@@ -748,24 +777,32 @@ WriteFileList(JSStructuredCloneWriter* a
   // starting from the offset.
   if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_FILELIST,
                           aFileList->Length()) ||
       !JS_WriteUint32Pair(aWriter, 0,
                           aHolder->BlobImpls().Length())) {
     return false;
   }
 
+  ErrorResult rv;
+  nsTArray<RefPtr<BlobImpl>> blobImpls;
+
   for (uint32_t i = 0; i < aFileList->Length(); ++i) {
     RefPtr<BlobImpl> blobImpl =
-      EnsureBlobForBackgroundManager(aFileList->Item(i)->Impl());
+      EnsureBlobForBackgroundManager(aFileList->Item(i)->Impl(), nullptr, rv);
+    if (NS_WARN_IF(rv.Failed())) {
+      rv.SuppressException();
+      return false;
+    }
+
     MOZ_ASSERT(blobImpl);
-
-    aHolder->BlobImpls().AppendElement(blobImpl);
+    blobImpls.AppendElement(blobImpl);
   }
 
+  aHolder->BlobImpls().AppendElements(blobImpls);
   return true;
 }
 
 // Read the WriteFormData for the format.
 JSObject*
 ReadFormData(JSContext* aCx,
              JSStructuredCloneReader* aReader,
              uint32_t aCount,
@@ -799,29 +836,38 @@ ReadFormData(JSContext* aCx,
         RefPtr<BlobImpl> blobImpl =
           aHolder->BlobImpls()[indexOrLengthOfString];
         MOZ_ASSERT(blobImpl->IsFile());
 
         RefPtr<File> file =
           File::Create(aHolder->ParentDuringRead(), blobImpl);
         MOZ_ASSERT(file);
 
-        formData->Append(name, *file, thirdArg);
+        ErrorResult rv;
+        formData->Append(name, *file, thirdArg, rv);
+        if (NS_WARN_IF(rv.Failed())) {
+          return nullptr;
+        }
+
       } else {
         MOZ_ASSERT(tag == 0);
 
         nsAutoString value;
         value.SetLength(indexOrLengthOfString);
         size_t charSize = sizeof(nsString::char_type);
         if (!JS_ReadBytes(aReader, (void*) value.BeginWriting(),
                           indexOrLengthOfString * charSize)) {
           return nullptr;
         }
 
-        formData->Append(name, value);
+        ErrorResult rv;
+        formData->Append(name, value, rv);
+        if (NS_WARN_IF(rv.Failed())) {
+          return nullptr;
+        }
       }
     }
 
     if (!ToJSValue(aCx, formData, &val)) {
       return nullptr;
     }
   }
 
--- a/dom/base/ThirdPartyUtil.cpp
+++ b/dom/base/ThirdPartyUtil.cpp
@@ -41,17 +41,19 @@ ThirdPartyUtil::Init()
 // Determine if aFirstDomain is a different base domain to aSecondURI; or, if
 // the concept of base domain does not apply, determine if the two hosts are not
 // string-identical.
 nsresult
 ThirdPartyUtil::IsThirdPartyInternal(const nsCString& aFirstDomain,
                                      nsIURI* aSecondURI,
                                      bool* aResult)
 {
-  NS_ENSURE_ARG(aSecondURI);
+  if (!aSecondURI) {
+    return NS_ERROR_INVALID_ARG;
+  }
 
   // Get the base domain for aSecondURI.
   nsCString secondDomain;
   nsresult rv = GetBaseDomain(aSecondURI, secondDomain);
   LOG(("ThirdPartyUtil::IsThirdPartyInternal %s =? %s", aFirstDomain.get(), secondDomain.get()));
   if (NS_FAILED(rv))
     return rv;
 
--- a/dom/base/nsDOMAttributeMap.cpp
+++ b/dom/base/nsDOMAttributeMap.cpp
@@ -235,35 +235,33 @@ nsDOMAttributeMap::GetNamedItem(const ns
 
 NS_IMETHODIMP
 nsDOMAttributeMap::SetNamedItem(nsIDOMAttr* aAttr, nsIDOMAttr** aReturn)
 {
   Attr* attribute = static_cast<Attr*>(aAttr);
   NS_ENSURE_ARG(attribute);
 
   ErrorResult rv;
-  *aReturn = SetNamedItem(*attribute, rv).take();
+  *aReturn = SetNamedItemNS(*attribute, rv).take();
   return rv.StealNSResult();
 }
 
 NS_IMETHODIMP
 nsDOMAttributeMap::SetNamedItemNS(nsIDOMAttr* aAttr, nsIDOMAttr** aReturn)
 {
   Attr* attribute = static_cast<Attr*>(aAttr);
   NS_ENSURE_ARG(attribute);
 
   ErrorResult rv;
   *aReturn = SetNamedItemNS(*attribute, rv).take();
   return rv.StealNSResult();
 }
 
 already_AddRefed<Attr>
-nsDOMAttributeMap::SetNamedItemInternal(Attr& aAttr,
-                                        bool aWithNS,
-                                        ErrorResult& aError)
+nsDOMAttributeMap::SetNamedItemNS(Attr& aAttr, ErrorResult& aError)
 {
   NS_ENSURE_TRUE(mContent, nullptr);
 
   // XXX should check same-origin between mContent and aAttr however
   // nsContentUtils::CheckSameOrigin can't deal with attributenodes yet
 
   // Check that attribute is not owned by somebody else
   nsDOMAttributeMap* owner = aAttr.GetMap();
@@ -288,34 +286,28 @@ nsDOMAttributeMap::SetNamedItemInternal(
     }
 
     NS_ASSERTION(adoptedNode == &aAttr, "Uh, adopt node changed nodes?");
   }
 
   // Get nodeinfo and preexisting attribute (if it exists)
   RefPtr<NodeInfo> oldNi;
 
-  if (!aWithNS) {
-    nsAutoString name;
-    aAttr.GetName(name);
-    oldNi = mContent->GetExistingAttrNameFromQName(name);
-  } else {
-    uint32_t i, count = mContent->GetAttrCount();
-    for (i = 0; i < count; ++i) {
-      const nsAttrName* name = mContent->GetAttrNameAt(i);
-      int32_t attrNS = name->NamespaceID();
-      nsIAtom* nameAtom = name->LocalName();
+  uint32_t i, count = mContent->GetAttrCount();
+  for (i = 0; i < count; ++i) {
+    const nsAttrName* name = mContent->GetAttrNameAt(i);
+    int32_t attrNS = name->NamespaceID();
+    nsIAtom* nameAtom = name->LocalName();
 
-      // we're purposefully ignoring the prefix.
-      if (aAttr.NodeInfo()->Equals(nameAtom, attrNS)) {
-        oldNi = mContent->NodeInfo()->NodeInfoManager()->
-          GetNodeInfo(nameAtom, name->GetPrefix(), aAttr.NodeInfo()->NamespaceID(),
-                      nsIDOMNode::ATTRIBUTE_NODE);
-        break;
-      }
+    // we're purposefully ignoring the prefix.
+    if (aAttr.NodeInfo()->Equals(nameAtom, attrNS)) {
+      oldNi = mContent->NodeInfo()->NodeInfoManager()->
+        GetNodeInfo(nameAtom, name->GetPrefix(), aAttr.NodeInfo()->NamespaceID(),
+                    nsIDOMNode::ATTRIBUTE_NODE);
+      break;
     }
   }
 
   RefPtr<Attr> attr;
 
   if (oldNi) {
     RefPtr<Attr> oldAttr = GetAttribute(oldNi, true);
 
--- a/dom/base/nsDOMAttributeMap.h
+++ b/dom/base/nsDOMAttributeMap.h
@@ -136,37 +136,29 @@ public:
   }
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   // WebIDL
   Attr* GetNamedItem(const nsAString& aAttrName);
   Attr* NamedGetter(const nsAString& aAttrName, bool& aFound);
   bool NameIsEnumerable(const nsAString& aName);
   already_AddRefed<Attr>
-  SetNamedItem(Attr& aAttr, ErrorResult& aError)
-  {
-    return SetNamedItemInternal(aAttr, false, aError);
-  }
-  already_AddRefed<Attr>
   RemoveNamedItem(mozilla::dom::NodeInfo* aNodeInfo, ErrorResult& aError);
   already_AddRefed<Attr>
   RemoveNamedItem(const nsAString& aName, ErrorResult& aError);
  
   Attr* Item(uint32_t aIndex);
   Attr* IndexedGetter(uint32_t aIndex, bool& aFound);
   uint32_t Length() const;
 
   Attr*
   GetNamedItemNS(const nsAString& aNamespaceURI,
                  const nsAString& aLocalName);
   already_AddRefed<Attr>
-  SetNamedItemNS(Attr& aNode, ErrorResult& aError)
-  {
-    return SetNamedItemInternal(aNode, true, aError);
-  }
+  SetNamedItemNS(Attr& aNode, ErrorResult& aError);
   already_AddRefed<Attr>
   RemoveNamedItemNS(const nsAString& aNamespaceURI, const nsAString& aLocalName,
                     ErrorResult& aError);
 
   void GetSupportedNames(unsigned, nsTArray<nsString>& aNames)
   {
     // No supported names we want to show up in iteration.
   }
@@ -179,23 +171,16 @@ protected:
 private:
   nsCOMPtr<Element> mContent;
 
   /**
    * Cache of Attrs.
    */
   AttrCache mAttributeCache;
 
-  /**
-   * SetNamedItem() (aWithNS = false) and SetNamedItemNS() (aWithNS =
-   * true) implementation.
-   */
-  already_AddRefed<Attr>
-  SetNamedItemInternal(Attr& aNode, bool aWithNS, ErrorResult& aError);
-
   already_AddRefed<mozilla::dom::NodeInfo>
   GetAttrNodeInfo(const nsAString& aNamespaceURI,
                   const nsAString& aLocalName);
 
   Attr* GetAttribute(mozilla::dom::NodeInfo* aNodeInfo, bool aNsAware);
 
   /**
    * Remove an attribute, returns the removed node.
--- a/dom/base/nsDOMFileReader.cpp
+++ b/dom/base/nsDOMFileReader.cpp
@@ -270,43 +270,51 @@ ReadFuncBinaryString(nsIInputStream* in,
   return NS_OK;
 }
 
 nsresult
 nsDOMFileReader::DoOnLoadEnd(nsresult aStatus,
                              nsAString& aSuccessEvent,
                              nsAString& aTerminationEvent)
 {
-
   // Make sure we drop all the objects that could hold files open now.
   nsCOMPtr<nsIAsyncInputStream> stream;
   mAsyncStream.swap(stream);
 
   RefPtr<Blob> blob;
   mBlob.swap(blob);
 
-  aSuccessEvent = NS_LITERAL_STRING(LOAD_STR);
-  aTerminationEvent = NS_LITERAL_STRING(LOADEND_STR);
-
   // Clear out the data if necessary
   if (NS_FAILED(aStatus)) {
     FreeFileData();
     return NS_OK;
   }
 
+  // In case we read a different number of bytes, we can assume that the
+  // underlying storage has changed. We should not continue.
+  if (mDataLen != mTotal) {
+    DispatchError(NS_ERROR_FAILURE, aTerminationEvent);
+    FreeFileData();
+    return NS_ERROR_FAILURE;
+  }
+
+  aSuccessEvent = NS_LITERAL_STRING(LOAD_STR);
+  aTerminationEvent = NS_LITERAL_STRING(LOADEND_STR);
+
   nsresult rv = NS_OK;
   switch (mDataFormat) {
     case FILE_AS_ARRAYBUFFER: {
       AutoJSAPI jsapi;
       if (NS_WARN_IF(!jsapi.Init(mozilla::DOMEventTargetHelper::GetParentObject()))) {
+        FreeFileData();
         return NS_ERROR_FAILURE;
       }
 
       RootResultArrayBuffer();
-      mResultArrayBuffer = JS_NewArrayBufferWithContents(jsapi.cx(), mTotal, mFileData);
+      mResultArrayBuffer = JS_NewArrayBufferWithContents(jsapi.cx(), mDataLen, mFileData);
       if (!mResultArrayBuffer) {
         JS_ClearPendingException(jsapi.cx());
         rv = NS_ERROR_OUT_OF_MEMORY;
       } else {
         mFileData = nullptr; // Transfer ownership
       }
       break;
     }
@@ -338,18 +346,17 @@ nsDOMFileReader::DoOnLoadEnd(nsresult aS
 nsresult
 nsDOMFileReader::DoReadData(nsIAsyncInputStream* aStream, uint64_t aCount)
 {
   MOZ_ASSERT(aStream);
 
   if (mDataFormat == FILE_AS_BINARY) {
     //Continuously update our binary string as data comes in
     uint32_t oldLen = mResult.Length();
-    NS_ASSERTION(mResult.Length() == mDataLen,
-                 "unexpected mResult length");
+    NS_ASSERTION(mResult.Length() == mDataLen, "unexpected mResult length");
     if (uint64_t(oldLen) + aCount > UINT32_MAX)
       return NS_ERROR_OUT_OF_MEMORY;
     char16_t *buf = nullptr;
     mResult.GetMutableData(&buf, oldLen + aCount, fallible);
     NS_ENSURE_TRUE(buf, NS_ERROR_OUT_OF_MEMORY);
 
     uint32_t bytesRead = 0;
     aStream->ReadSegments(ReadFuncBinaryString, buf + oldLen, aCount,
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -1070,38 +1070,39 @@ nsDOMWindowUtils::SendNativeTouchPoint(u
     return NS_ERROR_FAILURE;
   }
 
   if (aPressure < 0 || aPressure > 1 || aOrientation > 359) {
     return NS_ERROR_INVALID_ARG;
   }
 
   NS_DispatchToMainThread(NS_NewRunnableMethodWithArgs
-    <uint32_t, nsIWidget::TouchPointerState, nsIntPoint, double, uint32_t, nsIObserver*>
+    <uint32_t, nsIWidget::TouchPointerState, ScreenIntPoint, double, uint32_t, nsIObserver*>
     (widget, &nsIWidget::SynthesizeNativeTouchPoint, aPointerId,
-    (nsIWidget::TouchPointerState)aTouchState, nsIntPoint(aScreenX, aScreenY),
+    (nsIWidget::TouchPointerState)aTouchState,
+    ScreenIntPoint(aScreenX, aScreenY),
     aPressure, aOrientation, aObserver));
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::SendNativeTouchTap(int32_t aScreenX,
                                      int32_t aScreenY,
                                      bool aLongTap,
                                      nsIObserver* aObserver)
 {
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget) {
     return NS_ERROR_FAILURE;
   }
 
   NS_DispatchToMainThread(NS_NewRunnableMethodWithArgs
-    <nsIntPoint, bool, nsIObserver*>
+    <ScreenIntPoint, bool, nsIObserver*>
     (widget, &nsIWidget::SynthesizeNativeTouchTap,
-    nsIntPoint(aScreenX, aScreenY), aLongTap, aObserver));
+    ScreenIntPoint(aScreenX, aScreenY), aLongTap, aObserver));
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::ClearNativeTouchSequence(nsIObserver* aObserver)
 {
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget) {
--- a/dom/base/nsFormData.cpp
+++ b/dom/base/nsFormData.cpp
@@ -20,17 +20,18 @@ nsFormData::nsFormData(nsISupports* aOwn
   , mOwner(aOwner)
 {
 }
 
 namespace {
 
 // Implements steps 3 and 4 of the "create an entry" algorithm of FormData.
 already_AddRefed<File>
-CreateNewFileInstance(Blob& aBlob, const Optional<nsAString>& aFilename)
+CreateNewFileInstance(Blob& aBlob, const Optional<nsAString>& aFilename,
+                      ErrorResult& aRv)
 {
   // Step 3 "If value is a Blob object and not a File object, set value to
   // a new File object, representing the same bytes, whose name attribute value
   // is "blob"."
   // Step 4 "If value is a File object and filename is given, set value to
   // a new File object, representing the same bytes, whose name attribute
   // value is filename."
   nsAutoString filename;
@@ -42,17 +43,22 @@ CreateNewFileInstance(Blob& aBlob, const
     RefPtr<File> file = aBlob.ToFile();
     if (file) {
       return file.forget();
     }
 
     filename = NS_LITERAL_STRING("blob");
   }
 
-  return aBlob.ToFile(filename);
+  RefPtr<File> file = aBlob.ToFile(filename, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  return file.forget();
 }
 
 } // namespace
 
 // -------------------------------------------------------------------------
 // nsISupports
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsFormData)
@@ -96,26 +102,32 @@ nsresult
 nsFormData::GetEncodedSubmission(nsIURI* aURI,
                                  nsIInputStream** aPostDataStream)
 {
   NS_NOTREACHED("Shouldn't call nsFormData::GetEncodedSubmission");
   return NS_OK;
 }
 
 void
-nsFormData::Append(const nsAString& aName, const nsAString& aValue)
+nsFormData::Append(const nsAString& aName, const nsAString& aValue,
+                   ErrorResult& aRv)
 {
   AddNameValuePair(aName, aValue);
 }
 
 void
 nsFormData::Append(const nsAString& aName, Blob& aBlob,
-                   const Optional<nsAString>& aFilename)
+                   const Optional<nsAString>& aFilename,
+                   ErrorResult& aRv)
 {
-  RefPtr<File> file = CreateNewFileInstance(aBlob, aFilename);
+  RefPtr<File> file = CreateNewFileInstance(aBlob, aFilename, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return;
+  }
+
   AddNameFilePair(aName, file);
 }
 
 void
 nsFormData::Delete(const nsAString& aName)
 {
   // We have to use this slightly awkward for loop since uint32_t >= 0 is an
   // error for being always true.
@@ -191,35 +203,41 @@ nsFormData::RemoveAllOthersAndGetFirstFo
     }
   }
 
   return lastFoundTuple;
 }
 
 void
 nsFormData::Set(const nsAString& aName, Blob& aBlob,
-                const Optional<nsAString>& aFilename)
+                const Optional<nsAString>& aFilename,
+                ErrorResult& aRv)
 {
   FormDataTuple* tuple = RemoveAllOthersAndGetFirstFormDataTuple(aName);
   if (tuple) {
-    RefPtr<File> file = CreateNewFileInstance(aBlob, aFilename);
+    RefPtr<File> file = CreateNewFileInstance(aBlob, aFilename, aRv);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return;
+    }
+
     SetNameFilePair(tuple, aName, file);
   } else {
-    Append(aName, aBlob, aFilename);
+    Append(aName, aBlob, aFilename, aRv);
   }
 }
 
 void
-nsFormData::Set(const nsAString& aName, const nsAString& aValue)
+nsFormData::Set(const nsAString& aName, const nsAString& aValue,
+                ErrorResult& aRv)
 {
   FormDataTuple* tuple = RemoveAllOthersAndGetFirstFormDataTuple(aName);
   if (tuple) {
     SetNameValuePair(tuple, aName, aValue);
   } else {
-    Append(aName, aValue);
+    Append(aName, aValue, aRv);
   }
 }
 
 uint32_t
 nsFormData::GetIterableLength() const
 {
   return mFormData.Length();
 }
@@ -256,30 +274,40 @@ nsFormData::Append(const nsAString& aNam
     NS_ENSURE_SUCCESS(rv, rv);
 
     free(iid);
 
     nsCOMPtr<nsIDOMBlob> domBlob = do_QueryInterface(supports);
     RefPtr<Blob> blob = static_cast<Blob*>(domBlob.get());
     if (domBlob) {
       Optional<nsAString> temp;
-      Append(aName, *blob, temp);
+      ErrorResult rv;
+      Append(aName, *blob, temp, rv);
+      if (NS_WARN_IF(rv.Failed())) {
+        return rv.StealNSResult();
+      }
+
       return NS_OK;
     }
   }
 
   char16_t* stringData = nullptr;
   uint32_t stringLen = 0;
   rv = aValue->GetAsWStringWithSize(&stringLen, &stringData);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsString valAsString;
   valAsString.Adopt(stringData, stringLen);
 
-  Append(aName, valAsString);
+  ErrorResult error;
+  Append(aName, valAsString, error);
+  if (NS_WARN_IF(error.Failed())) {
+    return error.StealNSResult();
+  }
+
   return NS_OK;
 }
 
 /* virtual */ JSObject*
 nsFormData::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return FormDataBinding::Wrap(aCx, this, aGivenProto);
 }
--- a/dom/base/nsFormData.h
+++ b/dom/base/nsFormData.h
@@ -88,26 +88,30 @@ public:
   GetParentObject() const
   {
     return mOwner;
   }
   static already_AddRefed<nsFormData>
   Constructor(const mozilla::dom::GlobalObject& aGlobal,
               const mozilla::dom::Optional<mozilla::dom::NonNull<mozilla::dom::HTMLFormElement> >& aFormElement,
               mozilla::ErrorResult& aRv);
-  void Append(const nsAString& aName, const nsAString& aValue);
+  void Append(const nsAString& aName, const nsAString& aValue,
+              mozilla::ErrorResult& aRv);
   void Append(const nsAString& aName, Blob& aBlob,
-              const mozilla::dom::Optional<nsAString>& aFilename);
+              const mozilla::dom::Optional<nsAString>& aFilename,
+              mozilla::ErrorResult& aRv);
   void Delete(const nsAString& aName);
   void Get(const nsAString& aName, mozilla::dom::Nullable<OwningFileOrUSVString>& aOutValue);
   void GetAll(const nsAString& aName, nsTArray<OwningFileOrUSVString>& aValues);
   bool Has(const nsAString& aName);
   void Set(const nsAString& aName, Blob& aBlob,
-           const mozilla::dom::Optional<nsAString>& aFilename);
-  void Set(const nsAString& aName, const nsAString& aValue);
+           const mozilla::dom::Optional<nsAString>& aFilename,
+           mozilla::ErrorResult& aRv);
+  void Set(const nsAString& aName, const nsAString& aValue,
+           mozilla::ErrorResult& aRv);
 
   uint32_t GetIterableLength() const;
   const nsAString& GetKeyAtIndex(uint32_t aIndex) const;
   const OwningFileOrUSVString& GetValueAtIndex(uint32_t aIndex) const;
 
   // nsFormSubmission
   virtual nsresult GetEncodedSubmission(nsIURI* aURI,
                                         nsIInputStream** aPostDataStream) override;
--- a/dom/base/nsScriptLoader.cpp
+++ b/dom/base/nsScriptLoader.cpp
@@ -293,17 +293,17 @@ nsScriptLoader::StartLoad(nsScriptLoadRe
   nsIDocShell *docshell = window->GetDocShell();
   nsCOMPtr<nsIInterfaceRequestor> prompter(do_QueryInterface(docshell));
 
   nsSecurityFlags securityFlags =
     aRequest->mCORSMode == CORS_NONE
     ? nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL
     : nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS;
   if (aRequest->mCORSMode == CORS_USE_CREDENTIALS) {
-    securityFlags |= nsILoadInfo::SEC_REQUIRE_CORS_WITH_CREDENTIALS;
+    securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
   }
   securityFlags |= nsILoadInfo::SEC_ALLOW_CHROME;
 
   nsCOMPtr<nsIChannel> channel;
   nsresult rv = NS_NewChannel(getter_AddRefs(channel),
                               aRequest->mURI,
                               context,
                               securityFlags,
--- a/dom/base/nsXMLHttpRequest.cpp
+++ b/dom/base/nsXMLHttpRequest.cpp
@@ -117,21 +117,20 @@ using namespace mozilla::dom;
                                                    // terms.
 // The above states are mutually exclusive, change with ChangeState() only.
 // The states below can be combined.
 #define XML_HTTP_REQUEST_ABORTED        (1 << 7)  // Internal
 #define XML_HTTP_REQUEST_ASYNC          (1 << 8)  // Internal
 #define XML_HTTP_REQUEST_PARSEBODY      (1 << 9)  // Internal
 #define XML_HTTP_REQUEST_SYNCLOOPING    (1 << 10) // Internal
 #define XML_HTTP_REQUEST_BACKGROUND     (1 << 13) // Internal
-#define XML_HTTP_REQUEST_USE_XSITE_AC   (1 << 14) // Internal
-#define XML_HTTP_REQUEST_NEED_AC_PREFLIGHT_IF_XSITE (1 << 15) // Internal
-#define XML_HTTP_REQUEST_AC_WITH_CREDENTIALS (1 << 16) // Internal
-#define XML_HTTP_REQUEST_TIMED_OUT (1 << 17) // Internal
-#define XML_HTTP_REQUEST_DELETED (1 << 18) // Internal
+#define XML_HTTP_REQUEST_HAD_UPLOAD_LISTENERS_ON_SEND (1 << 14) // Internal
+#define XML_HTTP_REQUEST_AC_WITH_CREDENTIALS (1 << 15) // Internal
+#define XML_HTTP_REQUEST_TIMED_OUT (1 << 16) // Internal
+#define XML_HTTP_REQUEST_DELETED (1 << 17) // Internal
 
 #define XML_HTTP_REQUEST_LOADSTATES         \
   (XML_HTTP_REQUEST_UNSENT |                \
    XML_HTTP_REQUEST_OPENED |                \
    XML_HTTP_REQUEST_HEADERS_RECEIVED |      \
    XML_HTTP_REQUEST_LOADING |               \
    XML_HTTP_REQUEST_DONE |                  \
    XML_HTTP_REQUEST_SENT)
@@ -793,43 +792,42 @@ nsXMLHttpRequest::CreateResponseParsedJS
     return NS_ERROR_FAILURE;
   }
 
   mResultJSON = value;
   return NS_OK;
 }
 
 void
-nsXMLHttpRequest::CreatePartialBlob()
+nsXMLHttpRequest::CreatePartialBlob(ErrorResult& aRv)
 {
   if (mDOMBlob) {
     // Use progress info to determine whether load is complete, but use
     // mDataAvailable to ensure a slice is created based on the uncompressed
     // data count.
     if (mLoadTotal == mLoadTransferred) {
       mResponseBlob = mDOMBlob;
     } else {
-      ErrorResult rv;
       mResponseBlob = mDOMBlob->CreateSlice(0, mDataAvailable,
-                                            EmptyString(), rv);
+                                            EmptyString(), aRv);
     }
     return;
   }
 
   // mBlobSet can be null if the request has been canceled
   if (!mBlobSet) {
     return;
   }
 
   nsAutoCString contentType;
   if (mLoadTotal == mLoadTransferred) {
     mChannel->GetContentType(contentType);
   }
 
-  mResponseBlob = mBlobSet->GetBlobInternal(GetOwner(), contentType);
+  mResponseBlob = mBlobSet->GetBlobInternal(GetOwner(), contentType, aRv);
 }
 
 NS_IMETHODIMP nsXMLHttpRequest::GetResponseType(nsAString& aResponseType)
 {
   switch (mResponseType) {
   case XML_HTTP_RESPONSE_TYPE_DEFAULT:
     aResponseType.Truncate();
     break;
@@ -1013,17 +1011,17 @@ nsXMLHttpRequest::GetResponse(JSContext*
   {
     if (!(mState & XML_HTTP_REQUEST_DONE)) {
       if (mResponseType != XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) {
         aResponse.setNull();
         return;
       }
 
       if (!mResponseBlob) {
-        CreatePartialBlob();
+        CreatePartialBlob(aRv);
       }
     }
 
     if (!mResponseBlob) {
       aResponse.setNull();
       return;
     }
 
@@ -1066,19 +1064,32 @@ nsXMLHttpRequest::GetResponse(JSContext*
   default:
     NS_ERROR("Should not happen");
   }
 
   aResponse.setNull();
 }
 
 bool
-nsXMLHttpRequest::IsDeniedCrossSiteRequest()
+nsXMLHttpRequest::IsCrossSiteCORSRequest()
 {
-  if ((mState & XML_HTTP_REQUEST_USE_XSITE_AC) && mChannel) {
+  if (!mChannel) {
+    return false;
+  }
+
+  nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
+  MOZ_ASSERT(loadInfo);
+
+  return loadInfo->GetTainting() == LoadTainting::CORS;
+}
+
+bool
+nsXMLHttpRequest::IsDeniedCrossSiteCORSRequest()
+{
+  if (IsCrossSiteCORSRequest()) {
     nsresult rv;
     mChannel->GetStatus(&rv);
     if (NS_FAILED(rv)) {
       return true;
     }
   }
   return false;
 }
@@ -1090,17 +1101,17 @@ nsXMLHttpRequest::GetResponseURL(nsAStri
 
   uint16_t readyState = ReadyState();
   if ((readyState == UNSENT || readyState == OPENED) || !mChannel) {
     return;
   }
 
   // Make sure we don't leak responseURL information from denied cross-site
   // requests.
-  if (IsDeniedCrossSiteRequest()) {
+  if (IsDeniedCrossSiteCORSRequest()) {
     return;
   }
 
   nsCOMPtr<nsIURI> responseUrl;
   mChannel->GetURI(getter_AddRefs(responseUrl));
 
   if (!responseUrl) {
     return;
@@ -1118,17 +1129,17 @@ nsXMLHttpRequest::GetStatus(uint32_t *aS
   return NS_OK;
 }
 
 uint32_t
 nsXMLHttpRequest::Status()
 {
   // Make sure we don't leak status information from denied cross-site
   // requests.
-  if (IsDeniedCrossSiteRequest()) {
+  if (IsDeniedCrossSiteCORSRequest()) {
     return 0;
   }
 
   uint16_t readyState = ReadyState();
   if (readyState == UNSENT || readyState == OPENED) {
     return 0;
   }
 
@@ -1168,17 +1179,17 @@ IMPL_CSTRING_GETTER(GetStatusText)
 void
 nsXMLHttpRequest::GetStatusText(nsCString& aStatusText)
 {
   // Return an empty status text on all error loads.
   aStatusText.Truncate();
 
   // Make sure we don't leak status information from denied cross-site
   // requests.
-  if (IsDeniedCrossSiteRequest()) {
+  if (IsDeniedCrossSiteCORSRequest()) {
     return;
   }
 
   // Check the current XHR state to see if it is valid to obtain the statusText
   // value.  This check is to prevent the status text for redirects from being
   // available before all the redirects have been followed and HTTP headers have
   // been received.
   uint16_t readyState = ReadyState();
@@ -1263,17 +1274,17 @@ bool
 nsXMLHttpRequest::IsSafeHeader(const nsACString& header, nsIHttpChannel* httpChannel)
 {
   // See bug #380418. Hide "Set-Cookie" headers from non-chrome scripts.
   if (!IsSystemXHR() && nsContentUtils::IsForbiddenResponseHeader(header)) {
     NS_WARNING("blocked access to response header");
     return false;
   }
   // if this is not a CORS call all headers are safe
-  if (!(mState & XML_HTTP_REQUEST_USE_XSITE_AC)){
+  if (!IsCrossSiteCORSRequest()) {
     return true;
   }
   // Check for dangerous headers
   // Make sure we don't leak header information from denied cross-site
   // requests.
   if (mChannel) {
     nsresult status;
     mChannel->GetStatus(&status);
@@ -1525,27 +1536,16 @@ nsXMLHttpRequest::GetCurrentJARChannel()
 }
 
 bool
 nsXMLHttpRequest::IsSystemXHR()
 {
   return mIsSystem || nsContentUtils::IsSystemPrincipal(mPrincipal);
 }
 
-void
-nsXMLHttpRequest::CheckChannelForCrossSiteRequest(nsIChannel* aChannel)
-{
-  // A system XHR (chrome code or a web app with the right permission) can
-  // load anything, and same-origin loads are always allowed.
-  if (!IsSystemXHR() &&
-      !nsContentUtils::CheckMayLoad(mPrincipal, aChannel, true)) {
-    mState |= XML_HTTP_REQUEST_USE_XSITE_AC;
-  }
-}
-
 NS_IMETHODIMP
 nsXMLHttpRequest::Open(const nsACString& method, const nsACString& url,
                        bool async, const nsAString& user,
                        const nsAString& password, uint8_t optional_argc)
 {
   if (!optional_argc) {
     // No optional arguments were passed in. Default async to true.
     async = true;
@@ -1686,16 +1686,20 @@ nsXMLHttpRequest::Open(const nsACString&
   }
   else {
     // Otherwise use CORS. Again, make sure that potential result documents
     // use the same principal as the loader.
     secFlags = nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS |
                nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
   }
 
+  if (mIsAnon) {
+    secFlags |= nsILoadInfo::SEC_COOKIES_OMIT;
+  }
+
   // If we have the document, use it. Unfortunately, for dedicated workers
   // 'doc' ends up being the parent document, which is not the document
   // that we want to use. So make sure to avoid using 'doc' in that situation.
   if (doc && doc->NodePrincipal() == mPrincipal) {
     rv = NS_NewChannel(getter_AddRefs(mChannel),
                        uri,
                        doc,
                        secFlags,
@@ -1712,18 +1716,17 @@ nsXMLHttpRequest::Open(const nsACString&
                        nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST,
                        loadGroup,
                        nullptr,   // aCallbacks
                        loadFlags);
   }
 
   NS_ENSURE_SUCCESS(rv, rv);
 
-  mState &= ~(XML_HTTP_REQUEST_USE_XSITE_AC |
-              XML_HTTP_REQUEST_NEED_AC_PREFLIGHT_IF_XSITE);
+  mState &= ~XML_HTTP_REQUEST_HAD_UPLOAD_LISTENERS_ON_SEND;
 
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
   if (httpChannel) {
     rv = httpChannel->SetRequestMethod(method);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Set the initiator type
     nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChannel));
@@ -1920,22 +1923,16 @@ nsXMLHttpRequest::OnStartRequest(nsIRequ
     mRequestObserver->OnStartRequest(request, ctxt);
   }
 
   if (request != mChannel) {
     // Can this still happen?
     return NS_OK;
   }
 
-  // Always treat tainted channels as cross-origin.
-  nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
-  if (loadInfo && loadInfo->GetTainting() != LoadTainting::Basic) {
-    mState |= XML_HTTP_REQUEST_USE_XSITE_AC;
-  }
-
   // Don't do anything if we have been aborted
   if (mState & XML_HTTP_REQUEST_UNSENT)
     return NS_OK;
 
   /* Apparently, Abort() should set XML_HTTP_REQUEST_UNSENT.  See bug 361773.
      XHR2 spec says this is correct. */
   if (mState & XML_HTTP_REQUEST_ABORTED) {
     NS_ERROR("Ugh, still getting data on an aborted XMLHttpRequest!");
@@ -2111,30 +2108,34 @@ nsXMLHttpRequest::OnStartRequest(nsIRequ
     mResponseXML = do_QueryInterface(responseDoc);
     mResponseXML->SetChromeXHRDocURI(chromeXHRDocURI);
     mResponseXML->SetChromeXHRDocBaseURI(chromeXHRDocBaseURI);
 
     if (nsContentUtils::IsSystemPrincipal(mPrincipal)) {
       mResponseXML->ForceEnableXULXBL();
     }
 
-    if (mState & XML_HTTP_REQUEST_USE_XSITE_AC) {
+    nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
+    MOZ_ASSERT(loadInfo);
+    bool isCrossSite = loadInfo->GetTainting() != LoadTainting::Basic;
+
+    if (isCrossSite) {
       nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(mResponseXML);
       if (htmlDoc) {
         htmlDoc->DisableCookieAccess();
       }
     }
 
     nsCOMPtr<nsIStreamListener> listener;
     nsCOMPtr<nsILoadGroup> loadGroup;
     channel->GetLoadGroup(getter_AddRefs(loadGroup));
 
     rv = mResponseXML->StartDocumentLoad(kLoadAsData, channel, loadGroup,
                                          nullptr, getter_AddRefs(listener),
-                                         !(mState & XML_HTTP_REQUEST_USE_XSITE_AC));
+                                         !isCrossSite);
     NS_ENSURE_SUCCESS(rv, rv);
 
     mXMLParserStreamListener = listener;
     rv = mXMLParserStreamListener->OnStartRequest(request, ctxt);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // We won't get any progress events anyway if we didn't have progress
@@ -2206,18 +2207,24 @@ nsXMLHttpRequest::OnStopRequest(nsIReque
       // and if the response length is zero.
       if (!mBlobSet) {
         mBlobSet = new BlobSet();
       }
       // Smaller files may be written in cache map instead of separate files.
       // Also, no-store response cannot be written in persistent cache.
       nsAutoCString contentType;
       mChannel->GetContentType(contentType);
-      mResponseBlob = mBlobSet->GetBlobInternal(GetOwner(), contentType);
+
+      ErrorResult rv;
+      mResponseBlob = mBlobSet->GetBlobInternal(GetOwner(), contentType, rv);
       mBlobSet = nullptr;
+
+      if (NS_WARN_IF(rv.Failed())) {
+        return rv.StealNSResult();
+      }
     }
     NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
     NS_ASSERTION(mResponseText.IsEmpty(), "mResponseText should be empty");
   } else if (NS_SUCCEEDED(status) &&
              ((mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER &&
                !mIsMappedArrayBuffer) ||
               mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER)) {
     // set the capacity down to the actual length, to realloc back
@@ -2784,43 +2791,29 @@ nsXMLHttpRequest::Send(nsIVariant* aVari
           do_QueryInterface(httpChannel);
         uploadChannel->SetUploadStream(postDataStream, contentType, mUploadTotal);
         // Reset the method to its original value
         httpChannel->SetRequestMethod(method);
       }
     }
   }
 
-  if (httpChannel) {
-    nsAutoCString contentTypeHeader;
-    rv = httpChannel->GetRequestHeader(NS_LITERAL_CSTRING("Content-Type"),
-                                       contentTypeHeader);
-    if (NS_SUCCEEDED(rv)) {
-      if (!nsContentUtils::IsAllowedNonCorsContentType(contentTypeHeader)) {
-        mCORSUnsafeHeaders.AppendElement(NS_LITERAL_CSTRING("Content-Type"));
-      }
-    }
-  }
-
   ResetResponse();
 
-  CheckChannelForCrossSiteRequest(mChannel);
-
-  bool withCredentials = !!(mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS);
-
-  if (!IsSystemXHR() && withCredentials) {
+  if (!IsSystemXHR() && !mIsAnon &&
+      (mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS)) {
     // This is quite sad. We have to create the channel in .open(), since the
     // chrome-only xhr.channel API depends on that. However .withCredentials
     // can be modified after, so we don't know what to set the
-    // SEC_REQUIRE_CORS_WITH_CREDENTIALS flag to when the channel is
+    // SEC_COOKIES_INCLUDE flag to when the channel is
     // created. So set it here using a hacky internal API.
 
     // Not doing this for system XHR uses since those don't use CORS.
     nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
-    static_cast<LoadInfo*>(loadInfo.get())->SetWithCredentialsSecFlag();
+    static_cast<LoadInfo*>(loadInfo.get())->SetIncludeCookiesSecFlag();
   }
 
   // Blocking gets are common enough out of XHR that we should mark
   // the channel slow by default for pipeline purposes
   AddLoadFlags(mChannel, nsIRequest::INHIBIT_PIPELINE);
 
   nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(mChannel));
   if (cos) {
@@ -2832,20 +2825,17 @@ nsXMLHttpRequest::Send(nsIVariant* aVari
 
   nsCOMPtr<nsIHttpChannelInternal>
     internalHttpChannel(do_QueryInterface(mChannel));
   if (internalHttpChannel) {
     // Disable Necko-internal response timeouts.
     internalHttpChannel->SetResponseTimeoutEnabled(false);
   }
 
-  if (mIsAnon) {
-    AddLoadFlags(mChannel, nsIRequest::LOAD_ANONYMOUS);
-  }
-  else {
+  if (!mIsAnon) {
     AddLoadFlags(mChannel, nsIChannel::LOAD_EXPLICIT_CREDENTIALS);
   }
 
   // When we are sync loading, we need to bypass the local cache when it would
   // otherwise block us waiting for exclusive access to the cache.  If we don't
   // do this, then we could dead lock in some cases (see bug 309424).
   //
   // Also don't block on the cache entry on async if it is busy - favoring parallelism
@@ -2871,33 +2861,26 @@ nsXMLHttpRequest::Send(nsIVariant* aVari
       contentType.Equals(UNKNOWN_CONTENT_TYPE)) {
     mChannel->SetContentType(NS_LITERAL_CSTRING("application/xml"));
   }
 
   // We're about to send the request.  Start our timeout.
   mRequestSentTime = PR_Now();
   StartTimeoutTimer();
 
-  // Check if we need to do a preflight request.
-  if (!mCORSUnsafeHeaders.IsEmpty() ||
-      (mUpload && mUpload->HasListeners()) ||
-      (!method.LowerCaseEqualsLiteral("get") &&
-       !method.LowerCaseEqualsLiteral("post") &&
-       !method.LowerCaseEqualsLiteral("head"))) {
-    mState |= XML_HTTP_REQUEST_NEED_AC_PREFLIGHT_IF_XSITE;
+  // Check if we should enabled cross-origin upload listeners.
+  if (mUpload && mUpload->HasListeners()) {
+    mState |= XML_HTTP_REQUEST_HAD_UPLOAD_LISTENERS_ON_SEND;
   }
 
   // Set up the preflight if needed
-  if ((mState & XML_HTTP_REQUEST_USE_XSITE_AC) &&
-      (mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT_IF_XSITE)) {
-    NS_ENSURE_TRUE(internalHttpChannel, NS_ERROR_DOM_BAD_URI);
-
-    rv = internalHttpChannel->SetCorsPreflightParameters(mCORSUnsafeHeaders,
-                                                         withCredentials, mPrincipal);
-    NS_ENSURE_SUCCESS(rv, rv);
+  if (!IsSystemXHR()) {
+    nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
+    loadInfo->SetCorsPreflightInfo(mCORSUnsafeHeaders,
+                                   mState & XML_HTTP_REQUEST_HAD_UPLOAD_LISTENERS_ON_SEND);
   }
 
   mIsMappedArrayBuffer = false;
   if (mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER &&
       Preferences::GetBool("dom.mapped_arraybuffer.enabled", false)) {
     nsCOMPtr<nsIURI> uri;
     nsAutoCString scheme;
 
@@ -3057,17 +3040,17 @@ nsXMLHttpRequest::SetRequestHeader(const
         if (header.LowerCaseEqualsASCII(kCrossOriginSafeHeaders[i])) {
           safeHeader = true;
           break;
         }
       }
     }
 
     if (!safeHeader) {
-      if (!mCORSUnsafeHeaders.Contains(header)) {
+      if (!mCORSUnsafeHeaders.Contains(header, nsCaseInsensitiveCStringArrayComparator())) {
         mCORSUnsafeHeaders.AppendElement(header);
       }
     }
   } else {
     // Case 1 above
     if (nsContentUtils::IsForbiddenSystemRequestHeader(header)) {
       mergeHeaders = false;
     }
@@ -3262,18 +3245,19 @@ nsXMLHttpRequest::SetWithCredentials(boo
 }
 
 void
 nsXMLHttpRequest::SetWithCredentials(bool aWithCredentials, ErrorResult& aRv)
 {
   // Return error if we're already processing a request.  Note that we can't use
   // ReadyState() here, because it can't differentiate between "opened" and
   // "sent", so we use mState directly.
-  if (!(mState & XML_HTTP_REQUEST_UNSENT) &&
-      !(mState & XML_HTTP_REQUEST_OPENED)) {
+  if ((!(mState & XML_HTTP_REQUEST_UNSENT) &&
+       !(mState & XML_HTTP_REQUEST_OPENED)) ||
+      mIsAnon) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   // sync request is not allowed setting withCredentials in window context
   if (HasOrHasHadOwner() &&
       !(mState & (XML_HTTP_REQUEST_UNSENT | XML_HTTP_REQUEST_ASYNC))) {
     LogMessage("WithCredentialsSyncXHRWarning", GetOwner());
@@ -3316,132 +3300,80 @@ nsXMLHttpRequest::ChangeState(uint32_t a
     NS_ENSURE_SUCCESS(rv, rv);
 
     DispatchDOMEvent(nullptr, event, nullptr, nullptr);
   }
 
   return rv;
 }
 
-/*
- * Simple helper class that just forwards the redirect callback back
- * to the nsXMLHttpRequest.
- */
-class AsyncVerifyRedirectCallbackForwarder final : public nsIAsyncVerifyRedirectCallback
-{
-public:
-  explicit AsyncVerifyRedirectCallbackForwarder(nsXMLHttpRequest* xhr)
-    : mXHR(xhr)
-  {
-  }
-
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_CLASS(AsyncVerifyRedirectCallbackForwarder)
-
-  // nsIAsyncVerifyRedirectCallback implementation
-  NS_IMETHOD OnRedirectVerifyCallback(nsresult result) override
-  {
-    mXHR->OnRedirectVerifyCallback(result);
-
-    return NS_OK;
-  }
-
-private:
-  ~AsyncVerifyRedirectCallbackForwarder() {}
-
-  RefPtr<nsXMLHttpRequest> mXHR;
-};
-
-NS_IMPL_CYCLE_COLLECTION(AsyncVerifyRedirectCallbackForwarder, mXHR)
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AsyncVerifyRedirectCallbackForwarder)
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
-  NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
-NS_INTERFACE_MAP_END
-
-NS_IMPL_CYCLE_COLLECTING_ADDREF(AsyncVerifyRedirectCallbackForwarder)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(AsyncVerifyRedirectCallbackForwarder)
-
-
 /////////////////////////////////////////////////////
 // nsIChannelEventSink methods:
 //
 NS_IMETHODIMP
 nsXMLHttpRequest::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
                                          nsIChannel *aNewChannel,
                                          uint32_t    aFlags,
                                          nsIAsyncVerifyRedirectCallback *callback)
 {
   NS_PRECONDITION(aNewChannel, "Redirect without a channel?");
 
-  nsresult rv;
-
-  if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags)) {
-    CheckChannelForCrossSiteRequest(aNewChannel);
-
-    // Disable redirects for preflighted cross-site requests entirely for now
-    if ((mState & XML_HTTP_REQUEST_USE_XSITE_AC) &&
-        (mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT_IF_XSITE)) {
-       aOldChannel->Cancel(NS_ERROR_DOM_BAD_URI);
-       return NS_ERROR_DOM_BAD_URI;
-    }
-  }
-
   // Prepare to receive callback
   mRedirectCallback = callback;
   mNewRedirectChannel = aNewChannel;
 
   if (mChannelEventSink) {
-    RefPtr<AsyncVerifyRedirectCallbackForwarder> fwd =
-      new AsyncVerifyRedirectCallbackForwarder(this);
-
-    rv = mChannelEventSink->AsyncOnChannelRedirect(aOldChannel,
-                                                   aNewChannel,
-                                                   aFlags, fwd);
+    nsCOMPtr<nsIAsyncVerifyRedirectCallback> fwd =
+      EnsureXPCOMifier();
+
+    nsresult rv = mChannelEventSink->AsyncOnChannelRedirect(aOldChannel,
+                                                            aNewChannel,
+                                                            aFlags, fwd);
     if (NS_FAILED(rv)) {
         mRedirectCallback = nullptr;
         mNewRedirectChannel = nullptr;
     }
     return rv;
   }
   OnRedirectVerifyCallback(NS_OK);
   return NS_OK;
 }
 
-void
+nsresult
 nsXMLHttpRequest::OnRedirectVerifyCallback(nsresult result)
 {
   NS_ASSERTION(mRedirectCallback, "mRedirectCallback not set in callback");
   NS_ASSERTION(mNewRedirectChannel, "mNewRedirectChannel not set in callback");
 
   if (NS_SUCCEEDED(result)) {
     mChannel = mNewRedirectChannel;
 
     nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
     if (httpChannel) {
       // Ensure all original headers are duplicated for the new channel (bug #553888)
-      for (uint32_t i = mModifiedRequestHeaders.Length(); i > 0; ) {
-        --i;
-        if (mModifiedRequestHeaders[i].value.IsEmpty()) {
-          httpChannel->SetEmptyRequestHeader(mModifiedRequestHeaders[i].header);
+      for (RequestHeader& requestHeader : mModifiedRequestHeaders) {
+        if (requestHeader.value.IsEmpty()) {
+          httpChannel->SetEmptyRequestHeader(requestHeader.header);
         } else {
-          httpChannel->SetRequestHeader(mModifiedRequestHeaders[i].header,
-                                        mModifiedRequestHeaders[i].value,
+          httpChannel->SetRequestHeader(requestHeader.header,
+                                        requestHeader.value,
                                         false);
         }
       }
     }
   } else {
     mErrorLoad = true;
   }
 
   mNewRedirectChannel = nullptr;
 
   mRedirectCallback->OnRedirectVerifyCallback(result);
   mRedirectCallback = nullptr;
+
+  return result;
 }
 
 /////////////////////////////////////////////////////
 // nsIProgressEventSink methods:
 //
 
 void
 nsXMLHttpRequest::MaybeDispatchProgressEvents(bool aFinalProgress)
@@ -3535,18 +3467,18 @@ nsXMLHttpRequest::OnStatus(nsIRequest *a
   }
 
   return NS_OK;
 }
 
 bool
 nsXMLHttpRequest::AllowUploadProgress()
 {
-  return !(mState & XML_HTTP_REQUEST_USE_XSITE_AC) ||
-    (mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT_IF_XSITE);
+  return !IsCrossSiteCORSRequest() ||
+    (mState & XML_HTTP_REQUEST_HAD_UPLOAD_LISTENERS_ON_SEND);
 }
 
 /////////////////////////////////////////////////////
 // nsIInterfaceRequestor methods:
 //
 NS_IMETHODIMP
 nsXMLHttpRequest::GetInterface(const nsIID & aIID, void **aResult)
 {
@@ -3596,17 +3528,17 @@ nsXMLHttpRequest::GetInterface(const nsI
 
     // Verify that it's ok to prompt for credentials here, per spec
     // http://xhr.spec.whatwg.org/#the-send%28%29-method
     bool showPrompt = true;
 
     // If authentication fails, XMLHttpRequest origin and
     // the request URL are same origin, ...
     /* Disabled - bug: 799540
-    if (mState & XML_HTTP_REQUEST_USE_XSITE_AC) {
+    if (IsCrossSiteCORSRequest()) {
       showPrompt = false;
     }
     */
 
     // ... Authorization is not in the list of author request headers, ...
     if (showPrompt) {
       for (uint32_t i = 0, len = mModifiedRequestHeaders.Length(); i < len; ++i) {
         if (mModifiedRequestHeaders[i].header.
@@ -3803,16 +3735,17 @@ nsHeaderVisitor::VisitHeader(const nsACS
   return NS_OK;
 }
 
 // nsXMLHttpRequestXPCOMifier implementation
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXMLHttpRequestXPCOMifier)
   NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
   NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
   NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
+  NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
   NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
   NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStreamListener)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXMLHttpRequestXPCOMifier)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXMLHttpRequestXPCOMifier)
--- a/dom/base/nsXMLHttpRequest.h
+++ b/dom/base/nsXMLHttpRequest.h
@@ -36,17 +36,16 @@
 #include "mozilla/dom/XMLHttpRequestBinding.h"
 
 #ifdef Status
 /* Xlib headers insist on this for some reason... Nuke it because
    it'll override our member name */
 #undef Status
 #endif
 
-class AsyncVerifyRedirectCallbackForwarder;
 class nsFormData;
 class nsIJARChannel;
 class nsILoadGroup;
 class nsIUnicodeDecoder;
 class nsIJSID;
 
 namespace mozilla {
 
@@ -421,17 +420,18 @@ private:
   {
     return Send(nullptr, aBody);
   }
   nsresult Send(const RequestBody& aBody)
   {
     return Send(Nullable<RequestBody>(aBody));
   }
 
-  bool IsDeniedCrossSiteRequest();
+  bool IsCrossSiteCORSRequest();
+  bool IsDeniedCrossSiteCORSRequest();
 
   // Tell our channel what network interface ID we were told to use.
   // If it's an HTTP channel and we were told to use a non-default
   // interface ID.
   void PopulateNetworkInterfaceId();
 
 public:
   void Send(JSContext* /*aCx*/, ErrorResult& aRv)
@@ -600,43 +600,34 @@ protected:
   nsresult AppendToResponseText(const char * aBuffer, uint32_t aBufferLen);
   static NS_METHOD StreamReaderFunc(nsIInputStream* in,
                 void* closure,
                 const char* fromRawSegment,
                 uint32_t toOffset,
                 uint32_t count,
                 uint32_t *writeCount);
   nsresult CreateResponseParsedJSON(JSContext* aCx);
-  void CreatePartialBlob();
+  void CreatePartialBlob(ErrorResult& aRv);
   bool CreateDOMBlob(nsIRequest *request);
   // Change the state of the object with this. The broadcast argument
   // determines if the onreadystatechange listener should be called.
   nsresult ChangeState(uint32_t aState, bool aBroadcast = true);
   already_AddRefed<nsILoadGroup> GetLoadGroup() const;
   nsIURI *GetBaseURI();
 
   already_AddRefed<nsIHttpChannel> GetCurrentHttpChannel();
   already_AddRefed<nsIJARChannel> GetCurrentJARChannel();
 
   bool IsSystemXHR();
 
   void ChangeStateToDone();
 
-  /**
-   * Check if aChannel is ok for a cross-site request by making sure no
-   * inappropriate headers are set, and no username/password is set.
-   *
-   * Also updates the XML_HTTP_REQUEST_USE_XSITE_AC bit.
-   */
-  void CheckChannelForCrossSiteRequest(nsIChannel* aChannel);
-
   void StartProgressEventTimer();
 
-  friend class AsyncVerifyRedirectCallbackForwarder;
-  void OnRedirectVerifyCallback(nsresult result);
+  nsresult OnRedirectVerifyCallback(nsresult result);
 
   nsresult Open(const nsACString& method, const nsACString& url, bool async,
                 const mozilla::dom::Optional<nsAString>& user,
                 const mozilla::dom::Optional<nsAString>& password);
 
   already_AddRefed<nsXMLHttpRequestXPCOMifier> EnsureXPCOMifier();
 
   nsCOMPtr<nsISupports> mContext;
@@ -835,16 +826,17 @@ public:
 private:
   bool mOldVal;
 };
 
 // A shim class designed to expose the non-DOM interfaces of
 // XMLHttpRequest via XPCOM stuff.
 class nsXMLHttpRequestXPCOMifier final : public nsIStreamListener,
                                          public nsIChannelEventSink,
+                                         public nsIAsyncVerifyRedirectCallback,
                                          public nsIProgressEventSink,
                                          public nsIInterfaceRequestor,
                                          public nsITimerCallback
 {
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsXMLHttpRequestXPCOMifier,
                                            nsIStreamListener)
 
@@ -859,16 +851,17 @@ private:
       mXHR->mXPCOMifier = nullptr;
     }
   }
 
 public:
   NS_FORWARD_NSISTREAMLISTENER(mXHR->)
   NS_FORWARD_NSIREQUESTOBSERVER(mXHR->)
   NS_FORWARD_NSICHANNELEVENTSINK(mXHR->)
+  NS_FORWARD_NSIASYNCVERIFYREDIRECTCALLBACK(mXHR->)
   NS_FORWARD_NSIPROGRESSEVENTSINK(mXHR->)
   NS_FORWARD_NSITIMERCALLBACK(mXHR->)
 
   NS_DECL_NSIINTERFACEREQUESTOR
 
 private:
   RefPtr<nsXMLHttpRequest> mXHR;
 };
--- a/dom/base/test/bug435425_redirect.sjs
+++ b/dom/base/test/bug435425_redirect.sjs
@@ -1,6 +1,6 @@
 function handleRequest(request, response)
 {
   response.setStatusLine(null, 302, "Moved");
-  response.setHeader("Location", "http://www.mozilla.org", false);
+  response.setHeader("Location", "http://nosuchdomain.localhost", false);
 }
 
new file mode 100644
--- /dev/null
+++ b/dom/base/test/file_bug1198095.js
@@ -0,0 +1,26 @@
+var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+Cu.importGlobalProperties(["File"]);
+
+function createFileWithData(message) {
+  var dirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
+  var testFile = dirSvc.get("ProfD", Ci.nsIFile);
+  testFile.append("fileAPItestfileBug1198095");
+
+  var outStream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
+  outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
+                 0666, 0);
+
+  outStream.write(message, message.length);
+  outStream.close();
+
+  var domFile = new File(testFile);
+  return domFile;
+}
+
+addMessageListener("file.open", function (message) {
+  sendAsyncMessage("file.opened", createFileWithData(message));
+});
+
+addMessageListener("file.modify", function (message) {
+  sendAsyncMessage("file.modified", createFileWithData(message));
+});
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -252,16 +252,17 @@ support-files =
   iframe_postMessages.html
   test_performance_observer.js
   performance_observer.html
   test_anonymousContent_style_csp.html^headers^
   file_explicit_user_agent.sjs
   referrer_change_server.sjs
   file_change_policy_redirect.html
   empty_worker.js
+  file_bug1198095.js
 
 [test_anonymousContent_api.html]
 [test_anonymousContent_append_after_reflow.html]
 [test_anonymousContent_canvas.html]
 skip-if = buildapp == 'b2g' # Requires webgl support
 [test_anonymousContent_insert.html]
 [test_anonymousContent_manipulate_content.html]
 [test_anonymousContent_style_csp.html]
@@ -855,8 +856,9 @@ support-files = worker_postMessages.js
 [test_window_proto.html]
 [test_frameLoader_switchProcess.html]
 skip-if = e10s || os != 'linux' || buildapp != 'browser'
 [test_explicit_user_agent.html]
 [test_change_policy.html]
 skip-if = buildapp == 'b2g' #no ssl support
 [test_document.all_iteration.html]
 [test_performance_translate.html]
+[test_bug1198095.html]
--- a/dom/base/test/test_XHRDocURI.html
+++ b/dom/base/test/test_XHRDocURI.html
@@ -366,31 +366,30 @@ function runTest() {
     if (xhr.readyState == 4) {
       gen.next();
     }
   };
   xhr.send();
   yield undefined;
 
 
-  // use chrome XHR and access URI properties from chrome privileged script
+  // use the systemXHR special privilege
   SpecialPowers.addPermission("systemXHR", true, document);
   xhr = new XMLHttpRequest({mozAnon: false, mozSystem: true});
   xhr.open("GET", "http://mochi.test:8888/tests/dom/base/test/file_XHRDocURI.xml");
   xhr.onreadystatechange = function(e) {
     if (!xhr.responseXML) {
       return;
     }
     var expects = {
       documentURI: "http://mochi.test:8888/tests/dom/base/test/file_XHRDocURI.xml",
       baseURI: "http://mochi.test:8888/tests/dom/base/test/file_XHRDocURI.xml",
       elementBaseURI: "http://www.example.com/"
     };
-    var xml = SpecialPowers.wrap(xhr.responseXML);
-    testChromeXMLDocURI(xml, expects);
+    testXMLDocURI(xhr.responseXML, expects);
     if (xhr.readyState == 4) {
       gen.next();
     }
   };
   xhr.send();
   yield undefined;
 
   xhr = new XMLHttpRequest({mozAnon: false, mozSystem: true});
@@ -399,18 +398,17 @@ function runTest() {
   xhr.onreadystatechange = function(e) {
     if (!xhr.response) {
       return;
     }
     var expects = {
       documentURI: "http://mochi.test:8888/tests/dom/base/test/file_XHRDocURI.html",
       baseURI: "http://mochi.test:8888/tests/dom/base/test/file_XHRDocURI.html"
     };
-    var doc = SpecialPowers.wrap(xhr.response);
-    testChromeHTMLDocURI(doc, "http://mochi.test:8888/tests/dom/base/test/file_XHRDocURI.html", expects);
+    testHTMLDocURI(xhr.response, expects);
     if (xhr.readyState == 4) {
       gen.next();
     }
   };
   xhr.send();
   yield undefined;
 
   xhr = new XMLHttpRequest({mozAnon: false, mozSystem: true});
@@ -419,18 +417,17 @@ function runTest() {
     if (!xhr.responseXML) {
       return;
     }
     var expects = {
       documentURI: "http://example.com/tests/dom/base/test/file_XHRDocURI.xml",
       baseURI: "http://example.com/tests/dom/base/test/file_XHRDocURI.xml",
       elementBaseURI: "http://www.example.com/"
     };
-    var xml = SpecialPowers.wrap(xhr.responseXML);
-    testChromeXMLDocURI(xml, expects);
+    testXMLDocURI(xhr.responseXML, expects);
     if (xhr.readyState == 4) {
       gen.next();
     }
   };
   xhr.send();
   yield undefined;
 
   xhr = new XMLHttpRequest({mozAnon: false, mozSystem: true});
@@ -439,18 +436,17 @@ function runTest() {
   xhr.onreadystatechange = function(e) {
     if (!xhr.response) {
       return;
     }
     var expects = {
       documentURI: "http://example.com/tests/dom/base/test/file_XHRDocURI.html",
       baseURI: "http://example.com/tests/dom/base/test/file_XHRDocURI.html"
     };
-    var doc = SpecialPowers.wrap(xhr.response);
-    testChromeHTMLDocURI(doc, "http://example.com/tests/dom/base/test/file_XHRDocURI.html", expects);
+    testHTMLDocURI(xhr.response, expects);
     if (xhr.readyState == 4) {
       gen.next();
     }
   };
   xhr.send();
   yield undefined;
 
   xhr = new XMLHttpRequest({mozAnon: false, mozSystem: true});
@@ -459,18 +455,17 @@ function runTest() {
     if (!xhr.responseXML) {
       return;
     }
     var expects = {
       documentURI: "http://example.com/tests/dom/base/test/file_XHRDocURI.xml",
       baseURI: "http://example.com/tests/dom/base/test/file_XHRDocURI.xml",
       elementBaseURI: "http://www.example.com/"
     };
-    var xml = SpecialPowers.wrap(xhr.responseXML);
-    testChromeXMLDocURI(xml, expects);
+    testXMLDocURI(xhr.responseXML, expects);
     if (xhr.readyState == 4) {
       gen.next();
     }
   };
   xhr.send();
   yield undefined;
 
   xhr = new XMLHttpRequest({mozAnon: false, mozSystem: true});
@@ -479,18 +474,17 @@ function runTest() {
   xhr.onreadystatechange = function(e) {
     if (!xhr.response) {
       return;
     }
     var expects = {
       documentURI: "http://example.com/tests/dom/base/test/file_XHRDocURI.html",
       baseURI: "http://example.com/tests/dom/base/test/file_XHRDocURI.html"
     };
-    var doc = SpecialPowers.wrap(xhr.response);
-    testChromeHTMLDocURI(doc, "http://example.com/tests/dom/base/test/file_XHRDocURI.html", expects);
+    testHTMLDocURI(xhr.response, expects);
     if (xhr.readyState == 4) {
       gen.next();
     }
   };
   xhr.send();
   yield undefined;
 
   history.pushState({}, "pushStateTest", window.location.href + "/pushStateTest");
--- a/dom/base/test/test_bug1075702.html
+++ b/dom/base/test/test_bug1075702.html
@@ -24,17 +24,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   a1.nodeValue = "lowercase";
 
   var a2 = document.createAttributeNS("", "AA");
   a2.nodeValue = "UPPERCASE";
 
   document.documentElement.setAttributeNode(a1);
   document.documentElement.setAttributeNode(a2);
 
-  is(document.documentElement.getAttributeNS("", "aa"), null, "Should be NULL!");
+  is(document.documentElement.getAttributeNS("", "aa"), "lowercase", "Should be lowercase!");
   is(document.documentElement.getAttributeNS("", "AA"), "UPPERCASE", "Should be UPPERCASE!");
 
   var a3 = document.createAttribute("AA");
   a3.nodeValue = "UPPERCASE AGAIN";
   document.documentElement.setAttributeNode(a3);
 
   is(document.documentElement.getAttributeNS("", "aa"), "UPPERCASE AGAIN",
   "Should be UPPERCASE AGAIN!");
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_bug1198095.html
@@ -0,0 +1,77 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1198095
+-->
+  <title>Test for Bug 1198095</title>
+  <script type="text/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=1198095">Mozilla Bug 1198095</a>
+
+<pre id="test">
+<script class="testbody" type="text/javascript;version=1.7">
+
+var fileData1 = '1234567890';
+var fileData2 = '43210';
+var r, firstBlob;
+
+var openerURL = SimpleTest.getTestFileURL("file_bug1198095.js");
+
+var opener = SpecialPowers.loadChromeScript(openerURL);
+opener.addMessageListener("file.opened", onFileOpened);
+opener.addMessageListener("file.modified", onFileModified);
+opener.sendAsyncMessage("file.open", fileData1);
+
+function onLoadEnd1(e) {
+  e.target.removeEventListener('loadend', onLoadEnd1);
+
+  is(e.target, r, "Target and r are ok");
+  ok(e.target.readyState, FileReader.DONE, "The file has been read.");
+  ok(e.target.result instanceof ArrayBuffer, "The result is an ArrayBuffer");
+
+  var view = new Uint8Array(e.target.result);
+  is(view.length, fileData1.length, "File data length matches");
+  for (var i = 0; i < fileData1.length; ++i) {
+    is(String.fromCharCode(view[i]), fileData1[i], "Byte matches");
+  }
+
+  opener.sendAsyncMessage("file.modify", fileData2);
+}
+
+function onLoadEnd2(e) {
+  e.target.removeEventListener('loadend', onLoadEnd2);
+  ok(false, "This method should not be called - loadEnd2!");
+}
+
+function onError1(e) {
+  ok(false, "This method should not be called - error1!");
+}
+
+function onError2(e) {
+  e.target.removeEventListener('error', onError2);
+  SimpleTest.finish();
+}
+
+function onFileOpened(blob) {
+  firstBlob = blob;
+  r = new FileReader();
+  r.addEventListener("loadend", onLoadEnd1, false);
+  r.addEventListener("error", onError1, false);
+  r.readAsArrayBuffer(firstBlob);
+}
+
+function onFileModified(blob) {
+  r.addEventListener("loadend", onLoadEnd2, false);
+  r.removeEventListener('error', onError1);
+  r.addEventListener("error", onError2, false);
+  r.readAsArrayBuffer(firstBlob);
+}
+
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body> </html>
--- a/dom/base/test/test_bug338583.html
+++ b/dom/base/test/test_bug338583.html
@@ -461,17 +461,16 @@ https://bugzilla.mozilla.org/show_bug.cg
       setTestHasFinished(test_id);
     }, parseInt(3000*stress_factor));
   }
 
   function doTest5_c(test_id)
   {
     // credentials using the auth cache
     var xhr = new XMLHttpRequest({mozAnon: true, mozSystem: true});
-    xhr.withCredentials = true;
     // also, test mixed mode UI
     xhr.open("GET", "https://example.com/tests/dom/base/test/file_restrictedEventSource.sjs?test=user1_xhr", true, "user 1", "password 1");
     xhr.send();
     xhr.onloadend = function() {
       ok(xhr.status == 200, "Failed to set credentials in test 5.c");
 
       gEventSourceObj5_c = new EventSource("https://example.com/tests/dom/base/test/file_restrictedEventSource.sjs?test=user1_evtsrc",
                                            { withCredentials: true } );
@@ -490,17 +489,16 @@ https://bugzilla.mozilla.org/show_bug.cg
         doTest5_d(test_id);
       }, parseInt(3000*stress_factor));
     };
   }
 
   function doTest5_d(test_id)
   {
     var xhr = new XMLHttpRequest({mozAnon: true, mozSystem: true});
-    xhr.withCredentials = true;
     xhr.open("GET", "https://example.com/tests/dom/base/test/file_restrictedEventSource.sjs?test=user2_xhr", true, "user 2", "password 2");
     xhr.send();
     xhr.onloadend = function() {
       ok(xhr.status == 200, "Failed to set credentials in test 5.d");
   
       gEventSourceObj5_d = new EventSource("https://example.com/tests/dom/base/test/file_restrictedEventSource.sjs?test=user2_evtsrc");
       ok(!gEventSourceObj5_d.withCredentials, "Wrong withCredentials in test 5.d");
   
@@ -518,17 +516,16 @@ https://bugzilla.mozilla.org/show_bug.cg
       }, parseInt(3000*stress_factor));
     };
   }
 
   function doTest5_e(test_id)
   {
     // credentials using the auth cache
     var xhr = new XMLHttpRequest({mozAnon: true, mozSystem: true});
-    xhr.withCredentials = true;
     xhr.open("GET", "http://example.org/tests/dom/base/test/file_restrictedEventSource.sjs?test=user1_xhr", true, "user 1", "password 1");
     xhr.send();
     xhr.onloadend = function() {
       ok(xhr.status == 200, "Failed to set credentials in test 5.e");
 
       gEventSourceObj5_e = new EventSource("http://example.org/tests/dom/base/test/file_restrictedEventSource.sjs?test=user1_evtsrc",
                                            { get withCredentials() { return true; } } );
       ok(gEventSourceObj5_e.withCredentials, "Wrong withCredentials in test 5.e");
@@ -546,17 +543,16 @@ https://bugzilla.mozilla.org/show_bug.cg
         doTest5_f(test_id);
       }, parseInt(5000*stress_factor));
     };
   }
 
   function doTest5_f(test_id)
   {
     var xhr = new XMLHttpRequest({mozAnon: true, mozSystem: true});
-    xhr.withCredentials = true;
     xhr.open("GET", "http://example.org/tests/dom/base/test/file_restrictedEventSource.sjs?test=user2_xhr", true, "user 2", "password 2");
     xhr.send();
     xhr.onloadend = function() {
       ok(xhr.status == 200, "Failed to set credentials in test 5.f");
 
       gEventSourceObj5_f = new EventSource("http://example.org/tests/dom/base/test/file_restrictedEventSource.sjs?test=user2_evtsrc",
                                            { });
       ok(!gEventSourceObj5_f.withCredentials, "Wrong withCredentials in test 5.f");
--- a/dom/base/test/test_bug469304.html
+++ b/dom/base/test/test_bug469304.html
@@ -22,37 +22,37 @@ https://bugzilla.mozilla.org/show_bug.cg
 function testGetAttribute() {
   var a1 = document.createAttributeNS("", "aa");
   a1.nodeValue = "lowercase";
   var a2 = document.createAttributeNS("", "AA");
   a2.nodeValue = "UPPERCASE";
   document.body.setAttributeNode(a1);
   document.body.setAttributeNode(a2);
   var log = document.getElementById("log");
-  is(document.body.getAttribute('aa'), null, "Attribute has the localName AA and not aa.");
-  is(document.body.getAttribute('AA'), null, "Attribute has the localName AA and not aa.");
-  is(document.body.getAttributeNS("", "aa"), null, "Attribute should have localName AA.");
-  is(document.body.getAttributeNS("", "AA"), "UPPERCASE", "Attribute should have value UPPERCASE!");
+  is(document.body.getAttribute('aa'), "lowercase", "First attribute should have localname aa (1).");
+  is(document.body.getAttribute('AA'), "lowercase", "First attribute should have localname aa (2).");
+  is(document.body.getAttributeNS("", "aa"), "lowercase", "First attribute should have localName aa (3).");
+  is(document.body.getAttributeNS("", "AA"), "UPPERCASE", "Second attribute should have value UPPERCASE!");
 
   var s = "";
   for (var i = 0; i < document.body.attributes.length; ++i) {
     s += document.body.attributes[i].nodeName + "=" +
          document.body.attributes[i].nodeValue;
   }
-  is(s, "AA=UPPERCASE", "Wrong attribute!");
+  is(s, "aa=lowercaseAA=UPPERCASE", "Wrong attribute!");
 
   is(document.body.getAttributeNode("aa"), document.body.getAttributeNode("AA"),
      "Wrong node!");
 
   document.body.getAttributeNodeNS("", "AA").nodeValue = "FOO";
   is(document.body.getAttributeNS("", "AA"), "FOO", "Wrong value!");
 
-  document.body.removeAttributeNode(document.body.getAttributeNodeNS("", "AA"));
-  ok(!document.body.getAttributeNode("AA"), "Should not have attribute node!");
-  ok(!document.body.getAttributeNode("aa"), "Should not have attribute node!");
+  document.body.removeAttributeNode(document.body.getAttributeNodeNS("", "aa"));
+  ok(!document.body.getAttributeNode("AA"), "Should not have attribute node! (1)");
+  ok(!document.body.getAttributeNode("aa"), "Should not have attribute node! (2)");
 
   is(a2.nodeValue, "FOO", "Wrong value!");
   a2.nodeValue = "UPPERCASE";
   is(a2.nodeValue, "UPPERCASE", "Wrong value!");
 
   document.body.setAttributeNode(a2);
   is(document.body.getAttributeNS("", "AA"), "UPPERCASE", "Wrong value!");
   ok(document.body.getAttributeNodeNS("", "AA"), "Should have attribute node!");
--- a/dom/base/test/test_fileapi.html
+++ b/dom/base/test/test_fileapi.html
@@ -377,17 +377,17 @@ function onFilesOpened(message) {
   var didThrow = false;
   r.onerror = function (event) {
     is(event.target.readyState, FileReader.DONE, "should be DONE while firing onerror");
     is(event.target.error.name, "NotFoundError", "error set to NotFoundError for nonexistent files");
     is(event.target.result, null, "file data should be null on aborted reads");
     testHasRun();
   };
   r.onload = function (event) {
-    todo(false, "nonexistent file shouldn't load! (FIXME: bug 1122788)");
+    is(false, "nonexistent file shouldn't load! (FIXME: bug 1122788)");
     testHasRun();
   };
   try {
     r.readAsDataURL(nonExistingFile);
     expectedTestCount++;
   } catch(ex) {
     didThrow = true;
   }
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1003,16 +1003,18 @@ class CGHeaders(CGWrapper):
                                       interfacesImplementingSelf)
 
         # Grab the includes for the things that involve XPCOM interfaces
         hasInstanceIncludes = set("nsIDOM" + d.interface.identifier.name + ".h" for d
                                   in descriptors if
                                   d.interface.hasInterfaceObject() and
                                   NeedsGeneratedHasInstance(d) and
                                   d.interface.hasInterfacePrototypeObject())
+        if len(hasInstanceIncludes) > 0:
+            hasInstanceIncludes.add("nsContentUtils.h")
 
         # Now find all the things we'll need as arguments because we
         # need to wrap or unwrap them.
         bindingHeaders = set()
         declareIncludes = set(declareIncludes)
 
         def addHeadersForType((t, descriptor, dictionary)):
             """
@@ -13115,17 +13117,21 @@ class CGBindingRoot(CGThing):
 
             return (any(isChromeOnly(a) for a in desc.interface.members) or
                     desc.interface.getExtendedAttribute("ChromeOnly") is not None or
                     # JS-implemented interfaces with an interface object get a
                     # chromeonly _create method.  And interfaces with an
                     # interface object might have a ChromeOnly constructor.
                     (desc.interface.hasInterfaceObject() and
                      (desc.interface.isJSImplemented() or
-                      (ctor and isChromeOnly(ctor)))))
+                      (ctor and isChromeOnly(ctor)))) or
+                    # JS-implemented interfaces with clearable cached
+                    # attrs have chromeonly _clearFoo methods.
+                    (desc.interface.isJSImplemented() and
+                     any(clearableCachedAttrs(desc))))
 
         bindingHeaders["nsContentUtils.h"] = any(
             descriptorHasChromeOnly(d) for d in descriptors)
         # XXXkhuey ugly hack but this is going away soon.
         bindingHeaders['xpcprivate.h'] = webIDLFile.endswith("EventTarget.webidl")
         hasWorkerStuff = len(config.getDescriptors(webIDLFile=webIDLFile,
                                                    workers=True)) != 0
         bindingHeaders["WorkerPrivate.h"] = hasWorkerStuff
--- a/dom/browser-element/BrowserElementParent.cpp
+++ b/dom/browser-element/BrowserElementParent.cpp
@@ -114,18 +114,17 @@ DispatchCustomDOMEvent(Element* aFrameEl
                          res);
   if (res.Failed()) {
     return false;
   }
   event->SetTrusted(true);
   // Dispatch the event.
   // We don't initialize aStatus here, as our callers have already done so.
   nsresult rv =
-    EventDispatcher::DispatchDOMEvent(aFrameElement, nullptr,
-                                      static_cast<Event*>(event),
+    EventDispatcher::DispatchDOMEvent(aFrameElement, nullptr, event,
                                       presContext, aStatus);
   return NS_SUCCEEDED(rv);
 }
 
 } // namespace
 
 namespace mozilla {
 
--- a/dom/fetch/FetchDriver.cpp
+++ b/dom/fetch/FetchDriver.cpp
@@ -38,25 +38,24 @@
 #include "Fetch.h"
 #include "InternalRequest.h"
 #include "InternalResponse.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_ISUPPORTS(FetchDriver,
-                  nsIStreamListener, nsIChannelEventSink, nsIInterfaceRequestor,
+                  nsIStreamListener, nsIInterfaceRequestor,
                   nsIThreadRetargetableStreamListener)
 
 FetchDriver::FetchDriver(InternalRequest* aRequest, nsIPrincipal* aPrincipal,
                          nsILoadGroup* aLoadGroup)
   : mPrincipal(aPrincipal)
   , mLoadGroup(aLoadGroup)
   , mRequest(aRequest)
-  , mHasBeenCrossSite(false)
   , mResponseAvailableCalled(false)
   , mFetchCalled(false)
 {
 }
 
 FetchDriver::~FetchDriver()
 {
   // We assert this since even on failures, we should call
@@ -82,71 +81,16 @@ FetchDriver::Fetch(FetchDriverObserver* 
                      "Synchronous fetch not supported");
 
   nsCOMPtr<nsIRunnable> r =
     NS_NewRunnableMethod(this, &FetchDriver::ContinueFetch);
   return NS_DispatchToCurrentThread(r);
 }
 
 nsresult
-FetchDriver::SetTainting()
-{
-  workers::AssertIsOnMainThread();
-
-  // If we've already been cross-site then we should be fully updated
-  if (mHasBeenCrossSite) {
-    return NS_OK;
-  }
-
-  nsAutoCString url;
-  mRequest->GetURL(url);
-  nsCOMPtr<nsIURI> requestURI;
-  nsresult rv = NS_NewURI(getter_AddRefs(requestURI), url,
-                          nullptr, nullptr);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Begin Step 8 of the Main Fetch algorithm
-  // https://fetch.spec.whatwg.org/#fetching
-
-  // request's current url's origin is request's origin and the CORS flag is unset
-  // request's current url's scheme is "data" and request's same-origin data-URL flag is set
-  // request's current url's scheme is "about"
-
-  // We have to manually check about:blank here since it's not treated as
-  // an inheriting URL by CheckMayLoad.
-  if (NS_IsAboutBlank(requestURI) ||
-       NS_SUCCEEDED(mPrincipal->CheckMayLoad(requestURI, false /* report */,
-                                             true /*allowIfInheritsPrincipal*/))) {
-    // What the spec calls "basic fetch" is handled within our necko channel
-    // code.  Therefore everything goes through HTTP Fetch
-    return NS_OK;
-  }
-
-  mHasBeenCrossSite = true;
-
-  // request's mode is "same-origin"
-  if (mRequest->Mode() == RequestMode::Same_origin) {
-    return NS_ERROR_DOM_BAD_URI;
-  }
-
-  // request's mode is "no-cors"
-  if (mRequest->Mode() == RequestMode::No_cors) {
-    mRequest->MaybeIncreaseResponseTainting(LoadTainting::Opaque);
-    // What the spec calls "basic fetch" is handled within our necko channel
-    // code.  Therefore everything goes through HTTP Fetch
-    return NS_OK;
-  }
-
-  // Otherwise
-  mRequest->MaybeIncreaseResponseTainting(LoadTainting::CORS);
-
-  return NS_OK;
-}
-
-nsresult
 FetchDriver::ContinueFetch()
 {
   workers::AssertIsOnMainThread();
 
   nsresult rv = HttpFetch();
   if (NS_FAILED(rv)) {
     FailWithNetworkError();
   }
@@ -172,18 +116,24 @@ FetchDriver::HttpFetch()
   nsCOMPtr<nsIURI> uri;
   rv = NS_NewURI(getter_AddRefs(uri),
                           url,
                           nullptr,
                           nullptr,
                           ios);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = SetTainting();
-  NS_ENSURE_SUCCESS(rv, rv);
+  // Unsafe requests aren't allowed with when using no-core mode.
+  if (mRequest->Mode() == RequestMode::No_cors &&
+      mRequest->UnsafeRequest() &&
+      (!mRequest->HasSimpleMethod() ||
+       !mRequest->Headers()->HasOnlySimpleHeaders())) {
+    MOZ_ASSERT(false, "The API should have caught this");
+    return NS_ERROR_DOM_BAD_URI;
+  }
 
   // Step 2 deals with letting ServiceWorkers intercept requests. This is
   // handled by Necko after the channel is opened.
   // FIXME(nsm): Bug 1119026: The channel's skip service worker flag should be
   // set based on the Request's flag.
 
   // Step 3.1 "If the CORS preflight flag is set and one of these conditions is
   // true..." is handled by the CORS proxy.
@@ -197,100 +147,85 @@ FetchDriver::HttpFetch()
   // request too.
 
   // Step 3.3 "Let credentials flag be set if one of
   //  - request's credentials mode is "include"
   //  - request's credentials mode is "same-origin" and either the CORS flag
   //    is unset or response tainting is "opaque"
   // is true, and unset otherwise."
 
-  // This is effectivetly the opposite of the use credentials flag in "HTTP
-  // network or cache fetch" in the spec and decides whether to transmit
-  // cookies and other identifying information. LOAD_ANONYMOUS also prevents
-  // new cookies sent by the server from being stored.  This value will
-  // propagate across redirects, which is what we want.
-  const nsLoadFlags credentialsFlag =
-    (mRequest->GetCredentialsMode() == RequestCredentials::Omit ||
-    (mHasBeenCrossSite &&
-     mRequest->GetCredentialsMode() == RequestCredentials::Same_origin &&
-     mRequest->Mode() == RequestMode::No_cors)) ?
-    nsIRequest::LOAD_ANONYMOUS : 0;
-
   // Set skip serviceworker flag.
   // While the spec also gates on the client being a ServiceWorker, we can't
   // infer that here. Instead we rely on callers to set the flag correctly.
   const nsLoadFlags bypassFlag = mRequest->SkipServiceWorker() ?
                                  nsIChannel::LOAD_BYPASS_SERVICE_WORKER : 0;
 
-  nsSecurityFlags secFlags;
-  if (mRequest->Mode() == RequestMode::Cors &&
-      mRequest->GetCredentialsMode() == RequestCredentials::Include) {
-    secFlags = nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS |
-               nsILoadInfo::SEC_REQUIRE_CORS_WITH_CREDENTIALS;
-  } else if (mRequest->Mode() == RequestMode::Cors) {
-    secFlags = nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS;
+  nsSecurityFlags secFlags = nsILoadInfo::SEC_ABOUT_BLANK_INHERITS;
+  if (mRequest->Mode() == RequestMode::Cors) {
+    secFlags |= nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS;
   } else if (mRequest->Mode() == RequestMode::Same_origin) {
-    secFlags = nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS;
+    secFlags |= nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS;
   } else if (mRequest->Mode() == RequestMode::No_cors) {
-    secFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
+    secFlags |= nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
   } else {
     MOZ_ASSERT_UNREACHABLE("Unexpected request mode!");
     return NS_ERROR_UNEXPECTED;
   }
 
   if (mRequest->GetRedirectMode() != RequestRedirect::Follow) {
     secFlags |= nsILoadInfo::SEC_DONT_FOLLOW_REDIRECTS;
   }
 
+  // This is handles the use credentials flag in "HTTP
+  // network or cache fetch" in the spec and decides whether to transmit
+  // cookies and other identifying information.
+  if (mRequest->GetCredentialsMode() == RequestCredentials::Include) {
+    secFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
+  } else if (mRequest->GetCredentialsMode() == RequestCredentials::Omit) {
+    secFlags |= nsILoadInfo::SEC_COOKIES_OMIT;
+  } else if (mRequest->GetCredentialsMode() == RequestCredentials::Same_origin) {
+    secFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN;
+  } else {
+    MOZ_ASSERT_UNREACHABLE("Unexpected credentials mode!");
+    return NS_ERROR_UNEXPECTED;
+  }
+
   // From here on we create a channel and set its properties with the
   // information from the InternalRequest. This is an implementation detail.
   MOZ_ASSERT(mLoadGroup);
   nsCOMPtr<nsIChannel> chan;
 
-  nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL | credentialsFlag |
+  nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL |
     bypassFlag | nsIChannel::LOAD_CLASSIFY_URI;
   if (mDocument) {
     MOZ_ASSERT(mDocument->NodePrincipal() == mPrincipal);
     rv = NS_NewChannel(getter_AddRefs(chan),
                        uri,
                        mDocument,
-                       secFlags |
-                         nsILoadInfo::SEC_ABOUT_BLANK_INHERITS,
+                       secFlags,
                        mRequest->ContentPolicyType(),
                        mLoadGroup,
                        nullptr, /* aCallbacks */
                        loadFlags,
                        ios);
   } else {
     rv = NS_NewChannel(getter_AddRefs(chan),
                        uri,
                        mPrincipal,
-                       secFlags |
-                         nsILoadInfo::SEC_ABOUT_BLANK_INHERITS,
+                       secFlags,
                        mRequest->ContentPolicyType(),
                        mLoadGroup,
                        nullptr, /* aCallbacks */
                        loadFlags,
                        ios);
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
   mLoadGroup = nullptr;
 
-  // Insert ourselves into the notification callbacks chain so we can handle
-  // cross-origin redirects.
-#ifdef DEBUG
-  {
-    nsCOMPtr<nsIInterfaceRequestor> notificationCallbacks;
-    chan->GetNotificationCallbacks(getter_AddRefs(notificationCallbacks));
-    MOZ_ASSERT(!notificationCallbacks);
-  }
-#endif
-  chan->SetNotificationCallbacks(this);
-
   // FIXME(nsm): Bug 1120715.
   // Step 3.4 "If request's cache mode is default and request's header list
   // contains a header named `If-Modified-Since`, `If-None-Match`,
   // `If-Unmodified-Since`, `If-Match`, or `If-Range`, set request's cache mode
   // to no-store."
 
   // Step 3.5 begins "HTTP network or cache fetch".
   // HTTP network or cache fetch
@@ -405,52 +340,30 @@ FetchDriver::HttpFetch()
     }
   }
 
   // If preflight is required, start a "CORS preflight fetch"
   // https://fetch.spec.whatwg.org/#cors-preflight-fetch-0. All the
   // implementation is handled by the http channel calling into
   // nsCORSListenerProxy. We just inform it which unsafe headers are included
   // in the request.
-  if (IsUnsafeRequest()) {
-    if (mRequest->Mode() == RequestMode::No_cors) {
-      return NS_ERROR_DOM_BAD_URI;
-    }
-
-    mRequest->SetRedirectMode(RequestRedirect::Error);
-
+  if (mRequest->Mode() == RequestMode::Cors) {
     nsAutoTArray<nsCString, 5> unsafeHeaders;
     mRequest->Headers()->GetUnsafeHeaders(unsafeHeaders);
-
-    nsCOMPtr<nsIHttpChannelInternal> internalChan = do_QueryInterface(httpChan);
-    NS_ENSURE_TRUE(internalChan, NS_ERROR_DOM_BAD_URI);
-
-    rv = internalChan->SetCorsPreflightParameters(
-      unsafeHeaders,
-      mRequest->GetCredentialsMode() == RequestCredentials::Include,
-      mPrincipal);
-    NS_ENSURE_SUCCESS(rv, rv);
+    nsCOMPtr<nsILoadInfo> loadInfo = chan->GetLoadInfo();
+    loadInfo->SetCorsPreflightInfo(unsafeHeaders, false);
   }
 
   rv = chan->AsyncOpen2(this);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Step 4 onwards of "HTTP Fetch" is handled internally by Necko.
   return NS_OK;
 }
 
-bool
-FetchDriver::IsUnsafeRequest()
-{
-  return mHasBeenCrossSite &&
-         (mRequest->UnsafeRequest() &&
-          (!mRequest->HasSimpleMethod() ||
-           !mRequest->Headers()->HasOnlySimpleHeaders()));
-}
-
 already_AddRefed<InternalResponse>
 FetchDriver::BeginAndGetFilteredResponse(InternalResponse* aResponse,
                                          nsIURI* aFinalURI,
                                          bool aFoundOpaqueRedirect)
 {
   MOZ_ASSERT(aResponse);
   nsAutoCString reqURL;
   if (aFinalURI) {
@@ -554,16 +467,33 @@ FetchDriver::OnStartRequest(nsIRequest* 
   MOZ_ASSERT(!mPipeOutputStream);
   MOZ_ASSERT(mObserver);
 
   RefPtr<InternalResponse> response;
   nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
   nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(aRequest);
 
+  // On a successful redirect we perform the following substeps of HTTP Fetch,
+  // step 5, "redirect status", step 11.
+
+  // Step 11.5 "Append locationURL to request's url list." so that when we set the
+  // Response's URL from the Request's URL in Main Fetch, step 15, we get the
+  // final value. Note, we still use a single URL value instead of a list.
+  // Because of that we only need to do this after the request finishes.
+  nsCOMPtr<nsIURI> newURI;
+  rv = NS_GetFinalChannelURI(channel, getter_AddRefs(newURI));
+  if (NS_FAILED(rv)) {
+    FailWithNetworkError();
+    return rv;
+  }
+  nsAutoCString newUrl;
+  newURI->GetSpec(newUrl);
+  mRequest->SetURL(newUrl);
+
   bool foundOpaqueRedirect = false;
 
   if (httpChannel) {
     uint32_t responseStatus;
     httpChannel->GetResponseStatus(&responseStatus);
 
     if (mozilla::net::nsHttpChannel::IsRedirectStatus(responseStatus)) {
       if (mRequest->GetRedirectMode() == RequestRedirect::Error) {
@@ -660,28 +590,23 @@ FetchDriver::OnStartRequest(nsIRequest* 
 
   nsCOMPtr<nsILoadInfo> loadInfo;
   rv = channel->GetLoadInfo(getter_AddRefs(loadInfo));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     FailWithNetworkError();
     return rv;
   }
 
-  LoadTainting channelTainting = LoadTainting::Basic;
-  if (loadInfo) {
-    channelTainting = loadInfo->GetTainting();
-  }
-
   // Propagate any tainting from the channel back to our response here.  This
   // step is not reflected in the spec because the spec is written such that
   // FetchEvent.respondWith() just passes the already-tainted Response back to
   // the outer fetch().  In gecko, however, we serialize the Response through
   // the channel and must regenerate the tainting from the channel in the
   // interception case.
-  mRequest->MaybeIncreaseResponseTainting(channelTainting);
+  mRequest->MaybeIncreaseResponseTainting(loadInfo->GetTainting());
 
   // Resolves fetch() promise which may trigger code running in a worker.  Make
   // sure the Response is fully initialized before calling this.
   mResponse = BeginAndGetFilteredResponse(response, channelURI,
                                           foundOpaqueRedirect);
 
   nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -744,168 +669,25 @@ FetchDriver::OnStopRequest(nsIRequest* a
   if (mObserver) {
     mObserver->OnResponseEnd();
     mObserver = nullptr;
   }
 
   return NS_OK;
 }
 
-// This is called when the channel is redirected.
-NS_IMETHODIMP
-FetchDriver::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
-                                    nsIChannel* aNewChannel,
-                                    uint32_t aFlags,
-                                    nsIAsyncVerifyRedirectCallback *aCallback)
-{
-  NS_PRECONDITION(aNewChannel, "Redirect without a channel?");
-
-  if (NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags) ||
-      NS_IsHSTSUpgradeRedirect(aOldChannel, aNewChannel, aFlags)) {
-    aCallback->OnRedirectVerifyCallback(NS_OK);
-    return NS_OK;
-  }
-
-  // We should only ever get here if we use a "follow" redirect policy,
-  // or if if we set an "error" policy as a result of a CORS policy.
-  MOZ_ASSERT(mRequest->GetRedirectMode() == RequestRedirect::Follow ||
-             (mRequest->GetRedirectMode() == RequestRedirect::Error &&
-              IsUnsafeRequest()));
-
-  // HTTP Fetch step 5, "redirect status", step 1
-  if (NS_WARN_IF(mRequest->GetRedirectMode() == RequestRedirect::Error)) {
-    aOldChannel->Cancel(NS_BINDING_FAILED);
-    return NS_BINDING_FAILED;
-  }
-
-  // HTTP Fetch step 5, "redirect status", steps 2 through 6 are automatically
-  // handled by necko before calling AsyncOnChannelRedirect() with the new
-  // nsIChannel.
-
-  // HTTP Fetch step 5, "redirect status", steps 7 and 8 enforcing a redirect
-  // count are done by Necko.  The pref used is "network.http.redirection-limit"
-  // which is set to 20 by default.
-
-  // HTTP Fetch Step 9, "redirect status". This is enforced by the
-  // nsCORSListenerProxy. It forbids redirecting to data:
-
-  // HTTP Fetch step 5, "redirect status", step 10 requires us to halt the
-  // redirect, but successfully return an opaqueredirect Response to the
-  // initiating Fetch.
-
-  // The following steps are from HTTP Fetch step 5, "redirect status", step 11
-  // which requires the RequestRedirect to be "follow". We asserted that we're
-  // in either "follow" or "error" mode here.
-
-  // HTTP Fetch step 5, "redirect status", steps 11.1 and 11.2 block redirecting
-  // to a URL with credentials in CORS mode.  This is implemented in
-  // nsCORSListenerProxy.
-
-  // On a successful redirect we perform the following substeps of HTTP Fetch,
-  // step 5, "redirect status", step 11.
-
-  // Step 11.5 "Append locationURL to request's url list." so that when we set the
-  // Response's URL from the Request's URL in Main Fetch, step 15, we get the
-  // final value. Note, we still use a single URL value instead of a list.
-  nsCOMPtr<nsIURI> newURI;
-  nsresult rv = NS_GetFinalChannelURI(aNewChannel, getter_AddRefs(newURI));
-  if (NS_FAILED(rv)) {
-    aOldChannel->Cancel(rv);
-    return rv;
-  }
-
-  // We need to update our request's URL.
-  nsAutoCString newUrl;
-  newURI->GetSpec(newUrl);
-  mRequest->SetURL(newUrl);
-
-  // Implement Main Fetch step 8 again on redirect.
-  rv = SetTainting();
-  if (NS_FAILED(rv)) {
-    aOldChannel->Cancel(rv);
-    return rv;
-  }
-
-  // Requests that require preflight are not permitted to redirect.
-  // Fetch spec section 4.2 "HTTP Fetch", step 4.9 just uses the manual
-  // redirect flag to decide whether to execute step 4.10 or not. We do not
-  // represent it in our implementation.
-  // The only thing we do is to check if the request requires a preflight (part
-  // of step 4.9), in which case we abort. This part cannot be done by
-  // nsCORSListenerProxy since it does not have access to mRequest.
-  // which case. Step 4.10.3 is handled by OnRedirectVerifyCallback(), and all
-  // the other steps are handled by nsCORSListenerProxy.
-
-  if (IsUnsafeRequest()) {
-    // We can't handle redirects that require preflight yet.
-    // This is especially true for no-cors requests, which much always be
-    // blocked if they require preflight.
-
-    // Simply fire an error here.
-    aOldChannel->Cancel(NS_BINDING_FAILED);
-    return NS_BINDING_FAILED;
-  }
-
-  // Otherwise, we rely on necko and the CORS proxy to do the right thing
-  // as the redirect is followed.  In general this means http
-  // fetch.  If we've ever been CORS, we need to stay CORS.
-
-  // Possibly set the LOAD_ANONYMOUS flag on the channel.
-  if (mHasBeenCrossSite &&
-      mRequest->GetCredentialsMode() == RequestCredentials::Same_origin &&
-      mRequest->Mode() == RequestMode::No_cors) {
-    // In the case of a "no-cors" mode request with "same-origin" credentials,
-    // we have to set LOAD_ANONYMOUS manually here in order to avoid sending
-    // credentials on a cross-origin redirect.
-    nsLoadFlags flags;
-    rv = aNewChannel->GetLoadFlags(&flags);
-    if (NS_SUCCEEDED(rv)) {
-      flags |= nsIRequest::LOAD_ANONYMOUS;
-      rv = aNewChannel->SetLoadFlags(flags);
-    }
-    if (NS_FAILED(rv)) {
-      aOldChannel->Cancel(rv);
-      return rv;
-    }
-  }
-#ifdef DEBUG
-  {
-    // Make sure nothing in the redirect chain screws up our credentials
-    // settings. LOAD_ANONYMOUS must be set if we RequestCredentials is "omit"
-    // or "same-origin".
-    nsLoadFlags flags;
-    aNewChannel->GetLoadFlags(&flags);
-    bool shouldBeAnon =
-      mRequest->GetCredentialsMode() == RequestCredentials::Omit ||
-      (mHasBeenCrossSite &&
-       mRequest->GetCredentialsMode() == RequestCredentials::Same_origin);
-    MOZ_ASSERT(!!(flags & nsIRequest::LOAD_ANONYMOUS) == shouldBeAnon);
-  }
-#endif
-
-  aCallback->OnRedirectVerifyCallback(NS_OK);
-
-  return NS_OK;
-}
-
 NS_IMETHODIMP
 FetchDriver::CheckListenerChain()
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
 FetchDriver::GetInterface(const nsIID& aIID, void **aResult)
 {
-  if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
-    *aResult = static_cast<nsIChannelEventSink*>(this);
-    NS_ADDREF_THIS();
-    return NS_OK;
-  }
-
   if (aIID.Equals(NS_GET_IID(nsIStreamListener))) {
     *aResult = static_cast<nsIStreamListener*>(this);
     NS_ADDREF_THIS();
     return NS_OK;
   }
   if (aIID.Equals(NS_GET_IID(nsIRequestObserver))) {
     *aResult = static_cast<nsIRequestObserver*>(this);
     NS_ADDREF_THIS();
--- a/dom/fetch/FetchDriver.h
+++ b/dom/fetch/FetchDriver.h
@@ -50,25 +50,23 @@ protected:
 
   virtual void OnResponseAvailableInternal(InternalResponse* aResponse) = 0;
 
 private:
   bool mGotResponseAvailable;
 };
 
 class FetchDriver final : public nsIStreamListener,
-                          public nsIChannelEventSink,
                           public nsIInterfaceRequestor,
                           public nsIThreadRetargetableStreamListener
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSISTREAMLISTENER
-  NS_DECL_NSICHANNELEVENTSINK
   NS_DECL_NSIINTERFACEREQUESTOR
   NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
 
   explicit FetchDriver(InternalRequest* aRequest, nsIPrincipal* aPrincipal,
                        nsILoadGroup* aLoadGroup);
   NS_IMETHOD Fetch(FetchDriverObserver* aObserver);
 
   void
@@ -77,30 +75,27 @@ public:
 private:
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsCOMPtr<nsILoadGroup> mLoadGroup;
   RefPtr<InternalRequest> mRequest;
   RefPtr<InternalResponse> mResponse;
   nsCOMPtr<nsIOutputStream> mPipeOutputStream;
   RefPtr<FetchDriverObserver> mObserver;
   nsCOMPtr<nsIDocument> mDocument;
-  bool mHasBeenCrossSite;
 
   DebugOnly<bool> mResponseAvailableCalled;
   DebugOnly<bool> mFetchCalled;
 
   FetchDriver() = delete;
   FetchDriver(const FetchDriver&) = delete;
   FetchDriver& operator=(const FetchDriver&) = delete;
   ~FetchDriver();
 
-  nsresult SetTainting();
   nsresult ContinueFetch();
   nsresult HttpFetch();
-  bool IsUnsafeRequest();
   // Returns the filtered response sent to the observer.
   // Callers who don't have access to a channel can pass null for aFinalURI.
   already_AddRefed<InternalResponse>
   BeginAndGetFilteredResponse(InternalResponse* aResponse, nsIURI* aFinalURI,
                               bool aFoundOpaqueRedirect);
   // Utility since not all cases need to do any post processing of the filtered
   // response.
   nsresult FailWithNetworkError();
--- a/dom/fetch/FetchUtil.cpp
+++ b/dom/fetch/FetchUtil.cpp
@@ -198,17 +198,19 @@ public:
     : mFormData(aFormData)
   {
     MOZ_ASSERT(aFormData);
   }
 
   bool URLParamsIterator(const nsString& aName,
                          const nsString& aValue) override
   {
-    mFormData->Append(aName, aValue);
+    ErrorResult rv;
+    mFormData->Append(aName, aValue, rv);
+    MOZ_ASSERT(!rv.Failed());
     return true;
   }
 
 private:
   nsFormData* mFormData;
 };
 
 /**
@@ -387,17 +389,19 @@ private:
 
     if (!mFormData) {
       mFormData = new nsFormData();
     }
 
     NS_ConvertUTF8toUTF16 name(mName);
 
     if (mFilename.IsVoid()) {
-      mFormData->Append(name, NS_ConvertUTF8toUTF16(body));
+      ErrorResult rv;
+      mFormData->Append(name, NS_ConvertUTF8toUTF16(body), rv);
+      MOZ_ASSERT(!rv.Failed());
     } else {
       // Unfortunately we've to copy the data first since all our strings are
       // going to free it. We also need fallible alloc, so we can't just use
       // ToNewCString().
       char* copy = static_cast<char*>(moz_xmalloc(body.Length()));
       if (!copy) {
         NS_WARNING("Failed to copy File entry body.");
         return false;
@@ -412,17 +416,21 @@ private:
       p = nullptr;
 
       RefPtr<Blob> file =
         File::CreateMemoryFile(mParentObject,
                                reinterpret_cast<void *>(copy), body.Length(),
                                NS_ConvertUTF8toUTF16(mFilename),
                                NS_ConvertUTF8toUTF16(mContentType), /* aLastModifiedDate */ 0);
       Optional<nsAString> dummy;
-      mFormData->Append(name, *file, dummy);
+      ErrorResult rv;
+      mFormData->Append(name, *file, dummy, rv);
+      if (NS_WARN_IF(rv.Failed())) {
+        return false;
+      }
     }
 
     return true;
   }
 
 public:
   FormDataParser(const nsACString& aMimeType, const nsACString& aData, nsIGlobalObject* aParent)
     : mMimeType(aMimeType), mData(aData), mState(START_PART), mParentObject(aParent)
--- a/dom/geolocation/nsGeolocation.cpp
+++ b/dom/geolocation/nsGeolocation.cpp
@@ -1451,17 +1451,17 @@ Geolocation::GetCurrentPositionReady(nsG
 }
 
 int32_t
 Geolocation::WatchPosition(PositionCallback& aCallback,
                            PositionErrorCallback* aErrorCallback,
                            const PositionOptions& aOptions,
                            ErrorResult& aRv)
 {
-  int32_t ret;
+  int32_t ret = 0;
   GeoPositionCallback successCallback(&aCallback);
   GeoPositionErrorCallback errorCallback(aErrorCallback);
 
   nsresult rv = WatchPosition(successCallback, errorCallback,
                               CreatePositionOptionsCopy(aOptions), &ret);
 
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1284,17 +1284,17 @@ nsresult HTMLMediaElement::LoadResource(
   }
 
   // determine what security checks need to be performed in AsyncOpen2().
   nsSecurityFlags securityFlags =
     ShouldCheckAllowOrigin() ? nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS :
                                nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
 
   if (GetCORSMode() == CORS_USE_CREDENTIALS) {
-    securityFlags |= nsILoadInfo::SEC_REQUIRE_CORS_WITH_CREDENTIALS;
+    securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
   }
 
   MOZ_ASSERT(IsAnyOfHTMLElements(nsGkAtoms::audio, nsGkAtoms::video));
   nsContentPolicyType contentPolicyType = IsHTMLElement(nsGkAtoms::audio) ?
     nsIContentPolicy::TYPE_INTERNAL_AUDIO : nsIContentPolicy::TYPE_INTERNAL_VIDEO;
 
   nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
   nsCOMPtr<nsIChannel> channel;
--- a/dom/interfaces/events/nsIDOMCustomEvent.idl
+++ b/dom/interfaces/events/nsIDOMCustomEvent.idl
@@ -1,18 +1,18 @@
 /* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
 /* 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 "nsIDOMEvent.idl"
+#include "nsISupports.idl"
 interface nsIVariant;
 
-[builtinclass, uuid(55fa3a13-4812-45a7-98b7-3be6cec2df43)]
-interface nsIDOMCustomEvent : nsIDOMEvent
+[builtinclass, uuid(5be16b03-36f9-4ca8-b2c5-0daadf3cd1b3)]
+interface nsIDOMCustomEvent : nsISupports
 {
 
   readonly attribute nsIVariant detail;
 
   void initCustomEvent(in DOMString  typeArg, 
                        in boolean    canBubbleArg, 
                        in boolean    cancelableArg, 
                        in nsIVariant detailArg);
--- a/dom/ipc/Blob.cpp
+++ b/dom/ipc/Blob.cpp
@@ -899,23 +899,27 @@ CreateBlobImpl(const nsTArray<BlobData>&
       return nullptr;
     }
 
     DebugOnly<bool> isMutable;
     MOZ_ASSERT(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable)));
     MOZ_ASSERT(!isMutable);
   }
 
+  ErrorResult rv;
   RefPtr<BlobImpl> blobImpl;
   if (!hasRecursed && aMetadata.IsFile()) {
-    blobImpl =
-      new MultipartBlobImpl(blobImpls, aMetadata.mName, aMetadata.mContentType);
+    blobImpl = MultipartBlobImpl::Create(blobImpls, aMetadata.mName,
+                                         aMetadata.mContentType, rv);
   } else {
-    blobImpl =
-      new MultipartBlobImpl(blobImpls, aMetadata.mContentType);
+    blobImpl = MultipartBlobImpl::Create(blobImpls, aMetadata.mContentType, rv);
+  }
+
+  if (NS_WARN_IF(rv.Failed())) {
+    return nullptr;
   }
 
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(blobImpl->SetMutable(false)));
 
   return blobImpl.forget();
 }
 
 already_AddRefed<BlobImpl>
@@ -1156,19 +1160,19 @@ RemoteInputStream::ReallyBlockAndWaitFor
     }
   }
 
   MOZ_ASSERT(mStream);
 
 #ifdef DEBUG
   if (waited && mWeakSeekableStream) {
     int64_t position;
-    MOZ_ASSERT(NS_SUCCEEDED(mWeakSeekableStream->Tell(&position)),
-                "Failed to determine initial stream position!");
-    MOZ_ASSERT(!position, "Stream not starting at 0!");
+    if (NS_SUCCEEDED(mWeakSeekableStream->Tell(&position))) {
+      MOZ_ASSERT(!position, "Stream not starting at 0!");
+    }
   }
 #endif
 }
 
 bool
 RemoteInputStream::IsSeekableStream()
 {
   if (IsOnOwningThread()) {
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -21,18 +21,19 @@ include URIParams;
 include BrowserConfiguration;
 
 
 using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h";
 using class mozilla::gfx::Matrix from "mozilla/gfx/Matrix.h";
 using struct gfxSize from "gfxPoint.h";
 using CSSRect from "Units.h";
 using CSSSize from "Units.h";
-using LayoutDeviceIntRect from "Units.h";
+using mozilla::LayoutDeviceIntRect from "Units.h";
 using mozilla::LayoutDeviceIntPoint from "Units.h";
+using mozilla::ScreenIntPoint from "Units.h";
 using ScreenIntSize from "Units.h";
 using struct mozilla::layers::FrameMetrics from "FrameMetrics.h";
 using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h";
 using struct mozilla::layers::ZoomConstraints from "FrameMetrics.h";
 using mozilla::layers::MaybeZoomConstraints from "FrameMetrics.h";
 using FrameMetrics::ViewID from "FrameMetrics.h";
 using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
 using mozilla::WindowsHandle from "ipc/IPCMessageUtils.h";
@@ -485,21 +486,21 @@ parent:
                                      double aDeltaX,
                                      double aDeltaY,
                                      double aDeltaZ,
                                      uint32_t aModifierFlags,
                                      uint32_t aAdditionalFlags,
                                      uint64_t aObserverId);
     SynthesizeNativeTouchPoint(uint32_t aPointerId,
                                TouchPointerState aPointerState,
-                               IntPoint aPointerScreenPoint,
+                               ScreenIntPoint aPointerScreenPoint,
                                double aPointerPressure,
                                uint32_t aPointerOrientation,
                                uint64_t aObserverId);
-    SynthesizeNativeTouchTap(IntPoint aPointerScreenPoint,
+    SynthesizeNativeTouchTap(ScreenIntPoint aPointerScreenPoint,
                              bool aLongTap,
                              uint64_t aObserverId);
     ClearNativeTouchSequence(uint64_t aObserverId);
 child:
     NativeSynthesisResponse(uint64_t aObserverId, nsCString aResponse);
 
 
 parent:
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -1628,32 +1628,32 @@ TabParent::RecvSynthesizeNativeMouseScro
       responder.GetObserver());
   }
   return true;
 }
 
 bool
 TabParent::RecvSynthesizeNativeTouchPoint(const uint32_t& aPointerId,
                                           const TouchPointerState& aPointerState,
-                                          const nsIntPoint& aPointerScreenPoint,
+                                          const ScreenIntPoint& aPointerScreenPoint,
                                           const double& aPointerPressure,
                                           const uint32_t& aPointerOrientation,
                                           const uint64_t& aObserverId)
 {
   AutoSynthesizedEventResponder responder(this, aObserverId, "touchpoint");
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (widget) {
     widget->SynthesizeNativeTouchPoint(aPointerId, aPointerState, aPointerScreenPoint,
       aPointerPressure, aPointerOrientation, responder.GetObserver());
   }
   return true;
 }
 
 bool
-TabParent::RecvSynthesizeNativeTouchTap(const nsIntPoint& aPointerScreenPoint,
+TabParent::RecvSynthesizeNativeTouchTap(const ScreenIntPoint& aPointerScreenPoint,
                                         const bool& aLongTap,
                                         const uint64_t& aObserverId)
 {
   AutoSynthesizedEventResponder responder(this, aObserverId, "touchtap");
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (widget) {
     widget->SynthesizeNativeTouchTap(aPointerScreenPoint, aLongTap,
       responder.GetObserver());
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -323,21 +323,21 @@ public:
                                                       const double& aDeltaX,
                                                       const double& aDeltaY,
                                                       const double& aDeltaZ,
                                                       const uint32_t& aModifierFlags,
                                                       const uint32_t& aAdditionalFlags,
                                                       const uint64_t& aObserverId) override;
     virtual bool RecvSynthesizeNativeTouchPoint(const uint32_t& aPointerId,
                                                 const TouchPointerState& aPointerState,
-                                                const nsIntPoint& aPointerScreenPoint,
+                                                const ScreenIntPoint& aPointerScreenPoint,
                                                 const double& aPointerPressure,
                                                 const uint32_t& aPointerOrientation,
                                                 const uint64_t& aObserverId) override;
-    virtual bool RecvSynthesizeNativeTouchTap(const nsIntPoint& aPointerScreenPoint,
+    virtual bool RecvSynthesizeNativeTouchTap(const ScreenIntPoint& aPointerScreenPoint,
                                               const bool& aLongTap,
                                               const uint64_t& aObserverId) override;
     virtual bool RecvClearNativeTouchSequence(const uint64_t& aObserverId) override;
 
     void SendMouseEvent(const nsAString& aType, float aX, float aY,
                         int32_t aButton, int32_t aClickCount,
                         int32_t aModifiers, bool aIgnoreRootScrollFrame);
     void SendKeyEvent(const nsAString& aType, int32_t aKeyCode,
new file mode 100644
--- /dev/null
+++ b/dom/media/ADTSDecoder.cpp
@@ -0,0 +1,51 @@
+/* -*- 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 "ADTSDecoder.h"
+#include "ADTSDemuxer.h"
+#include "MediaDecoderStateMachine.h"
+#include "MediaFormatReader.h"
+#include "PDMFactory.h"
+
+namespace mozilla {
+
+MediaDecoder*
+ADTSDecoder::Clone(MediaDecoderOwner* aOwner)
+{
+  if (!IsEnabled())
+    return nullptr;
+
+  return new ADTSDecoder(aOwner);
+}
+
+MediaDecoderStateMachine*
+ADTSDecoder::CreateStateMachine()
+{
+  RefPtr<MediaDecoderReader> reader =
+      new MediaFormatReader(this, new ADTSDemuxer(GetResource()));
+  return new MediaDecoderStateMachine(this, reader);
+}
+
+/* static */ bool
+ADTSDecoder::IsEnabled()
+{
+  PDMFactory::Init();
+  RefPtr<PDMFactory> platform = new PDMFactory();
+  return platform->SupportsMimeType(NS_LITERAL_CSTRING("audio/mp4a-latm"));
+}
+
+/* static */ bool
+ADTSDecoder::CanHandleMediaType(const nsACString& aType,
+                                const nsAString& aCodecs)
+{
+  if (aType.EqualsASCII("audio/aac") || aType.EqualsASCII("audio/aacp")) {
+    return IsEnabled() && (aCodecs.IsEmpty() || aCodecs.EqualsASCII("aac"));
+  }
+
+  return false;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/ADTSDecoder.h
@@ -0,0 +1,30 @@
+/* -*- 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 ADTS_DECODER_H_
+#define ADTS_DECODER_H_
+
+#include "MediaDecoder.h"
+
+namespace mozilla {
+
+class ADTSDecoder : public MediaDecoder {
+public:
+  // MediaDecoder interface.
+  explicit ADTSDecoder(MediaDecoderOwner* aOwner) : MediaDecoder(aOwner) {}
+  MediaDecoder* Clone(MediaDecoderOwner* aOwner) override;
+  MediaDecoderStateMachine* CreateStateMachine() override;
+
+  // Returns true if the MP3 backend is pref'ed on, and we're running on a
+  // platform that is likely to have decoders for the format.
+  static bool IsEnabled();
+  static bool CanHandleMediaType(const nsACString& aType,
+                                 const nsAString& aCodecs);
+};
+
+} // namespace mozilla
+
+#endif // !ADTS_DECODER_H_
new file mode 100644
--- /dev/null
+++ b/dom/media/ADTSDemuxer.cpp
@@ -0,0 +1,877 @@
+/* -*- 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 "ADTSDemuxer.h"
+
+#include <inttypes.h>
+
+#include "VideoUtils.h"
+#include "TimeUnits.h"
+#include "prenv.h"
+
+#ifdef PR_LOGGING
+mozilla::LazyLogModule gADTSDemuxerLog("ADTSDemuxer");
+#define ADTSLOG(msg, ...) \
+  MOZ_LOG(gADTSDemuxerLog, LogLevel::Debug, ("ADTSDemuxer " msg, ##__VA_ARGS__))
+#define ADTSLOGV(msg, ...) \
+  MOZ_LOG(gADTSDemuxerLog, LogLevel::Verbose, ("ADTSDemuxer " msg, ##__VA_ARGS__))
+#else
+#define ADTSLOG(msg, ...) do {} while (false)
+#define ADTSLOG(msg, ...) do {} while (false)
+#endif
+
+namespace mozilla {
+namespace adts {
+
+// adts::FrameHeader - Holds the ADTS frame header and its parsing
+// state.
+//
+// ADTS Frame Structure
+//
+// 11111111 1111BCCD EEFFFFGH HHIJKLMM MMMMMMMM MMMOOOOO OOOOOOPP(QQQQQQQQ QQQQQQQQ)
+//
+// Header consists of 7 or 9 bytes(without or with CRC).
+// Letter   Length(bits)  Description
+// { sync } 12            syncword 0xFFF, all bits must be 1
+// B        1             MPEG Version: 0 for MPEG-4, 1 for MPEG-2
+// C        2             Layer: always 0
+// D        1             protection absent, Warning, set to 1 if there is no
+//                        CRC and 0 if there is CRC
+// E        2             profile, the MPEG-4 Audio Object Type minus 1
+// F        4             MPEG-4 Sampling Frequency Index (15 is forbidden)
+// H        3             MPEG-4 Channel Configuration (in the case of 0, the
+//                        channel configuration is sent via an in-band PCE)
+// M        13            frame length, this value must include 7 or 9 bytes of
+//                        header length: FrameLength =
+//                          (ProtectionAbsent == 1 ? 7 : 9) + size(AACFrame)
+// O        11            Buffer fullness
+// P        2             Number of AAC frames(RDBs) in ADTS frame minus 1, for
+//                        maximum compatibility always use 1 AAC frame per ADTS
+//                        frame
+// Q        16            CRC if protection absent is 0
+class FrameHeader {
+public:
+  uint32_t mFrameLength;
+  uint32_t mSampleRate;
+  uint32_t mSamples;
+  uint32_t mChannels;
+  uint8_t  mObjectType;
+  uint8_t  mSamplingIndex;
+  uint8_t  mChannelConfig;
+  uint8_t  mNumAACFrames;
+  bool     mHaveCrc;
+
+  // Returns whether aPtr matches a valid ADTS header sync marker
+  static bool MatchesSync(const uint8_t* aPtr) {
+    return aPtr[0] == 0xFF && (aPtr[1] & 0xF6) == 0xF0;
+  }
+
+  FrameHeader() { Reset(); }
+
+  // Header size
+  size_t HeaderSize() const { return (mHaveCrc) ? 9 : 7; }
+
+  bool IsValid() const { return mFrameLength > 0; }
+
+  // Resets the state to allow for a new parsing session.
+  void Reset() { PodZero(this); }
+
+  // Returns whether the byte creates a valid sequence up to this point.
+  bool Parse(const uint8_t* aPtr) {
+    const uint8_t* p = aPtr;
+
+    if (!MatchesSync(p)) {
+      return false;
+    }
+
+    // AAC has 1024 samples per frame per channel.
+    mSamples = 1024;
+
+    mHaveCrc = !(p[1] & 0x01);
+    mObjectType = ((p[2] & 0xC0) >> 6) + 1;
+    mSamplingIndex = (p[2] & 0x3C) >> 2;
+    mChannelConfig = (p[2] & 0x01) << 2 | (p[3] & 0xC0) >> 6;
+    mFrameLength = (p[3] & 0x03) << 11 | (p[4] & 0xFF) << 3 | (p[5] & 0xE0) >> 5;
+    mNumAACFrames = (p[6] & 0x03) + 1;
+
+    static const int32_t SAMPLE_RATES[16] = {
+      96000, 88200, 64000, 48000,
+      44100, 32000, 24000, 22050,
+      16000, 12000, 11025,  8000,
+      7350
+    };
+    mSampleRate = SAMPLE_RATES[mSamplingIndex];
+
+    MOZ_ASSERT(mChannelConfig < 8);
+    mChannels = (mChannelConfig == 7) ? 8 : mChannelConfig;
+
+    return true;
+  }
+};
+
+
+// adts::Frame - Frame meta container used to parse and hold a frame
+// header and side info.
+class Frame {
+public:
+  Frame() : mOffset(0), mHeader() {}
+
+  int64_t Offset() const { return mOffset; }
+  size_t Length() const {
+    // TODO: If fields are zero'd when invalid, this check wouldn't be necessary.
+    if (!mHeader.IsValid()) {
+      return 0;
+    }
+
+    return mHeader.mFrameLength;
+  }
+
+  // Returns the offset to the start of frame's raw data.
+  int64_t PayloadOffset() const {
+    return mOffset + mHeader.HeaderSize();
+  }
+
+  // Returns the length of the frame's raw data (excluding the header) in bytes.
+  size_t PayloadLength() const {
+    // TODO: If fields are zero'd when invalid, this check wouldn't be necessary.
+    if (!mHeader.IsValid()) {
+      return 0;
+    }
+
+    return mHeader.mFrameLength - mHeader.HeaderSize();
+  }
+
+  // Returns the parsed frame header.
+  const FrameHeader& Header() const {
+    return mHeader;
+  }
+
+  bool IsValid() const {
+    return mHeader.IsValid();
+  }
+
+  // Resets the frame header and data.
+  void Reset() {
+    mHeader.Reset();
+    mOffset = 0;
+  }
+
+  // Returns whether the valid
+  bool Parse(int64_t aOffset, uint8_t* aStart, uint8_t* aEnd) {
+    MOZ_ASSERT(aStart && aEnd);
+
+    bool found = false;
+    uint8_t* ptr = aStart;
+    // Require at least 7 bytes of data at the end of the buffer for the minimum
+    // ADTS frame header.
+    while (ptr < aEnd - 7 && !found) {
+      found = mHeader.Parse(ptr);
+      ptr++;
+    }
+
+    mOffset = aOffset + (ptr - aStart) - 1;
+
+    return found;
+  }
+
+private:
+  // The offset to the start of the header.
+  int64_t mOffset;
+
+  // The currently parsed frame header.
+  FrameHeader mHeader;
+};
+
+
+class FrameParser {
+public:
+
+  // Returns the currently parsed frame. Reset via Reset or EndFrameSession.
+  const Frame& CurrentFrame() const { return mFrame; }
+
+
+#ifdef ENABLE_TESTS
+  // Returns the previously parsed frame. Reset via Reset.
+  const Frame& PrevFrame() const { return mPrevFrame; }
+#endif
+
+  // Returns the first parsed frame. Reset via Reset.
+  const Frame& FirstFrame() const { return mFirstFrame; }
+
+  // Resets the parser. Don't use between frames as first frame data is reset.
+  void Reset() {
+    EndFrameSession();
+    mFirstFrame.Reset();
+  }
+
+  // Clear the last parsed frame to allow for next frame parsing, i.e.:
+  // - sets PrevFrame to CurrentFrame
+  // - resets the CurrentFrame
+  // - resets ID3Header if no valid header was parsed yet
+  void EndFrameSession() {
+#ifdef ENABLE_TESTS
+    mPrevFrame = mFrame;
+#endif
+    mFrame.Reset();
+  }
+
+  // Parses contents of given ByteReader for a valid frame header and returns true
+  // if one was found. After returning, the variable passed to 'aBytesToSkip' holds
+  // the amount of bytes to be skipped (if any) in order to jump across a large
+  // ID3v2 tag spanning multiple buffers.
+  bool Parse(int64_t aOffset, uint8_t* aStart, uint8_t* aEnd) {
+    const bool found = mFrame.Parse(aOffset, aStart, aEnd);
+
+    if (mFrame.Length() && !mFirstFrame.Length()) {
+      mFirstFrame = mFrame;
+    }
+
+    return found;
+  }
+
+private:
+  // We keep the first parsed frame around for static info access, the
+  // previously parsed frame for debugging and the currently parsed frame.
+  Frame mFirstFrame;
+  Frame mFrame;
+#ifdef ENABLE_TESTS
+  Frame mPrevFrame;
+#endif
+};
+
+
+// Return the AAC Profile Level Indication based upon sample rate and channels
+// Information based upon table 1.10 from ISO/IEC 14496-3:2005(E)
+static int8_t
+ProfileLevelIndication(const Frame& frame)
+{
+  const FrameHeader& header = frame.Header();
+  MOZ_ASSERT(header.IsValid());
+
+  if (!header.IsValid()) {
+    return 0;
+  }
+
+  const int channels = header.mChannels;
+  const int sampleRate = header.mSampleRate;
+
+  if (channels <= 2) {
+    if (sampleRate <= 24000) {
+      // AAC Profile  L1
+      return 0x28;
+    }
+    else if (sampleRate <= 48000) {
+      // AAC Profile  L2
+      return 0x29;
+    }
+  }
+  else if (channels <= 5) {
+    if (sampleRate <= 48000) {
+      // AAC Profile  L4
+      return 0x2A;
+    }
+    else if (sampleRate <= 96000) {
+      // AAC Profile  L5
+      return 0x2B;
+    }
+  }
+
+  // TODO: Should this be 0xFE for 'no audio profile specified'?
+  return 0;
+}
+
+
+// Initialize the AAC AudioSpecificConfig.
+// Only handles two-byte version for AAC-LC.
+static void
+InitAudioSpecificConfig(const Frame& frame,
+                        MediaByteBuffer* aBuffer)
+{
+  const FrameHeader& header = frame.Header();
+  MOZ_ASSERT(header.IsValid());
+
+  int audioObjectType = header.mObjectType;
+  int samplingFrequencyIndex = header.mSamplingIndex;
+  int channelConfig = header.mChannelConfig;
+
+  uint8_t asc[2];
+  asc[0] = (audioObjectType & 0x1F) << 3 | (samplingFrequencyIndex & 0x0E) >> 1;
+  asc[1] = (samplingFrequencyIndex & 0x01) << 7 | (channelConfig & 0x0F) << 3;
+
+  aBuffer->AppendElements(asc, 2);
+}
+
+} // namespace adts
+
+// ADTSDemuxer
+
+ADTSDemuxer::ADTSDemuxer(MediaResource* aSource)
+  : mSource(aSource)
+{}
+
+bool
+ADTSDemuxer::InitInternal()
+{
+  if (!mTrackDemuxer) {
+    mTrackDemuxer = new ADTSTrackDemuxer(mSource);
+  }
+  return mTrackDemuxer->Init();
+}
+
+RefPtr<ADTSDemuxer::InitPromise>
+ADTSDemuxer::Init()
+{
+  if (!InitInternal()) {
+    ADTSLOG("Init() failure: waiting for data");
+
+    return InitPromise::CreateAndReject(
+      DemuxerFailureReason::DEMUXER_ERROR, __func__);
+  }
+
+  ADTSLOG("Init() successful");
+  return InitPromise::CreateAndResolve(NS_OK, __func__);
+}
+
+bool
+ADTSDemuxer::HasTrackType(TrackInfo::TrackType aType) const
+{
+  return aType == TrackInfo::kAudioTrack;
+}
+
+uint32_t
+ADTSDemuxer::GetNumberTracks(TrackInfo::TrackType aType) const
+{
+  return (aType == TrackInfo::kAudioTrack) ? 1 : 0;
+}
+
+already_AddRefed<MediaTrackDemuxer>
+ADTSDemuxer::GetTrackDemuxer(TrackInfo::TrackType aType, uint32_t aTrackNumber)
+{
+  if (!mTrackDemuxer) {
+    return nullptr;
+  }
+
+  return RefPtr<ADTSTrackDemuxer>(mTrackDemuxer).forget();
+}
+
+bool
+ADTSDemuxer::IsSeekable() const
+{
+  int64_t length = mSource->GetLength();
+  if (length > -1)
+    return true;
+  return false;
+}
+
+
+// ADTSTrackDemuxer
+ADTSTrackDemuxer::ADTSTrackDemuxer(MediaResource* aSource)
+  : mSource(aSource)
+  , mParser(new adts::FrameParser())
+  , mOffset(0)
+  , mNumParsedFrames(0)
+  , mFrameIndex(0)
+  , mTotalFrameLen(0)
+  , mSamplesPerFrame(0)
+  , mSamplesPerSecond(0)
+  , mChannels(0)
+{
+  Reset();
+}
+
+ADTSTrackDemuxer::~ADTSTrackDemuxer()
+{
+  delete mParser;
+  mParser = nullptr;
+}
+
+bool
+ADTSTrackDemuxer::Init()
+{
+
+  FastSeek(media::TimeUnit());
+  // Read the first frame to fetch sample rate and other meta data.
+  RefPtr<MediaRawData> frame(GetNextFrame(FindNextFrame(true)));
+
+  ADTSLOG("Init StreamLength()=%" PRId64 " first-frame-found=%d",
+          StreamLength(), !!frame);
+
+  if (!frame) {
+    return false;
+  }
+
+  // Rewind back to the stream begin to avoid dropping the first frame.
+  FastSeek(media::TimeUnit());
+
+  if (!mInfo) {
+    mInfo = MakeUnique<AudioInfo>();
+  }
+
+  mInfo->mRate = mSamplesPerSecond;
+  mInfo->mChannels = mChannels;
+  mInfo->mBitDepth = 16;
+  mInfo->mDuration = Duration().ToMicroseconds();
+
+  // AAC Specific information
+  mInfo->mMimeType = "audio/mp4a-latm";
+
+  // Configure AAC codec-specific values.
+
+  // According to
+  // https://msdn.microsoft.com/en-us/library/windows/desktop/dd742784%28v=vs.85%29.aspx,
+  // wAudioProfileLevelIndication, which is passed mInfo->mProfile, is
+  // a value from Table 1.12 -- audioProfileLevelIndication values, ISO/IEC 14496-3.
+  mInfo->mProfile = ProfileLevelIndication(mParser->FirstFrame());
+  // For AAC, mExtendedProfile contains the audioObjectType from Table
+  // 1.3 -- Audio Profile definition, ISO/IEC 14496-3. Eg. 2 == AAC LC
+  mInfo->mExtendedProfile = mParser->FirstFrame().Header().mObjectType;
+  InitAudioSpecificConfig(mParser->FirstFrame(), mInfo->mCodecSpecificConfig);
+
+  ADTSLOG("Init mInfo={mRate=%u mChannels=%u mBitDepth=%u mDuration=%" PRId64 "}",
+          mInfo->mRate, mInfo->mChannels, mInfo->mBitDepth, mInfo->mDuration);
+
+  return mSamplesPerSecond && mChannels;
+}
+
+#ifdef ENABLE_TESTS
+const adts::Frame&
+ADTSTrackDemuxer::LastFrame() const
+{
+  return mParser->PrevFrame();
+}
+
+RefPtr<MediaRawData>
+ADTSTrackDemuxer::DemuxSample()
+{
+  return GetNextFrame(FindNextFrame());
+}
+
+media::TimeUnit
+ADTSTrackDemuxer::SeekPosition() const
+{
+  return Duration(mFrameIndex);
+}
+#endif
+
+UniquePtr<TrackInfo>
+ADTSTrackDemuxer::GetInfo() const
+{
+  return mInfo->Clone();
+}
+
+RefPtr<ADTSTrackDemuxer::SeekPromise>
+ADTSTrackDemuxer::Seek(media::TimeUnit aTime)
+{
+  // Efficiently seek to the position.
+  FastSeek(aTime);
+  // Correct seek position by scanning the next frames.
+  const media::TimeUnit seekTime = ScanUntil(aTime);
+
+  return SeekPromise::CreateAndResolve(seekTime, __func__);
+}
+
+media::TimeUnit
+ADTSTrackDemuxer::FastSeek(const media::TimeUnit& aTime)
+{
+  ADTSLOG("FastSeek(%" PRId64 ") avgFrameLen=%f mNumParsedFrames=%" PRIu64
+         " mFrameIndex=%" PRId64 " mOffset=%" PRIu64,
+         aTime, AverageFrameLength(), mNumParsedFrames, mFrameIndex, mOffset);
+
+  const int64_t firstFrameOffset = mParser->FirstFrame().Offset();
+  if (!aTime.ToMicroseconds()) {
+    // Quick seek to the beginning of the stream.
+    mOffset = firstFrameOffset;
+  } else if (AverageFrameLength() > 0) {
+    mOffset = firstFrameOffset + FrameIndexFromTime(aTime) *
+      AverageFrameLength();
+  }
+
+  if (mOffset > firstFrameOffset && StreamLength() > 0) {
+    mOffset = std::min(StreamLength() - 1, mOffset);
+  }
+
+  mFrameIndex = FrameIndexFromOffset(mOffset);
+  mParser->EndFrameSession();
+
+  ADTSLOG("FastSeek End avgFrameLen=%f mNumParsedFrames=%" PRIu64
+          " mFrameIndex=%" PRId64 " mFirstFrameOffset=%llu mOffset=%" PRIu64
+          " SL=%llu",
+          AverageFrameLength(), mNumParsedFrames, mFrameIndex,
+          firstFrameOffset, mOffset, StreamLength());
+
+  return Duration(mFrameIndex);
+}
+
+media::TimeUnit
+ADTSTrackDemuxer::ScanUntil(const media::TimeUnit& aTime)
+{
+  ADTSLOG("ScanUntil(%" PRId64 ") avgFrameLen=%f mNumParsedFrames=%" PRIu64
+          " mFrameIndex=%" PRId64 " mOffset=%" PRIu64,
+          aTime, AverageFrameLength(), mNumParsedFrames, mFrameIndex, mOffset);
+
+  if (!aTime.ToMicroseconds()) {
+    return FastSeek(aTime);
+  }
+
+  if (Duration(mFrameIndex) > aTime) {
+    FastSeek(aTime);
+  }
+
+  while (SkipNextFrame(FindNextFrame()) && Duration(mFrameIndex + 1) < aTime) {
+    ADTSLOGV("ScanUntil* avgFrameLen=%f mNumParsedFrames=%" PRIu64
+             " mFrameIndex=%" PRId64 " mOffset=%" PRIu64 " Duration=%" PRId64,
+             aTime, AverageFrameLength(), mNumParsedFrames, mFrameIndex,
+             mOffset, Duration(mFrameIndex + 1));
+  }
+
+  ADTSLOG("ScanUntil End avgFrameLen=%f mNumParsedFrames=%" PRIu64
+          " mFrameIndex=%" PRId64 " mOffset=%" PRIu64,
+          aTime, AverageFrameLength(), mNumParsedFrames, mFrameIndex, mOffset);
+
+  return Duration(mFrameIndex);
+}
+
+RefPtr<ADTSTrackDemuxer::SamplesPromise>
+ADTSTrackDemuxer::GetSamples(int32_t aNumSamples)
+{
+  ADTSLOGV("GetSamples(%d) Begin mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
+          " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64 " mSamplesPerFrame=%d "
+          "mSamplesPerSecond=%d mChannels=%d",
+          aNumSamples, mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen,
+          mSamplesPerFrame, mSamplesPerSecond, mChannels);
+
+  if (!aNumSamples) {
+    return SamplesPromise::CreateAndReject(
+      DemuxerFailureReason::DEMUXER_ERROR, __func__);
+  }
+
+  RefPtr<SamplesHolder> frames = new SamplesHolder();
+
+  while (aNumSamples--) {
+    RefPtr<MediaRawData> frame(GetNextFrame(FindNextFrame()));
+    if (!frame)
+      break;
+
+    frames->mSamples.AppendElement(frame);
+  }
+
+  ADTSLOGV("GetSamples() End mSamples.Size()=%d aNumSamples=%d mOffset=%" PRIu64
+          " mNumParsedFrames=%" PRIu64 " mFrameIndex=%" PRId64
+          " mTotalFrameLen=%" PRIu64 " mSamplesPerFrame=%d mSamplesPerSecond=%d "
+          "mChannels=%d",
+          frames->mSamples.Length(), aNumSamples, mOffset, mNumParsedFrames,
+          mFrameIndex, mTotalFrameLen, mSamplesPerFrame, mSamplesPerSecond,
+          mChannels);
+
+  if (frames->mSamples.IsEmpty()) {
+    return SamplesPromise::CreateAndReject(
+      DemuxerFailureReason::END_OF_STREAM, __func__);
+  }
+
+  return SamplesPromise::CreateAndResolve(frames, __func__);
+}
+
+void
+ADTSTrackDemuxer::Reset()
+{
+  ADTSLOG("Reset()");
+  MOZ_ASSERT(mParser);
+  if (mParser) {
+    mParser->Reset();
+  }
+  FastSeek(media::TimeUnit());
+}
+
+RefPtr<ADTSTrackDemuxer::SkipAccessPointPromise>
+ADTSTrackDemuxer::SkipToNextRandomAccessPoint(media::TimeUnit aTimeThreshold)
+{
+  // Will not be called for audio-only resources.
+  return SkipAccessPointPromise::CreateAndReject(
+    SkipFailureHolder(DemuxerFailureReason::DEMUXER_ERROR, 0), __func__);
+}
+
+int64_t
+ADTSTrackDemuxer::GetResourceOffset() const
+{
+  return mOffset;
+}
+
+media::TimeIntervals
+ADTSTrackDemuxer::GetBuffered()
+{
+  media::TimeUnit duration = Duration();
+
+  if (duration <= media::TimeUnit()) {
+    return media::TimeIntervals();
+  }
+
+  AutoPinned<MediaResource> stream(mSource.GetResource());
+  return GetEstimatedBufferedTimeRanges(stream, duration.ToMicroseconds());
+}
+
+int64_t
+ADTSTrackDemuxer::StreamLength() const
+{
+  return mSource.GetLength();
+}
+
+media::TimeUnit
+ADTSTrackDemuxer::Duration() const
+{
+  if (!mNumParsedFrames) {
+    return media::TimeUnit::FromMicroseconds(-1);
+  }
+
+  const int64_t streamLen = StreamLength();
+  if (streamLen < 0) {
+    // Unknown length, we can't estimate duration.
+    return media::TimeUnit::FromMicroseconds(-1);
+  }
+  const int64_t firstFrameOffset = mParser->FirstFrame().Offset();
+  int64_t numFrames = (streamLen - firstFrameOffset) / AverageFrameLength();
+  return Duration(numFrames);
+}
+
+media::TimeUnit
+ADTSTrackDemuxer::Duration(int64_t aNumFrames) const
+{
+  if (!mSamplesPerSecond) {
+    return media::TimeUnit::FromMicroseconds(-1);
+  }
+
+  const double usPerFrame = USECS_PER_S * mSamplesPerFrame / mSamplesPerSecond;
+  return media::TimeUnit::FromMicroseconds(aNumFrames * usPerFrame);
+}
+
+const adts::Frame&
+ADTSTrackDemuxer::FindNextFrame(bool findFirstFrame /*= false*/)
+{
+  static const int BUFFER_SIZE = 4096;
+  static const int MAX_SKIPPED_BYTES = 10 * BUFFER_SIZE;
+
+  ADTSLOGV("FindNext() Begin mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
+          " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64
+          " mSamplesPerFrame=%d mSamplesPerSecond=%d mChannels=%d",
+          mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen,
+          mSamplesPerFrame, mSamplesPerSecond, mChannels);
+
+  uint8_t buffer[BUFFER_SIZE];
+  int32_t read = 0;
+
+  bool foundFrame = false;
+  int64_t frameHeaderOffset = mOffset;
+
+  // Prepare the parser for the next frame parsing session.
+  mParser->EndFrameSession();
+
+  // Check whether we've found a valid ADTS frame.
+  while (!foundFrame) {
+    if ((read = Read(buffer, frameHeaderOffset, BUFFER_SIZE)) == 0) {
+      ADTSLOG("FindNext() EOS without a frame");
+      break;
+    }
+
+    if (frameHeaderOffset - mOffset > MAX_SKIPPED_BYTES) {
+      ADTSLOG("FindNext() exceeded MAX_SKIPPED_BYTES without a frame");
+      break;
+    }
+
+    const adts::Frame& currentFrame = mParser->CurrentFrame();
+    foundFrame = mParser->Parse(frameHeaderOffset, buffer, buffer + read);
+    if (findFirstFrame && foundFrame) {
+      // Check for sync marker after the found frame, since it's
+      // possible to find sync marker in AAC data. If sync marker
+      // exists after the current frame then we've found a frame
+      // header.
+      int64_t nextFrameHeaderOffset = currentFrame.Offset() + currentFrame.Length();
+      int32_t read = Read(buffer, nextFrameHeaderOffset, 2);
+      if (read != 2 || !adts::FrameHeader::MatchesSync(buffer)) {
+        frameHeaderOffset = currentFrame.Offset() + 1;
+        mParser->Reset();
+        foundFrame = false;
+        continue;
+      }
+    }
+
+    if (foundFrame) {
+      break;
+    }
+
+    // Minimum header size is 7 bytes.
+    int64_t advance = read - 7;
+
+    // Check for offset overflow.
+    if (frameHeaderOffset + advance <= frameHeaderOffset) {
+      break;
+    }
+
+    frameHeaderOffset += advance;
+  }
+
+  if (!foundFrame || !mParser->CurrentFrame().Length()) {
+    ADTSLOG("FindNext() Exit foundFrame=%d mParser->CurrentFrame().Length()=%d ",
+           foundFrame, mParser->CurrentFrame().Length());
+    mParser->Reset();
+    return mParser->CurrentFrame();
+  }
+
+  ADTSLOGV("FindNext() End mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
+          " mFrameIndex=%" PRId64 " frameHeaderOffset=%d"
+          " mTotalFrameLen=%" PRIu64 " mSamplesPerFrame=%d mSamplesPerSecond=%d"
+          " mChannels=%d",
+          mOffset, mNumParsedFrames, mFrameIndex, frameHeaderOffset,
+          mTotalFrameLen, mSamplesPerFrame, mSamplesPerSecond, mChannels);
+
+  return mParser->CurrentFrame();
+}
+
+bool
+ADTSTrackDemuxer::SkipNextFrame(const adts::Frame& aFrame)
+{
+  if (!mNumParsedFrames || !aFrame.Length()) {
+    RefPtr<MediaRawData> frame(GetNextFrame(aFrame));
+    return frame;
+  }
+
+  UpdateState(aFrame);
+
+  ADTSLOGV("SkipNext() End mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
+          " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64
+          " mSamplesPerFrame=%d mSamplesPerSecond=%d mChannels=%d",
+          mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen,
+          mSamplesPerFrame, mSamplesPerSecond, mChannels);
+
+  return true;
+}
+
+already_AddRefed<MediaRawData>
+ADTSTrackDemuxer::GetNextFrame(const adts::Frame& aFrame)
+{
+  ADTSLOG("GetNext() Begin({mOffset=%" PRId64 " HeaderSize()=%d Length()=%d})",
+         aFrame.Offset(), aFrame.Header().HeaderSize(), aFrame.PayloadLength());
+  if (!aFrame.IsValid())
+    return nullptr;
+
+  const int64_t offset = aFrame.PayloadOffset();
+  const uint32_t length = aFrame.PayloadLength();
+
+  RefPtr<MediaRawData> frame = new MediaRawData();
+  frame->mOffset = offset;
+
+  nsAutoPtr<MediaRawDataWriter> frameWriter(frame->CreateWriter());
+  if (!frameWriter->SetSize(length)) {
+    ADTSLOG("GetNext() Exit failed to allocated media buffer");
+    return nullptr;
+  }
+
+  const uint32_t read = Read(frameWriter->Data(), offset, length);
+  if (read != length) {
+    ADTSLOG("GetNext() Exit read=%u frame->Size()=%u", read, frame->Size());
+    return nullptr;
+  }
+
+  UpdateState(aFrame);
+
+  frame->mTime = Duration(mFrameIndex - 1).ToMicroseconds();
+  frame->mDuration = Duration(1).ToMicroseconds();
+  frame->mTimecode = frame->mTime;
+  frame->mKeyframe = true;
+
+  MOZ_ASSERT(frame->mTime >= 0);
+  MOZ_ASSERT(frame->mDuration > 0);
+
+  ADTSLOGV("GetNext() End mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
+          " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64
+          " mSamplesPerFrame=%d mSamplesPerSecond=%d mChannels=%d",
+          mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen,
+          mSamplesPerFrame, mSamplesPerSecond, mChannels);
+
+  return frame.forget();
+}
+
+int64_t
+ADTSTrackDemuxer::FrameIndexFromOffset(int64_t aOffset) const
+{
+  int64_t frameIndex = 0;
+
+  if (AverageFrameLength() > 0) {
+    frameIndex = (aOffset - mParser->FirstFrame().Offset()) / AverageFrameLength();
+  }
+
+  ADTSLOGV("FrameIndexFromOffset(%" PRId64 ") -> %" PRId64, aOffset, frameIndex);
+  return std::max<int64_t>(0, frameIndex);
+}
+
+int64_t
+ADTSTrackDemuxer::FrameIndexFromTime(const media::TimeUnit& aTime) const
+{
+  int64_t frameIndex = 0;
+  if (mSamplesPerSecond > 0 && mSamplesPerFrame > 0) {
+    frameIndex = aTime.ToSeconds() * mSamplesPerSecond / mSamplesPerFrame - 1;
+  }
+
+  ADTSLOGV("FrameIndexFromOffset(%fs) -> %" PRId64, aTime.ToSeconds(), frameIndex);
+  return std::max<int64_t>(0, frameIndex);
+}
+
+void
+ADTSTrackDemuxer::UpdateState(const adts::Frame& aFrame)
+{
+  int32_t frameLength = aFrame.Length();
+  // Prevent overflow.
+  if (mTotalFrameLen + frameLength < mTotalFrameLen) {
+    // These variables have a linear dependency and are only used to derive the
+    // average frame length.
+    mTotalFrameLen /= 2;
+    mNumParsedFrames /= 2;
+  }
+
+  // Full frame parsed, move offset to its end.
+  mOffset = aFrame.Offset() + frameLength;
+  mTotalFrameLen += frameLength;
+
+  if (!mSamplesPerFrame) {
+    const adts::FrameHeader& header = aFrame.Header();
+    mSamplesPerFrame = header.mSamples;
+    mSamplesPerSecond = header.mSampleRate;
+    mChannels = header.mChannels;
+  }
+
+  ++mNumParsedFrames;
+  ++mFrameIndex;
+  MOZ_ASSERT(mFrameIndex > 0);
+}
+
+int32_t
+ADTSTrackDemuxer::Read(uint8_t* aBuffer, int64_t aOffset, int32_t aSize)
+{
+  ADTSLOGV("ADTSTrackDemuxer::Read(%p %" PRId64 " %d)", aBuffer, aOffset, aSize);
+
+  const int64_t streamLen = StreamLength();
+  if (mInfo && streamLen > 0) {
+    // Prevent blocking reads after successful initialization.
+    aSize = std::min<int64_t>(aSize, streamLen - aOffset);
+  }
+
+  uint32_t read = 0;
+  ADTSLOGV("ADTSTrackDemuxer::Read        -> ReadAt(%d)", aSize);
+  const nsresult rv = mSource.ReadAt(aOffset, reinterpret_cast<char*>(aBuffer),
+                                     static_cast<uint32_t>(aSize), &read);
+  NS_ENSURE_SUCCESS(rv, 0);
+  return static_cast<int32_t>(read);
+}
+
+double
+ADTSTrackDemuxer::AverageFrameLength() const
+{
+  if (mNumParsedFrames) {
+    return static_cast<double>(mTotalFrameLen) / mNumParsedFrames;
+  }
+
+  return 0.0;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/ADTSDemuxer.h
@@ -0,0 +1,146 @@
+/* -*- 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 ADTS_DEMUXER_H_
+#define ADTS_DEMUXER_H_
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Maybe.h"
+#include "MediaDataDemuxer.h"
+#include "MediaResource.h"
+#include "mp4_demuxer/ByteReader.h"
+
+namespace mozilla {
+
+namespace adts {
+class Frame;
+class FrameParser;
+}
+
+class ADTSTrackDemuxer;
+
+class ADTSDemuxer : public MediaDataDemuxer {
+public:
+  // MediaDataDemuxer interface.
+  explicit ADTSDemuxer(MediaResource* aSource);
+  RefPtr<InitPromise> Init() override;
+  bool HasTrackType(TrackInfo::TrackType aType) const override;
+  uint32_t GetNumberTracks(TrackInfo::TrackType aType) const override;
+  already_AddRefed<MediaTrackDemuxer> GetTrackDemuxer(
+    TrackInfo::TrackType aType, uint32_t aTrackNumber) override;
+  bool IsSeekable() const override;
+  bool ShouldComputeStartTime() const override { return false; }
+
+private:
+  bool InitInternal();
+
+  RefPtr<MediaResource> mSource;
+  RefPtr<ADTSTrackDemuxer> mTrackDemuxer;
+};
+
+class ADTSTrackDemuxer : public MediaTrackDemuxer {
+public:
+  explicit ADTSTrackDemuxer(MediaResource* aSource);
+
+  // Initializes the track demuxer by reading the first frame for meta data.
+  // Returns initialization success state.
+  bool Init();
+
+  // Returns the total stream length if known, -1 otherwise.
+  int64_t StreamLength() const;
+
+  // Returns the estimated stream duration, or a 0-duration if unknown.
+  media::TimeUnit Duration() const;
+
+  // Returns the estimated duration up to the given frame number,
+  // or a 0-duration if unknown.
+  media::TimeUnit Duration(int64_t aNumFrames) const;
+
+#ifdef ENABLE_TESTS
+  const adts::Frame& LastFrame() const;
+  RefPtr<MediaRawData> DemuxSample();
+  media::TimeUnit SeekPosition() const;
+#endif
+
+  // MediaTrackDemuxer interface.
+  UniquePtr<TrackInfo> GetInfo() const override;
+  RefPtr<SeekPromise> Seek(media::TimeUnit aTime) override;
+  RefPtr<SamplesPromise> GetSamples(int32_t aNumSamples = 1) override;
+  void Reset() override;
+  RefPtr<SkipAccessPointPromise> SkipToNextRandomAccessPoint(
+    media::TimeUnit aTimeThreshold) override;
+  int64_t GetResourceOffset() const override;
+  media::TimeIntervals GetBuffered() override;
+
+private:
+  // Destructor.
+  ~ADTSTrackDemuxer();
+
+  // Fast approximate seeking to given time.
+  media::TimeUnit FastSeek(const media::TimeUnit& aTime);
+
+  // Seeks by scanning the stream up to the given time for more accurate results.
+  media::TimeUnit ScanUntil(const media::TimeUnit& aTime);
+
+  // Finds the next valid frame and returns its byte range.
+  const adts::Frame& FindNextFrame(bool findFirstFrame = false);
+
+  // Skips the next frame given the provided byte range.
+  bool SkipNextFrame(const adts::Frame& aFrame);
+
+  // Returns the next ADTS frame, if available.
+  already_AddRefed<MediaRawData> GetNextFrame(const adts::Frame& aFrame);
+
+  // Updates post-read meta data.
+  void UpdateState(const adts::Frame& aFrame);
+
+  // Returns the frame index for the given offset.
+  int64_t FrameIndexFromOffset(int64_t aOffset) const;
+
+  // Returns the frame index for the given time.
+  int64_t FrameIndexFromTime(const media::TimeUnit& aTime) const;
+
+  // Reads aSize bytes into aBuffer from the source starting at aOffset.
+  // Returns the actual size read.
+  int32_t Read(uint8_t* aBuffer, int64_t aOffset, int32_t aSize);
+
+  // Returns the average frame length derived from the previously parsed frames.
+  double AverageFrameLength() const;
+
+  // The (hopefully) ADTS resource.
+  MediaResourceIndex mSource;
+
+  // ADTS frame parser used to detect frames and extract side info.
+  adts::FrameParser* mParser;
+
+  // Current byte offset in the source stream.
+  int64_t mOffset;
+
+  // Total parsed frames.
+  uint64_t mNumParsedFrames;
+
+  // Current frame index.
+  int64_t mFrameIndex;
+
+  // Sum of parsed frames' lengths in bytes.
+  uint64_t mTotalFrameLen;
+
+  // Samples per frame metric derived from frame headers or 0 if none available.
+  uint32_t mSamplesPerFrame;
+
+  // Samples per second metric derived from frame headers or 0 if none available.
+  uint32_t mSamplesPerSecond;
+
+  // Channel count derived from frame headers or 0 if none available.
+  uint32_t mChannels;
+
+  // Audio track config info.
+  UniquePtr<AudioInfo> mInfo;
+};
+
+} // mozilla
+
+#endif // !ADTS_DEMUXER_H_
--- a/dom/media/DecoderTraits.cpp
+++ b/dom/media/DecoderTraits.cpp
@@ -52,16 +52,19 @@
 #include "MP4Decoder.h"
 #include "MP4Demuxer.h"
 #endif
 #include "MediaFormatReader.h"
 
 #include "MP3Decoder.h"
 #include "MP3Demuxer.h"
 
+#include "ADTSDecoder.h"
+#include "ADTSDemuxer.h"
+
 namespace mozilla
 {
 
 template <class String>
 static bool
 CodecListContains(char const *const * aCodecs, const String& aCodec)
 {
   for (int32_t i = 0; aCodecs[i]; ++i) {
@@ -349,16 +352,23 @@ IsMP3SupportedType(const nsACString& aTy
                    const nsAString& aCodecs = EmptyString())
 {
 #ifdef MOZ_OMX_DECODER
   return false;
 #endif
   return MP3Decoder::CanHandleMediaType(aType, aCodecs);
 }
 
+static bool
+IsAACSupportedType(const nsACString& aType,
+                   const nsAString& aCodecs = EmptyString())
+{
+  return ADTSDecoder::CanHandleMediaType(aType, aCodecs);
+}
+
 /* static */
 bool DecoderTraits::ShouldHandleMediaType(const char* aMIMEType)
 {
 #ifdef MOZ_WAVE
   if (IsWaveType(nsDependentCString(aMIMEType))) {
     // We should not return true for Wave types, since there are some
     // Wave codecs actually in use in the wild that we don't support, and
     // we should allow those to be handled by plugins or helper apps.
@@ -409,16 +419,19 @@ DecoderTraits::CanHandleCodecsType(const
       // fmp4 is supported and working: the codec must be invalid.
       return CANPLAY_NO;
     }
   }
 #endif
   if (IsMP3SupportedType(nsDependentCString(aMIMEType), aRequestedCodecs)) {
     return CANPLAY_YES;
   }
+  if (IsAACSupportedType(nsDependentCString(aMIMEType), aRequestedCodecs)) {
+    return CANPLAY_YES;
+  }
 #ifdef MOZ_OMX_DECODER
   if (IsOmxSupportedType(nsDependentCString(aMIMEType))) {
     if (nsDependentCString(aMIMEType).EqualsASCII("audio/mpeg")) {
       codecList = gMpegAudioCodecs;
 #ifdef MOZ_OMX_WEBM_DECODER
     } else if (nsDependentCString(aMIMEType).EqualsASCII("audio/webm") ||
                nsDependentCString(aMIMEType).EqualsASCII("video/webm")) {
       codecList = gOMXWebMCodecs;
@@ -494,16 +507,19 @@ DecoderTraits::CanHandleMediaType(const 
 #if !defined(MOZ_OMX_WEBM_DECODER)
   if (IsWebMTypeAndEnabled(nsDependentCString(aMIMEType))) {
     return CANPLAY_MAYBE;
   }
 #endif
   if (IsMP3SupportedType(nsDependentCString(aMIMEType))) {
     return CANPLAY_MAYBE;
   }
+  if (IsAACSupportedType(nsDependentCString(aMIMEType))) {
+    return CANPLAY_MAYBE;
+  }
 #ifdef MOZ_GSTREAMER
   if (GStreamerDecoder::CanHandleMediaType(nsDependentCString(aMIMEType),
                                            aHaveRequestedCodecs ? &aRequestedCodecs : nullptr)) {
     return aHaveRequestedCodecs ? CANPLAY_YES : CANPLAY_MAYBE;
   }
 #endif
 #ifdef MOZ_OMX_DECODER
   if (IsOmxSupportedType(nsDependentCString(aMIMEType))) {
@@ -542,16 +558,20 @@ InstantiateDecoder(const nsACString& aTy
     decoder = new MP4Decoder(aOwner);
     return decoder.forget();
   }
 #endif
   if (IsMP3SupportedType(aType)) {
     decoder = new MP3Decoder(aOwner);
     return decoder.forget();
   }
+  if (IsAACSupportedType(aType)) {
+    decoder = new ADTSDecoder(aOwner);
+    return decoder.forget();
+  }
 #ifdef MOZ_GSTREAMER
   if (IsGStreamerSupportedType(aType)) {
     decoder = new GStreamerDecoder(aOwner);
     return decoder.forget();
   }
 #endif
 #ifdef MOZ_RAW
   if (IsRawType(aType)) {
@@ -641,16 +661,19 @@ MediaDecoderReader* DecoderTraits::Creat
 #ifdef MOZ_FMP4
   if (IsMP4SupportedType(aType)) {
     decoderReader = new MediaFormatReader(aDecoder, new MP4Demuxer(aDecoder->GetResource()));
   } else
 #endif
   if (IsMP3SupportedType(aType)) {
     decoderReader = new MediaFormatReader(aDecoder, new mp3::MP3Demuxer(aDecoder->GetResource()));
   } else
+  if (IsAACSupportedType(aType)) {
+    decoderReader = new MediaFormatReader(aDecoder, new ADTSDemuxer(aDecoder->GetResource()));
+  } else
 #ifdef MOZ_GSTREAMER
   if (IsGStreamerSupportedType(aType)) {
     decoderReader = new GStreamerReader(aDecoder);
   } else
 #endif
 #ifdef MOZ_RAW
   if (IsRawType(aType)) {
     decoderReader = new RawReader(aDecoder);
@@ -720,16 +743,17 @@ bool DecoderTraits::IsSupportedInVideoDo
 #endif
 #ifdef MOZ_ANDROID_OMX
     (MediaDecoder::IsAndroidMediaEnabled() && IsAndroidMediaType(aType)) ||
 #endif
 #ifdef MOZ_FMP4
     IsMP4SupportedType(aType) ||
 #endif
     IsMP3SupportedType(aType) ||
+    IsAACSupportedType(aType) ||
 #ifdef MOZ_DIRECTSHOW
     IsDirectShowSupportedType(aType) ||
 #endif
 #ifdef NECKO_PROTOCOL_rtsp
     IsRtspSupportedType(aType) ||
 #endif
     false;
 }
--- a/dom/media/GraphDriver.cpp
+++ b/dom/media/GraphDriver.cpp
@@ -1,14 +1,15 @@
 /* -*- 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 <MediaStreamGraphImpl.h>
+#include "mozilla/dom/AudioContext.h"
 #include "CubebUtils.h"
 
 #ifdef XP_MACOSX
 #include <sys/sysctl.h>
 #endif
 
 extern mozilla::LazyLogModule gMediaStreamGraphLog;
 #define STREAM_LOG(type, msg) MOZ_LOG(gMediaStreamGraphLog, type, msg)
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -1243,16 +1243,17 @@ MediaFormatReader::Error(TrackType aTrac
 void
 MediaFormatReader::Flush(TrackType aTrack)
 {
   MOZ_ASSERT(OnTaskQueue());
   LOG("Flush(%s) BEGIN", TrackTypeToStr(aTrack));
 
   auto& decoder = GetDecoderData(aTrack);
   if (!decoder.mDecoder) {
+    decoder.ResetState();
     return;
   }
 
   decoder.mDecoder->Flush();
   // Purge the current decoder's state.
   // ResetState clears mOutputRequested flag so that we ignore all output until
   // the next request for more data.
   decoder.ResetState();
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -1186,17 +1186,17 @@ MediaManager::SelectSettings(
       } else {
         RefPtr<AudioDevice> audio = static_cast<AudioDevice*>(source.get());
         audios.AppendElement(audio);
       }
     }
     sources.Clear();
     const char* badConstraint = nullptr;
 
-    if (IsOn(aConstraints.mVideo)) {
+    if (videos.Length() && IsOn(aConstraints.mVideo)) {
       badConstraint = MediaConstraintsHelper::SelectSettings(
           GetInvariant(aConstraints.mVideo), videos);
       for (auto& video : videos) {
         sources.AppendElement(video);
       }
     }
     if (audios.Length() && IsOn(aConstraints.mAudio)) {
       badConstraint = MediaConstraintsHelper::SelectSettings(
@@ -1422,16 +1422,18 @@ private:
 
 already_AddRefed<MediaManager::PledgeSourceSet>
 MediaManager::EnumerateRawDevices(uint64_t aWindowId,
                                   MediaSourceEnum aVideoType,
                                   MediaSourceEnum aAudioType,
                                   bool aFake, bool aFakeTracks)
 {
   MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aVideoType != MediaSourceEnum::Other ||
+             aAudioType != MediaSourceEnum::Other);
   RefPtr<PledgeSourceSet> p = new PledgeSourceSet();
   uint32_t id = mOutstandingPledges.Append(*p);
 
   nsAdoptingCString audioLoopDev, videoLoopDev;
   if (!aFake) {
     // Fake stream not requested. The entire device stack is available.
     // Loop in loopback devices if they are set, and their respective type is
     // requested. This is currently used for automated media tests only.
@@ -1447,53 +1449,62 @@ MediaManager::EnumerateRawDevices(uint64
     // Fake tracks only make sense when we have a fake stream.
     aFakeTracks = false;
   }
 
   MediaManager::PostTask(FROM_HERE, NewTaskFrom([id, aWindowId, audioLoopDev,
                                                  videoLoopDev, aVideoType,
                                                  aAudioType, aFake,
                                                  aFakeTracks]() mutable {
-    RefPtr<MediaEngine> backend;
-    if (aFake) {
-      backend = new MediaEngineDefault(aFakeTracks);
-    } else {
+    // Only enumerate what's asked for, and only fake cams and mics.
+    bool hasVideo = aVideoType != MediaSourceEnum::Other;
+    bool hasAudio = aAudioType != MediaSourceEnum::Other;
+    bool fakeCams = aFake && aVideoType == MediaSourceEnum::Camera;
+    bool fakeMics = aFake && aAudioType == MediaSourceEnum::Microphone;
+
+    RefPtr<MediaEngine> fakeBackend, realBackend;
+    if (fakeCams || fakeMics) {
+      fakeBackend = new MediaEngineDefault(aFakeTracks);
+    }
+    if ((!fakeCams && hasVideo) || (!fakeMics && hasAudio)) {
       RefPtr<MediaManager> manager = MediaManager_GetInstance();
-      backend = manager->GetBackend(aWindowId);
+      realBackend = manager->GetBackend(aWindowId);
     }
 
     ScopedDeletePtr<SourceSet> result(new SourceSet);
 
-    nsTArray<RefPtr<VideoDevice>> videos;
-    GetSources(backend, aVideoType, &MediaEngine::EnumerateVideoDevices, videos,
-               videoLoopDev);
-    for (auto& source : videos) {
-      result->AppendElement(source);
+    if (hasVideo) {
+      nsTArray<RefPtr<VideoDevice>> videos;
+      GetSources(fakeCams? fakeBackend : realBackend, aVideoType,
+                 &MediaEngine::EnumerateVideoDevices, videos, videoLoopDev);
+      for (auto& source : videos) {
+        result->AppendElement(source);
+      }
     }
-
-    nsTArray<RefPtr<AudioDevice>> audios;
-    GetSources(backend, aAudioType,
-               &MediaEngine::EnumerateAudioDevices, audios, audioLoopDev);
-    for (auto& source : audios) {
-      result->AppendElement(source);
+    if (hasAudio) {
+      nsTArray<RefPtr<AudioDevice>> audios;
+      GetSources(fakeMics? fakeBackend : realBackend, aAudioType,
+                 &MediaEngine::EnumerateAudioDevices, audios, audioLoopDev);
+      for (auto& source : audios) {
+        result->AppendElement(source);
+      }
     }
-
     SourceSet* handoff = result.forget();
     NS_DispatchToMainThread(do_AddRef(NewRunnableFrom([id, handoff]() mutable {
       ScopedDeletePtr<SourceSet> result(handoff); // grab result
       RefPtr<MediaManager> mgr = MediaManager_GetInstance();
       if (!mgr) {
         return NS_OK;
       }
       RefPtr<PledgeSourceSet> p = mgr->mOutstandingPledges.Remove(id);
       if (p) {
         p->Resolve(result.forget());
       }
       return NS_OK;
-          })));
+    })));
   }));
   return p.forget();
 }
 
 MediaManager::MediaManager()
   : mMediaThread(nullptr)
   , mBackend(nullptr) {
   mPrefs.mFreq   = 1000; // 1KHz test tone
@@ -1862,18 +1873,18 @@ MediaManager::GetUserMedia(nsPIDOMWindow
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (!Preferences::GetBool("media.navigator.video.enabled", true)) {
     c.mVideo.SetAsBoolean() = false;
   }
 
-  MediaSourceEnum videoType = dom::MediaSourceEnum::Camera;
-  MediaSourceEnum audioType = dom::MediaSourceEnum::Microphone;
+  MediaSourceEnum videoType = dom::MediaSourceEnum::Other; // none
+  MediaSourceEnum audioType = dom::MediaSourceEnum::Other; // none
 
   if (c.mVideo.IsMediaTrackConstraints()) {
     auto& vc = c.mVideo.GetAsMediaTrackConstraints();
     videoType = StringToEnum(dom::MediaSourceEnumValues::strings,
                              vc.mMediaSource,
                              dom::MediaSourceEnum::Other);
     Telemetry::Accumulate(loop ? Telemetry::LOOP_GET_USER_MEDIA_TYPE :
                                  Telemetry::WEBRTC_GET_USER_MEDIA_TYPE,
@@ -1966,16 +1977,18 @@ MediaManager::GetUserMedia(nsPIDOMWindow
     // permission menu for selection of the device currently. For tab sharing,
     // Loop has implicit permissions within Firefox, as it is built-in,
     // and will manage the active tab and provide appropriate UI.
     if (loop && (videoType == dom::MediaSourceEnum::Window ||
                  videoType == dom::MediaSourceEnum::Application ||
                  videoType == dom::MediaSourceEnum::Screen)) {
        privileged = false;
     }
+  } else if (IsOn(c.mVideo)) {
+    videoType = dom::MediaSourceEnum::Camera;
   }
 
   if (c.mAudio.IsMediaTrackConstraints()) {
     auto& ac = c.mAudio.GetAsMediaTrackConstraints();
     audioType = StringToEnum(dom::MediaSourceEnumValues::strings,
                              ac.mMediaSource,
                              dom::MediaSourceEnum::Other);
     // Work around WebIDL default since spec uses same dictionary w/audio & video.
@@ -2020,17 +2033,20 @@ MediaManager::GetUserMedia(nsPIDOMWindow
       const char *unset = EnumToASCII(dom::MediaSourceEnumValues::strings,
                                       dom::MediaSourceEnum::Camera);
       for (MediaTrackConstraintSet& cs : ac.mAdvanced.Value()) {
         if (cs.mMediaSource.EqualsASCII(unset)) {
           cs.mMediaSource = ac.mMediaSource;
         }
       }
     }
+  } else if (IsOn(c.mAudio)) {
+   audioType = dom::MediaSourceEnum::Microphone;
   }
+
   StreamListeners* listeners = AddWindowID(windowID);
 
   // Create a disabled listener to act as a placeholder
   RefPtr<GetUserMediaCallbackMediaStreamListener> listener =
     new GetUserMediaCallbackMediaStreamListener(mMediaThread, windowID);
 
   // No need for locking because we always do this in the main thread.
   listeners->AppendElement(listener);
@@ -2083,18 +2099,18 @@ MediaManager::GetUserMedia(nsPIDOMWindow
       Preferences::GetBool("media.navigator.streams.fake");
 
   bool fakeTracks = c.mFakeTracks.WasPassed()? c.mFakeTracks.Value() : false;
 
   bool askPermission = !privileged &&
       (!fake || Preferences::GetBool("media.navigator.permission.fake"));
 
   RefPtr<PledgeSourceSet> p = EnumerateDevicesImpl(windowID, videoType,
-                                                     audioType, fake,
-                                                     fakeTracks);
+                                                   audioType, fake,
+                                                   fakeTracks);
   p->Then([this, onSuccess, onFailure, windowID, c, listener, askPermission,
            prefs, isHTTPS, callID, origin](SourceSet*& aDevices) mutable {
 
     RefPtr<Refcountable<ScopedDeletePtr<SourceSet>>> devices(
          new Refcountable<ScopedDeletePtr<SourceSet>>(aDevices)); // grab result
 
     // Ensure that the captured 'this' pointer and our windowID are still good.
     if (!MediaManager::Exists() ||
@@ -2304,18 +2320,18 @@ MediaManager::EnumerateDevicesImpl(uint6
   RefPtr<Pledge<nsCString>> p = media::GetOriginKey(origin, privateBrowsing,
                                                       persist);
   p->Then([id, aWindowId, aVideoType, aAudioType,
            aFake, aFakeTracks](const nsCString& aOriginKey) mutable {
     MOZ_ASSERT(NS_IsMainThread());
     RefPtr<MediaManager> mgr = MediaManager_GetInstance();
 
     RefPtr<PledgeSourceSet> p = mgr->EnumerateRawDevices(aWindowId,
-                                                           aVideoType, aAudioType,
-                                                           aFake, aFakeTracks);
+                                                         aVideoType, aAudioType,
+                                                         aFake, aFakeTracks);
     p->Then([id, aWindowId, aOriginKey](SourceSet*& aDevices) mutable {
       ScopedDeletePtr<SourceSet> devices(aDevices); // secondary result
 
       // Only run if window is still on our active list.
       RefPtr<MediaManager> mgr = MediaManager_GetInstance();
       if (!mgr) {
         return NS_OK;
       }
--- a/dom/media/mediasource/MediaSourceDemuxer.cpp
+++ b/dom/media/mediasource/MediaSourceDemuxer.cpp
@@ -290,16 +290,17 @@ MediaSourceDemuxer::GetMozDebugReaderDat
 
 MediaSourceTrackDemuxer::MediaSourceTrackDemuxer(MediaSourceDemuxer* aParent,
                                                  TrackInfo::TrackType aType,
                                                  TrackBuffersManager* aManager)
   : mParent(aParent)
   , mManager(aManager)
   , mType(aType)
   , mMonitor("MediaSourceTrackDemuxer")
+  , mLastSeek(Some(TimeUnit()))
 {
 }
 
 UniquePtr<TrackInfo>
 MediaSourceTrackDemuxer::GetInfo() const
 {
   return mParent->GetTrackInfo(mType)->Clone();
 }
@@ -322,16 +323,17 @@ MediaSourceTrackDemuxer::GetSamples(int3
 
 void
 MediaSourceTrackDemuxer::Reset()
 {
   MOZ_ASSERT(mParent, "Called after BreackCycle()");
   RefPtr<MediaSourceTrackDemuxer> self = this;
   nsCOMPtr<nsIRunnable> task =
     NS_NewRunnableFunction([self] () {
+      self->mLastSeek = Some(TimeUnit());
       self->mManager->Seek(self->mType, TimeUnit(), TimeUnit());
       {
         MonitorAutoLock mon(self->mMonitor);
         self->mNextRandomAccessPoint =
           self->mManager->GetNextRandomAccessPoint(self->mType);
       }
     });
   mParent->GetTaskQueue()->Dispatch(task.forget());
@@ -373,32 +375,47 @@ MediaSourceTrackDemuxer::BreakCycles()
 
 RefPtr<MediaSourceTrackDemuxer::SeekPromise>
 MediaSourceTrackDemuxer::DoSeek(media::TimeUnit aTime)
 {
   TimeIntervals buffered = mManager->Buffered(mType);
   buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ);
 
   if (!buffered.Contains(aTime)) {
+    mLastSeek = Some(aTime);
     // We don't have the data to seek to.
     return SeekPromise::CreateAndReject(DemuxerFailureReason::WAITING_FOR_DATA,
                                         __func__);
   }
   TimeUnit seekTime =
     mManager->Seek(mType, aTime, MediaSourceDemuxer::EOS_FUZZ);
   {
     MonitorAutoLock mon(mMonitor);
     mNextRandomAccessPoint = mManager->GetNextRandomAccessPoint(mType);
   }
+  mLastSeek = Some(aTime);
   return SeekPromise::CreateAndResolve(seekTime, __func__);
 }
 
 RefPtr<MediaSourceTrackDemuxer::SamplesPromise>
 MediaSourceTrackDemuxer::DoGetSamples(int32_t aNumSamples)
 {
+  if (mLastSeek) {
+    // If a seek (or reset) was recently performed, we ensure that the data
+    // we are about to retrieve is still available.
+    TimeIntervals buffered = mManager->Buffered(mType);
+    buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ);
+
+    if (!buffered.Contains(mLastSeek.ref())) {
+      return SamplesPromise::CreateAndReject(
+        mManager->IsEnded() ? DemuxerFailureReason::END_OF_STREAM :
+                              DemuxerFailureReason::WAITING_FOR_DATA, __func__);
+    }
+    mLastSeek.reset();
+  }
   bool error;
   RefPtr<MediaRawData> sample =
     mManager->GetSample(mType,
                         MediaSourceDemuxer::EOS_FUZZ,
                         error);
   if (!sample) {
     if (error) {
       return SamplesPromise::CreateAndReject(DemuxerFailureReason::DEMUXER_ERROR, __func__);
--- a/dom/media/mediasource/MediaSourceDemuxer.h
+++ b/dom/media/mediasource/MediaSourceDemuxer.h
@@ -124,13 +124,14 @@ private:
   media::TimeUnit GetNextRandomAccessPoint();
 
   RefPtr<MediaSourceDemuxer> mParent;
   RefPtr<TrackBuffersManager> mManager;
   TrackInfo::TrackType mType;
   // Monitor protecting members below accessed from multiple threads.
   Monitor mMonitor;
   media::TimeUnit mNextRandomAccessPoint;
+  Maybe<media::TimeUnit> mLastSeek;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -89,16 +89,18 @@ XPIDL_SOURCES += [
     'nsIDOMNavigatorUserMedia.idl',
     'nsIMediaManager.idl',
 ]
 
 XPIDL_MODULE = 'dom_media'
 
 EXPORTS += [
     'AbstractMediaDecoder.h',
+    'ADTSDecoder.h',
+    'ADTSDemuxer.h',
     'AudioBufferUtils.h',
     'AudioChannelFormat.h',
     'AudioCompactor.h',
     'AudioMixer.h',
     'AudioPacketizer.h',
     'AudioSampleFormat.h',
     'AudioSegment.h',
     'AudioStream.h',
@@ -185,16 +187,18 @@ EXPORTS.mozilla.dom += [
     'TextTrackRegion.h',
     'VideoPlaybackQuality.h',
     'VideoStreamTrack.h',
     'VideoTrack.h',
     'VideoTrackList.h',
 ]
 
 UNIFIED_SOURCES += [
+    'ADTSDecoder.cpp',
+    'ADTSDemuxer.cpp',
     'AudioCaptureStream.cpp',
     'AudioChannelFormat.cpp',
     'AudioCompactor.cpp',
     'AudioSegment.cpp',
     'AudioStream.cpp',
     'AudioStreamTrack.cpp',
     'AudioTrack.cpp',
     'AudioTrackList.cpp',
--- a/dom/media/platforms/agnostic/BlankDecoderModule.cpp
+++ b/dom/media/platforms/agnostic/BlankDecoderModule.cpp
@@ -7,16 +7,17 @@
 #include "ImageContainer.h"
 #include "MediaDecoderReader.h"
 #include "MediaInfo.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/mozalloc.h" // for operator new, and new (fallible)
 #include "mozilla/RefPtr.h"
 #include "mozilla/TaskQueue.h"
 #include "mozilla/UniquePtr.h"
+#include "mozilla/UniquePtrExtensions.h"
 #include "nsRect.h"
 #include "PlatformDecoderModule.h"
 #include "TimeUnits.h"
 #include "VideoUtils.h"
 
 namespace mozilla {
 
 // Decoder that uses a passed in object's Create function to create blank
@@ -183,18 +184,18 @@ public:
     CheckedInt64 frames =
       UsecsToFrames(aDuration.ToMicroseconds()+1, mSampleRate);
     if (!frames.isValid() ||
         !mChannelCount ||
         !mSampleRate ||
         frames.value() > (UINT32_MAX / mChannelCount)) {
       return nullptr;
     }
-    UniquePtr<AudioDataValue[]> samples(
-      new (fallible) AudioDataValue[frames.value() * mChannelCount]);
+    auto samples =
+      MakeUniqueFallible<AudioDataValue[]>(frames.value() * mChannelCount);
     if (!samples) {
       return nullptr;
     }
     // Fill the sound buffer with an A4 tone.
     static const float pi = 3.14159265f;
     static const float noteHz = 440.0f;
     for (int i = 0; i < frames.value(); i++) {
       float f = sin(2 * pi * noteHz * mFrameSum / mSampleRate);
--- a/dom/media/platforms/gonk/GonkVideoDecoderManager.cpp
+++ b/dom/media/platforms/gonk/GonkVideoDecoderManager.cpp
@@ -36,16 +36,69 @@
 extern mozilla::LogModule* GetPDMLog();
 #define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
 using namespace mozilla::layers;
 using namespace android;
 typedef android::MediaCodecProxy MediaCodecProxy;
 
 namespace mozilla {
 
+class GonkTextureClientAllocationHelper : public layers::ITextureClientAllocationHelper
+{
+public:
+  GonkTextureClientAllocationHelper(uint32_t aGrallocFormat,
+                                    gfx::IntSize aSize)
+    : ITextureClientAllocationHelper(gfx::SurfaceFormat::UNKNOWN,
+                                     aSize,
+                                     BackendSelector::Content,
+                                     TextureFlags::DEALLOCATE_CLIENT,
+                                     ALLOC_DISALLOW_BUFFERTEXTURECLIENT)
+    , mGrallocFormat(aGrallocFormat)
+  {}
+
+  already_AddRefed<TextureClient> Allocate(CompositableForwarder* aAllocator) override
+  {
+    uint32_t usage = android::GraphicBuffer::USAGE_SW_READ_OFTEN |
+                     android::GraphicBuffer::USAGE_SW_WRITE_OFTEN |
+                     android::GraphicBuffer::USAGE_HW_TEXTURE;
+
+    GrallocTextureData* texData = GrallocTextureData::Create(mSize, mGrallocFormat,
+                                                             gfx::BackendType::NONE,
+                                                             usage, aAllocator);
+    if (!texData) {
+      return nullptr;
+    }
+    sp<GraphicBuffer> graphicBuffer = texData->GetGraphicBuffer();
+    if (!graphicBuffer.get()) {
+      return nullptr;
+    }
+    RefPtr<TextureClient> textureClient =
+      TextureClient::CreateWithData(texData, TextureFlags::DEALLOCATE_CLIENT, aAllocator);
+    return textureClient.forget();
+  }
+
+  bool IsCompatible(TextureClient* aTextureClient) override
+  {
+    if (!aTextureClient) {
+      return false;
+    }
+    sp<GraphicBuffer> graphicBuffer =
+      static_cast<GrallocTextureData*>(aTextureClient->GetInternalData())->GetGraphicBuffer();
+    if (!graphicBuffer.get() ||
+        static_cast<uint32_t>(graphicBuffer->getPixelFormat()) != mGrallocFormat ||
+        aTextureClient->GetSize() != mSize) {
+      return false;
+    }
+    return true;
+  }
+
+private:
+  uint32_t mGrallocFormat;
+};
+
 GonkVideoDecoderManager::GonkVideoDecoderManager(
   mozilla::layers::ImageContainer* aImageContainer,
   const VideoInfo& aConfig)
   : mImageContainer(aImageContainer)
   , mColorConverterBufferSize(0)
   , mPendingReleaseItemsLock("GonkVideoDecoderManager::mPendingReleaseItemsLock")
   , mNeedsCopyBuffer(false)
 {
@@ -264,84 +317,105 @@ CopyYUV(PlanarYCbCrData& aSource, Planar
 }
 
 inline static int
 Align(int aX, int aAlign)
 {
   return (aX + aAlign - 1) & ~(aAlign - 1);
 }
 
+// Venus formats are doucmented in kernel/include/media/msm_media_info.h:
+// * Y_Stride : Width aligned to 128
+// * UV_Stride : Width aligned to 128
+// * Y_Scanlines: Height aligned to 32
+// * UV_Scanlines: Height/2 aligned to 16
+// * Total size = align((Y_Stride * Y_Scanlines
+// *          + UV_Stride * UV_Scanlines + 4096), 4096)
+static void
+CopyVenus(uint8_t* aSrc, uint8_t* aDest, uint32_t aWidth, uint32_t aHeight)
+{
+  size_t yStride = Align(aWidth, 128);
+  uint8_t* s = aSrc;
+  uint8_t* d = aDest;
+  for (size_t i = 0; i < aHeight; i++) {
+    memcpy(d, s, aWidth);
+    s += yStride;
+    d += yStride;
+  }
+  size_t uvStride = yStride;
+  size_t uvLines = (aHeight + 1) / 2;
+  size_t ySize = yStride * Align(aHeight, 32);
+  s = aSrc + ySize;
+  d = aDest + ySize;
+  for (size_t i = 0; i < uvLines; i++) {
+    memcpy(d, s, aWidth);
+    s += uvStride;
+    d += uvStride;
+  }
+}
+
 static void
 CopyGraphicBuffer(sp<GraphicBuffer>& aSource, sp<GraphicBuffer>& aDestination)
 {
   void* srcPtr = nullptr;
   aSource->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, &srcPtr);
   void* destPtr = nullptr;
   aDestination->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN, &destPtr);
   MOZ_ASSERT(srcPtr && destPtr);
 
   // Build PlanarYCbCrData for source buffer.
   PlanarYCbCrData srcData;
   switch (aSource->getPixelFormat()) {
-    case HAL_PIXEL_FORMAT_YV12:
+    case HAL_PIXEL_FORMAT_YV12: {
       // Android YV12 format is defined in system/core/include/system/graphics.h
       srcData.mYChannel = static_cast<uint8_t*>(srcPtr);
       srcData.mYSkip = 0;
       srcData.mYSize.width = aSource->getWidth();
       srcData.mYSize.height = aSource->getHeight();
       srcData.mYStride = aSource->getStride();
       // 4:2:0.
       srcData.mCbCrSize.width = srcData.mYSize.width / 2;
       srcData.mCbCrSize.height = srcData.mYSize.height / 2;
       srcData.mCrChannel = srcData.mYChannel + (srcData.mYStride * srcData.mYSize.height);
       // Aligned to 16 bytes boundary.
       srcData.mCbCrStride = Align(srcData.mYStride / 2, 16);
       srcData.mCrSkip = 0;
       srcData.mCbChannel = srcData.mCrChannel + (srcData.mCbCrStride * srcData.mCbCrSize.height);
       srcData.mCbSkip = 0;
+
+      // Build PlanarYCbCrData for destination buffer.
+      PlanarYCbCrData destData;
+      destData.mYChannel = static_cast<uint8_t*>(destPtr);
+      destData.mYSkip = 0;
+      destData.mYSize.width = aDestination->getWidth();
+      destData.mYSize.height = aDestination->getHeight();
+      destData.mYStride = aDestination->getStride();
+      // 4:2:0.
+      destData.mCbCrSize.width = destData.mYSize.width / 2;
+      destData.mCbCrSize.height = destData.mYSize.height / 2;
+      destData.mCrChannel = destData.mYChannel + (destData.mYStride * destData.mYSize.height);
+      // Aligned to 16 bytes boundary.
+      destData.mCbCrStride = Align(destData.mYStride / 2, 16);
+      destData.mCrSkip = 0;
+      destData.mCbChannel = destData.mCrChannel + (destData.mCbCrStride * destData.mCbCrSize.height);
+      destData.mCbSkip = 0;
+
+      CopyYUV(srcData, destData);
       break;
+    }
     case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS:
-      // Venus formats are doucmented in kernel/include/media/msm_media_info.h:
-      srcData.mYChannel = static_cast<uint8_t*>(srcPtr);
-      srcData.mYSkip = 0;
-      srcData.mYSize.width = aSource->getWidth();
-      srcData.mYSize.height = aSource->getHeight();
-      // - Y & UV Width aligned to 128
-      srcData.mYStride = aSource->getStride();
-      srcData.mCbCrSize.width = (srcData.mYSize.width + 1) / 2;
-      srcData.mCbCrSize.height = (srcData.mYSize.height + 1) / 2;
-      // - Y height aligned to 32
-      srcData.mCbChannel = srcData.mYChannel + (srcData.mYStride * Align(srcData.mYSize.height, 32));
-      // Interleaved VU plane.
-      srcData.mCbSkip = 1;
-      srcData.mCrChannel = srcData.mCbChannel + 1;
-      srcData.mCrSkip = 1;
-      srcData.mCbCrStride = srcData.mYStride;
+      CopyVenus(static_cast<uint8_t*>(srcPtr),
+                static_cast<uint8_t*>(destPtr),
+                aSource->getWidth(),
+                aSource->getHeight());
       break;
     default:
       NS_ERROR("Unsupported input gralloc image type. Should never be here.");
   }
-  // Build PlanarYCbCrData for destination buffer.
-  PlanarYCbCrData destData;
-  destData.mYChannel = static_cast<uint8_t*>(destPtr);
-  destData.mYSkip = 0;
-  destData.mYSize.width = aDestination->getWidth();
-  destData.mYSize.height = aDestination->getHeight();
-  destData.mYStride = aDestination->getStride();
-  // 4:2:0.
-  destData.mCbCrSize.width = destData.mYSize.width / 2;
-  destData.mCbCrSize.height = destData.mYSize.height / 2;
-  destData.mCrChannel = destData.mYChannel + (destData.mYStride * destData.mYSize.height);
-  // Aligned to 16 bytes boundary.
-  destData.mCbCrStride = Align(destData.mYStride / 2, 16);
-  destData.mCrSkip = 0;
-  destData.mCbChannel = destData.mCrChannel + (destData.mCbCrStride * destData.mCbCrSize.height);
-  destData.mCbSkip = 0;
 
-  CopyYUV(srcData, destData);
 
   aSource->unlock();
   aDestination->unlock();
 }
 
 already_AddRefed<VideoData>
 GonkVideoDecoderManager::CreateVideoDataFromGraphicBuffer(MediaBuffer* aSource,
                                                           gfx::IntRect& aPicture)
@@ -354,29 +428,23 @@ GonkVideoDecoderManager::CreateVideoData
     if (!mCopyAllocator) {
       mCopyAllocator = new TextureClientRecycleAllocator(ImageBridgeChild::GetSingleton());
     }
     if (!mCopyAllocator) {
       GVDM_LOG("Create buffer allocator failed!");
       return nullptr;
     }
 
-    gfx::IntSize size(Align(aPicture.width, 2) , Align(aPicture.height, 2));
-    textureClient =
-      mCopyAllocator->CreateOrRecycle(gfx::SurfaceFormat::YUV, size,
-                                      BackendSelector::Content,
-                                      TextureFlags::DEFAULT,
-                                      ALLOC_DISALLOW_BUFFERTEXTURECLIENT);
+    gfx::IntSize size(srcBuffer->getWidth(), srcBuffer->getHeight());
+    GonkTextureClientAllocationHelper helper(srcBuffer->getPixelFormat(), size);
+    textureClient = mCopyAllocator->CreateOrRecycle(helper);
     if (!textureClient) {
       GVDM_LOG("Copy buffer allocation failed!");
       return nullptr;
     }
-    // Update size to match buffer's.
-    aPicture.width = size.width;
-    aPicture.height = size.height;
 
     sp<GraphicBuffer> destBuffer =
       static_cast<GrallocTextureData*>(textureClient->GetInternalData())->GetGraphicBuffer();
 
     CopyGraphicBuffer(srcBuffer, destBuffer);
   } else {
     textureClient = mNativeWindow->getTextureClientFromBuffer(srcBuffer.get());
     textureClient->SetRecycleCallback(GonkVideoDecoderManager::RecycleCallback, this);
--- a/dom/media/test/test_can_play_type_mpeg.html
+++ b/dom/media/test/test_can_play_type_mpeg.html
@@ -28,19 +28,19 @@ function check_mp4(v, enabled) {
   check("video/mp4", "maybe");
   check("video/x-m4v", "maybe");
   check("audio/mp4", "maybe");
   check("audio/x-m4a", "maybe");
 
   // Not the MIME type that other browsers respond to, so we won't either.
   check("audio/m4a", "");
   check("video/m4v", "");
-  // Only Safari responds affirmatively to "audio/aac",
-  // so we'll let x-m4a cover aac support.
-  check("audio/aac", "");
+
+  check("audio/aac", "maybe");
+  check("audio/aacp", "maybe");
 
   // H.264 Constrained Baseline Profile Level 3.0, AAC-LC
   check("video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\"", "probably");
 
   // H.264 Constrained Baseline Profile Level 3.0, mp3
   check("video/mp4; codecs=\"avc1.42E01E, mp3\"", "probably");
 
   check("video/mp4; codecs=\"avc1.42001E, mp4a.40.2\"", "probably");
--- a/dom/media/webrtc/MediaTrackConstraints.h
+++ b/dom/media/webrtc/MediaTrackConstraints.h
@@ -112,28 +112,29 @@ protected:
 
   static uint32_t
   GetMinimumFitnessDistance(const dom::MediaTrackConstraintSet &aConstraints,
                             bool aAdvanced,
                             const nsString& aDeviceId);
 
   template<class DeviceType>
   static bool
-  AreUnfitSettings(const dom::MediaTrackConstraints &aConstraints,
-                   nsTArray<RefPtr<DeviceType>>& aSources)
+  SomeSettingsFit(const dom::MediaTrackConstraints &aConstraints,
+                  nsTArray<RefPtr<DeviceType>>& aSources)
   {
     nsTArray<const dom::MediaTrackConstraintSet*> aggregateConstraints;
     aggregateConstraints.AppendElement(&aConstraints);
 
+    MOZ_ASSERT(aSources.Length());
     for (auto& source : aSources) {
       if (source->GetBestFitnessDistance(aggregateConstraints) != UINT32_MAX) {
-        return false;
+        return true;
       }
     }
-    return true;
+    return false;
   }
 
 public:
   // Apply constrains to a supplied list of sources (removes items from the list)
 
   template<class DeviceType>
   static const char*
   SelectSettings(const dom::MediaTrackConstraints &aConstraints,
@@ -163,48 +164,52 @@ public:
         ++i;
       }
     }
     if (!aSources.Length()) {
       // None selected. The spec says to report a constraint that satisfies NONE
       // of the sources. Unfortunately, this is a bit laborious to find out, and
       // requires updating as new constraints are added!
 
+      if (!unsatisfactory.Length() ||
+          !SomeSettingsFit(dom::MediaTrackConstraints(), unsatisfactory)) {
+        return "";
+      }
       if (c.mDeviceId.IsConstrainDOMStringParameters()) {
         dom::MediaTrackConstraints fresh;
         fresh.mDeviceId = c.mDeviceId;
-        if (AreUnfitSettings(fresh, unsatisfactory)) {
+        if (!SomeSettingsFit(fresh, unsatisfactory)) {
           return "deviceId";
         }
       }
       if (c.mWidth.IsConstrainLongRange()) {
         dom::MediaTrackConstraints fresh;
         fresh.mWidth = c.mWidth;
-        if (AreUnfitSettings(fresh, unsatisfactory)) {
+        if (!SomeSettingsFit(fresh, unsatisfactory)) {
           return "width";
         }
       }
       if (c.mHeight.IsConstrainLongRange()) {
         dom::MediaTrackConstraints fresh;
         fresh.mHeight = c.mHeight;
-        if (AreUnfitSettings(fresh, unsatisfactory)) {
+        if (!SomeSettingsFit(fresh, unsatisfactory)) {
           return "height";
         }
       }
       if (c.mFrameRate.IsConstrainDoubleRange()) {
         dom::MediaTrackConstraints fresh;
         fresh.mFrameRate = c.mFrameRate;
-        if (AreUnfitSettings(fresh, unsatisfactory)) {
+        if (!SomeSettingsFit(fresh, unsatisfactory)) {
           return "frameRate";
         }
       }
       if (c.mFacingMode.IsConstrainDOMStringParameters()) {
         dom::MediaTrackConstraints fresh;
         fresh.mFacingMode = c.mFacingMode;
-        if (AreUnfitSettings(fresh, unsatisfactory)) {
+        if (!SomeSettingsFit(fresh, unsatisfactory)) {
           return "facingMode";
         }
       }
       return "";
     }
 
     // Order devices by shortest distance
     for (auto& ordinal : ordered) {
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -3379,17 +3379,18 @@ nsresult nsPluginHost::NewPluginURLStrea
   // form |nsDocShell::OnLinkClickSync| bug 166613
   rv = NS_NewChannel(getter_AddRefs(channel),
                      url,
                      requestingNode,
                      nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS |
                      nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
                      nsIContentPolicy::TYPE_OBJECT_SUBREQUEST,
                      nullptr,  // aLoadGroup
-                     listenerPeer);
+                     listenerPeer,
+                     nsIRequest::LOAD_NORMAL | nsIChannel::LOAD_CLASSIFY_URI);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (doc) {
     // And if it's a script allow it to execute against the
     // document's script context.
     nsCOMPtr<nsIScriptChannel> scriptChannel(do_QueryInterface(channel));
     if (scriptChannel) {
       scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
--- a/dom/security/nsContentSecurityManager.cpp
+++ b/dom/security/nsContentSecurityManager.cpp
@@ -3,38 +3,34 @@
 #include "nsIStreamListener.h"
 #include "nsILoadInfo.h"
 #include "nsContentUtils.h"
 #include "nsCORSListenerProxy.h"
 #include "nsIStreamListener.h"
 
 #include "mozilla/dom/Element.h"
 
-NS_IMPL_ISUPPORTS(nsContentSecurityManager, nsIContentSecurityManager)
+NS_IMPL_ISUPPORTS(nsContentSecurityManager,
+                  nsIContentSecurityManager,
+                  nsIChannelEventSink)
 
 static nsresult
 ValidateSecurityFlags(nsILoadInfo* aLoadInfo)
 {
   nsSecurityFlags securityMode = aLoadInfo->GetSecurityMode();
 
   if (securityMode != nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS &&
       securityMode != nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED &&
       securityMode != nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS &&
       securityMode != nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL &&
       securityMode != nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
     MOZ_ASSERT(false, "need one securityflag from nsILoadInfo to perform security checks");
     return NS_ERROR_FAILURE;
   }
 
-  // make sure that cors-with-credentials is only used in combination with CORS.
-  if (aLoadInfo->GetRequireCorsWithCredentials() &&
-      securityMode != nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
-    MOZ_ASSERT(false, "can not use cors-with-credentials without cors");
-    return NS_ERROR_FAILURE;
-  }
   // all good, found the right security flags
   return NS_OK;
 }
 
 static bool SchemeIs(nsIURI* aURI, const char* aScheme)
 {
   nsCOMPtr<nsIURI> baseURI = NS_GetInnermostURI(aURI);
   NS_ENSURE_TRUE(baseURI, false);
@@ -84,50 +80,42 @@ URIHasFlags(nsIURI* aURI, uint32_t aURIF
   bool hasFlags;
   nsresult rv = NS_URIChainHasFlags(aURI, aURIFlags, &hasFlags);
   NS_ENSURE_SUCCESS(rv, false);
 
   return hasFlags;
 }
 
 static nsresult
-DoSOPChecks(nsIURI* aURI, nsILoadInfo* aLoadInfo)
+DoSOPChecks(nsIURI* aURI, nsILoadInfo* aLoadInfo, nsIChannel* aChannel)
 {
   if (aLoadInfo->GetAllowChrome() &&
       (URIHasFlags(aURI, nsIProtocolHandler::URI_IS_UI_RESOURCE) ||
        SchemeIs(aURI, "moz-safe-about"))) {
     // UI resources are allowed.
     return DoCheckLoadURIChecks(aURI, aLoadInfo);
   }
 
-  nsIPrincipal* loadingPrincipal = aLoadInfo->LoadingPrincipal();
-  bool sameOriginDataInherits =
-    aLoadInfo->GetSecurityMode() == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS;
+  NS_ENSURE_FALSE(NS_HasBeenCrossOrigin(aChannel, true),
+                  NS_ERROR_DOM_BAD_URI);
 
-  if (sameOriginDataInherits &&
-      aLoadInfo->GetAboutBlankInherits() &&
-      NS_IsAboutBlank(aURI)) {
-    return NS_OK;
-  }
-
-  return loadingPrincipal->CheckMayLoad(aURI,
-                                        true, // report to console
-                                        sameOriginDataInherits);
+  return NS_OK;
 }
 
 static nsresult
 DoCORSChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo,
              nsCOMPtr<nsIStreamListener>& aInAndOutListener)
 {
   MOZ_RELEASE_ASSERT(aInAndOutListener, "can not perform CORS checks without a listener");
   nsIPrincipal* loadingPrincipal = aLoadInfo->LoadingPrincipal();
   RefPtr<nsCORSListenerProxy> corsListener =
     new nsCORSListenerProxy(aInAndOutListener,
                             loadingPrincipal,
-                            aLoadInfo->GetRequireCorsWithCredentials());
+                            aLoadInfo->GetCookiePolicy() ==
+                              nsILoadInfo::SEC_COOKIES_INCLUDE);
   // XXX: @arg: DataURIHandling::Allow
   // lets use  DataURIHandling::Allow for now and then decide on callsite basis. see also:
   // http://mxr.mozilla.org/mozilla-central/source/dom/security/nsCORSListenerProxy.h#33
   nsresult rv = corsListener->Init(aChannel, DataURIHandling::Allow);
   NS_ENSURE_SUCCESS(rv, rv);
   aInAndOutListener = corsListener;
   return NS_OK;
 }
@@ -337,82 +325,174 @@ nsContentSecurityManager::doContentSecur
   NS_ENSURE_ARG(aChannel);
   nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
 
   if (!loadInfo) {
     MOZ_ASSERT(false, "channel needs to have loadInfo to perform security checks");
     return NS_ERROR_UNEXPECTED;
   }
 
+  // if dealing with a redirected channel then we have already installed
+  // streamlistener and redirect proxies and so we are done.
+  if (loadInfo->GetInitialSecurityCheckDone()) {
+    return NS_OK;
+  }
+
   // make sure that only one of the five security flags is set in the loadinfo
   // e.g. do not require same origin and allow cross origin at the same time
   nsresult rv = ValidateSecurityFlags(loadInfo);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // lets store the initialSecurityCheckDone flag which indicates whether the channel
-  // was initialy evaluated by the contentSecurityManager. Once the inital
-  // asyncOpen() of the channel went through the contentSecurityManager then
-  // redirects do not have perform all the security checks, e.g. no reason
-  // to setup CORS again.
-  bool initialSecurityCheckDone = loadInfo->GetInitialSecurityCheckDone();
-
-  // now lets set the initalSecurityFlag for subsequent calls
-  rv = loadInfo->SetInitialSecurityCheckDone(true);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   // since aChannel was openend using asyncOpen2() we have to make sure
   // that redirects of that channel also get openend using asyncOpen2()
   // please note that some implementations of ::AsyncOpen2 might already
   // have set that flag to true (e.g. nsViewSourceChannel) in which case
   // we just set the flag again.
   rv = loadInfo->SetEnforceSecurity(true);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  if (loadInfo->GetSecurityMode() == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
+    rv = DoCORSChecks(aChannel, loadInfo, aInAndOutListener);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  rv = CheckChannel(aChannel);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   nsCOMPtr<nsIURI> finalChannelURI;
   rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(finalChannelURI));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsSecurityFlags securityMode = loadInfo->GetSecurityMode();
-
-  // if none of the REQUIRE_SAME_ORIGIN flags are set, then SOP does not apply
-  if ((securityMode == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS) ||
-      (securityMode == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED)) {
-    rv = DoSOPChecks(finalChannelURI, loadInfo);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  // if dealing with a redirected channel then we only enforce SOP
-  // and can return at this point.
-  if (initialSecurityCheckDone) {
-    return NS_OK;
-  }
-
-  if ((securityMode == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS) ||
-      (securityMode == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL)) {
-    // Please note that DoCheckLoadURIChecks should only be enforced for
-    // cross origin requests. If the flag SEC_REQUIRE_CORS_DATA_INHERITS is set
-    // within the loadInfo, then then CheckLoadURIWithPrincipal is performed
-    // within nsCorsListenerProxy
-    rv = DoCheckLoadURIChecks(finalChannelURI, loadInfo);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  if (securityMode == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
-    rv = DoCORSChecks(aChannel, loadInfo, aInAndOutListener);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
   // Perform all ContentPolicy checks (MixedContent, CSP, ...)
   rv = DoContentSecurityChecks(finalChannelURI, loadInfo);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  // now lets set the initalSecurityFlag for subsequent calls
+  rv = loadInfo->SetInitialSecurityCheckDone(true);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   // all security checks passed - lets allow the load
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsContentSecurityManager::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
+                                                 nsIChannel* aNewChannel,
+                                                 uint32_t aRedirFlags,
+                                                 nsIAsyncVerifyRedirectCallback *aCb)
+{
+  nsCOMPtr<nsILoadInfo> loadInfo = aOldChannel->GetLoadInfo();
+  // Are we enforcing security using LoadInfo?
+  if (loadInfo && loadInfo->GetEnforceSecurity()) {
+    nsresult rv = CheckChannel(aNewChannel);
+    if (NS_FAILED(rv)) {
+      aOldChannel->Cancel(rv);
+      return rv;
+    }
+  }
+
+  // Also verify that the redirecting server is allowed to redirect to the
+  // given URI
+  nsCOMPtr<nsIPrincipal> oldPrincipal;
+  nsContentUtils::GetSecurityManager()->
+    GetChannelResultPrincipal(aOldChannel, getter_AddRefs(oldPrincipal));
+
+  nsCOMPtr<nsIURI> newURI;
+  aNewChannel->GetURI(getter_AddRefs(newURI));
+  nsCOMPtr<nsIURI> newOriginalURI;
+  aNewChannel->GetOriginalURI(getter_AddRefs(newOriginalURI));
+
+  NS_ENSURE_STATE(oldPrincipal && newURI && newOriginalURI);
+
+  const uint32_t flags =
+      nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT |
+      nsIScriptSecurityManager::DISALLOW_SCRIPT;
+  nsresult rv = nsContentUtils::GetSecurityManager()->
+    CheckLoadURIWithPrincipal(oldPrincipal, newURI, flags);
+  if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) {
+      rv = nsContentUtils::GetSecurityManager()->
+        CheckLoadURIWithPrincipal(oldPrincipal, newOriginalURI, flags);
+  }
+  NS_ENSURE_SUCCESS(rv, rv);  
+
+  aCb->OnRedirectVerifyCallback(NS_OK);
+  return NS_OK;
+}
+
+static void
+AddLoadFlags(nsIRequest *aRequest, nsLoadFlags aNewFlags)
+{
+  nsLoadFlags flags;
+  aRequest->GetLoadFlags(&flags);
+  flags |= aNewFlags;
+  aRequest->SetLoadFlags(flags);
+}
+
+/*
+ * Check that this channel passes all security checks. Returns an error code
+ * if this requesst should not be permitted.
+ */
+nsresult
+nsContentSecurityManager::CheckChannel(nsIChannel* aChannel)
+{
+  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
+  MOZ_ASSERT(loadInfo);
+
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Handle cookie policies
+  uint32_t cookiePolicy = loadInfo->GetCookiePolicy();
+  if (cookiePolicy == nsILoadInfo::SEC_COOKIES_SAME_ORIGIN) {
+    nsIPrincipal* loadingPrincipal = loadInfo->LoadingPrincipal();
+
+    // It doesn't matter what we pass for the third, data-inherits, argument.
+    // Any protocol which inherits won't pay attention to cookies anyway.
+    rv = loadingPrincipal->CheckMayLoad(uri, false, false);
+    if (NS_FAILED(rv)) {
+      AddLoadFlags(aChannel, nsIRequest::LOAD_ANONYMOUS);
+    }
+  }
+  else if (cookiePolicy == nsILoadInfo::SEC_COOKIES_OMIT) {
+    AddLoadFlags(aChannel, nsIRequest::LOAD_ANONYMOUS);
+  }
+
+  nsSecurityFlags securityMode = loadInfo->GetSecurityMode();
+
+  // CORS mode is handled by nsCORSListenerProxy
+  if (securityMode == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
+    if (NS_HasBeenCrossOrigin(aChannel)) {
+      loadInfo->MaybeIncreaseTainting(LoadTainting::CORS);
+    }
+    return NS_OK;
+  }
+
+  // if none of the REQUIRE_SAME_ORIGIN flags are set, then SOP does not apply
+  if ((securityMode == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS) ||
+      (securityMode == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED)) {
+    rv = DoSOPChecks(uri, loadInfo, aChannel);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  if ((securityMode == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS) ||
+      (securityMode == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL)) {
+    if (NS_HasBeenCrossOrigin(aChannel)) {
+      loadInfo->MaybeIncreaseTainting(LoadTainting::Opaque);
+    }
+    // Please note that DoCheckLoadURIChecks should only be enforced for
+    // cross origin requests. If the flag SEC_REQUIRE_CORS_DATA_INHERITS is set
+    // within the loadInfo, then then CheckLoadURIWithPrincipal is performed
+    // within nsCorsListenerProxy
+    rv = DoCheckLoadURIChecks(uri, loadInfo);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  return NS_OK;
+}
 
 // ==== nsIContentSecurityManager implementation =====
 
 NS_IMETHODIMP
 nsContentSecurityManager::PerformSecurityCheck(nsIChannel* aChannel,
                                                nsIStreamListener* aStreamListener,
                                                nsIStreamListener** outStreamListener)
 {
--- a/dom/security/nsContentSecurityManager.h
+++ b/dom/security/nsContentSecurityManager.h
@@ -4,34 +4,39 @@
  * 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 nsContentSecurityManager_h___
 #define nsContentSecurityManager_h___
 
 #include "nsIContentSecurityManager.h"
 #include "nsIChannel.h"
+#include "nsIChannelEventSink.h"
 
 class nsIStreamListener;
 
 #define NS_CONTENTSECURITYMANAGER_CONTRACTID "@mozilla.org/contentsecuritymanager;1"
 // cdcc1ab8-3cea-4e6c-a294-a651fa35227f
 #define NS_CONTENTSECURITYMANAGER_CID \
 { 0xcdcc1ab8, 0x3cea, 0x4e6c, \
   { 0xa2, 0x94, 0xa6, 0x51, 0xfa, 0x35, 0x22, 0x7f } }
 
 class nsContentSecurityManager : public nsIContentSecurityManager
+                               , public nsIChannelEventSink
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSICONTENTSECURITYMANAGER
+  NS_DECL_NSICHANNELEVENTSINK
 
   nsContentSecurityManager() {}
 
   static nsresult doContentSecurityCheck(nsIChannel* aChannel,
                                          nsCOMPtr<nsIStreamListener>& aInAndOutListener);
 
 private:
+  static nsresult CheckChannel(nsIChannel* aChannel);
+
   virtual ~nsContentSecurityManager() {}
 
 };
 
 #endif /* nsContentSecurityManager_h___ */
--- a/dom/security/test/cors/file_CrossSiteXHR_server.sjs
+++ b/dom/security/test/cors/file_CrossSiteXHR_server.sjs
@@ -22,17 +22,19 @@ function handleRequest(request, response
     escape(String.fromCharCode.apply(null, bodyBytes)));
 
   if (query.hop) {
     query.hop = parseInt(query.hop, 10);
     hops = eval(query.hops);
     var curHop = hops[query.hop - 1];
     query.allowOrigin = curHop.allowOrigin;
     query.allowHeaders = curHop.allowHeaders;
+    query.allowMethods = curHop.allowMethods;
     query.allowCred = curHop.allowCred;
+    query.noAllowPreflight = curHop.noAllowPreflight;
     if (curHop.setCookie) {
       query.setCookie = unescape(curHop.setCookie);
     }
     if (curHop.cookie) {
       query.cookie = unescape(curHop.cookie);
     }
     query.noCookie = curHop.noCookie;
   }
--- a/dom/security/test/cors/test_CrossSiteXHR.html
+++ b/dom/security/test/cors/test_CrossSiteXHR.html
@@ -1079,36 +1079,158 @@ function runTest() {
              headers: { "Content-Type": "text/plain" },
              hops: [{ server: "http://example.org",
                     },
                     { server: "http://example.com",
                       allowOrigin: origin,
                     },
                     ],
            },
-           { pass: 0,
+           { pass: 1,
              method: "POST",
              body: "hi there",
              headers: { "Content-Type": "text/plain",
                         "my-header": "myValue",
                       },
              hops: [{ server: "http://example.org",
                     },
                     { server: "http://example.com",
                       allowOrigin: origin,
                       allowHeaders: "my-header",
                     },
                     ],
            },
            { pass: 0,
+             method: "POST",
+             body: "hi there",
+             headers: { "Content-Type": "text/plain",
+                        "my-header": "myValue",
+                      },
+             hops: [{ server: "http://example.org",
+                    },
+                    { server: "http://example.com",
+                      allowOrigin: origin,
+                      allowHeaders: "my-header",
+                    },
+                    { server: "http://sub1.test1.example.org",
+                      allowOrigin: origin,
+                      allowHeaders: "my-header",
+                    },
+                    ],
+           },
+           { pass: 0,
+             method: "POST",
+             body: "hi there",
+             headers: { "Content-Type": "text/plain",
+                        "my-header": "myValue",
+                      },
+             hops: [{ server: "http://example.org",
+                    },
+                    { server: "http://example.com",
+                      allowOrigin: origin,
+                      allowHeaders: "my-header",
+                    },
+                    { server: "http://example.com",
+                      allowOrigin: origin,
+                      allowHeaders: "my-header",
+                    },
+                    ],
+           },
+           { pass: 0,
+             method: "POST",
+             body: "hi there",
+             headers: { "Content-Type": "text/plain",
+                        "my-header": "myValue",
+                      },
+             hops: [{ server: "http://example.org",
+                    },
+                    { server: "http://example.com",
+                      allowOrigin: origin,
+                      allowHeaders: "my-header",
+                    },
+                    { server: "http://example.org",
+                      allowOrigin: origin,
+                      allowHeaders: "my-header",
+                    },
+                    ],
+           },
+           { pass: 0,
+             method: "POST",
+             body: "hi there",
+             headers: { "Content-Type": "text/plain",
+                        "my-header": "myValue",
+                      },
+             hops: [{ server: "http://example.org",
+                    },
+                    { server: "http://example.com",
+                      allowOrigin: origin,
+                      noAllowPreflight: 1,
+                    },
+                    ],
+           },
+           { pass: 1,
              method: "DELETE",
              hops: [{ server: "http://example.org",
                     },
                     { server: "http://example.com",
                       allowOrigin: origin,
+                      allowMethods: "DELETE",
+                    },
+                    ],
+           },
+           { pass: 0,
+             method: "DELETE",
+             hops: [{ server: "http://example.org",
+                    },
+                    { server: "http://example.com",
+                      allowOrigin: origin,
+                      allowMethods: "DELETE",
+                    },
+                    { server: "http://sub1.test1.example.org",
+                      allowOrigin: origin,
+                      allowMethods: "DELETE",
+                    },
+                    ],
+           },
+           { pass: 0,
+             method: "DELETE",
+             hops: [{ server: "http://example.org",
+                    },
+                    { server: "http://example.com",
+                      allowOrigin: origin,
+                      allowMethods: "DELETE",
+                    },
+                    { server: "http://example.com",
+                      allowOrigin: origin,
+                      allowMethods: "DELETE",
+                    },
+                    ],
+           },
+           { pass: 0,
+             method: "DELETE",
+             hops: [{ server: "http://example.org",
+                    },
+                    { server: "http://example.com",
+                      allowOrigin: origin,
+                      allowMethods: "DELETE",
+                    },
+                    { server: "http://example.org",
+                      allowOrigin: origin,
+                      allowMethods: "DELETE",
+                    },
+                    ],
+           },
+           { pass: 0,
+             method: "DELETE",
+             hops: [{ server: "http://example.org",
+                    },
+                    { server: "http://example.com",
+                      allowOrigin: origin,
+                      allowMethods: "DELETE",
+                      noAllowPreflight: 1,
                     },
                     ],
            },
            { pass: 0,
              method: "POST",
              body: "hi there",
              headers: { "Content-Type": "text/plain",
                         "my-header": "myValue",
--- a/dom/security/test/csp/file_redirects_main.html
+++ b/dom/security/test/csp/file_redirects_main.html
@@ -13,17 +13,16 @@ var page = "/tests/dom/security/test/csp
 
 var tests = { "font-src": thisSite+page+"?testid=font-src",
               "frame-src": thisSite+page+"?testid=frame-src",
               "img-src":  thisSite+page+"?testid=img-src",
               "media-src":  thisSite+page+"?testid=media-src",
               "object-src":  thisSite+page+"?testid=object-src",
               "script-src":  thisSite+page+"?testid=script-src",
               "style-src":  thisSite+page+"?testid=style-src",
-              "worker":  thisSite+page+"?testid=worker",
               "xhr-src":  thisSite+page+"?testid=xhr-src",
               "from-worker": thisSite+page+"?testid=from-worker",
               "from-blob-worker": thisSite+page+"?testid=from-blob-worker",
               "img-src-from-css":  thisSite+page+"?testid=img-src-from-css",
             };
 
 var container = document.getElementById("container");
 
--- a/dom/security/test/csp/file_redirects_page.sjs
+++ b/dom/security/test/csp/file_redirects_page.sjs
@@ -9,23 +9,18 @@ function handleRequest(request, response
   });
 
   response.setHeader("Cache-Control", "no-cache", false);
   response.setHeader("Content-Type", "text/html", false);
 
   var resource = "/tests/dom/security/test/csp/file_redirects_resource.sjs";
 
   // CSP header value
-  var additional = ""
-  if (query['testid'] == "worker") {
-    additional = "; script-src 'self' 'unsafe-inline'";
-  }
   response.setHeader("Content-Security-Policy",
-      "default-src 'self' blob: ; style-src 'self' 'unsafe-inline'" + additional,
-      false);
+      "default-src 'self' blob: ; style-src 'self' 'unsafe-inline'", false);
 
   // downloadable font that redirects to another site
   if (query["testid"] == "font-src") {
     var resp = '<style type="text/css"> @font-face { font-family:' +
                '"Redirecting Font"; src: url("' + resource +
                '?res=font&redir=other&id=font-src-redir")} #test{font-family:' +
                '"Redirecting Font"}</style></head><body>' +
                '<div id="test">test</div></body>';
@@ -64,22 +59,16 @@ function handleRequest(request, response
   }
 
   // external stylesheet that redirects to another site
   if (query["testid"] == "style-src") {
     response.write('<link rel="stylesheet" type="text/css" href="'+resource+'?res=style&redir=other&id=style-src-redir"></link>');
     return;
   }
 
-  // worker script resource that redirects to another site
-  if (query["testid"] == "worker") {
-    response.write('<script>var worker = new Worker("'+resource+'?res=worker&redir=other&id=worker-redir");</script>');
-    return;
-  }
-
   // script that XHR's to a resource that redirects to another site
   if (query["testid"] == "xhr-src") {
     response.write('<script src="'+resource+'?res=xhr"></script>');
     return;
   }
 
   // for bug949706
   if (query["testid"] == "img-src-from-css") {
--- a/dom/security/test/csp/file_redirects_resource.sjs
+++ b/dom/security/test/csp/file_redirects_resource.sjs
@@ -80,23 +80,16 @@ function handleRequest(request, response
 
   // external stylesheet
   if (query["res"] == "style") {
     response.setHeader("Content-Type", "text/css", false);
     response.write("css data...");
     return;
   }
 
-  // web worker resource
-  if (query["res"] == "worker") {
-    response.setHeader("Content-Type", "application/javascript", false);
-    response.write("worker script data...");
-    return;
-  }
-
   // internal stylesheet that loads an image from an external site
   if (query["res"] == "cssLoader") {
     let bgURL = thisSite + resource + '?redir=other&res=image&id=' + query["id"];
     response.setHeader("Content-Type", "text/css", false);
     response.write("body { background:url('" + bgURL + "'); }");
     return;
   }
 
--- a/dom/security/test/csp/test_redirects.html
+++ b/dom/security/test/csp/test_redirects.html
@@ -77,24 +77,22 @@ var testExpectedResults = { "font-src": 
                             "media-src": true,
                             "media-src-redir": false,
                             "object-src": true,
                             "object-src-redir": false,
                             "script-src": true,
                             "script-src-redir": false,
                             "style-src": true,
                             "style-src-redir": false,
-                            "worker": true,
-                            "worker-redir": false,
                             "xhr-src": true,
                             "xhr-src-redir": false,
                             "from-worker": true,
-                            "script-src-redir-from-worker": true, /* redir is allowed since policy isn't inherited */
-                            "xhr-src-redir-from-worker": true, /* redir is allowed since policy isn't inherited */
-                            "fetch-src-redir-from-worker": true, /* redir is allowed since policy isn't inherited */
+                            "script-src-redir-from-worker": true, // redir is allowed since policy isn't inherited
+                            "xhr-src-redir-from-worker": true, // redir is allowed since policy isn't inherited
+                            "fetch-src-redir-from-worker": true, // redir is allowed since policy isn't inherited
                             "from-blob-worker": true,
                             "script-src-redir-from-blob-worker": false,
                             "xhr-src-redir-from-blob-worker": false,
                             "fetch-src-redir-from-blob-worker": false,
                             "img-src-from-css": true,
                             "img-src-redir-from-css": false,
                           };
 
--- a/dom/svg/DOMSVGPathSegList.cpp
+++ b/dom/svg/DOMSVGPathSegList.cpp
@@ -380,16 +380,17 @@ DOMSVGPathSegList::InsertItemBefore(DOMS
       !InternalList().mData.SetCapacity(InternalList().mData.Length() + 1 + argCount,
                                         fallible)) {
     aError.Throw(NS_ERROR_OUT_OF_MEMORY);
     return nullptr;
   }
   if (AnimListMirrorsBaseList()) {
     DOMSVGPathSegList *animVal =
       GetDOMWrapperIfExists(InternalAList().GetAnimValKey());
+    MOZ_ASSERT(animVal, "animVal should be a valid pointer");
     if (!animVal->mItems.SetCapacity(
           animVal->mItems.Length() + 1, fallible)) {
       aError.Throw(NS_ERROR_OUT_OF_MEMORY);
       return nullptr;
     }
   }
 
   AutoChangePathSegListNotifier notifier(this);
--- a/dom/svg/DOMSVGPointList.cpp
+++ b/dom/svg/DOMSVGPointList.cpp
@@ -316,16 +316,17 @@ DOMSVGPointList::InsertItemBefore(nsISVG
   if (!mItems.SetCapacity(mItems.Length() + 1, fallible) ||
       !InternalList().SetCapacity(InternalList().Length() + 1)) {
     aError.Throw(NS_ERROR_OUT_OF_MEMORY);
     return nullptr;
   }
   if (AnimListMirrorsBaseList()) {
     DOMSVGPointList *animVal =
       GetDOMWrapperIfExists(InternalAList().GetAnimValKey());
+    MOZ_ASSERT(animVal, "animVal must be a valid pointer");
     if (!animVal->mItems.SetCapacity(
           animVal->mItems.Length() + 1, fallible)) {
       aError.Throw(NS_ERROR_OUT_OF_MEMORY);
       return nullptr;
     }
   }
 
   AutoChangePointListNotifier notifier(this);
--- a/dom/tests/mochitest/fetch/test_fetch_cors.js
+++ b/dom/tests/mochitest/fetch/test_fetch_cors.js
@@ -1278,27 +1278,42 @@ function testCORSRedirects() {
              headers: { "Content-Type": "text/plain" },
              hops: [{ server: "http://mochi.test:8888",
                     },
                     { server: "http://example.com",
                       allowOrigin: origin,
                     },
                     ],
            },
+           { pass: 1,
+             method: "POST",
+             body: "hi there",
+             headers: { "Content-Type": "text/plain",
+                        "my-header": "myValue",
+                      },
+             hops: [{ server: "http://mochi.test:8888",
+                    },
+                    { server: "http://example.com",
+                      allowOrigin: origin,
+                      allowHeaders: "my-header",
+                    },
+                    ],
+           },
            { pass: 0,
              method: "POST",
              body: "hi there",
              headers: { "Content-Type": "text/plain",
                         "my-header": "myValue",
                       },
              hops: [{ server: "http://mochi.test:8888",
                     },
                     { server: "http://example.com",
                       allowOrigin: origin,
                       allowHeaders: "my-header",
+                      noAllowPreflight: 1,
                     },
                     ],
            },
            { pass: 0,
              method: "POST",
              body: "hi there",
              headers: { "Content-Type": "text/plain",
                         "my-header": "myValue",
@@ -1310,34 +1325,48 @@ function testCORSRedirects() {
                       allowHeaders: "my-header",
                     },
                     { server: "http://test2.example.com",
                       allowOrigin: origin,
                       allowHeaders: "my-header",
                     }
                     ],
            },
+           { pass: 1,
+             method: "DELETE",
+             hops: [{ server: "http://mochi.test:8888",
+                    },
+                    { server: "http://example.com",
+                      allowOrigin: origin,
+                      allowMethods: "DELETE",
+                    },
+                    ],
+           },
            { pass: 0,
              method: "DELETE",
              hops: [{ server: "http://mochi.test:8888",
                     },
                     { server: "http://example.com",
                       allowOrigin: origin,
+                      allowMethods: "DELETE",
+                      noAllowPreflight: 1,
                     },
                     ],
            },
            { pass: 0,
              method: "DELETE",
              hops: [{ server: "http://mochi.test:8888",
                     },
                     { server: "http://test1.example.com",
                       allowOrigin: origin,
+                      allowMethods: "DELETE",
                     },
                     { server: "http://test2.example.com",
                       allowOrigin: origin,
+                      allowMethods: "DELETE",
                     },
                     ],
            },
            { pass: 0,
              method: "POST",
              body: "hi there",
              headers: { "Content-Type": "text/plain",
                         "my-header": "myValue",
@@ -1349,19 +1378,21 @@ function testCORSRedirects() {
                       allowOrigin: origin,
                     },
                     ],
            },
            { pass: 0,
              method: "DELETE",
              hops: [{ server: "http://example.com",
                       allowOrigin: origin,
+                      allowMethods: "DELETE",
                     },
                     { server: "http://sub1.test1.mochi.test:8888",
                       allowOrigin: origin,
+                      allowMethods: "DELETE",
                     },
                     ],
            },
            { pass: 0,
              method: "POST",
              body: "hi there",
              headers: { "Content-Type": "text/plain",
                         "my-header": "myValue",
--- a/dom/webidl/FormData.webidl
+++ b/dom/webidl/FormData.webidl
@@ -7,18 +7,22 @@
  * http://xhr.spec.whatwg.org
  */
 
 typedef (File or USVString) FormDataEntryValue;
 
 [Constructor(optional HTMLFormElement form),
  Exposed=(Window,Worker)]
 interface FormData {
+  [Throws]
   void append(USVString name, Blob value, optional USVString filename);
+  [Throws]
   void append(USVString name, USVString value);
   void delete(USVString name);
   FormDataEntryValue? get(USVString name);
   sequence<FormDataEntryValue> getAll(USVString name);
   boolean has(USVString name);
+  [Throws]
   void set(USVString name, Blob value, optional USVString filename);
+  [Throws]
   void set(USVString name, USVString value);
   iterable<USVString, FormDataEntryValue>;
 };
--- a/dom/webidl/NamedNodeMap.webidl
+++ b/dom/webidl/NamedNodeMap.webidl
@@ -1,16 +1,16 @@
 /* -*- Mode: IDL; 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/. */
 
 interface NamedNodeMap {
   getter Attr? getNamedItem(DOMString name);
-  [Throws]
+  [Throws, BinaryName="setNamedItemNS"]
   Attr? setNamedItem(Attr arg);
   [Throws]
   Attr removeNamedItem(DOMString name);
 
   getter Attr? item(unsigned long index);
   readonly attribute unsigned long length;
 
   Attr? getNamedItemNS(DOMString? namespaceURI, DOMString localName);
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -1074,17 +1074,17 @@ public:
     mRegistration->NotifyListenersOnChange();
 
     Succeed();
 
     // The job should NOT call fail from this point on.
 
     // Step 8 "Queue a task..." for updatefound.
     nsCOMPtr<nsIRunnable> upr =
-      NS_NewRunnableMethodWithArg<ServiceWorkerRegistrationInfo*>(
+      NS_NewRunnableMethodWithArg<RefPtr<ServiceWorkerRegistrationInfo>>(
         swm,
         &ServiceWorkerManager::FireUpdateFoundOnServiceWorkerRegistrations,
         mRegistration);
 
     NS_DispatchToMainThread(upr);
 
     // Call ContinueAfterInstallEvent(false) on main thread if the SW
     // script fails to load.
@@ -1762,19 +1762,18 @@ ServiceWorkerRegistrationInfo::Activate(
   NotifyListenersOnChange();
 
   // FIXME(nsm): Unlink appcache if there is one.
 
   swm->CheckPendingReadyPromises();
 
   // "Queue a task to fire a simple event named controllerchange..."
   nsCOMPtr<nsIRunnable> controllerChangeRunnable =
-    NS_NewRunnableMethodWithArg<ServiceWorkerRegistrationInfo*>(swm,
-                                                                &ServiceWorkerManager::FireControllerChange,
-                                                                this);
+    NS_NewRunnableMethodWithArg<RefPtr<ServiceWorkerRegistrationInfo>>(
+      swm, &ServiceWorkerManager::FireControllerChange, this);
   NS_DispatchToMainThread(controllerChangeRunnable);
 
   nsCOMPtr<nsIRunnable> failRunnable =
     NS_NewRunnableMethodWithArg<bool>(this,
                                       &ServiceWorkerRegistrationInfo::FinishActivate,
                                       false /* success */);
 
   nsMainThreadPtrHandle<ContinueLifecycleTask> continueActivateTask(
@@ -2647,17 +2646,18 @@ ServiceWorkerManager::HandleError(JSCont
   // each client.
   ReportToAllClients(aScope, aMessage, aFilename, aLine, aLineNumber,
                      aColumnNumber, aFlags);
 }
 
 void
 ServiceWorkerRegistrationInfo::FinishActivate(bool aSuccess)
 {
-  if (mPendingUninstall || !mActiveWorker) {
+  if (mPendingUninstall || !mActiveWorker ||
+      mActiveWorker->State() != ServiceWorkerState::Activating) {
     return;
   }
 
   // Activation never fails, so aSuccess is ignored.
   mActiveWorker->UpdateState(ServiceWorkerState::Activated);
   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
   swm->StoreRegistration(mPrincipal, this);
 }
--- a/dom/xml/nsXMLPrettyPrinter.cpp
+++ b/dom/xml/nsXMLPrettyPrinter.cpp
@@ -166,17 +166,17 @@ nsXMLPrettyPrinter::PrettyPrint(nsIDocum
     rv = resultFragmentVariant->SetAsISupports(resultFragment);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
     rv = event->InitCustomEvent(NS_LITERAL_STRING("prettyprint-dom-created"),
                                 /* bubbles = */ false, /* cancelable = */ false,
                                 /* detail = */ resultFragmentVariant);
     NS_ENSURE_SUCCESS(rv, rv);
     event->SetTrusted(true);
     bool dummy;
-    rv = rootCont->DispatchEvent(static_cast<Event*>(event), &dummy);
+    rv = rootCont->DispatchEvent(event, &dummy);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Observe the document so we know when to switch to "normal" view
     aDocument->AddObserver(this);
     mDocument = aDocument;
 
     NS_ADDREF_THIS();
 
--- a/dom/xslt/base/txURIUtils.cpp
+++ b/dom/xslt/base/txURIUtils.cpp
@@ -12,40 +12,16 @@
 
 using mozilla::LoadInfo;
 
 /**
  * URIUtils
  * A set of utilities for handling URIs
 **/
 
-/**
- * Resolves the given href argument, using the given documentBase
- * if necessary.
- * The new resolved href will be appended to the given dest String
-**/
-void URIUtils::resolveHref(const nsAString& href, const nsAString& base,
-                           nsAString& dest) {
-    if (base.IsEmpty()) {
-        dest.Append(href);
-        return;
-    }
-    if (href.IsEmpty()) {
-        dest.Append(base);
-        return;
-    }
-    nsCOMPtr<nsIURI> pURL;
-    nsAutoString resultHref;
-    nsresult result = NS_NewURI(getter_AddRefs(pURL), base);
-    if (NS_SUCCEEDED(result)) {
-        NS_MakeAbsoluteURI(resultHref, href, pURL);
-        dest.Append(resultHref);
-    }
-} //-- resolveHref
-
 // static
 void
 URIUtils::ResetWithSource(nsIDocument *aNewDoc, nsIDOMNode *aSourceNode)
 {
     nsCOMPtr<nsINode> node = do_QueryInterface(aSourceNode);
     if (!node) {
         // XXXbz passing nullptr as the first arg to Reset is illegal
         aNewDoc->Reset(nullptr, nullptr);
--- a/dom/xslt/base/txURIUtils.h
+++ b/dom/xslt/base/txURIUtils.h
@@ -18,20 +18,12 @@ class nsIDOMNode;
 
 class URIUtils {
 public:
 
     /**
      * Reset the given document with the document of the source node
      */
     static void ResetWithSource(nsIDocument *aNewDoc, nsIDOMNode *aSourceNode);
-
-    /**
-     * Resolves the given href argument, using the given documentBase
-     * if necessary.
-     * The new resolved href will be appended to the given dest String
-    **/
-    static void resolveHref(const nsAString& href, const nsAString& base,
-                            nsAString& dest);
 }; //-- URIUtils
 
 /* */
 #endif
--- a/dom/xslt/nsIXSLTProcessorPrivate.idl
+++ b/dom/xslt/nsIXSLTProcessorPrivate.idl
@@ -1,19 +1,26 @@
 /* -*- 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 "nsISupports.idl"
 
-[scriptable, uuid(b8d727f7-67f4-4dc1-a318-ec0c87280816)]
+[scriptable, uuid(75d14f5d-293d-4872-8a26-e79268de592f)]
 interface nsIXSLTProcessorPrivate : nsISupports
 {
   /**
+   * This needs to be called if the XSLTProcessor is instantiated
+   * through the XPCOM registry (i.e. using do_createInstance) and the
+   * stylesheet uses xsl:import/xsl:include or the document() xpath function.
+   */
+  void init(in nsISupports global);
+
+  /**
    * Disables all loading of external documents, such as from
    * <xsl:import> and document()
    * Defaults to off and is *not* reset by calls to reset()
    */
   const unsigned long DISABLE_ALL_LOADS = 1;
 
   /**
    * Flags for this processor. Defaults to 0. See individual flags above
new file mode 100644
--- /dev/null
+++ b/dom/xslt/tests/mochitest/file_bug1222624.xml
@@ -0,0 +1,4 @@
+<?xml-stylesheet type="text/xsl" href="file_bug1222624.xsl"?>
+<root>
+  <load>file_bug1222624_data2.xml</load>
+</root>
new file mode 100644
--- /dev/null
+++ b/dom/xslt/tests/mochitest/file_bug1222624.xsl
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+  <xsl:import href="file_bug1222624_sub.xsl"/>
+  <xsl:template match="/root">
+    <result>
+      <xsl:call-template name="external"/>
+      <xsl:value-of select="document('file_bug1222624_data1.xml')"/>
+      <xsl:text>!</xsl:text>
+      <xsl:value-of select="document(load)"/>
+    </result>
+  </xsl:template>
+</xsl:stylesheet>
new file mode 100644
--- /dev/null
+++ b/dom/xslt/tests/mochitest/file_bug1222624_data1.xml
@@ -0,0 +1,1 @@
+<data>doc1</data>
new file mode 100644
--- /dev/null
+++ b/dom/xslt/tests/mochitest/file_bug1222624_data2.xml
@@ -0,0 +1,1 @@
+<data>doc2</data>
new file mode 100644
--- /dev/null
+++ b/dom/xslt/tests/mochitest/file_bug1222624_sub.xsl
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+  <xsl:include href="file_bug1222624_sub_sub.xsl"/>
+</xsl:stylesheet>
new file mode 100644
--- /dev/null
+++ b/dom/xslt/tests/mochitest/file_bug1222624_sub_sub.xsl
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+  <xsl:template name="external">
+    <external/>
+  </xsl:template>
+</xsl:stylesheet>
--- a/dom/xslt/tests/mochitest/mochitest.ini
+++ b/dom/xslt/tests/mochitest/mochitest.ini
@@ -11,10 +11,12 @@
 [test_bug551654.html]
 [test_bug566629.html]
 [test_bug566629.xhtml]
 [test_bug603159.html]
 [test_bug616774.html]
 [test_bug667315.html]
 [test_bug1135764.html]
 support-files = file_bug1135764.xml file_bug1135764.xsl
+[test_bug1222624.html]
+support-files = file_bug1222624.xml file_bug1222624.xsl file_bug1222624_sub.xsl file_bug1222624_sub_sub.xsl file_bug1222624_data1.xml file_bug1222624_data2.xml
 [test_exslt_regex.html]
 [test_parameter.html]
new file mode 100644
--- /dev/null
+++ b/dom/xslt/tests/mochitest/test_bug1222624.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1222624
+-->
+<head>
+  <title>Test for Bug 1222624</title>
+  <script type="text/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=1222624">Mozilla Bug 1222624</a>
+<p id="display"></p>
+<iframe id="frame"></iframe>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 1222624 **/
+
+const nl = (navigator.platform == 'Win32' ||
+            navigator.platform == 'Win64') ? '\r\n' : '\n';
+
+const transformRes = '<?xml version="1.0" encoding="UTF-8"?>' + nl + '<result><external/>doc1!doc2</result>';
+
+xhr = new XMLHttpRequest();
+xhr.open("GET", "file_bug1222624.xml", false);
+xhr.send();
+var xmlDoc = xhr.responseXML;
+
+xhr.open("GET", "file_bug1222624.xsl", false);
+xhr.send();
+var xslDoc = xhr.responseXML;
+
+var processor = new XSLTProcessor;
+processor.importStylesheet(xslDoc);
+var apiRes = new XMLSerializer().serializeToString(processor.transformToDocument(xmlDoc));
+
+is(apiRes, transformRes, "API transformation correct");
+
+SimpleTest.waitForExplicitFinish();
+
+var frame = $("frame");
+frame.src = "file_bug1222624.xml";
+frame.onload = function () {
+  var piRes = new XMLSerializer().serializeToString(frame.contentDocument);
+  is(piRes, transformRes, "processing-instruction transformation correct");
+  SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/xslt/xml/txXMLParser.cpp
+++ b/dom/xslt/xml/txXMLParser.cpp
@@ -10,51 +10,49 @@
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
 #include "nsSyncLoadService.h"
 #include "nsNetUtil.h"
 #include "nsIURI.h"
 #include "nsIPrincipal.h"
 
 nsresult
-txParseDocumentFromURI(const nsAString& aHref,
-                       const txXPathNode& aLoader,
+txParseDocumentFromURI(nsIURI* aUri,
+                       nsIDocument* aLoadingDocument,
                        nsAString& aErrMsg,
                        txXPathNode** aResult)
 {
-    NS_ENSURE_ARG_POINTER(aResult);
     *aResult = nullptr;
-    nsCOMPtr<nsIURI> documentURI;
-    nsresult rv = NS_NewURI(getter_AddRefs(documentURI), aHref);
-    NS_ENSURE_SUCCESS(rv, rv);
 
-    nsIDocument* loaderDocument = txXPathNativeNode::getDocument(aLoader);
-
-    nsCOMPtr<nsILoadGroup> loadGroup = loaderDocument->GetDocumentLoadGroup();
+    nsCOMPtr<nsILoadGroup> loadGroup = aLoadingDocument->GetDocumentLoadGroup();
 
     // For the system principal loaderUri will be null here, which is good
     // since that means that chrome documents can load any uri.
 
     // Raw pointer, we want the resulting txXPathNode to hold a reference to
     // the document.
     nsIDOMDocument* theDocument = nullptr;
-    nsAutoSyncOperation sync(loaderDocument);
-    rv = nsSyncLoadService::LoadDocument(documentURI,
-                                         nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST,
-                                         loaderDocument->NodePrincipal(),
-                                         nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS,
-                                         loadGroup, true,
-                                         loaderDocument->GetReferrerPolicy(),
-                                         &theDocument);
+    nsAutoSyncOperation sync(aLoadingDocument);
+    nsresult rv =
+        nsSyncLoadService::LoadDocument(aUri,
+                                        nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST,
+                                        aLoadingDocument->NodePrincipal(),
+                                        nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS,
+                                        loadGroup,
+                                        true,
+                                        aLoadingDocument->GetReferrerPolicy(),
+                                        &theDocument);
 
     if (NS_FAILED(rv)) {
         aErrMsg.AppendLiteral("Document load of ");
-        aErrMsg.Append(aHref);
+        nsAutoCString spec;
+        aUri->GetSpec(spec);
+        aErrMsg.Append(NS_ConvertUTF8toUTF16(spec));
         aErrMsg.AppendLiteral(" failed.");
-        return NS_FAILED(rv) ? rv : NS_ERROR_FAILURE;
+        return rv;
     }
 
     *aResult = txXPathNativeNode::createXPathNode(theDocument);
     if (!*aResult) {
         NS_RELEASE(theDocument);
         return NS_ERROR_FAILURE;
     }
 
--- a/dom/xslt/xml/txXMLParser.h
+++ b/dom/xslt/xml/txXMLParser.h
@@ -4,23 +4,27 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MITRE_XMLPARSER_H
 #define MITRE_XMLPARSER_H
 
 #include "txCore.h"
 
 class txXPathNode;
+class nsIURI;
+class nsIDocument;
 
 /**
  * API to load XML files into DOM datastructures.
  * Parsing is either done by expat, or by expat via the syncloaderservice
  */
 
 /**
  * Parse a document from the aHref location, with referrer URI on behalf
  * of the document aLoader.
  */
 extern "C" nsresult
-txParseDocumentFromURI(const nsAString& aHref, const txXPathNode& aLoader,
-                       nsAString& aErrMsg, txXPathNode** aResult);
+txParseDocumentFromURI(nsIURI* aUri,
+                       nsIDocument* aLoadingDocument,
+                       nsAString& aErrMsg,
+                       txXPathNode** aResult);
 
 #endif
--- a/dom/xslt/xpath/txMozillaXPathTreeWalker.cpp
+++ b/dom/xslt/xpath/txMozillaXPathTreeWalker.cpp
@@ -558,19 +558,19 @@ txXPathNodeUtils::getXSLTId(const txXPat
                                          nodeid, aNode.mIndex), aResult);
     }
 
     return NS_OK;
 }
 
 /* static */
 void
-txXPathNodeUtils::getBaseURI(const txXPathNode& aNode, nsAString& aURI)
+txXPathNodeUtils::getBaseURI(const txXPathNode& aNode, nsIURI** aUri)
 {
-    aNode.mNode->GetBaseURI(aURI);
+    *aUri = aNode.mNode->GetBaseURI().take();
 }
 
 /* static */
 int
 txXPathNodeUtils::comparePosition(const txXPathNode& aNode,
                                   const txXPathNode& aOtherNode)
 {
     // First check for equal nodes or attribute-nodes on the same element.
--- a/dom/xslt/xpath/txXPathTreeWalker.h
+++ b/dom/xslt/xpath/txXPathTreeWalker.h
@@ -88,17 +88,17 @@ public:
     static uint16_t getNodeType(const txXPathNode& aNode);
     static void appendNodeValue(const txXPathNode& aNode, nsAString& aResult);
     static bool isWhitespace(const txXPathNode& aNode);
     static txXPathNode* getOwnerDocument(const txXPathNode& aNode);
     static int32_t getUniqueIdentifier(const txXPathNode& aNode);
     static nsresult getXSLTId(const txXPathNode& aNode,
                               const txXPathNode& aBase, nsAString& aResult);
     static void release(txXPathNode* aNode);
-    static void getBaseURI(const txXPathNode& aNode, nsAString& aURI);
+    static void getBaseURI(const txXPathNode& aNode, nsIURI** aURI);
     static int comparePosition(const txXPathNode& aNode,
                                const txXPathNode& aOtherNode);
     static bool localNameEquals(const txXPathNode& aNode,
                                   nsIAtom* aLocalName);
     static bool isRoot(const txXPathNode& aNode);
     static bool isElement(const txXPathNode& aNode);
     static bool isAttribute(const txXPathNode& aNode);
     static bool isProcessingInstruction(const txXPathNode& aNode);
--- a/dom/xslt/xslt/txDocumentFunctionCall.cpp
+++ b/dom/xslt/xslt/txDocumentFunctionCall.cpp
@@ -8,56 +8,43 @@
  * A representation of the XSLT additional function: document()
  */
 
 #include "nsGkAtoms.h"
 #include "txIXPathContext.h"
 #include "txXSLTFunctions.h"
 #include "txExecutionState.h"
 #include "txURIUtils.h"
-
-/*
- * Creates a new DocumentFunctionCall.
- */
-DocumentFunctionCall::DocumentFunctionCall(const nsAString& aBaseURI)
-    : mBaseURI(aBaseURI)
-{
-}
+#include "nsIURI.h"
+#include "nsNetUtil.h"
 
 static void
-retrieveNode(txExecutionState* aExecutionState, const nsAString& aUri,
-             const nsAString& aBaseUri, txNodeSet* aNodeSet)
+retrieveNode(txExecutionState* aExecutionState,
+             const nsAString& aUri,
+             nsIURI* aBaseUri,
+             txNodeSet* aNodeSet)
 {
-    nsAutoString absUrl;
-    URIUtils::resolveHref(aUri, aBaseUri, absUrl);
-
-    int32_t hash = absUrl.RFindChar(char16_t('#'));
-    uint32_t urlEnd, fragStart, fragEnd;
-    if (hash == kNotFound) {
-        urlEnd = absUrl.Length();
-        fragStart = 0;
-        fragEnd = 0;
-    }
-    else {
-        urlEnd = hash;
-        fragStart = hash + 1;
-        fragEnd = absUrl.Length();
+    nsCOMPtr<nsIURI> uri;
+    nsresult rv = NS_NewURI(getter_AddRefs(uri), aUri, nullptr, aBaseUri);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+        return;
     }
 
-    nsDependentSubstring docUrl(absUrl, 0, urlEnd);
-    nsDependentSubstring frag(absUrl, fragStart, fragEnd);
+    nsAutoCString frag;
+    uri->GetRef(frag);
+    uri->SetRef(EmptyCString());
 
-    const txXPathNode* loadNode = aExecutionState->retrieveDocument(docUrl);
+    const txXPathNode* loadNode = aExecutionState->retrieveDocument(uri);
     if (loadNode) {
         if (frag.IsEmpty()) {
             aNodeSet->add(*loadNode);
         }
         else {
             txXPathTreeWalker walker(*loadNode);
-            if (walker.moveToElementById(frag)) {
+            if (walker.moveToElementById(NS_ConvertUTF8toUTF16(frag))) {
                 aNodeSet->add(walker.getCurrentPosition());
             }
         }
     }
 }
 
 /*
  * Evaluates this Expr based on the given context node and processor state
@@ -82,65 +69,66 @@ DocumentFunctionCall::evaluate(txIEvalCo
     if (!requireParams(1, 2, aContext)) {
         return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT;
     }
 
     RefPtr<txAExprResult> exprResult1;
     rv = mParams[0]->evaluate(aContext, getter_AddRefs(exprResult1));
     NS_ENSURE_SUCCESS(rv, rv);
 
-    nsAutoString baseURI;
+    nsCOMPtr<nsIURI> baseURI;
     bool baseURISet = false;
 
     if (mParams.Length() == 2) {
         // We have 2 arguments, get baseURI from the first node
         // in the resulting nodeset
         RefPtr<txNodeSet> nodeSet2;
         rv = evaluateToNodeSet(mParams[1],
                                aContext, getter_AddRefs(nodeSet2));
         NS_ENSURE_SUCCESS(rv, rv);
 
         // Make this true, even if nodeSet2 is empty. For relative URLs,
         // we'll fail to load the document with an empty base URI, and for
         // absolute URLs, the base URI doesn't matter
         baseURISet = true;
 
         if (!nodeSet2->isEmpty()) {
-            txXPathNodeUtils::getBaseURI(nodeSet2->get(0), baseURI);
+            txXPathNodeUtils::getBaseURI(nodeSet2->get(0),
+                                         getter_AddRefs(baseURI));
         }
     }
 
     if (exprResult1->getResultType() == txAExprResult::NODESET) {
         // The first argument is a NodeSet, iterate on its nodes
         txNodeSet* nodeSet1 = static_cast<txNodeSet*>
                                          (static_cast<txAExprResult*>
                                                      (exprResult1));
         int32_t i;
         for (i = 0; i < nodeSet1->size(); ++i) {
             const txXPathNode& node = nodeSet1->get(i);
             nsAutoString uriStr;
             txXPathNodeUtils::appendNodeValue(node, uriStr);
             if (!baseURISet) {
                 // if the second argument wasn't specified, use
                 // the baseUri of node itself
-                txXPathNodeUtils::getBaseURI(node, baseURI);
+                txXPathNodeUtils::getBaseURI(node, getter_AddRefs(baseURI));
             }
             retrieveNode(es, uriStr, baseURI, nodeSet);
         }
         
         NS_ADDREF(*aResult = nodeSet);
         
         return NS_OK;
     }
 
     // The first argument is not a NodeSet
     nsAutoString uriStr;
     exprResult1->stringValue(uriStr);
-    const nsAString* base = baseURISet ? &baseURI : &mBaseURI;
-    retrieveNode(es, uriStr, *base, nodeSet);
+    nsIURI* base = baseURISet ? baseURI.get() : mBaseURI.get();
+    retrieveNode(es, uriStr, base, nodeSet);
 
     NS_ADDREF(*aResult = nodeSet);
 
     return NS_OK;
 }
 
 Expr::ResultType
 DocumentFunctionCall::getReturnType()
--- a/dom/xslt/xslt/txExecutionState.cpp
+++ b/dom/xslt/xslt/txExecutionState.cpp
@@ -16,46 +16,48 @@
 
 const int32_t txExecutionState::kMaxRecursionDepth = 20000;
 
 void
 txLoadedDocumentsHash::init(txXPathNode* aSourceDocument)
 {
     mSourceDocument = aSourceDocument;
 
-    nsAutoString baseURI;
-    txXPathNodeUtils::getBaseURI(*mSourceDocument, baseURI);
+    nsCOMPtr<nsIURI> baseURI;
+    txXPathNodeUtils::getBaseURI(*mSourceDocument, getter_AddRefs(baseURI));
 
-    PutEntry(baseURI)->mDocument = mSourceDocument;
+    LookupOrAdd(baseURI)->mDocument = mSourceDocument;
 }
 
 txLoadedDocumentsHash::~txLoadedDocumentsHash()
 {
     if (mSourceDocument) {
-        nsAutoString baseURI;
-        txXPathNodeUtils::getBaseURI(*mSourceDocument, baseURI);
+        nsCOMPtr<nsIURI> baseURI;
+        txXPathNodeUtils::getBaseURI(*mSourceDocument, getter_AddRefs(baseURI));
 
-        txLoadedDocumentEntry* entry = GetEntry(baseURI);
-        if (entry) {
-            delete entry->mDocument.forget();
+        txLoadedDocumentInfo* info = Get(baseURI);
+        if (info) {
+            delete info->mDocument.forget();
         }
     }
 }
 
 txExecutionState::txExecutionState(txStylesheet* aStylesheet,
-                                   bool aDisableLoads)
+                                   bool aDisableLoads,
+                                   nsIDocument* aLoadingDocument)
     : mOutputHandler(nullptr),
       mResultHandler(nullptr),
       mStylesheet(aStylesheet),
       mNextInstruction(nullptr),
       mLocalVariables(nullptr),
       mRecursionDepth(0),
       mEvalContext(nullptr),
       mInitialEvalContext(nullptr),
       mGlobalParams(nullptr),
+      mLoadingDocument(aLoadingDocument),
       mKeyHash(aStylesheet->getKeyMap()),
       mDisableLoads(aDisableLoads)
 {
     MOZ_COUNT_CTOR(txExecutionState);
 }
 
 txExecutionState::~txExecutionState()
 {
@@ -367,51 +369,58 @@ txExecutionState::popTemplateRule()
 
 txIEvalContext*
 txExecutionState::getEvalContext()
 {
     return mEvalContext;
 }
 
 const txXPathNode*
-txExecutionState::retrieveDocument(const nsAString& aUri)
+txExecutionState::retrieveDocument(nsIURI* aUri)
 {
-    NS_ASSERTION(!aUri.Contains(char16_t('#')),
-                 "Remove the fragment.");
-
-    if (mDisableLoads) {
-        return nullptr;
+#ifdef DEBUG
+    {
+        bool hasFrag;
+        aUri->GetHasRef(&hasFrag);
+        MOZ_ASSERT(!hasFrag, "Remove the fragment");
     }
-
-    MOZ_LOG(txLog::xslt, LogLevel::Debug,
-           ("Retrieve Document %s", NS_LossyConvertUTF16toASCII(aUri).get()));
+#endif
 
-    // try to get already loaded document
-    txLoadedDocumentEntry *entry = mLoadedDocuments.PutEntry(aUri);
-    if (!entry) {
+    if (mDisableLoads || !mLoadingDocument) {
         return nullptr;
     }
 
-    if (!entry->mDocument && !entry->LoadingFailed()) {
+    if (MOZ_LOG_TEST(txLog::xslt, LogLevel::Debug)) {
+        nsAutoCString spec;
+        aUri->GetSpec(spec);
+        MOZ_LOG(txLog::xslt, LogLevel::Debug,
+                ("Retrieve Document %s", spec.get()));
+    }
+
+    // try to get already loaded document
+    txLoadedDocumentInfo* info = mLoadedDocuments.LookupOrAdd(aUri);
+
+    if (!info->mDocument && !info->LoadingFailed()) {
         // open URI
         nsAutoString errMsg;
-        // XXX we should get the loader from the actual node
-        // triggering the load, but this will do for the time being
-        entry->mLoadResult =
-            txParseDocumentFromURI(aUri, *mLoadedDocuments.mSourceDocument,
-                                   errMsg, getter_Transfers(entry->mDocument));
+        info->mLoadResult =
+            txParseDocumentFromURI(aUri, mLoadingDocument,
+                                   errMsg, getter_Transfers(info->mDocument));
 
-        if (entry->LoadingFailed()) {
+        if (info->LoadingFailed()) {
+            nsAutoCString spec;
+            aUri->GetSpec(spec);
             receiveError(NS_LITERAL_STRING("Couldn't load document '") +
-                         aUri + NS_LITERAL_STRING("': ") + errMsg,
-                         entry->mLoadResult);
+                         NS_ConvertUTF8toUTF16(spec) +
+                         NS_LITERAL_STRING("': ") + errMsg,
+                         info->mLoadResult);
         }
     }
 
-    return entry->mDocument;
+    return info->mDocument;
 }
 
 nsresult
 txExecutionState::getKeyNodes(const txExpandedName& aKeyName,
                               const txXPathNode& aRoot,
                               const nsAString& aKeyValue,
                               bool aIndexIfNotFound,
                               txNodeSet** aResult)
--- a/dom/xslt/xslt/txExecutionState.h
+++ b/dom/xslt/xslt/txExecutionState.h
@@ -12,34 +12,29 @@
 #include "txIXPathContext.h"
 #include "txVariableMap.h"
 #include "nsTHashtable.h"
 #include "nsHashKeys.h"
 #include "txKey.h"
 #include "txStylesheet.h"
 #include "txXPathTreeWalker.h"
 #include "nsTArray.h"
+#include "nsURIHashKey.h"
 
 class txAOutputHandlerFactory;
 class txAXMLEventHandler;
 class txInstruction;
 
-class txLoadedDocumentEntry : public nsStringHashKey
+class txLoadedDocumentInfo
 {
 public:
-    explicit txLoadedDocumentEntry(KeyTypePointer aStr) : nsStringHashKey(aStr),
-                                                          mLoadResult(NS_OK)
+    explicit txLoadedDocumentInfo() : mLoadResult(NS_OK)
     {
     }
-    txLoadedDocumentEntry(const txLoadedDocumentEntry& aToCopy)
-        : nsStringHashKey(aToCopy)
-    {
-        NS_ERROR("We're horked.");
-    }
-    ~txLoadedDocumentEntry()
+    ~txLoadedDocumentInfo()
     {
         if (mDocument) {
             txXPathNodeUtils::release(mDocument);
         }
     }
     bool LoadingFailed()
     {
         NS_ASSERTION(NS_SUCCEEDED(mLoadResult) || !mDocument,
@@ -47,37 +42,38 @@ public:
 
         return NS_FAILED(mLoadResult);
     }
 
     nsAutoPtr<txXPathNode> mDocument;
     nsresult mLoadResult;
 };
 
-class txLoadedDocumentsHash : public nsTHashtable<txLoadedDocumentEntry>
+class txLoadedDocumentsHash : public nsClassHashtable<nsURIHashKey, txLoadedDocumentInfo>
 {
 public:
     txLoadedDocumentsHash()
-        : nsTHashtable<txLoadedDocumentEntry>(4),
+        : nsClassHashtable<nsURIHashKey, txLoadedDocumentInfo>(4),
           mSourceDocument(nullptr)
     {
     }
     ~txLoadedDocumentsHash();
     void init(txXPathNode* aSourceDocument);
 
 private:
     friend class txExecutionState;
     txXPathNode* mSourceDocument;
 };
 
 
 class txExecutionState : public txIMatchContext
 {
 public:
-    txExecutionState(txStylesheet* aStylesheet, bool aDisableLoads);
+    txExecutionState(txStylesheet* aStylesheet, bool aDisableLoads,
+                     nsIDocument* aLoaderDocument);
     ~txExecutionState();
     nsresult init(const txXPathNode& aNode,
                   txOwningExpandedNameMap<txIGlobalParameter>* aGlobalParams);
     nsresult end(nsresult aResult);
 
     TX_DECL_MATCH_CONTEXT;
 
     /**
@@ -102,17 +98,17 @@ public:
                           const txExpandedName& aMode,
                           txVariableMap* aParams);
     void popTemplateRule();
     nsresult pushParamMap(txVariableMap* aParams);
     txVariableMap* popParamMap();
 
     // state-getting functions
     txIEvalContext* getEvalContext();
-    const txXPathNode* retrieveDocument(const nsAString& aUri);
+    const txXPathNode* retrieveDocument(nsIURI* aUri);
     nsresult getKeyNodes(const txExpandedName& aKeyName,
                          const txXPathNode& aRoot,
                          const nsAString& aKeyValue, bool aIndexIfNotFound,
                          txNodeSet** aResult);
     TemplateRule* getCurrentTemplateRule();
     const txXPathNode& getSourceDocument()
     {
         NS_ASSERTION(mLoadedDocuments.mSourceDocument,
@@ -156,16 +152,17 @@ private:
 
     AutoInfallibleTArray<TemplateRule, 10> mTemplateRules;
 
     txIEvalContext* mEvalContext;
     txIEvalContext* mInitialEvalContext;
     //Document* mRTFDocument;
     txOwningExpandedNameMap<txIGlobalParameter>* mGlobalParams;
 
+    nsCOMPtr<nsIDocument> mLoadingDocument;
     txLoadedDocumentsHash mLoadedDocuments;
     txKeyHash mKeyHash;
     RefPtr<txResultRecycler> mRecycler;
     bool mDisableLoads;
 
     static const int32_t kMaxRecursionDepth;
 };
 
--- a/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp
+++ b/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp
@@ -251,16 +251,26 @@ txStylesheetSink::OnDataAvailable(nsIReq
 
 NS_IMETHODIMP
 txStylesheetSink::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
 {
     int32_t charsetSource = kCharsetFromDocTypeDefault;
 
     nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
 
+    nsCOMPtr<nsIPrincipal> channelPrincipal;
+    nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
+        channel, getter_AddRefs(channelPrincipal));
+    mCompiler->setPrincipal(channelPrincipal);
+
+    nsCOMPtr<nsIURI> baseURI;
+    nsresult rv = NS_GetFinalChannelURI(channel, getter_AddRefs(baseURI));
+    NS_ENSURE_SUCCESS(rv, rv);
+    mCompiler->setBaseURI(baseURI);
+
     // check channel's charset...
     nsAutoCString charsetVal;
     nsAutoCString charset;
     if (NS_SUCCEEDED(channel->GetContentCharset(charsetVal))) {
         if (EncodingUtils::FindEncodingForLabel(charsetVal, charset)) {
             charsetSource = kCharsetFromChannel;
         }
     }
@@ -369,19 +379,18 @@ class txCompileObserver final : public t
 {
 public:
     txCompileObserver(txMozillaXSLTProcessor* aProcessor,
                       nsIDocument* aLoaderDocument);
 
     TX_DECL_ACOMPILEOBSERVER
     NS_INLINE_DECL_REFCOUNTING(txCompileObserver)
 
-    nsresult startLoad(nsIURI* aUri, txStylesheetCompiler* aCompiler,
-                       nsIPrincipal* aSourcePrincipal,
-                       ReferrerPolicy aReferrerPolicy);
+    nsresult startLoad(nsIURI* aUri, nsIPrincipal* aSourcePrincipal,
+                       txStylesheetCompiler* aCompiler);
 
 private:
     RefPtr<txMozillaXSLTProcessor> mProcessor;
     nsCOMPtr<nsIDocument> mLoaderDocument;
 
     // This exists solely to suppress a warning from nsDerivedSafe
     txCompileObserver();
 
@@ -394,40 +403,25 @@ private:
 txCompileObserver::txCompileObserver(txMozillaXSLTProcessor* aProcessor,
                                      nsIDocument* aLoaderDocument)
     : mProcessor(aProcessor),
       mLoaderDocument(aLoaderDocument)
 {
 }
 
 nsresult
-txCompileObserver::loadURI(const nsAString& aUri,
-                           const nsAString& aReferrerUri,
-                           ReferrerPolicy aReferrerPolicy,
+txCompileObserver::loadURI(nsIURI* aUri,
+                           nsIPrincipal* aReferrerPrincipal,
                            txStylesheetCompiler* aCompiler)
 {
-    if (mProcessor->IsLoadDisabled()) {
+    if (mProcessor->IsLoadDisabled() || !mLoaderDocument) {
         return NS_ERROR_XSLT_LOAD_BLOCKED_ERROR;
     }
 
-    nsCOMPtr<nsIURI> uri;
-    nsresult rv = NS_NewURI(getter_AddRefs(uri), aUri);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsCOMPtr<nsIURI> referrerUri;
-    rv = NS_NewURI(getter_AddRefs(referrerUri), aReferrerUri);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsCOMPtr<nsIPrincipal> referrerPrincipal;
-    rv = nsContentUtils::GetSecurityManager()->
-      GetSimpleCodebasePrincipal(referrerUri,
-                                 getter_AddRefs(referrerPrincipal));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    return startLoad(uri, aCompiler, referrerPrincipal, aReferrerPolicy);
+    return startLoad(aUri, aReferrerPrincipal, aCompiler);
 }
 
 void
 txCompileObserver::onDoneCompiling(txStylesheetCompiler* aCompiler,
                                    nsresult aResult,
                                    const char16_t *aErrorText,
                                    const char16_t *aParam)
 {
@@ -435,20 +429,21 @@ txCompileObserver::onDoneCompiling(txSty
         mProcessor->setStylesheet(aCompiler->getStylesheet());
     }
     else {
         mProcessor->reportError(aResult, aErrorText, aParam);
     }
 }
 
 nsresult
-txCompileObserver::startLoad(nsIURI* aUri, txStylesheetCompiler* aCompiler,
-                             nsIPrincipal* aReferrerPrincipal,
-                             ReferrerPolicy aReferrerPolicy)
+txCompileObserver::startLoad(nsIURI* aUri, nsIPrincipal* aReferrerPrincipal,
+                             txStylesheetCompiler* aCompiler)
 {
+    MOZ_ASSERT(aReferrerPrincipal);
+
     nsCOMPtr<nsILoadGroup> loadGroup = mLoaderDocument->GetDocumentLoadGroup();
     if (!loadGroup) {
         return NS_ERROR_FAILURE;
     }
 
     nsCOMPtr<nsIChannel> channel;
     nsresult rv = NS_NewChannelWithTriggeringPrincipal(
                     getter_AddRefs(channel),
@@ -467,55 +462,57 @@ txCompileObserver::startLoad(nsIURI* aUr
     if (httpChannel) {
         httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
                                       NS_LITERAL_CSTRING("*/*"),
                                       false);
 
         nsCOMPtr<nsIURI> referrerURI;
         aReferrerPrincipal->GetURI(getter_AddRefs(referrerURI));
         if (referrerURI) {
-            httpChannel->SetReferrerWithPolicy(referrerURI, aReferrerPolicy);
+            httpChannel->SetReferrerWithPolicy(referrerURI,
+                mLoaderDocument->GetReferrerPolicy());
         }
     }
 
     nsCOMPtr<nsIParser> parser = do_CreateInstance(kCParserCID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     RefPtr<txStylesheetSink> sink = new txStylesheetSink(aCompiler, parser);
-    NS_ENSURE_TRUE(sink, NS_ERROR_OUT_OF_MEMORY);
 
     channel->SetNotificationCallbacks(sink);
 
     parser->SetCommand(kLoadAsData);
     parser->SetContentSink(sink);
     parser->Parse(aUri);
 
     return channel->AsyncOpen2(sink);
 }
 
 nsresult
 TX_LoadSheet(nsIURI* aUri, txMozillaXSLTProcessor* aProcessor,
-             nsIDocument* aLoaderDocument, ReferrerPolicy aReferrerPolicy)
+             nsIDocument* aLoaderDocument)
 {
-    nsIPrincipal* principal = aLoaderDocument->NodePrincipal();
-
-    nsAutoCString spec;
-    aUri->GetSpec(spec);
-    MOZ_LOG(txLog::xslt, LogLevel::Info, ("TX_LoadSheet: %s\n", spec.get()));
+    if (MOZ_LOG_TEST(txLog::xslt, LogLevel::Info)) {
+        nsAutoCString spec;
+        aUri->GetSpec(spec);
+        MOZ_LOG(txLog::xslt, LogLevel::Info,
+                ("TX_LoadSheet: %s\n", spec.get()));
+    }
 
     RefPtr<txCompileObserver> observer =
         new txCompileObserver(aProcessor, aLoaderDocument);
-    NS_ENSURE_TRUE(observer, NS_ERROR_OUT_OF_MEMORY);
+
+    nsAutoCString fragment;
+    aUri->GetRef(fragment);
 
     RefPtr<txStylesheetCompiler> compiler =
-        new txStylesheetCompiler(NS_ConvertUTF8toUTF16(spec), aReferrerPolicy,
-                                 observer);
-    NS_ENSURE_TRUE(compiler, NS_ERROR_OUT_OF_MEMORY);
+        new txStylesheetCompiler(NS_ConvertUTF8toUTF16(fragment), observer);
 
-    return observer->startLoad(aUri, compiler, principal, aReferrerPolicy);
+    return observer->startLoad(aUri, aLoaderDocument->NodePrincipal(),
+                               compiler);
 }
 
 /**
  * handling DOM->txStylesheet
  * Observer needs to do synchronous loads.
  */
 static nsresult
 handleNode(nsINode* aNode, txStylesheetCompiler* aCompiler)
@@ -579,138 +576,99 @@ handleNode(nsINode* aNode, txStylesheetC
     }
 
     return NS_OK;
 }
 
 class txSyncCompileObserver final : public txACompileObserver
 {
 public:
-    explicit txSyncCompileObserver(txMozillaXSLTProcessor* aProcessor);
+    explicit txSyncCompileObserver(txMozillaXSLTProcessor* aProcessor,
+                                   nsIDocument* aLoaderDocument)
+        : mProcessor(aProcessor),
+          mLoaderDocument(aLoaderDocument)
+    {}
 
     TX_DECL_ACOMPILEOBSERVER
     NS_INLINE_DECL_REFCOUNTING(txSyncCompileObserver)
 
 private:
     // Private destructor, to discourage deletion outside of Release():
     ~txSyncCompileObserver()
     {
     }
 
     RefPtr<txMozillaXSLTProcessor> mProcessor;
+    nsCOMPtr<nsIDocument> mLoaderDocument;
 };
 
-txSyncCompileObserver::txSyncCompileObserver(txMozillaXSLTProcessor* aProcessor)
-  : mProcessor(aProcessor)
-{
-}
-
 nsresult
-txSyncCompileObserver::loadURI(const nsAString& aUri,
-                               const nsAString& aReferrerUri,
-                               ReferrerPolicy aReferrerPolicy,
+txSyncCompileObserver::loadURI(nsIURI* aUri,
+                               nsIPrincipal* aReferrerPrincipal,
                                txStylesheetCompiler* aCompiler)
 {
-    if (mProcessor->IsLoadDisabled()) {
+    MOZ_ASSERT(aReferrerPrincipal);
+
+    if (mProcessor->IsLoadDisabled() || !mLoaderDocument) {
         return NS_ERROR_XSLT_LOAD_BLOCKED_ERROR;
     }
 
-    nsCOMPtr<nsIURI> uri;
-    nsresult rv = NS_NewURI(getter_AddRefs(uri), aUri);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsCOMPtr<nsIURI> referrerUri;
-    rv = NS_NewURI(getter_AddRefs(referrerUri), aReferrerUri);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsCOMPtr<nsIPrincipal> referrerPrincipal;
-    rv = nsContentUtils::GetSecurityManager()->
-      GetSimpleCodebasePrincipal(referrerUri,
-                                 getter_AddRefs(referrerPrincipal));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // This is probably called by js, a loadGroup for the channel doesn't
-    // make sense.
-    nsCOMPtr<nsINode> source;
-    if (mProcessor) {
-      source =
-        do_QueryInterface(mProcessor->GetSourceContentModel());
-    }
-    nsAutoSyncOperation sync(source ? source->OwnerDoc() : nullptr);
+    nsAutoSyncOperation sync(mLoaderDocument);
     nsCOMPtr<nsIDOMDocument> document;
 
-    rv = nsSyncLoadService::LoadDocument(uri, nsIContentPolicy::TYPE_XSLT,
-                                         referrerPrincipal,
-                                         nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS,
-                                         nullptr, false,
-                                         aReferrerPolicy,
-                                         getter_AddRefs(document));
+    nsCOMPtr<nsILoadGroup> loadGroup = mLoaderDocument->GetDocumentLoadGroup();
+    nsresult rv =
+        nsSyncLoadService::LoadDocument(aUri,
+                                        nsIContentPolicy::TYPE_XSLT,
+                                        aReferrerPrincipal,
+                                        nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS,
+                                        loadGroup,
+                                        false,
+                                        mLoaderDocument->GetReferrerPolicy(),
+                                        getter_AddRefs(document));
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIDocument> doc = do_QueryInterface(document);
+    nsCOMPtr<nsIURI> baseURI = doc->GetBaseURI();
+    aCompiler->setBaseURI(baseURI);
+    aCompiler->setPrincipal(doc->NodePrincipal());
     rv = handleNode(doc, aCompiler);
     if (NS_FAILED(rv)) {
         nsAutoCString spec;
-        uri->GetSpec(spec);
+        aUri->GetSpec(spec);
         aCompiler->cancel(rv, nullptr, NS_ConvertUTF8toUTF16(spec).get());
         return rv;
     }
 
     rv = aCompiler->doneLoading();
     return rv;
 }
 
 void txSyncCompileObserver::onDoneCompiling(txStylesheetCompiler* aCompiler,
                                             nsresult aResult,
                                             const char16_t *aErrorText,
                                             const char16_t *aParam)
 {
 }
 
 nsresult
-TX_CompileStylesheet(nsINode* aNode, txMozillaXSLTProcessor* aProcessor,
+TX_CompileStylesheet(nsINode* aNode,
+                     nsIDocument* aLoaderDocument,
+                     txMozillaXSLTProcessor* aProcessor,
                      txStylesheet** aStylesheet)
 {
-    // If we move GetBaseURI to nsINode this can be simplified.
-    nsCOMPtr<nsIDocument> doc = aNode->OwnerDoc();
-
-    nsCOMPtr<nsIURI> uri;
-    if (aNode->IsNodeOfType(nsINode::eCONTENT)) {
-      uri = static_cast<nsIContent*>(aNode)->GetBaseURI();
-    }
-    else { 
-      NS_ASSERTION(aNode->IsNodeOfType(nsINode::eDOCUMENT), "not a doc");
-      uri = static_cast<nsIDocument*>(aNode)->GetBaseURI();
-    }
-    NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
-    
-    nsAutoCString spec;
-    uri->GetSpec(spec);
-    NS_ConvertUTF8toUTF16 baseURI(spec);
-
-    nsIURI* docUri = doc->GetDocumentURI();
-    NS_ENSURE_TRUE(docUri, NS_ERROR_FAILURE);
-
-    // We need to remove the ref, a URI with a ref would mean that we have an
-    // embedded stylesheet.
-    docUri->CloneIgnoringRef(getter_AddRefs(uri));
-    NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
-
-    uri->GetSpec(spec);
-    NS_ConvertUTF8toUTF16 stylesheetURI(spec);
-
     RefPtr<txSyncCompileObserver> obs =
-        new txSyncCompileObserver(aProcessor);
-    NS_ENSURE_TRUE(obs, NS_ERROR_OUT_OF_MEMORY);
+        new txSyncCompileObserver(aProcessor, aLoaderDocument);
 
     RefPtr<txStylesheetCompiler> compiler =
-        new txStylesheetCompiler(stylesheetURI, doc->GetReferrerPolicy(), obs);
-    NS_ENSURE_TRUE(compiler, NS_ERROR_OUT_OF_MEMORY);
+        new txStylesheetCompiler(EmptyString(), obs);
 
+    nsCOMPtr<nsIURI> baseURI = aNode->GetBaseURI();
     compiler->setBaseURI(baseURI);
+    compiler->setPrincipal(aNode->NodePrincipal());
 
     nsresult rv = handleNode(aNode, compiler);
     if (NS_FAILED(rv)) {
         compiler->cancel(rv);
         return rv;
     }
 
     rv = compiler->doneLoading();
--- a/dom/xslt/xslt/txMozillaXSLTProcessor.cpp
+++ b/dom/xslt/xslt/txMozillaXSLTProcessor.cpp
@@ -356,16 +356,30 @@ txMozillaXSLTProcessor::txMozillaXSLTPro
   : mOwner(aOwner),
     mStylesheetDocument(nullptr),
     mTransformResult(NS_OK),
     mCompileResult(NS_OK),
     mFlags(0)
 {
 }
 
+NS_IMETHODIMP
+txMozillaXSLTProcessor::Init(nsISupports* aOwner)
+{
+    mOwner = aOwner;
+    if (nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aOwner)) {
+        if (win->IsOuterWindow()) {
+            // Must be bound to inner window, innerize if necessary.
+            mOwner = win->GetCurrentInnerWindow();
+        }
+    }
+
+    return NS_OK;
+}
+
 txMozillaXSLTProcessor::~txMozillaXSLTProcessor()
 {
     if (mStylesheetDocument) {
         mStylesheetDocument->RemoveMutationObserver(this);
     }
 }
 
 NS_IMETHODIMP
@@ -599,17 +613,17 @@ txMozillaXSLTProcessor::ImportStylesheet
     }
     
     nsCOMPtr<nsINode> styleNode = do_QueryInterface(aStyle);
     NS_ENSURE_TRUE(styleNode &&
                    (styleNode->IsElement() ||
                     styleNode->IsNodeOfType(nsINode::eDOCUMENT)),
                    NS_ERROR_INVALID_ARG);
 
-    nsresult rv = TX_CompileStylesheet(styleNode, this,
+    nsresult rv = TX_CompileStylesheet(styleNode, getLoaderDoc(), this,
                                        getter_AddRefs(mStylesheet));
     // XXX set up exception context, bug 204658
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (styleNode->IsElement()) {
         mStylesheetDocument = styleNode->OwnerDoc();
         NS_ENSURE_TRUE(mStylesheetDocument, NS_ERROR_UNEXPECTED);
 
@@ -654,17 +668,17 @@ txMozillaXSLTProcessor::TransformToDoc(n
     }
 
     nsCOMPtr<nsIDOMDocument> sourceDOMDocument;
     mSource->GetOwnerDocument(getter_AddRefs(sourceDOMDocument));
     if (!sourceDOMDocument) {
         sourceDOMDocument = do_QueryInterface(mSource);
     }
 
-    txExecutionState es(mStylesheet, IsLoadDisabled());
+    txExecutionState es(mStylesheet, IsLoadDisabled(), getLoaderDoc());
 
     // XXX Need to add error observers
 
     // If aResult is non-null, we're a data document
     txToDocHandlerFactory handlerFactory(&es, sourceDOMDocument, mObserver,
                                          aCreateDataDocument);
     es.mOutputHandlerFactory = &handlerFactory;
 
@@ -722,17 +736,17 @@ txMozillaXSLTProcessor::TransformToFragm
     nsresult rv = ensureStylesheet();
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsAutoPtr<txXPathNode> sourceNode(txXPathNativeNode::createXPathNode(aSource));
     if (!sourceNode) {
         return NS_ERROR_OUT_OF_MEMORY;
     }
 
-    txExecutionState es(mStylesheet, IsLoadDisabled());
+    txExecutionState es(mStylesheet, IsLoadDisabled(), getLoaderDoc());
 
     // XXX Need to add error observers
 
     rv = aOutput->CreateDocumentFragment(aResult);
     NS_ENSURE_SUCCESS(rv, rv);
     txToFragmentHandlerFactory handlerFactory(*aResult);
     es.mOutputHandlerFactory = &handlerFactory;
 
@@ -1031,22 +1045,17 @@ txMozillaXSLTProcessor::GetFlags(uint32_
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 txMozillaXSLTProcessor::LoadStyleSheet(nsIURI* aUri,
                                        nsIDocument* aLoaderDocument)
 {
-    mozilla::net::ReferrerPolicy refpol = mozilla::net::RP_Default;
-    if (mStylesheetDocument) {
-        refpol = mStylesheetDocument->GetReferrerPolicy();
-    }
-
-    nsresult rv = TX_LoadSheet(aUri, this, aLoaderDocument, refpol);
+    nsresult rv = TX_LoadSheet(aUri, this, aLoaderDocument);
     if (NS_FAILED(rv) && mObserver) {
         // This is most likely a network or security error, just
         // use the uri as context.
         nsAutoCString spec;
         aUri->GetSpec(spec);
         CopyUTF8toUTF16(spec, mSourceText);
         nsresult status = NS_ERROR_GET_MODULE(rv) == NS_ERROR_MODULE_XSLT ? rv :
                           NS_ERROR_XSLT_NETWORK_ERROR;
@@ -1193,31 +1202,50 @@ txMozillaXSLTProcessor::notifyError()
     MOZ_ASSERT(document->GetReadyStateEnum() ==
                  nsIDocument::READYSTATE_LOADING,
                "Bad readyState.");
     document->SetReadyStateInternal(nsIDocument::READYSTATE_INTERACTIVE);
 
     mObserver->OnTransformDone(mTransformResult, document);
 }
 
+nsIDocument*
+txMozillaXSLTProcessor::getLoaderDoc()
+{
+    if (mOwner) {
+        nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(mOwner);
+        if (win) {
+            return win->GetExtantDoc();
+        }
+    }
+
+    if (mSource) {