Merge mozilla-central to autoland
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 29 Sep 2016 11:50:17 +0200
changeset 315757 c81754c082f08d5e95595cb1ee09d67f17eaa7b0
parent 315756 022b1fcfe8337d5bccf0e341626ab0a4cb32533e (current diff)
parent 315728 f7d5008ee2ab9200052e45ad6ecc3f3a348f7f86 (diff)
child 315758 a7c3db5394f216f41c4bd92bd4f70004fed11afe
push id20634
push usercbook@mozilla.com
push dateFri, 30 Sep 2016 10:10:13 +0000
treeherderfx-team@afe79b010d13 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone52.0a1
Merge mozilla-central to autoland
config/external/sqlite/Makefile.in
dom/tests/mochitest/geolocation/chrome.ini
dom/tests/mochitest/geolocation/test_mozsettings.html
dom/tests/mochitest/geolocation/test_mozsettingsWatch.html
dom/webidl/SVGDocument.webidl
layout/style/test/test_transitions_with_displaynone.html
testing/web-platform/meta/XMLHttpRequest/progress-events-response-data-gzip.htm.ini
testing/web-platform/meta/html/semantics/embedded-content/the-img-element/environment-changes/viewport-change.html.ini
--- a/accessible/windows/msaa/EnumVariant.cpp
+++ b/accessible/windows/msaa/EnumVariant.cpp
@@ -9,20 +9,18 @@
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // ChildrenEnumVariant
 ////////////////////////////////////////////////////////////////////////////////
 
 IMPL_IUNKNOWN_QUERY_HEAD(ChildrenEnumVariant)
-IMPL_IUNKNOWN_QUERY_IFACE(IEnumVARIANT);
-IMPL_IUNKNOWN_QUERY_IFACE(IUnknown);
-IMPL_IUNKNOWN_QUERY_AGGR_COND(mAnchorAcc, !mAnchorAcc->IsDefunct());
-IMPL_IUNKNOWN_QUERY_TAIL
+IMPL_IUNKNOWN_QUERY_IFACE(IEnumVARIANT)
+IMPL_IUNKNOWN_QUERY_TAIL_AGGREGATED(mAnchorAcc)
 
 STDMETHODIMP
 ChildrenEnumVariant::Next(ULONG aCount, VARIANT FAR* aItems,
                           ULONG FAR* aCountFetched)
 {
   A11Y_TRYBLOCK_BEGIN
 
   if (!aItems || !aCountFetched)
--- a/caps/BasePrincipal.cpp
+++ b/caps/BasePrincipal.cpp
@@ -61,16 +61,23 @@ PrincipalOriginAttributes::InheritFromNe
   mUserContextId = aAttrs.mUserContextId;
   mSignedPkg = aAttrs.mSignedPkg;
 
   mPrivateBrowsingId = aAttrs.mPrivateBrowsingId;
   mFirstPartyDomain = aAttrs.mFirstPartyDomain;
 }
 
 void
+PrincipalOriginAttributes::StripUserContextIdAndFirstPartyDomain()
+{
+  mUserContextId = nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID;
+  mFirstPartyDomain.Truncate();
+}
+
+void
 DocShellOriginAttributes::InheritFromDocToChildDocShell(const PrincipalOriginAttributes& aAttrs)
 {
   mAppId = aAttrs.mAppId;
   mInIsolatedMozBrowser = aAttrs.mInIsolatedMozBrowser;
 
   // addonId is computed from the principal URI and never propagated
   mUserContextId = aAttrs.mUserContextId;
 
@@ -712,16 +719,33 @@ BasePrincipal::CreateCodebasePrincipal(c
 
   nsCOMPtr<nsIURI> uri;
   nsresult rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix);
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   return BasePrincipal::CreateCodebasePrincipal(uri, attrs);
 }
 
+already_AddRefed<BasePrincipal>
+BasePrincipal::CloneStrippingUserContextIdAndFirstPartyDomain()
+{
+  PrincipalOriginAttributes attrs = OriginAttributesRef();
+  attrs.StripUserContextIdAndFirstPartyDomain();
+
+  nsAutoCString originNoSuffix;
+  nsresult rv = GetOriginNoSuffix(originNoSuffix);
+  NS_ENSURE_SUCCESS(rv, nullptr);
+
+  nsCOMPtr<nsIURI> uri;
+  rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix);
+  NS_ENSURE_SUCCESS(rv, nullptr);
+
+  return BasePrincipal::CreateCodebasePrincipal(uri, attrs);
+}
+
 bool
 BasePrincipal::AddonAllowsLoad(nsIURI* aURI)
 {
   if (mOriginAttributes.mAddonId.IsEmpty()) {
     return false;
   }
 
   nsCOMPtr<nsIAddonPolicyService> aps = do_GetService("@mozilla.org/addons/policy-service;1");
--- a/caps/BasePrincipal.h
+++ b/caps/BasePrincipal.h
@@ -99,16 +99,18 @@ public:
   //
   // @param aAttrs  Origin Attributes of the docshell.
   // @param aURI    The URI of the document.
   void InheritFromDocShellToDoc(const DocShellOriginAttributes& aAttrs,
                                 const nsIURI* aURI);
 
   // Inherit OriginAttributes from Necko.
   void InheritFromNecko(const NeckoOriginAttributes& aAttrs);
+
+  void StripUserContextIdAndFirstPartyDomain();
 };
 
 // For OriginAttributes stored on docshells / loadcontexts / browsing contexts.
 class DocShellOriginAttributes : public OriginAttributes
 {
 public:
   DocShellOriginAttributes() {}
   DocShellOriginAttributes(uint32_t aAppId, bool aInIsolatedMozBrowser)
@@ -309,16 +311,18 @@ public:
     eNullPrincipal,
     eCodebasePrincipal,
     eExpandedPrincipal,
     eSystemPrincipal
   };
 
   virtual PrincipalKind Kind() = 0;
 
+  already_AddRefed<BasePrincipal> CloneStrippingUserContextIdAndFirstPartyDomain();
+
 protected:
   virtual ~BasePrincipal();
 
   virtual nsresult GetOriginInternal(nsACString& aOrigin) = 0;
   virtual bool SubsumesInternal(nsIPrincipal* aOther, DocumentDomainConsideration aConsider) = 0;
 
   // Internal, side-effect-free check to determine whether the concrete
   // principal would allow the load ignoring any common behavior implemented in
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -142,24 +142,25 @@ GetPrincipalDomainOrigin(nsIPrincipal* a
   if (!uri) {
     aPrincipal->GetURI(getter_AddRefs(uri));
   }
   NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
 
   return GetOriginFromURI(uri, aOrigin);
 }
 
-inline void SetPendingException(JSContext *cx, const char *aMsg)
+inline void SetPendingExceptionASCII(JSContext *cx, const char *aMsg)
 {
-    JS_ReportError(cx, "%s", aMsg);
+    JS_ReportErrorASCII(cx, "%s", aMsg);
 }
 
 inline void SetPendingException(JSContext *cx, const char16_t *aMsg)
 {
-    JS_ReportError(cx, "%hs", aMsg);
+    // FIXME: Need to convert to UTF-8 (bug XXX).
+    JS_ReportErrorLatin1(cx, "%hs", aMsg);
 }
 
 // Helper class to get stuff from the ClassInfo and not waste extra time with
 // virtual method calls for things it has already gotten
 class ClassInfoData
 {
 public:
     ClassInfoData(nsIClassInfo *aClassInfo, const char *aName)
@@ -591,17 +592,17 @@ nsScriptSecurityManager::CheckLoadURIFro
 
     // Report error.
     nsAutoCString spec;
     if (NS_FAILED(aURI->GetAsciiSpec(spec)))
         return NS_ERROR_FAILURE;
     nsAutoCString msg("Access to '");
     msg.Append(spec);
     msg.AppendLiteral("' from script denied");
-    SetPendingException(cx, msg.get());
+    SetPendingExceptionASCII(cx, msg.get());
     return NS_ERROR_DOM_BAD_URI;
 }
 
 /**
  * Helper method to handle cases where a flag passed to
  * CheckLoadURIWithPrincipal means denying loading if the given URI has certain
  * nsIProtocolHandler flags set.
  * @return if success, access is allowed. Otherwise, deny access
@@ -1300,34 +1301,34 @@ nsScriptSecurityManager::CanCreateInstan
         return NS_OK;
     }
 
     //-- Access denied, report an error
     nsAutoCString errorMsg("Permission denied to create instance of class. CID=");
     char cidStr[NSID_LENGTH];
     aCID.ToProvidedString(cidStr);
     errorMsg.Append(cidStr);
-    SetPendingException(cx, errorMsg.get());
+    SetPendingExceptionASCII(cx, errorMsg.get());
     return NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED;
 }
 
 NS_IMETHODIMP
 nsScriptSecurityManager::CanGetService(JSContext *cx,
                                        const nsCID &aCID)
 {
     if (nsContentUtils::IsCallerChrome()) {
         return NS_OK;
     }
 
     //-- Access denied, report an error
     nsAutoCString errorMsg("Permission denied to get service. CID=");
     char cidStr[NSID_LENGTH];
     aCID.ToProvidedString(cidStr);
     errorMsg.Append(cidStr);
-    SetPendingException(cx, errorMsg.get());
+    SetPendingExceptionASCII(cx, errorMsg.get());
     return NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED;
 }
 
 /////////////////////////////////////
 // Method implementing nsIObserver //
 /////////////////////////////////////
 const char sJSEnabledPrefName[] = "javascript.enabled";
 const char sFileOriginPolicyPrefName[] =
deleted file mode 100644
--- a/config/external/sqlite/Makefile.in
+++ /dev/null
@@ -1,5 +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/.
-
-LIB_IS_C_ONLY    = 1
--- a/devtools/client/debugger/test/mochitest/browser_dbg_instruments-pane-collapse.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_instruments-pane-collapse.js
@@ -8,32 +8,35 @@
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_recursion-stack.html";
 
 var gTab, gPanel, gDebugger;
 var gPrefs, gOptions;
 
 function test() {
-  let options = {
-    source: TAB_URL,
-    line: 1
-  };
-  initDebugger(TAB_URL, options).then(([aTab,, aPanel]) => {
+  Task.spawn(function* () {
+    let options = {
+      source: TAB_URL,
+      line: 1
+    };
+
+    let [aTab,, aPanel] = yield initDebugger(TAB_URL, options);
+
     gTab = aTab;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gPrefs = gDebugger.Prefs;
     gOptions = gDebugger.DebuggerView.Options;
 
     testPanesState();
 
     gDebugger.DebuggerView.toggleInstrumentsPane({ visible: true, animated: false });
 
-    testInstrumentsPaneCollapse();
+    yield testInstrumentsPaneCollapse();
     testPanesStartupPref();
 
     closeDebuggerAndFinish(gPanel);
   });
 }
 
 function testPanesState() {
   let instrumentsPane =
@@ -45,17 +48,17 @@ function testPanesState() {
      instrumentsPaneToggleButton.classList.contains("pane-collapsed"),
     "The debugger view instruments pane should initially be hidden.");
   is(gPrefs.panesVisibleOnStartup, false,
     "The debugger view instruments pane should initially be preffed as hidden.");
   isnot(gOptions._showPanesOnStartupItem.getAttribute("checked"), "true",
     "The options menu item should not be checked.");
 }
 
-function testInstrumentsPaneCollapse() {
+function* testInstrumentsPaneCollapse () {
   let instrumentsPane =
     gDebugger.document.getElementById("instruments-pane");
   let instrumentsPaneToggleButton =
     gDebugger.document.getElementById("instruments-pane-toggle");
 
   let width = parseInt(instrumentsPane.getAttribute("width"));
   is(width, gPrefs.instrumentsWidth,
     "The instruments pane has an incorrect width.");
@@ -64,31 +67,37 @@ function testInstrumentsPaneCollapse() {
   is(instrumentsPane.style.marginRight, "0px",
     "The instruments pane has an incorrect right margin.");
   ok(!instrumentsPane.hasAttribute("animated"),
     "The instruments pane has an incorrect animated attribute.");
   ok(!instrumentsPane.classList.contains("pane-collapsed") &&
      !instrumentsPaneToggleButton.classList.contains("pane-collapsed"),
     "The instruments pane should at this point be visible.");
 
+  // Trigger reflow to make sure the UI is in required state.
+  gDebugger.document.documentElement.getBoundingClientRect();
+
   gDebugger.DebuggerView.toggleInstrumentsPane({ visible: false, animated: true });
 
+  yield once(instrumentsPane, "transitionend");
+
   is(gPrefs.panesVisibleOnStartup, false,
     "The debugger view panes should still initially be preffed as hidden.");
   isnot(gOptions._showPanesOnStartupItem.getAttribute("checked"), "true",
     "The options menu item should still not be checked.");
 
   let margin = -(width + 1) + "px";
   is(width, gPrefs.instrumentsWidth,
     "The instruments pane has an incorrect width after collapsing.");
   is(instrumentsPane.style.marginLeft, margin,
     "The instruments pane has an incorrect left margin after collapsing.");
   is(instrumentsPane.style.marginRight, margin,
     "The instruments pane has an incorrect right margin after collapsing.");
-  ok(instrumentsPane.hasAttribute("animated"),
+
+  ok(!instrumentsPane.hasAttribute("animated"),
     "The instruments pane has an incorrect attribute after an animated collapsing.");
   ok(instrumentsPane.classList.contains("pane-collapsed") &&
      instrumentsPaneToggleButton.classList.contains("pane-collapsed"),
     "The instruments pane should not be visible after collapsing.");
 
   gDebugger.DebuggerView.toggleInstrumentsPane({ visible: true, animated: false });
 
   is(gPrefs.panesVisibleOnStartup, false,
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -330,16 +330,17 @@ Toolbox.prototype = {
   },
 
   /**
    * Get the toggled state of the split console
    */
   get splitConsole() {
     return this._splitConsole;
   },
+
   /**
    * Get the focused state of the split console
    */
   isSplitConsoleFocused: function () {
     if (!this._splitConsole) {
       return false;
     }
     let focusedWin = Services.focus.focusedWindow;
@@ -1262,16 +1263,26 @@ Toolbox.prototype = {
       // Prevent flicker while loading by waiting to make visible until now.
       iframe.style.visibility = "visible";
 
       // The build method should return a panel instance, so events can
       // be fired with the panel as an argument. However, in order to keep
       // backward compatibility with existing extensions do a check
       // for a promise return value.
       let built = definition.build(iframe.contentWindow, this);
+
+      // Set the dir attribute on the documents of panels using HTML.
+      let docEl = iframe.contentWindow && iframe.contentWindow.document.documentElement;
+      if (docEl && docEl.namespaceURI === HTML_NS) {
+        let top = this.win.top;
+        let topDocEl = top.document.documentElement;
+        let isRtl = top.getComputedStyle(topDocEl).direction === "rtl";
+        docEl.setAttribute("dir", isRtl ? "rtl" : "ltr");
+      }
+
       if (!(typeof built.then == "function")) {
         let panel = built;
         iframe.panel = panel;
 
         // The panel instance is expected to fire (and listen to) various
         // framework events, so make sure it's properly decorated with
         // appropriate API (on, off, once, emit).
         // In this case we decorate panel instances directly returned by
--- a/devtools/client/inspector/markup/markup.js
+++ b/devtools/client/inspector/markup/markup.js
@@ -425,18 +425,16 @@ MarkupView.prototype = {
   _showBoxModel: function (nodeFront) {
     return this._inspector.toolbox.highlighterUtils
       .highlightNodeFront(nodeFront);
   },
 
   /**
    * Hide the box model highlighter on a given node front
    *
-   * @param  {NodeFront} nodeFront
-   *         The node to hide the highlighter for
    * @param  {Boolean} forceHide
    *         See toolbox-highlighter-utils/unhighlight
    * @return {Promise} Resolves when the highlighter for this nodeFront is
    *         hidden, taking into account that there could already be highlighter
    *         requests queued up
    */
   _hideBoxModel: function (forceHide) {
     return this._inspector.toolbox.highlighterUtils.unhighlight(forceHide);
--- a/devtools/client/inspector/rules/test/browser.ini
+++ b/devtools/client/inspector/rules/test/browser.ini
@@ -194,16 +194,17 @@ skip-if = (os == "win" && debug) # bug 9
 [browser_rules_search-filter_context-menu.js]
 subsuite = clipboard
 [browser_rules_search-filter_escape-keypress.js]
 [browser_rules_select-and-copy-styles.js]
 subsuite = clipboard
 [browser_rules_selector-highlighter_01.js]
 [browser_rules_selector-highlighter_02.js]
 [browser_rules_selector-highlighter_03.js]
+[browser_rules_selector-highlighter_04.js]
 [browser_rules_selector_highlight.js]
 [browser_rules_strict-search-filter-computed-list_01.js]
 [browser_rules_strict-search-filter_01.js]
 [browser_rules_strict-search-filter_02.js]
 [browser_rules_strict-search-filter_03.js]
 [browser_rules_style-editor-link.js]
 [browser_rules_urls-clickable.js]
 [browser_rules_user-agent-styles.js]
--- a/devtools/client/inspector/rules/test/browser_rules_selector-highlighter_02.js
+++ b/devtools/client/inspector/rules/test/browser_rules_selector-highlighter_02.js
@@ -71,14 +71,8 @@ add_task(function* () {
   yield selectNode("body", inspector);
   icon = getRuleViewSelectorHighlighterIcon(view, "body");
   yield clickSelectorIcon(icon, view);
   is(HighlighterFront.nodeFront.tagName, "BODY",
     "The right NodeFront is passed to the highlighter (2)");
   is(HighlighterFront.options.selector, "body",
     "The right selector option is passed to the highlighter (2)");
 });
-
-function* clickSelectorIcon(icon, view) {
-  let onToggled = view.once("ruleview-selectorhighlighter-toggled");
-  EventUtils.synthesizeMouseAtCenter(icon, {}, view.styleWindow);
-  yield onToggled;
-}
--- a/devtools/client/inspector/rules/test/browser_rules_selector-highlighter_03.js
+++ b/devtools/client/inspector/rules/test/browser_rules_selector-highlighter_03.js
@@ -71,14 +71,8 @@ add_task(function* () {
 
   info("Switching back to .node-1 and clicking on the div selector");
   yield selectNode(".node-1", inspector);
   icon = getRuleViewSelectorHighlighterIcon(view, "div");
   yield clickSelectorIcon(icon, view);
   ok(!HighlighterFront.isShown,
     "The highlighter is hidden now that the same selector was clicked");
 });
-
-function* clickSelectorIcon(icon, view) {
-  let onToggled = view.once("ruleview-selectorhighlighter-toggled");
-  EventUtils.synthesizeMouseAtCenter(icon, {}, view.styleWindow);
-  yield onToggled;
-}
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/rules/test/browser_rules_selector-highlighter_04.js
@@ -0,0 +1,53 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that the selector highlighter is shown when clicking on a selector icon
+// for the 'element {}' rule
+
+// Note that in this test, we mock the highlighter front, merely testing the
+// behavior of the style-inspector UI for now
+
+const TEST_URI = `
+<p>Testing the selector highlighter for the 'element {}' rule</p>
+`;
+
+add_task(function* () {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let {inspector, view} = yield openRuleView();
+
+  // Mock the highlighter front to get the reference of the NodeFront
+  let HighlighterFront = {
+    isShown: false,
+    nodeFront: null,
+    options: null,
+    show: function (nodeFront, options) {
+      this.nodeFront = nodeFront;
+      this.options = options;
+      this.isShown = true;
+    },
+    hide: function () {
+      this.nodeFront = null;
+      this.options = null;
+      this.isShown = false;
+    }
+  };
+  // Inject the mock highlighter in the rule-view
+  view.selectorHighlighter = HighlighterFront;
+
+  info("Checking that the right NodeFront reference and options are passed");
+  yield selectNode("p", inspector);
+  let icon = getRuleViewSelectorHighlighterIcon(view, "element");
+
+  yield clickSelectorIcon(icon, view);
+  is(HighlighterFront.nodeFront.tagName, "P",
+     "The right NodeFront is passed to the highlighter (1)");
+  is(HighlighterFront.options.selector, "body > p:nth-child(1)",
+     "The right selector option is passed to the highlighter (1)");
+  ok(HighlighterFront.isShown, "The toggle event says the highlighter is visible");
+
+  yield clickSelectorIcon(icon, view);
+  ok(!HighlighterFront.isShown, "The toggle event says the highlighter is not visible");
+});
--- a/devtools/client/inspector/rules/test/head.js
+++ b/devtools/client/inspector/rules/test/head.js
@@ -813,16 +813,27 @@ function waitForStyleModification(inspec
         }
       }
     }
     inspector.on("markupmutation", checkForStyleModification);
   });
 }
 
 /**
+ * Click on the selector icon
+ * @param {DOMNode} icon
+ * @param {CSSRuleView} view
+ */
+function* clickSelectorIcon(icon, view) {
+  let onToggled = view.once("ruleview-selectorhighlighter-toggled");
+  EventUtils.synthesizeMouseAtCenter(icon, {}, view.styleWindow);
+  yield onToggled;
+}
+
+/**
  * Make sure window is properly focused before sending a key event.
  * @param {Window} win
  * @param {Event} key
  */
 function focusAndSendKey(win, key) {
   win.document.documentElement.focus();
   EventUtils.sendKey(key, win);
 }
--- a/devtools/client/inspector/rules/views/rule-editor.js
+++ b/devtools/client/inspector/rules/views/rule-editor.js
@@ -144,19 +144,20 @@ RuleEditor.prototype = {
 
       editableField({
         element: this.selectorText,
         done: this._onSelectorDone,
         cssProperties: this.rule.cssProperties
       });
     }
 
-    if (this.rule.domRule.type !== CSSRule.KEYFRAME_RULE &&
-        this.rule.domRule.selectors) {
-      let selector = this.rule.domRule.selectors.join(", ");
+    if (this.rule.domRule.type !== CSSRule.KEYFRAME_RULE) {
+      let selector = this.rule.domRule.selectors
+               ? this.rule.domRule.selectors.join(", ")
+               : this.ruleView.inspector.selectionCssSelector;
 
       let selectorHighlighter = createChild(header, "span", {
         class: "ruleview-selectorhighlighter" +
                (this.ruleView.highlightedSelector === selector ?
                 " highlighted" : ""),
         title: l10n("rule.selectorHighlighter.tooltip")
       });
       selectorHighlighter.addEventListener("click", () => {
--- a/devtools/client/memory/memory.xhtml
+++ b/devtools/client/memory/memory.xhtml
@@ -1,30 +1,27 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE html [
   <!ENTITY % htmlDTD
     PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
     "DTD/xhtml1-strict.dtd">
   %htmlDTD;
-  <!ENTITY % globalDTD
-    SYSTEM "chrome://global/locale/global.dtd">
-  %globalDTD;
 ]>
 
 <!-- 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/. -->
 <html xmlns="http://www.w3.org/1999/xhtml">
   <head>
     <link rel="stylesheet" href="chrome://devtools/skin/widgets.css" type="text/css"/>
     <link rel="stylesheet" href="chrome://devtools/skin/memory.css" type="text/css"/>
     <link rel="stylesheet" href="chrome://devtools/skin/components-frame.css" type="text/css"/>
     <link rel="stylesheet" href="chrome://devtools/skin/components-h-split-box.css" type="text/css"/>
   </head>
-  <body class="theme-body" dir="&locale.dir;">
+  <body class="theme-body">
     <div id="app"></div>
 
     <script type="application/javascript;version=1.8"
             src="chrome://devtools/content/shared/theme-switching.js"
             defer="true">
     </script>
 
     <script type="application/javascript;version=1.8"
--- a/devtools/client/netmonitor/test/browser_net_pane-collapse.js
+++ b/devtools/client/netmonitor/test/browser_net_pane-collapse.js
@@ -29,26 +29,31 @@ add_task(function* () {
   is(detailsPane.style.marginRight, "0px",
     "The details pane has an incorrect right margin.");
   ok(!detailsPane.hasAttribute("animated"),
     "The details pane has an incorrect animated attribute.");
   ok(!detailsPane.classList.contains("pane-collapsed") &&
      !detailsPaneToggleButton.classList.contains("pane-collapsed"),
     "The details pane should at this point be visible.");
 
+  // Trigger reflow to make sure the UI is in required state.
+  document.documentElement.getBoundingClientRect();
+
   NetMonitorView.toggleDetailsPane({ visible: false, animated: true });
 
+  yield once(detailsPane, "transitionend");
+
   let margin = -(width + 1) + "px";
   is(width, Prefs.networkDetailsWidth,
     "The details pane has an incorrect width after collapsing.");
   is(detailsPane.style.marginLeft, margin,
     "The details pane has an incorrect left margin after collapsing.");
   is(detailsPane.style.marginRight, margin,
     "The details pane has an incorrect right margin after collapsing.");
-  ok(detailsPane.hasAttribute("animated"),
+  ok(!detailsPane.hasAttribute("animated"),
     "The details pane has an incorrect attribute after an animated collapsing.");
   ok(detailsPane.classList.contains("pane-collapsed") &&
      detailsPaneToggleButton.classList.contains("pane-collapsed"),
     "The details pane should not be visible after collapsing.");
 
   NetMonitorView.toggleDetailsPane({ visible: true, animated: false });
 
   is(width, Prefs.networkDetailsWidth,
--- a/devtools/client/shared/components/sidebar-toggle.css
+++ b/devtools/client/shared/components/sidebar-toggle.css
@@ -2,31 +2,31 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 .sidebar-toggle {
   display: block;
 }
 
-.sidebar-toggle:-moz-locale-dir(ltr)::before,
-.sidebar-toggle.pane-collapsed:-moz-locale-dir(rtl)::before {
+.sidebar-toggle::before,
+.sidebar-toggle.pane-collapsed:dir(rtl)::before {
   background-image: var(--theme-pane-collapse-image);
 }
 
-.sidebar-toggle.pane-collapsed:-moz-locale-dir(ltr)::before,
-.sidebar-toggle:-moz-locale-dir(rtl)::before {
+.sidebar-toggle.pane-collapsed::before,
+.sidebar-toggle:dir(rtl)::before {
   background-image: var(--theme-pane-expand-image);
 }
 
 /* Rotate button icon 90deg if the toolbox container is
   in vertical mode (sidebar displayed under the main panel) */
 @media (max-width: 700px) {
-  .sidebar-toggle:-moz-locale-dir(ltr)::before {
+  .sidebar-toggle::before {
     transform: rotate(90deg);
   }
 
   /* Since RTL swaps the used images, we need to flip them
      the other way round */
-  .sidebar-toggle:-moz-locale-dir(rtl)::before {
+  .sidebar-toggle:dir(rtl)::before {
     transform: rotate(-90deg);
   }
 }
--- a/devtools/client/shared/developer-toolbar.js
+++ b/devtools/client/shared/developer-toolbar.js
@@ -24,17 +24,17 @@ loader.lazyGetter(this, "prefBranch", fu
                     .QueryInterface(Ci.nsIPrefBranch2);
 });
 
 loader.lazyRequireGetter(this, "gcliInit", "devtools/shared/gcli/commands/index");
 loader.lazyRequireGetter(this, "util", "gcli/util/util");
 loader.lazyRequireGetter(this, "ConsoleServiceListener", "devtools/server/actors/utils/webconsole-utils", true);
 loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
 loader.lazyRequireGetter(this, "gDevToolsBrowser", "devtools/client/framework/devtools-browser", true);
-loader.lazyRequireGetter(this, "nodeConstants", "devtools/shared/dom-node-constants", true);
+loader.lazyRequireGetter(this, "nodeConstants", "devtools/shared/dom-node-constants");
 loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
 
 /**
  * A collection of utilities to help working with commands
  */
 var CommandUtils = {
   /**
    * Utility to ensure that things are loaded in the correct order
--- a/devtools/client/shared/widgets/view-helpers.js
+++ b/devtools/client/shared/widgets/view-helpers.js
@@ -247,16 +247,21 @@ const ViewHelpers = exports.ViewHelpers 
     }
 
     // Hiding is always handled via margins, not the hidden attribute.
     pane.removeAttribute("hidden");
 
     // Add a class to the pane to handle min-widths, margins and animations.
     pane.classList.add("generic-toggled-pane");
 
+    // Avoid toggles in the middle of animation.
+    if (pane.hasAttribute("animated")) {
+      return;
+    }
+
     // Avoid useless toggles.
     if (flags.visible == !pane.classList.contains("pane-collapsed")) {
       if (flags.callback) {
         flags.callback();
       }
       return;
     }
 
@@ -278,33 +283,46 @@ const ViewHelpers = exports.ViewHelpers 
         pane.style.marginBottom = "0";
         pane.classList.remove("pane-collapsed");
       } else {
         let width = Math.floor(pane.getAttribute("width")) + 1;
         let height = Math.floor(pane.getAttribute("height")) + 1;
         pane.style.marginLeft = -width + "px";
         pane.style.marginRight = -width + "px";
         pane.style.marginBottom = -height + "px";
-        pane.classList.add("pane-collapsed");
       }
 
       // Wait for the animation to end before calling afterToggle()
       if (flags.animated) {
-        pane.addEventListener("transitionend", function onEvent() {
-          pane.removeEventListener("transitionend", onEvent, false);
+        let options = {
+          useCapture: false,
+          once: true
+        };
+
+        pane.addEventListener("transitionend", () => {
           // Prevent unwanted transitions: if the panel is hidden and the layout
           // changes margins will be updated and the panel will pop out.
           pane.removeAttribute("animated");
+
+          if (!flags.visible) {
+            pane.classList.add("pane-collapsed");
+          }
           if (flags.callback) {
             flags.callback();
           }
-        }, false);
-      } else if (flags.callback) {
+        }, options);
+      } else {
+        if (!flags.visible) {
+          pane.classList.add("pane-collapsed");
+        }
+
         // Invoke the callback immediately since there's no transition.
-        flags.callback();
+        if (flags.callback) {
+          flags.callback();
+        }
       }
     };
 
     // Sometimes it's useful delaying the toggle a few ticks to ensure
     // a smoother slide in-out animation.
     if (flags.delayed) {
       pane.ownerDocument.defaultView.setTimeout(doToggle,
                                                 PANE_APPEARANCE_DELAY);
--- a/devtools/client/themes/rules.css
+++ b/devtools/client/themes/rules.css
@@ -320,17 +320,17 @@
   visibility: hidden;
 }
 
 .ruleview-expander {
   vertical-align: middle;
   display: inline-block;
 }
 
-.ruleview-expander.theme-twisty:-moz-locale-dir(rtl) {
+.ruleview-expander.theme-twisty:dir(rtl) {
   /* for preventing .theme-twisty's wrong direction in rtl; Bug 1296648 */
   transform: none;
 }
 
 .ruleview-newproperty {
   /* (enable checkbox width: 12px) + (expander width: 15px) */
   margin-inline-start: 27px;
 }
--- a/devtools/client/themes/widgets.css
+++ b/devtools/client/themes/widgets.css
@@ -277,33 +277,33 @@
 
 .breadcrumbs-widget-item:first-child {
   background-image: none;
 }
 
 /* RTL support: move the images that were on the left to the right,
  * and move images that were on the right to the left.
  */
-.breadcrumbs-widget-item:-moz-locale-dir(rtl) {
+.breadcrumbs-widget-item:dir(rtl) {
   padding: 0 20px 0 8px;
 }
 
-.breadcrumbs-widget-item:-moz-locale-dir(rtl),
-.breadcrumbs-widget-item[checked] + .breadcrumbs-widget-item:-moz-locale-dir(rtl) {
+.breadcrumbs-widget-item:dir(rtl),
+.breadcrumbs-widget-item[checked] + .breadcrumbs-widget-item:dir(rtl) {
   background-position: center right;
 }
 
-#breadcrumb-separator-before:-moz-locale-dir(rtl),
-#breadcrumb-separator-after:-moz-locale-dir(rtl),
-#breadcrumb-separator-normal:-moz-locale-dir(rtl) {
+#breadcrumb-separator-before:dir(rtl),
+#breadcrumb-separator-after:dir(rtl),
+#breadcrumb-separator-normal:dir(rtl) {
   transform: scaleX(-1);
 }
 
-#breadcrumb-separator-before:-moz-locale-dir(rtl):after,
-#breadcrumb-separator-after:-moz-locale-dir(rtl):after {
+#breadcrumb-separator-before:dir(rtl):after,
+#breadcrumb-separator-after:dir(rtl):after {
   transform: translateX(-5px) rotate(45deg);
 }
 
 .breadcrumbs-widget-item[checked] .breadcrumbs-widget-item-id,
 .breadcrumbs-widget-item[checked] .breadcrumbs-widget-item-tag,
 .breadcrumbs-widget-item[checked] .breadcrumbs-widget-item-pseudo-classes,
 .breadcrumbs-widget-item[checked] .breadcrumbs-widget-item-classes {
   color: var(--theme-selection-color);
--- a/devtools/client/webconsole/test/browser_webconsole_inspect-parsed-documents.js
+++ b/devtools/client/webconsole/test/browser_webconsole_inspect-parsed-documents.js
@@ -16,17 +16,17 @@ const TEST_CASES = [
   },
   {
     input: '(new DOMParser()).parseFromString("<a />", "application/xml")',
     output: "XMLDocument",
     inspectable: true,
   },
   {
     input: '(new DOMParser()).parseFromString("<svg></svg>", "image/svg+xml")',
-    output: "SVGDocument",
+    output: "XMLDocument",
     inspectable: true,
   },
 ];
 
 const TEST_URI = "data:text/html;charset=utf8," +
   "browser_webconsole_inspect-parsed-documents.js";
 add_task(function* () {
   let {tab} = yield loadTab(TEST_URI);
--- a/devtools/server/actors/source.js
+++ b/devtools/server/actors/source.js
@@ -621,35 +621,42 @@ let SourceActor = ActorClassWithSpec(sou
    */
   unblackbox: function () {
     this.threadActor.sources.unblackBox(this.url);
   },
 
   /**
    * Handle a request to set a breakpoint.
    *
-   * @param JSON request
-   *        A JSON object representing the request.
+   * @param Number line
+   *        Line to break on.
+   * @param Number column
+   *        Column to break on.
+   * @param String condition
+   *        A condition which must be true for breakpoint to be hit.
+   * @param Boolean noSliding
+   *        If true, disables breakpoint sliding.
    *
    * @returns Promise
    *          A promise that resolves to a JSON object representing the
    *          response.
    */
-  setBreakpoint: function (line, column, condition) {
+  setBreakpoint: function (line, column, condition, noSliding) {
     if (this.threadActor.state !== "paused") {
       throw {
         error: "wrongState",
         message: "Cannot set breakpoint while debuggee is running."
       };
     }
 
     let location = new OriginalLocation(this, line, column);
     return this._getOrCreateBreakpointActor(
       location,
-      condition
+      condition,
+      noSliding
     ).then((actor) => {
       let response = {
         actor: actor.actorID,
         isPending: actor.isPending
       };
 
       let actualLocation = actor.originalLocation;
       if (!actualLocation.equals(location)) {
@@ -666,31 +673,33 @@ let SourceActor = ActorClassWithSpec(sou
    * match the given location.
    *
    * @param OriginalLocation originalLocation
    *        An OriginalLocation representing the location of the breakpoint in
    *        the original source.
    * @param String condition
    *        A string that is evaluated whenever the breakpoint is hit. If the
    *        string evaluates to false, the breakpoint is ignored.
+   * @param Boolean noSliding
+   *        If true, disables breakpoint sliding.
    *
    * @returns BreakpointActor
    *          A BreakpointActor representing the breakpoint.
    */
-  _getOrCreateBreakpointActor: function (originalLocation, condition) {
+  _getOrCreateBreakpointActor: function (originalLocation, condition, noSliding) {
     let actor = this.breakpointActorMap.getActor(originalLocation);
     if (!actor) {
       actor = new BreakpointActor(this.threadActor, originalLocation);
       this.threadActor.threadLifetimePool.addActor(actor);
       this.breakpointActorMap.setActor(originalLocation, actor);
     }
 
     actor.condition = condition;
 
-    return this._setBreakpoint(actor);
+    return this._setBreakpoint(actor, noSliding);
   },
 
   /*
    * Ensure the given BreakpointActor is set as a breakpoint handler on all
    * scripts that match its location in the original source.
    *
    * If there are no scripts that match the location of the BreakpointActor,
    * we slide its location to the next closest line (for line breakpoints) or
@@ -700,28 +709,29 @@ let SourceActor = ActorClassWithSpec(sou
    * any code for the given location, or they were all garbage collected before
    * the debugger started running. We cannot distinguish between these two
    * cases, so we insert the BreakpointActor in the BreakpointActorMap as
    * a pending breakpoint. Whenever a new script is introduced, this method is
    * called again for each pending breakpoint.
    *
    * @param BreakpointActor actor
    *        The BreakpointActor to be set as a breakpoint handler.
+   * @param Boolean noSliding
+   *        If true, disables breakpoint sliding.
    *
    * @returns A Promise that resolves to the given BreakpointActor.
    */
-  _setBreakpoint: function (actor) {
+  _setBreakpoint: function (actor, noSliding) {
     const { originalLocation } = actor;
     const { originalLine, originalSourceActor } = originalLocation;
 
     if (!this.isSourceMapped) {
-      if (!this._setBreakpointAtGeneratedLocation(
-        actor,
-        GeneratedLocation.fromOriginalLocation(originalLocation)
-      )) {
+      const generatedLocation = GeneratedLocation.fromOriginalLocation(originalLocation);
+      if (!this._setBreakpointAtGeneratedLocation(actor, generatedLocation) &&
+          !noSliding) {
         const query = { line: originalLine };
         // For most cases, we have a real source to query for. The
         // only time we don't is for HTML pages. In that case we want
         // to query for scripts in an HTML page based on its URL, as
         // there could be several sources within an HTML page.
         if (this.source) {
           query.source = this.source;
         } else {
--- a/devtools/server/tests/unit/test_breakpoint-03.js
+++ b/devtools/server/tests/unit/test_breakpoint-03.js
@@ -31,50 +31,62 @@ function run_test_with_server(aServer, a
                            "test-stack",
                            function (aResponse, aTabClient, aThreadClient) {
                              gThreadClient = aThreadClient;
                              test_skip_breakpoint();
                            });
   });
 }
 
-function test_skip_breakpoint()
-{
-  gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) {
+var test_no_skip_breakpoint = Task.async(function*(source, location) {
+  let [aResponse, bpClient] = yield source.setBreakpoint(
+    Object.assign({}, location, { noSliding: true })
+  );
+
+  do_check_true(!aResponse.actualLocation);
+  do_check_eq(bpClient.location.line, gDebuggee.line0 + 3);
+  yield bpClient.remove();
+});
+
+var test_skip_breakpoint = function() {
+  gThreadClient.addOneTimeListener("paused", Task.async(function *(aEvent, aPacket) {
     let location = { line: gDebuggee.line0 + 3 };
     let source = gThreadClient.source(aPacket.frame.where.source);
 
-    source.setBreakpoint(location, function (aResponse, bpClient) {
-      // Check that the breakpoint has properly skipped forward one line.
-      do_check_true(!!aResponse.actualLocation);
-      do_check_eq(aResponse.actualLocation.source.actor, source.actor);
-      do_check_eq(aResponse.actualLocation.line, location.line + 1);
+    // First, make sure that we can disable sliding with the
+    // `noSliding` option.
+    yield test_no_skip_breakpoint(source, location);
+
+    // Now make sure that the breakpoint properly slides forward one line.
+    const [aResponse, bpClient] = yield source.setBreakpoint(location);
+    do_check_true(!!aResponse.actualLocation);
+    do_check_eq(aResponse.actualLocation.source.actor, source.actor);
+    do_check_eq(aResponse.actualLocation.line, location.line + 1);
 
-      gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) {
-        // Check the return value.
-        do_check_eq(aPacket.type, "paused");
-        do_check_eq(aPacket.frame.where.source.actor, source.actor);
-        do_check_eq(aPacket.frame.where.line, location.line + 1);
-        do_check_eq(aPacket.why.type, "breakpoint");
-        do_check_eq(aPacket.why.actors[0], bpClient.actor);
-        // Check that the breakpoint worked.
-        do_check_eq(gDebuggee.a, 1);
-        do_check_eq(gDebuggee.b, undefined);
+    gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) {
+      // Check the return value.
+      do_check_eq(aPacket.type, "paused");
+      do_check_eq(aPacket.frame.where.source.actor, source.actor);
+      do_check_eq(aPacket.frame.where.line, location.line + 1);
+      do_check_eq(aPacket.why.type, "breakpoint");
+      do_check_eq(aPacket.why.actors[0], bpClient.actor);
+      // Check that the breakpoint worked.
+      do_check_eq(gDebuggee.a, 1);
+      do_check_eq(gDebuggee.b, undefined);
 
-        // Remove the breakpoint.
-        bpClient.remove(function (aResponse) {
-          gThreadClient.resume(function () {
-            gClient.close().then(gCallback);
-          });
+      // Remove the breakpoint.
+      bpClient.remove(function (aResponse) {
+        gThreadClient.resume(function () {
+          gClient.close().then(gCallback);
         });
       });
+    });
 
-      gThreadClient.resume();
-    });
-  });
+    gThreadClient.resume();
+  }));
 
   // Use `evalInSandbox` to make the debugger treat it as normal
   // globally-scoped code, where breakpoint sliding rules apply.
   Cu.evalInSandbox(
     "var line0 = Error().lineNumber;\n" +
     "debugger;\n" +      // line0 + 1
     "var a = 1;\n" +     // line0 + 2
     "// A comment.\n" +  // line0 + 3
--- a/devtools/shared/client/main.js
+++ b/devtools/shared/client/main.js
@@ -2888,30 +2888,31 @@ SourceClient.prototype = {
    * Request to set a breakpoint in the specified location.
    *
    * @param object aLocation
    *        The location and condition of the breakpoint in
    *        the form of { line[, column, condition] }.
    * @param function aOnResponse
    *        Called with the thread's response.
    */
-  setBreakpoint: function ({ line, column, condition }, aOnResponse = noop) {
+  setBreakpoint: function ({ line, column, condition, noSliding }, aOnResponse = noop) {
     // A helper function that sets the breakpoint.
     let doSetBreakpoint = aCallback => {
       let root = this._client.mainRoot;
       let location = {
         line: line,
         column: column
       };
 
       let packet = {
         to: this.actor,
         type: "setBreakpoint",
         location: location,
-        condition: condition
+        condition: condition,
+        noSliding: noSliding
       };
 
       // Backwards compatibility: send the breakpoint request to the
       // thread if the server doesn't support Debugger.Source actors.
       if (!root.traits.debuggerSourceActors) {
         packet.to = this._activeThread.actor;
         packet.location.url = this.url;
       }
--- a/devtools/shared/specs/source.js
+++ b/devtools/shared/specs/source.js
@@ -24,16 +24,17 @@ const sourceSpec = generateActorSpec({
     blackbox: { response: { pausedInSource: RetVal("boolean") } },
     unblackbox: {},
     setBreakpoint: {
       request: {
         location: {
           line: Arg(0, "number"),
           column: Arg(1, "nullable:number")
         },
-        condition: Arg(2, "nullable:string")
+        condition: Arg(2, "nullable:string"),
+        noSliding: Arg(3, "nullable:boolean")
       },
       response: RetVal("json")
     },
   },
 });
 
 exports.sourceSpec = sourceSpec;
--- a/dom/animation/test/css-transitions/file_animation-cancel.html
+++ b/dom/animation/test/css-transitions/file_animation-cancel.html
@@ -113,25 +113,53 @@ test(function(t) {
   assert_equals(getComputedStyle(div).marginLeft, '1000px',
                 'margin-left style is still not animated after updating'
                 + ' transition-duration');
   assert_equals(animation.playState, 'idle',
                 'Transition is still idle after updating transition-duration');
 }, 'After cancelling a transition, updating transition properties doesn\'t make'
    + ' it live again');
 
-test(function(t) {
+promise_test(function(t) {
   var div = addDiv(t, { style: 'margin-left: 0px' });
   flushComputedStyle(div);
 
   div.style.transition = 'margin-left 100s';
   div.style.marginLeft = '1000px';
   flushComputedStyle(div);
 
   var animation = div.getAnimations()[0];
-  div.style.display = 'none';
-  assert_equals(animation.playState, 'idle');
-  assert_equals(getComputedStyle(div).marginLeft, '1000px');
+  return animation.ready.then(function() {
+    assert_equals(animation.playState, 'running');
+    div.style.display = 'none';
+    return waitForFrame();
+  }).then(function() {
+    assert_equals(animation.playState, 'idle');
+    assert_equals(getComputedStyle(div).marginLeft, '1000px');
+  });
 }, 'Setting display:none on an element cancels its transitions');
 
+promise_test(function(t) {
+  var parentDiv = addDiv(t);
+  var childDiv = document.createElement('div');
+  parentDiv.appendChild(childDiv);
+  childDiv.setAttribute('style', 'margin-left: 0px');
+
+  flushComputedStyle(childDiv);
+
+  childDiv.style.transition = 'margin-left 100s';
+  childDiv.style.marginLeft = '1000px';
+  flushComputedStyle(childDiv);
+
+  var animation = childDiv.getAnimations()[0];
+  return animation.ready.then(function() {
+    assert_equals(animation.playState, 'running');
+    parentDiv.style.display = 'none';
+    return waitForFrame();
+  }).then(function() {
+    assert_equals(animation.playState, 'idle');
+    assert_equals(getComputedStyle(childDiv).marginLeft, '1000px');
+  });
+}, 'Setting display:none cancels transitions on a child element');
+
 done();
 </script>
 </body>
--- a/dom/base/BlobSet.cpp
+++ b/dom/base/BlobSet.cpp
@@ -7,49 +7,34 @@
 #include "mozilla/dom/BlobSet.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/dom/File.h"
 #include "MultipartBlobImpl.h"
 
 namespace mozilla {
 namespace dom {
 
-already_AddRefed<Blob>
-BlobSet::GetBlobInternal(nsISupports* aParent,
-                         const nsACString& aContentType,
-                         ErrorResult& aRv)
-{
-  nsTArray<RefPtr<BlobImpl>> subImpls(GetBlobImpls());
-  RefPtr<BlobImpl> blobImpl =
-    MultipartBlobImpl::Create(Move(subImpls),
-                              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);
   if (!aLength) {
     return NS_OK;
   }
 
-  uint64_t offset = mDataLen;
-
-  if (!ExpandBufferSize(aLength)) {
+  void* data = malloc(aLength);
+  if (!data) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  memcpy((char*)mData + offset, aData, aLength);
+  memcpy((char*)data, aData, aLength);
+
+  RefPtr<BlobImpl> blobImpl = new BlobImplMemory(data, aLength, EmptyString());
+  mBlobImpls.AppendElement(blobImpl);
+
   return NS_OK;
 }
 
 nsresult
 BlobSet::AppendString(const nsAString& aString, bool nativeEOL, JSContext* aCx)
 {
   nsCString utf8Str = NS_ConvertUTF16toUTF8(aString);
 
@@ -66,74 +51,14 @@ BlobSet::AppendString(const nsAString& a
   return AppendVoidPtr((void*)utf8Str.Data(),
                        utf8Str.Length());
 }
 
 nsresult
 BlobSet::AppendBlobImpl(BlobImpl* aBlobImpl)
 {
   NS_ENSURE_ARG_POINTER(aBlobImpl);
-
-  Flush();
   mBlobImpls.AppendElement(aBlobImpl);
-
-  return NS_OK;
-}
-
-nsresult
-BlobSet::AppendBlobImpls(const nsTArray<RefPtr<BlobImpl>>& aBlobImpls)
-{
-  Flush();
-  mBlobImpls.AppendElements(aBlobImpls);
-
   return NS_OK;
 }
 
-bool
-BlobSet::ExpandBufferSize(uint64_t aSize)
-{
-  if (mDataBufferLen >= mDataLen + aSize) {
-    mDataLen += aSize;
-    return true;
-  }
-
-  // Start at 1 or we'll loop forever.
-  CheckedUint32 bufferLen =
-    std::max<uint32_t>(static_cast<uint32_t>(mDataBufferLen), 1);
-  while (bufferLen.isValid() && bufferLen.value() < mDataLen + aSize) {
-    bufferLen *= 2;
-  }
-
-  if (!bufferLen.isValid()) {
-    return false;
-  }
-
-  void* data = realloc(mData, bufferLen.value());
-  if (!data) {
-    return false;
-  }
-
-  mData = data;
-  mDataBufferLen = bufferLen.value();
-  mDataLen += aSize;
-  return true;
-}
-
-void
-BlobSet::Flush()
-{
-  if (mData) {
-    // If we have some data, create a blob for it
-    // and put it on the stack
-
-    RefPtr<BlobImpl> blobImpl =
-      new BlobImplMemory(mData, mDataLen, EmptyString());
-    mBlobImpls.AppendElement(blobImpl);
-
-    mData = nullptr; // The nsDOMMemoryFile takes ownership of the buffer
-    mDataLen = 0;
-    mDataBufferLen = 0;
-    return;
-  }
-}
-
 } // dom namespace
 } // mozilla namespace
--- a/dom/base/BlobSet.h
+++ b/dom/base/BlobSet.h
@@ -7,55 +7,30 @@
 #ifndef mozilla_dom_BlobSet_h
 #define mozilla_dom_BlobSet_h
 
 #include "mozilla/RefPtr.h"
 
 namespace mozilla {
 namespace dom {
 
-class Blob;
 class BlobImpl;
 
 class BlobSet final
 {
 public:
-  BlobSet()
-    : mData(nullptr)
-    , mDataLen(0)
-    , mDataBufferLen(0)
-  {}
-
-  ~BlobSet()
-  {
-    free(mData);
-  }
-
   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,
-                                         ErrorResult& aRv);
+  nsTArray<RefPtr<BlobImpl>>& GetBlobImpls() { return mBlobImpls; }
 
 private:
-  bool ExpandBufferSize(uint64_t aSize);
-
-  void Flush();
-
   nsTArray<RefPtr<BlobImpl>> mBlobImpls;
-  void* mData;
-  uint64_t mDataLen;
-  uint64_t mDataBufferLen;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_BlobSet_h
--- a/dom/base/Location.cpp
+++ b/dom/base/Location.cpp
@@ -905,30 +905,33 @@ Location::GetSourceBaseURL(JSContext* cx
     }
   }
   NS_ENSURE_TRUE(doc, NS_OK);
   *sourceURL = doc->GetBaseURI().take();
   return NS_OK;
 }
 
 bool
-Location::CallerSubsumes()
+Location::CallerSubsumes(nsIPrincipal* aSubjectPrincipal)
 {
+  MOZ_ASSERT(aSubjectPrincipal);
+
   // Get the principal associated with the location object.  Note that this is
   // the principal of the page which will actually be navigated, not the
   // principal of the Location object itself.  This is why we need this check
   // even though we only allow limited cross-origin access to Location objects
   // in general.
   nsCOMPtr<nsPIDOMWindowOuter> outer = mInnerWindow->GetOuterWindow();
   if (MOZ_UNLIKELY(!outer))
     return false;
   nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(outer);
   bool subsumes = false;
   nsresult rv =
-    nsContentUtils::SubjectPrincipal()->SubsumesConsideringDomain(sop->GetPrincipal(), &subsumes);
+    aSubjectPrincipal->SubsumesConsideringDomain(sop->GetPrincipal(),
+                                                 &subsumes);
   NS_ENSURE_SUCCESS(rv, false);
   return subsumes;
 }
 
 JSObject*
 Location::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return LocationBinding::Wrap(aCx, this, aGivenProto);
--- a/dom/base/Location.h
+++ b/dom/base/Location.h
@@ -39,129 +39,190 @@ public:
 
   void SetDocShell(nsIDocShell *aDocShell);
   nsIDocShell *GetDocShell();
 
   // nsIDOMLocation
   NS_DECL_NSIDOMLOCATION
 
   #define THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME() { \
-    if (!CallerSubsumes()) { \
+    MOZ_ASSERT(aSubjectPrincipal.isSome()); \
+    if (!CallerSubsumes(aSubjectPrincipal.value())) { \
       aError.Throw(NS_ERROR_DOM_SECURITY_ERR); \
       return; \
     } \
   }
 
   // WebIDL API:
-  void Assign(const nsAString& aUrl, ErrorResult& aError)
+  void Assign(const nsAString& aUrl,
+              const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+              ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = Assign(aUrl);
   }
 
-  void Replace(const nsAString& aUrl, ErrorResult& aError)
+  void Replace(const nsAString& aUrl,
+               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               ErrorResult& aError)
   {
     aError = Replace(aUrl);
   }
 
-  void Reload(bool aForceget, ErrorResult& aError)
+  void Reload(bool aForceget,
+              const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+              ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = Reload(aForceget);
   }
-  void GetHref(nsAString& aHref, ErrorResult& aError)
+
+  void GetHref(nsAString& aHref,
+               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetHref(aHref);
   }
-  void SetHref(const nsAString& aHref, ErrorResult& aError)
+
+  void SetHref(const nsAString& aHref,
+               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               ErrorResult& aError)
   {
     aError = SetHref(aHref);
   }
-  void GetOrigin(nsAString& aOrigin, ErrorResult& aError)
+
+  void GetOrigin(nsAString& aOrigin,
+                 const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                 ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetOrigin(aOrigin);
   }
-  void GetProtocol(nsAString& aProtocol, ErrorResult& aError)
+
+  void GetProtocol(nsAString& aProtocol,
+                   const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                   ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetProtocol(aProtocol);
   }
-  void SetProtocol(const nsAString& aProtocol, ErrorResult& aError)
+
+  void SetProtocol(const nsAString& aProtocol,
+                   const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                   ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = SetProtocol(aProtocol);
   }
-  void GetHost(nsAString& aHost, ErrorResult& aError)
+
+  void GetHost(nsAString& aHost,
+               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetHost(aHost);
   }
-  void SetHost(const nsAString& aHost, ErrorResult& aError)
+
+  void SetHost(const nsAString& aHost,
+               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = SetHost(aHost);
   }
-  void GetHostname(nsAString& aHostname, ErrorResult& aError)
+
+  void GetHostname(nsAString& aHostname,
+                   const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                   ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetHostname(aHostname);
   }
-  void SetHostname(const nsAString& aHostname, ErrorResult& aError)
+
+  void SetHostname(const nsAString& aHostname,
+                   const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                   ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = SetHostname(aHostname);
   }
-  void GetPort(nsAString& aPort, ErrorResult& aError)
+
+  void GetPort(nsAString& aPort,
+               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetPort(aPort);
   }
-  void SetPort(const nsAString& aPort, ErrorResult& aError)
+
+  void SetPort(const nsAString& aPort,
+               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = SetPort(aPort);
   }
-  void GetPathname(nsAString& aPathname, ErrorResult& aError)
+
+  void GetPathname(nsAString& aPathname,
+                   const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                   ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetPathname(aPathname);
   }
-  void SetPathname(const nsAString& aPathname, ErrorResult& aError)
+
+  void SetPathname(const nsAString& aPathname,
+                   const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                   ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = SetPathname(aPathname);
   }
-  void GetSearch(nsAString& aSeach, ErrorResult& aError)
+
+  void GetSearch(nsAString& aSeach,
+                 const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                 ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetSearch(aSeach);
   }
-  void SetSearch(const nsAString& aSeach, ErrorResult& aError)
+
+  void SetSearch(const nsAString& aSeach,
+                 const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                 ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = SetSearch(aSeach);
   }
 
-  void GetHash(nsAString& aHash, ErrorResult& aError)
+  void GetHash(nsAString& aHash,
+               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetHash(aHash);
   }
-  void SetHash(const nsAString& aHash, ErrorResult& aError)
+
+  void SetHash(const nsAString& aHash,
+               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = SetHash(aHash);
   }
-  void Stringify(nsAString& aRetval, ErrorResult& aError)
+
+  void Stringify(nsAString& aRetval,
+                 const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                 ErrorResult& aError)
   {
     // GetHref checks CallerSubsumes.
-    GetHref(aRetval, aError);
+    GetHref(aRetval, aSubjectPrincipal, aError);
   }
+
   nsPIDOMWindowInner* GetParentObject() const
   {
     return mInnerWindow;
   }
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
@@ -180,17 +241,17 @@ protected:
   nsresult SetURI(nsIURI* aURL, bool aReplace = false);
   nsresult SetHrefWithBase(const nsAString& aHref, nsIURI* aBase,
                            bool aReplace);
   nsresult SetHrefWithContext(JSContext* cx, const nsAString& aHref,
                               bool aReplace);
 
   nsresult GetSourceBaseURL(JSContext* cx, nsIURI** sourceURL);
   nsresult CheckURL(nsIURI *url, nsIDocShellLoadInfo** aLoadInfo);
-  bool CallerSubsumes();
+  bool CallerSubsumes(nsIPrincipal* aSubjectPrincipal);
 
   nsString mCachedHash;
   nsCOMPtr<nsPIDOMWindowInner> mInnerWindow;
   nsWeakPtr mDocShell;
 };
 
 } // dom namespace
 } // mozilla namespace
copy from dom/base/BlobSet.cpp
copy to dom/base/MutableBlobStorage.cpp
--- a/dom/base/BlobSet.cpp
+++ b/dom/base/MutableBlobStorage.cpp
@@ -1,100 +1,497 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "mozilla/dom/BlobSet.h"
+#include "mozilla/dom/MutableBlobStorage.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/dom/File.h"
-#include "MultipartBlobImpl.h"
+#include "nsAnonymousTemporaryFile.h"
+#include "nsNetCID.h"
+#include "WorkerPrivate.h"
+
+#define BLOB_MEMORY_TEMPORARY_FILE 1048576
 
 namespace mozilla {
 namespace dom {
 
-already_AddRefed<Blob>
-BlobSet::GetBlobInternal(nsISupports* aParent,
-                         const nsACString& aContentType,
-                         ErrorResult& aRv)
+namespace {
+
+// This class uses the callback to inform when the Blob is created or when the
+// error must be propagated.
+class BlobCreationDoneRunnable final : public Runnable
+{
+public:
+  BlobCreationDoneRunnable(MutableBlobStorage* aBlobStorage,
+                           MutableBlobStorageCallback* aCallback,
+                           Blob* aBlob,
+                           nsresult aRv)
+    : mBlobStorage(aBlobStorage)
+    , mCallback(aCallback)
+    , mBlob(aBlob)
+    , mRv(aRv)
+  {
+    MOZ_ASSERT(aBlobStorage);
+    MOZ_ASSERT(aCallback);
+    MOZ_ASSERT((NS_FAILED(aRv) && !aBlob) ||
+               (NS_SUCCEEDED(aRv) && aBlob));
+  }
+
+  NS_IMETHOD
+  Run() override
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    mCallback->BlobStoreCompleted(mBlobStorage, mBlob, mRv);
+    mCallback = nullptr;
+    mBlob = nullptr;
+    return NS_OK;
+  }
+
+private:
+  ~BlobCreationDoneRunnable()
+  {
+    // If something when wrong, we still have to release these objects in the
+    // correct thread.
+    NS_ReleaseOnMainThread(mCallback.forget());
+    NS_ReleaseOnMainThread(mBlob.forget());
+  }
+
+  RefPtr<MutableBlobStorage> mBlobStorage;
+  RefPtr<MutableBlobStorageCallback> mCallback;
+  RefPtr<Blob> mBlob;
+  nsresult mRv;
+};
+
+// This runnable goes back to the main-thread and informs the BlobStorage about
+// the temporary file.
+class FileCreatedRunnable final : public Runnable
+{
+public:
+  FileCreatedRunnable(MutableBlobStorage* aBlobStorage, PRFileDesc* aFD)
+    : mBlobStorage(aBlobStorage)
+    , mFD(aFD)
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+    MOZ_ASSERT(aBlobStorage);
+    MOZ_ASSERT(aFD);
+  }
+
+  NS_IMETHOD
+  Run() override
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    mBlobStorage->TemporaryFileCreated(mFD);
+    mFD = nullptr;
+    return NS_OK;
+  }
+
+private:
+  ~FileCreatedRunnable()
+  {
+    // If something when wrong, we still have to close the FileDescriptor.
+    if (mFD) {
+      PR_Close(mFD);
+    }
+  }
+
+  RefPtr<MutableBlobStorage> mBlobStorage;
+  PRFileDesc* mFD;
+};
+
+// This runnable creates the temporary file. When done, FileCreatedRunnable is
+// dispatched back to the main-thread.
+class CreateTemporaryFileRunnable final : public Runnable
+{
+public:
+  explicit CreateTemporaryFileRunnable(MutableBlobStorage* aBlobStorage)
+    : mBlobStorage(aBlobStorage)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(aBlobStorage);
+  }
+
+  NS_IMETHOD
+  Run() override
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+
+    PRFileDesc* tempFD = nullptr;
+    nsresult rv = NS_OpenAnonymousTemporaryFile(&tempFD);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      // In sandboxed context we are not allowed to create temporary files, but
+      // this doesn't mean that BlobStorage should fail. We can continue to
+      // store data in memory.  We don't change the storageType so that we don't
+      // try to create a temporary file again.
+      return NS_OK;
+    }
+
+    // The ownership of the tempFD is moved to the FileCreatedRunnable.
+    return NS_DispatchToMainThread(new FileCreatedRunnable(mBlobStorage, tempFD));
+  }
+
+private:
+  RefPtr<MutableBlobStorage> mBlobStorage;
+};
+
+// Simple runnable to propagate the error to the BlobStorage.
+class ErrorPropagationRunnable final : public Runnable
+{
+public:
+  ErrorPropagationRunnable(MutableBlobStorage* aBlobStorage, nsresult aRv)
+    : mBlobStorage(aBlobStorage)
+    , mRv(aRv)
+  {}
+
+  NS_IMETHOD
+  Run() override
+  {
+    mBlobStorage->ErrorPropagated(mRv);
+    return NS_OK;
+  }
+
+private:
+  RefPtr<MutableBlobStorage> mBlobStorage;
+  nsresult mRv;
+};
+
+// This runnable moves a buffer to the IO thread and there, it writes it into
+// the temporary file.
+class WriteRunnable final : public Runnable
 {
-  nsTArray<RefPtr<BlobImpl>> subImpls(GetBlobImpls());
-  RefPtr<BlobImpl> blobImpl =
-    MultipartBlobImpl::Create(Move(subImpls),
-                              NS_ConvertASCIItoUTF16(aContentType),
-                              aRv);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
+public:
+  static WriteRunnable*
+  CopyBuffer(MutableBlobStorage* aBlobStorage, PRFileDesc* aFD,
+             const void* aData, uint32_t aLength)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(aBlobStorage);
+    MOZ_ASSERT(aFD);
+    MOZ_ASSERT(aData);
+
+    // We have to take a copy of this buffer.
+    void* data = malloc(aLength);
+    if (!data) {
+      return nullptr;
+    }
+
+    memcpy((char*)data, aData, aLength);
+    return new WriteRunnable(aBlobStorage, aFD, data, aLength);
+  }
+
+  static WriteRunnable*
+  AdoptBuffer(MutableBlobStorage* aBlobStorage, PRFileDesc* aFD,
+              void* aData, uint32_t aLength)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(aBlobStorage);
+    MOZ_ASSERT(aFD);
+    MOZ_ASSERT(aData);
+
+    return new WriteRunnable(aBlobStorage, aFD, aData, aLength);
+  }
+
+  NS_IMETHOD
+  Run() override
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+
+    int32_t written = PR_Write(mFD, mData, mLength);
+    if (NS_WARN_IF(written < 0 || uint32_t(written) != mLength)) {
+      return NS_DispatchToMainThread(
+        new ErrorPropagationRunnable(mBlobStorage, NS_ERROR_FAILURE));
+    }
+
+    return NS_OK;
+  }
+
+private:
+  WriteRunnable(MutableBlobStorage* aBlobStorage, PRFileDesc* aFD,
+                void* aData, uint32_t aLength)
+    : mBlobStorage(aBlobStorage)
+    , mFD(aFD)
+    , mData(aData)
+    , mLength(aLength)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(mBlobStorage);
+    MOZ_ASSERT(aFD);
+    MOZ_ASSERT(aData);
+  }
+
+  ~WriteRunnable()
+  {
+    free(mData);
+  }
+
+  RefPtr<MutableBlobStorage> mBlobStorage;
+  PRFileDesc* mFD;
+  void* mData;
+  uint32_t mLength;
+};
+
+// This runnable closes the FD in case something goes wrong or the temporary
+// file is not needed anymore.
+class CloseFileRunnable final : public Runnable
+{
+public:
+  explicit CloseFileRunnable(PRFileDesc* aFD)
+    : mFD(aFD)
+  {}
+
+  NS_IMETHOD
+  Run() override
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+    PR_Close(mFD);
+    mFD = nullptr;
+    return NS_OK;
+  }
+
+private:
+  ~CloseFileRunnable()
+  {
+    if (mFD) {
+      PR_Close(mFD);
+    }
+  }
+
+  PRFileDesc* mFD;
+};
+
+// This runnable is dispatched to the main-thread from the IO thread and its
+// task is to create the blob and inform the callback.
+class CreateBlobRunnable final : public Runnable
+{
+public:
+  CreateBlobRunnable(MutableBlobStorage* aBlobStorage,
+                     already_AddRefed<nsISupports> aParent,
+                     const nsACString& aContentType,
+                     already_AddRefed<MutableBlobStorageCallback> aCallback)
+    : mBlobStorage(aBlobStorage)
+    , mParent(aParent)
+    , mContentType(aContentType)
+    , mCallback(aCallback)
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+  }
+
+  NS_IMETHOD
+  Run() override
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    mBlobStorage->CreateBlobAndRespond(mParent.forget(), mContentType,
+                                       mCallback.forget());
+    return NS_OK;
+  }
+
+private:
+  ~CreateBlobRunnable()
+  {
+    // If something when wrong, we still have to release data in the correct
+    // thread.
+    NS_ReleaseOnMainThread(mParent.forget());
+    NS_ReleaseOnMainThread(mCallback.forget());
+  }
+
+  RefPtr<MutableBlobStorage> mBlobStorage;
+  nsCOMPtr<nsISupports> mParent;
+  nsCString mContentType;
+  RefPtr<MutableBlobStorageCallback> mCallback;
+};
+
+// This task is used to know when the writing is completed. From the IO thread
+// it dispatches a CreateBlobRunnable to the main-thread.
+class LastRunnable final : public Runnable
+{
+public:
+  LastRunnable(MutableBlobStorage* aBlobStorage,
+               nsISupports* aParent,
+               const nsACString& aContentType,
+               MutableBlobStorageCallback* aCallback)
+    : mBlobStorage(aBlobStorage)
+    , mParent(aParent)
+    , mContentType(aContentType)
+    , mCallback(aCallback)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(mBlobStorage);
+    MOZ_ASSERT(aParent);
+    MOZ_ASSERT(aCallback);
+  }
+
+  NS_IMETHOD
+  Run() override
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+    RefPtr<Runnable> runnable =
+      new CreateBlobRunnable(mBlobStorage, mParent.forget(),
+                             mContentType, mCallback.forget());
+    return NS_DispatchToMainThread(runnable);
+  }
+
+private:
+  ~LastRunnable()
+  {
+    // If something when wrong, we still have to release data in the correct
+    // thread.
+    NS_ReleaseOnMainThread(mParent.forget());
+    NS_ReleaseOnMainThread(mCallback.forget());
+  }
+
+  RefPtr<MutableBlobStorage> mBlobStorage;
+  nsCOMPtr<nsISupports> mParent;
+  nsCString mContentType;
+  RefPtr<MutableBlobStorageCallback> mCallback;
+};
+
+} // anonymous namespace
+
+MutableBlobStorage::MutableBlobStorage(MutableBlobStorageType aType)
+  : mData(nullptr)
+  , mDataLen(0)
+  , mDataBufferLen(0)
+  , mStorageState(aType == eOnlyInMemory ? eKeepInMemory : eInMemory)
+  , mFD(nullptr)
+  , mErrorResult(NS_OK)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+}
+
+MutableBlobStorage::~MutableBlobStorage()
+{
+  free(mData);
+
+  if (mFD) {
+    DispatchToIOThread(new CloseFileRunnable(mFD));
+  }
+}
+
+uint64_t
+MutableBlobStorage::GetBlobWhenReady(nsISupports* aParent,
+                                     const nsACString& aContentType,
+                                     MutableBlobStorageCallback* aCallback)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aParent);
+  MOZ_ASSERT(aCallback);
+
+  // GetBlob can be called just once.
+  MOZ_ASSERT(mStorageState != eClosed);
+  StorageState previousState = mStorageState;
+  mStorageState = eClosed;
+
+  if (previousState == eInTemporaryFile) {
+    MOZ_ASSERT(mFD);
+
+    if (NS_FAILED(mErrorResult)) {
+      NS_DispatchToMainThread(
+        new BlobCreationDoneRunnable(this, aCallback, nullptr, mErrorResult));
+      return 0;
+    }
+
+    // We want to wait until all the WriteRunnable are completed. The way we do
+    // this is to go to the I/O thread and then we come back: the runnables are
+    // executed in order and this LastRunnable will be... the last one.
+    nsresult rv = DispatchToIOThread(new LastRunnable(this, aParent,
+                                                      aContentType, aCallback));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      NS_DispatchToMainThread(
+        new BlobCreationDoneRunnable(this, aCallback, nullptr, rv));
+      return 0;
+    }
+
+    return mDataLen;
+  }
+
+  RefPtr<BlobImpl> blobImpl;
+
+  if (mData) {
+    blobImpl = new BlobImplMemory(mData, mDataLen,
+                                  NS_ConvertUTF8toUTF16(aContentType));
+
+    mData = nullptr; // The BlobImplMemory takes ownership of the buffer
+    mDataLen = 0;
+    mDataBufferLen = 0;
+  } else {
+    blobImpl = new EmptyBlobImpl(NS_ConvertUTF8toUTF16(aContentType));
   }
 
   RefPtr<Blob> blob = Blob::Create(aParent, blobImpl);
-  return blob.forget();
+  RefPtr<BlobCreationDoneRunnable> runnable =
+    new BlobCreationDoneRunnable(this, aCallback, blob, NS_OK);
+
+  nsresult error = NS_DispatchToMainThread(runnable);
+  if (NS_WARN_IF(NS_FAILED(error))) {
+    return 0;
+  }
+
+  return mDataLen;
 }
 
 nsresult
-BlobSet::AppendVoidPtr(const void* aData, uint32_t aLength)
+MutableBlobStorage::Append(const void* aData, uint32_t aLength)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mStorageState != eClosed);
   NS_ENSURE_ARG_POINTER(aData);
+
   if (!aLength) {
     return NS_OK;
   }
 
+  // If eInMemory is the current Storage state, we could maybe migrate to
+  // a temporary file.
+  if (mStorageState == eInMemory && ShouldBeTemporaryStorage(aLength)) {
+    nsresult rv = MaybeCreateTemporaryFile();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  // If we are already in the temporaryFile mode, we have to dispatch a
+  // runnable.
+  if (mStorageState == eInTemporaryFile) {
+    MOZ_ASSERT(mFD);
+
+    RefPtr<WriteRunnable> runnable =
+      WriteRunnable::CopyBuffer(this, mFD, aData, aLength);
+    if (NS_WARN_IF(!runnable)) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    nsresult rv = DispatchToIOThread(runnable);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    mDataLen += aLength;
+    return NS_OK;
+  }
+
+  // By default, we store in memory.
+
   uint64_t offset = mDataLen;
 
   if (!ExpandBufferSize(aLength)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   memcpy((char*)mData + offset, aData, aLength);
   return NS_OK;
 }
 
-nsresult
-BlobSet::AppendString(const nsAString& aString, bool nativeEOL, JSContext* aCx)
+bool
+MutableBlobStorage::ExpandBufferSize(uint64_t aSize)
 {
-  nsCString utf8Str = NS_ConvertUTF16toUTF8(aString);
-
-  if (nativeEOL) {
-    if (utf8Str.Contains('\r')) {
-      utf8Str.ReplaceSubstring("\r\n", "\n");
-      utf8Str.ReplaceSubstring("\r", "\n");
-    }
-#ifdef XP_WIN
-    utf8Str.ReplaceSubstring("\n", "\r\n");
-#endif
-  }
-
-  return AppendVoidPtr((void*)utf8Str.Data(),
-                       utf8Str.Length());
-}
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mStorageState < eInTemporaryFile);
 
-nsresult
-BlobSet::AppendBlobImpl(BlobImpl* aBlobImpl)
-{
-  NS_ENSURE_ARG_POINTER(aBlobImpl);
-
-  Flush();
-  mBlobImpls.AppendElement(aBlobImpl);
-
-  return NS_OK;
-}
-
-nsresult
-BlobSet::AppendBlobImpls(const nsTArray<RefPtr<BlobImpl>>& aBlobImpls)
-{
-  Flush();
-  mBlobImpls.AppendElements(aBlobImpls);
-
-  return NS_OK;
-}
-
-bool
-BlobSet::ExpandBufferSize(uint64_t aSize)
-{
   if (mDataBufferLen >= mDataLen + aSize) {
     mDataLen += aSize;
     return true;
   }
 
   // Start at 1 or we'll loop forever.
   CheckedUint32 bufferLen =
     std::max<uint32_t>(static_cast<uint32_t>(mDataBufferLen), 1);
@@ -112,28 +509,109 @@ BlobSet::ExpandBufferSize(uint64_t aSize
   }
 
   mData = data;
   mDataBufferLen = bufferLen.value();
   mDataLen += aSize;
   return true;
 }
 
-void
-BlobSet::Flush()
+bool
+MutableBlobStorage::ShouldBeTemporaryStorage(uint64_t aSize) const
 {
-  if (mData) {
-    // If we have some data, create a blob for it
-    // and put it on the stack
+  MOZ_ASSERT(mStorageState == eInMemory);
+
+  CheckedUint32 bufferSize = mDataLen;
+  bufferSize += aSize;
+
+  if (!bufferSize.isValid()) {
+    return false;
+  }
+
+  return bufferSize.value() >= Preferences::GetUint("dom.blob.memoryToTemporaryFile",
+                                                    BLOB_MEMORY_TEMPORARY_FILE);
+}
+
+nsresult
+MutableBlobStorage::MaybeCreateTemporaryFile()
+{
+  nsresult rv = DispatchToIOThread(new CreateTemporaryFileRunnable(this));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
-    RefPtr<BlobImpl> blobImpl =
-      new BlobImplMemory(mData, mDataLen, EmptyString());
-    mBlobImpls.AppendElement(blobImpl);
+  mStorageState = eWaitingForTemporaryFile;
+  return NS_OK;
+}
+
+void
+MutableBlobStorage::TemporaryFileCreated(PRFileDesc* aFD)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mStorageState == eWaitingForTemporaryFile ||
+             mStorageState == eClosed);
 
-    mData = nullptr; // The nsDOMMemoryFile takes ownership of the buffer
-    mDataLen = 0;
-    mDataBufferLen = 0;
+  if (mStorageState == eClosed) {
+    DispatchToIOThread(new CloseFileRunnable(aFD));
+    return;
+  }
+
+  mStorageState = eInTemporaryFile;
+  mFD = aFD;
+
+  RefPtr<WriteRunnable> runnable =
+    WriteRunnable::AdoptBuffer(this, mFD, mData, mDataLen);
+  MOZ_ASSERT(runnable);
+
+  mData = nullptr;
+
+  nsresult rv = DispatchToIOThread(runnable);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    mErrorResult = rv;
     return;
   }
 }
 
+void
+MutableBlobStorage::CreateBlobAndRespond(already_AddRefed<nsISupports> aParent,
+                                         const nsACString& aContentType,
+                                         already_AddRefed<MutableBlobStorageCallback> aCallback)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mStorageState == eClosed);
+  MOZ_ASSERT(mFD);
+
+  nsCOMPtr<nsISupports> parent(aParent);
+  RefPtr<MutableBlobStorageCallback> callback(aCallback);
+
+  RefPtr<Blob> blob =
+    File::CreateTemporaryBlob(parent, mFD, 0, mDataLen,
+                              NS_ConvertUTF8toUTF16(aContentType));
+  callback->BlobStoreCompleted(this, blob, NS_OK);
+
+  // ownership of this FD is moved to the BlobImpl.
+  mFD = nullptr;
+}
+
+void
+MutableBlobStorage::ErrorPropagated(nsresult aRv)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  mErrorResult = aRv;
+}
+
+/* static */ nsresult
+MutableBlobStorage::DispatchToIOThread(Runnable* aRunnable)
+{
+  nsCOMPtr<nsIEventTarget> target
+    = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+  MOZ_ASSERT(target);
+
+  nsresult rv = target->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
 } // dom namespace
 } // mozilla namespace
copy from dom/base/BlobSet.h
copy to dom/base/MutableBlobStorage.h
--- a/dom/base/BlobSet.h
+++ b/dom/base/MutableBlobStorage.h
@@ -1,61 +1,98 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef mozilla_dom_BlobSet_h
-#define mozilla_dom_BlobSet_h
+#ifndef mozilla_dom_MutableBlobStorage_h
+#define mozilla_dom_MutableBlobStorage_h
 
 #include "mozilla/RefPtr.h"
+#include "prio.h"
 
 namespace mozilla {
 namespace dom {
 
 class Blob;
 class BlobImpl;
+class MutableBlobStorage;
 
-class BlobSet final
+class MutableBlobStorageCallback
+{
+public:
+  NS_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0;
+
+  NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0;
+
+  virtual void BlobStoreCompleted(MutableBlobStorage* aBlobStorage,
+                                  Blob* aBlob,
+                                  nsresult aRv) = 0;
+};
+
+// This class is main-thread only.
+class MutableBlobStorage final
 {
 public:
-  BlobSet()
-    : mData(nullptr)
-    , mDataLen(0)
-    , mDataBufferLen(0)
-  {}
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MutableBlobStorage);
 
-  ~BlobSet()
+  enum MutableBlobStorageType
   {
-    free(mData);
-  }
+    eOnlyInMemory,
+    eCouldBeInTemporaryFile,
+  };
 
-  nsresult AppendVoidPtr(const void* aData, uint32_t aLength);
+  explicit MutableBlobStorage(MutableBlobStorageType aType);
+
+  nsresult Append(const void* aData, uint32_t aLength);
 
-  nsresult AppendString(const nsAString& aString, bool nativeEOL,
-                        JSContext* aCx);
-
-  nsresult AppendBlobImpl(BlobImpl* aBlobImpl);
+  // This method can be called just once.
+  // The callback will be called when the Blob is ready.
+  // The return value is the total size of the blob, when created.
+  uint64_t GetBlobWhenReady(nsISupports* aParent,
+                            const nsACString& aContentType,
+                            MutableBlobStorageCallback* aCallback);
 
-  nsresult AppendBlobImpls(const nsTArray<RefPtr<BlobImpl>>& aBlobImpls);
-
-  nsTArray<RefPtr<BlobImpl>>& GetBlobImpls() { Flush(); return mBlobImpls; }
+  void TemporaryFileCreated(PRFileDesc* aFD);
 
-  already_AddRefed<Blob> GetBlobInternal(nsISupports* aParent,
-                                         const nsACString& aContentType,
-                                         ErrorResult& aRv);
+  void  CreateBlobAndRespond(already_AddRefed<nsISupports> aParent,
+                             const nsACString& aContentType,
+                             already_AddRefed<MutableBlobStorageCallback> aCallback);
+
+  void ErrorPropagated(nsresult aRv);
 
 private:
+  ~MutableBlobStorage();
+
   bool ExpandBufferSize(uint64_t aSize);
 
-  void Flush();
+  bool ShouldBeTemporaryStorage(uint64_t aSize) const;
+
+  nsresult MaybeCreateTemporaryFile();
 
-  nsTArray<RefPtr<BlobImpl>> mBlobImpls;
+  static nsresult DispatchToIOThread(Runnable* aRunnable);
+
+  // All these variables are touched on the main thread only.
+
   void* mData;
   uint64_t mDataLen;
   uint64_t mDataBufferLen;
+
+  enum StorageState {
+    eKeepInMemory,
+    eInMemory,
+    eWaitingForTemporaryFile,
+    eInTemporaryFile,
+    eClosed
+  };
+
+  StorageState mStorageState;
+
+  PRFileDesc* mFD;
+
+  nsresult mErrorResult;
 };
 
 } // namespace dom
 } // namespace mozilla
 
-#endif // mozilla_dom_BlobSet_h
+#endif // mozilla_dom_MutableBlobStorage_h
--- a/dom/base/domerr.msg
+++ b/dom/base/domerr.msg
@@ -161,8 +161,21 @@ DOM4_MSG_DEF(InvalidStateError, "A subsc
 
 DOM_MSG_DEF(NS_ERROR_DOM_JS_EXCEPTION, "A callback threw an exception")
 DOM_MSG_DEF(NS_ERROR_DOM_DOMEXCEPTION, "A DOMException was thrown")
 
 /* Media errors */
 DOM4_MSG_DEF(AbortError,        "The fetching process for the media resource was aborted by the user agent at the user's request.", NS_ERROR_DOM_MEDIA_ABORT_ERR)
 DOM4_MSG_DEF(NotAllowedError,   "The play method is not allowed by the user agent or the platform in the current context, possibly because the user denied permission.", NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR)
 DOM4_MSG_DEF(NotSupportedError, "The media resource indicated by the src attribute or assigned media provider object was not suitable.", NS_ERROR_DOM_MEDIA_NOT_SUPPORTED_ERR)
+
+DOM4_MSG_DEF(SyntaxError, "The URI is malformed.", NS_ERROR_DOM_MALFORMED_URI)
+DOM4_MSG_DEF(SyntaxError, "Invalid header name.", NS_ERROR_DOM_INVALID_HEADER_NAME)
+
+/* XMLHttpRequest errors. */
+DOM4_MSG_DEF(InvalidStateError, "XMLHttpRequest has an invalid context.", NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT)
+DOM4_MSG_DEF(InvalidStateError, "XMLHttpRequest state must be OPENED.", NS_ERROR_DOM_INVALID_STATE_XHR_MUST_BE_OPENED)
+DOM4_MSG_DEF(InvalidStateError, "XMLHttpRequest must not be sending.", NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_SENDING)
+DOM4_MSG_DEF(InvalidStateError, "XMLHttpRequest state must not be LOADING or DONE.", NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_LOADING_OR_DONE)
+DOM4_MSG_DEF(InvalidStateError, "responseXML is only available if responseType is '' or 'document'.", NS_ERROR_DOM_INVALID_STATE_XHR_HAS_WRONG_RESPONSETYPE_FOR_RESPONSEXML)
+DOM4_MSG_DEF(InvalidStateError, "responseText is only available if responseType is '', 'document', or 'moz-chunked-text'.", NS_ERROR_DOM_INVALID_STATE_XHR_HAS_WRONG_RESPONSETYPE_FOR_RESPONSETEXT)
+DOM4_MSG_DEF(InvalidStateError, "synchronous XMLHttpRequests do not support 'moz-chunked-text' or 'moz-chunked-arraybuffer' responseType.", NS_ERROR_DOM_INVALID_STATE_XHR_CHUNKED_RESPONSETYPES_UNSUPPORTED_FOR_SYNC)
+DOM4_MSG_DEF(InvalidAccessError, "synchronous XMLHttpRequests do not support timeout and responseType.", NS_ERROR_DOM_INVALID_ACCESS_XHR_TIMEOUT_AND_RESPONSETYPE_UNSUPPORTED_FOR_SYNC)
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -181,16 +181,17 @@ EXPORTS.mozilla.dom += [
     'FileReader.h',
     'FormData.h',
     'FragmentOrElement.h',
     'FromParser.h',
     'ImageEncoder.h',
     'ImportManager.h',
     'Link.h',
     'Location.h',
+    'MutableBlobStorage.h',
     'NameSpaceConstants.h',
     'Navigator.h',
     'NodeInfo.h',
     'NodeInfoInlines.h',
     'NodeIterator.h',
     'ProcessGlobal.h',
     'ResponsiveImageSelector.h',
     'SameProcessMessageQueue.h',
@@ -242,16 +243,17 @@ UNIFIED_SOURCES += [
     'FileReader.cpp',
     'FormData.cpp',
     'FragmentOrElement.cpp',
     'ImageEncoder.cpp',
     'ImportManager.cpp',
     'Link.cpp',
     'Location.cpp',
     'MultipartBlobImpl.cpp',
+    'MutableBlobStorage.cpp',
     'Navigator.cpp',
     'NodeInfo.cpp',
     'NodeIterator.cpp',
     'nsAtomListUtils.cpp',
     'nsAttrAndChildArray.cpp',
     'nsAttrValue.cpp',
     'nsAttrValueOrString.cpp',
     'nsCCUncollectableMarker.cpp',
--- a/dom/base/nsCCUncollectableMarker.cpp
+++ b/dom/base/nsCCUncollectableMarker.cpp
@@ -23,16 +23,17 @@
 #include "nsIXULWindow.h"
 #include "nsIAppShellService.h"
 #include "nsAppShellCID.h"
 #include "nsContentUtils.h"
 #include "nsGlobalWindow.h"
 #include "nsJSEnvironment.h"
 #include "nsInProcessTabChildGlobal.h"
 #include "nsFrameLoader.h"
+#include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/ProcessGlobal.h"
 #include "xpcpublic.h"
 #include "nsObserverService.h"
 #include "nsFocusManager.h"
 #include "nsIInterfaceRequestorUtils.h"
 
@@ -308,16 +309,18 @@ MarkWindowList(nsISimpleEnumerator* aWin
       MarkDocShell(rootDocShell, aCleanupJS, aPrepareForCC);
 
       nsCOMPtr<nsITabChild> tabChild =
         rootDocShell ? rootDocShell->GetTabChild() : nullptr;
       if (tabChild) {
         nsCOMPtr<nsIContentFrameMessageManager> mm;
         tabChild->GetMessageManager(getter_AddRefs(mm));
         if (mm) {
+          // MarkForCC ends up calling UnmarkGray on message listeners, which
+          // TraceBlackJS can't do yet.
           mm->MarkForCC();
         }
       }
     }
   }
 }
 
 nsresult
@@ -484,29 +487,61 @@ mozilla::dom::TraceBlackJS(JSTracer* aTr
     }
   }
 #endif
 
   if (!nsCCUncollectableMarker::sGeneration) {
     return;
   }
 
+  if (nsFrameMessageManager::GetChildProcessManager()) {
+    nsIContentProcessMessageManager* pg = ProcessGlobal::Get();
+    if (pg) {
+      mozilla::TraceScriptHolder(pg, aTrc);
+    }
+  }
+
   // Mark globals of active windows black.
   nsGlobalWindow::WindowByIdTable* windowsById =
     nsGlobalWindow::GetWindowsTable();
   if (windowsById) {
     for (auto iter = windowsById->Iter(); !iter.Done(); iter.Next()) {
       nsGlobalWindow* window = iter.Data();
       if (window->GetDocShell() && window->IsOuterWindow()) {
         window->TraceGlobalJSObject(aTrc);
         EventListenerManager* elm = window->GetExistingListenerManager();
         if (elm) {
           elm->TraceListeners(aTrc);
         }
 
+        if (window->IsRootOuterWindow()) {
+          // In child process trace all the TabChildGlobals.
+          // Since there is one root outer window per TabChildGlobal, we need
+          // to look for only those windows, not all.
+          nsIDocShell* ds = window->GetDocShell();
+          if (ds) {
+            nsCOMPtr<nsITabChild> tabChild = ds->GetTabChild();
+            if (tabChild) {
+              nsCOMPtr<nsIContentFrameMessageManager> mm;
+              tabChild->GetMessageManager(getter_AddRefs(mm));
+              nsCOMPtr<EventTarget> et = do_QueryInterface(mm);
+              if (et) {
+                nsCOMPtr<nsISupports> tabChildAsSupports =
+                  do_QueryInterface(tabChild);
+                mozilla::TraceScriptHolder(tabChildAsSupports, aTrc);
+                EventListenerManager* elm = et->GetExistingListenerManager();
+                if (elm) {
+                  elm->TraceListeners(aTrc);
+                }
+                // As of now there isn't an easy way to trace message listeners.
+              }
+            }
+          }
+        }
+
 #ifdef MOZ_XUL
         nsIDocument* doc = window->GetExtantDoc();
         if (doc && doc->IsXULDocument()) {
           XULDocument* xulDoc = static_cast<XULDocument*>(doc);
           xulDoc->TraceProtos(aTrc, aGCNumber);
         }
 #endif
       }
--- a/dom/base/nsDeprecatedOperationList.h
+++ b/dom/base/nsDeprecatedOperationList.h
@@ -28,16 +28,17 @@ DEPRECATED_OPERATION(PrefixedVisibilityA
 DEPRECATED_OPERATION(NodeIteratorDetach)
 DEPRECATED_OPERATION(LenientThis)
 DEPRECATED_OPERATION(GetPreventDefault)
 DEPRECATED_OPERATION(GetSetUserData)
 DEPRECATED_OPERATION(MozGetAsFile)
 DEPRECATED_OPERATION(UseOfCaptureEvents)
 DEPRECATED_OPERATION(UseOfReleaseEvents)
 DEPRECATED_OPERATION(UseOfDOM3LoadMethod)
+DEPRECATED_OPERATION(ChromeUseOfDOM3LoadMethod)
 DEPRECATED_OPERATION(ShowModalDialog)
 DEPRECATED_OPERATION(Window_Content)
 DEPRECATED_OPERATION(SyncXMLHttpRequest)
 DEPRECATED_OPERATION(DataContainerEvent)
 DEPRECATED_OPERATION(Window_Controllers)
 DEPRECATED_OPERATION(ImportXULIntoContent)
 DEPRECATED_OPERATION(PannerNodeDoppler)
 DEPRECATED_OPERATION(NavigatorGetUserMedia)
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -247,17 +247,17 @@
 #include "mozilla/dom/FontFaceSet.h"
 #include "mozilla/dom/BoxObject.h"
 #include "gfxPrefs.h"
 #include "nsISupportsPrimitives.h"
 #include "mozilla/StyleSetHandle.h"
 #include "mozilla/StyleSetHandleInlines.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/StyleSheetInlines.h"
-
+#include "mozilla/dom/SVGSVGElement.h"
 #include "mozilla/DocLoadingTimelineMarker.h"
 
 #include "nsISpeculativeConnect.h"
 
 #include "mozilla/MediaManager.h"
 #ifdef MOZ_WEBRTC
 #include "IPeerConnection.h"
 #endif // MOZ_WEBRTC
@@ -3033,16 +3033,26 @@ nsDocument::GetAnimations(nsTArray<RefPt
   if (!root) {
     return;
   }
   AnimationFilter filter;
   filter.mSubtree = true;
   root->GetAnimations(filter, aAnimations);
 }
 
+SVGSVGElement*
+nsIDocument::GetSVGRootElement() const
+{
+  Element* root = GetRootElement();
+  if (!root || !root->IsSVGElement(nsGkAtoms::svg)) {
+    return nullptr;
+  }
+  return static_cast<SVGSVGElement*>(root);
+}
+
 /* Return true if the document is in the focused top-level window, and is an
  * ancestor of the focused DOMWindow. */
 NS_IMETHODIMP
 nsDocument::HasFocus(bool* aResult)
 {
   ErrorResult rv;
   *aResult = nsIDocument::HasFocus(rv);
   return rv.StealNSResult();
@@ -5967,40 +5977,16 @@ nsDocument::GetElementsByTagNameNS(const
   }
 
   // transfer ref to aReturn
   list.forget(aReturn);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDocument::GetAsync(bool *aAsync)
-{
-  NS_ERROR("nsDocument::GetAsync() should be overriden by subclass!");
-
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-NS_IMETHODIMP
-nsDocument::SetAsync(bool aAsync)
-{
-  NS_ERROR("nsDocument::SetAsync() should be overriden by subclass!");
-
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-NS_IMETHODIMP
-nsDocument::Load(const nsAString& aUrl, bool *aReturn)
-{
-  NS_ERROR("nsDocument::Load() should be overriden by subclass!");
-
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-NS_IMETHODIMP
 nsDocument::GetStyleSheets(nsIDOMStyleSheetList** aStyleSheets)
 {
   NS_ADDREF(*aStyleSheets = StyleSheets());
   return NS_OK;
 }
 
 StyleSheetList*
 nsDocument::StyleSheets()
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -14,17 +14,17 @@
 #include "nsIDocument.h"
 
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "nsCRT.h"
 #include "nsWeakReference.h"
 #include "nsWeakPtr.h"
 #include "nsTArray.h"
-#include "nsIDOMXMLDocument.h"
+#include "nsIDOMDocument.h"
 #include "nsIDOMDocumentXBL.h"
 #include "nsStubDocumentObserver.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIContent.h"
 #include "nsIPrincipal.h"
 #include "nsIParser.h"
 #include "nsBindingManager.h"
 #include "nsInterfaceHashtable.h"
@@ -480,26 +480,18 @@ protected:
                                nsIDocument* aDisplayDocument);
 
   nsClassHashtable<nsURIHashKey, ExternalResource> mMap;
   nsRefPtrHashtable<nsURIHashKey, PendingLoad> mPendingLoads;
   bool mHaveShutDown;
 };
 
 // Base class for our document implementations.
-//
-// Note that this class *implements* nsIDOMXMLDocument, but it's not
-// really an nsIDOMXMLDocument. The reason for implementing
-// nsIDOMXMLDocument on this class is to avoid having to duplicate all
-// its inherited methods on document classes that *are*
-// nsIDOMXMLDocument's. nsDocument's QI should *not* claim to support
-// nsIDOMXMLDocument unless someone writes a real implementation of
-// the interface.
 class nsDocument : public nsIDocument,
-                   public nsIDOMXMLDocument, // inherits nsIDOMDocument
+                   public nsIDOMDocument,
                    public nsIDOMDocumentXBL,
                    public nsSupportsWeakReference,
                    public nsIScriptObjectPrincipal,
                    public nsIRadioGroupContainer,
                    public nsIApplicationCacheContainer,
                    public nsStubMutationObserver,
                    public nsIObserver,
                    public nsIDOMXPathEvaluator
@@ -793,19 +785,16 @@ private:
 
 public:
   // nsIDOMNode
   NS_FORWARD_NSIDOMNODE_TO_NSINODE_OVERRIDABLE
 
   // nsIDOMDocument
   NS_DECL_NSIDOMDOCUMENT
 
-  // nsIDOMXMLDocument
-  NS_DECL_NSIDOMXMLDOCUMENT
-
   // nsIDOMDocumentXBL
   NS_DECL_NSIDOMDOCUMENTXBL
 
   // nsIDOMEventTarget
   virtual nsresult PreHandleEvent(
                      mozilla::EventChainPreVisitor& aVisitor) override;
   virtual mozilla::EventListenerManager*
     GetOrCreateListenerManager() override;
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -617,17 +617,17 @@ nsPIDOMWindow<T>::nsPIDOMWindow(nsPIDOMW
   mMayHaveMouseEnterLeaveEventListener(false),
   mMayHavePointerEnterLeaveEventListener(false),
   mInnerObjectsFreed(false),
   mIsModalContentWindow(false),
   mIsActive(false), mIsBackground(false),
   mMediaSuspend(Preferences::GetBool("media.block-autoplay-until-in-foreground", true) ?
     nsISuspendedTypes::SUSPENDED_BLOCK : nsISuspendedTypes::NONE_SUSPENDED),
   mAudioMuted(false), mAudioVolume(1.0), mAudioCaptured(false),
-  mDesktopModeViewport(false), mInnerWindow(nullptr),
+  mDesktopModeViewport(false), mIsRootOuterWindow(false), mInnerWindow(nullptr),
   mOuterWindow(aOuterWindow),
   // Make sure no actual window ends up with mWindowID == 0
   mWindowID(NextWindowID()), mHasNotifiedGlobalCreated(false),
   mMarkedCCGeneration(0), mServiceWorkersTestingEnabled(false)
  {}
 
 template<class T>
 nsPIDOMWindow<T>::~nsPIDOMWindow() {}
@@ -3062,16 +3062,17 @@ nsGlobalWindow::SetDocShell(nsIDocShell*
     // handler and receive all events that occur anywhere inside
     // our window.
     nsCOMPtr<nsPIDOMWindowOuter> parentWindow = GetParent();
     if (parentWindow.get() != AsOuter()) {
       mChromeEventHandler = parentWindow->GetChromeEventHandler();
     }
     else {
       mChromeEventHandler = NS_NewWindowRoot(AsOuter());
+      mIsRootOuterWindow = true;
     }
   }
 
   bool docShellActive;
   mDocShell->GetIsActive(&docShellActive);
   mIsBackground = !docShellActive;
 }
 
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -143,16 +143,17 @@ class MediaQueryList;
 class GlobalObject;
 class NodeFilter;
 class NodeIterator;
 enum class OrientationType : uint32_t;
 class ProcessingInstruction;
 class Promise;
 class StyleSheetList;
 class SVGDocument;
+class SVGSVGElement;
 class Touch;
 class TouchList;
 class TreeWalker;
 class UndoManager;
 class XPathEvaluator;
 class XPathExpression;
 class XPathNSResolver;
 class XPathResult;
@@ -2323,16 +2324,18 @@ public:
 
   virtual already_AddRefed<mozilla::dom::UndoManager> GetUndoManager() = 0;
 
   virtual mozilla::dom::DocumentTimeline* Timeline() = 0;
 
   virtual void GetAnimations(
       nsTArray<RefPtr<mozilla::dom::Animation>>& aAnimations) = 0;
 
+  mozilla::dom::SVGSVGElement* GetSVGRootElement() const;
+
   nsresult ScheduleFrameRequestCallback(mozilla::dom::FrameRequestCallback& aCallback,
                                         int32_t *aHandle);
   void CancelFrameRequestCallback(int32_t aHandle);
 
   typedef nsTArray<RefPtr<mozilla::dom::FrameRequestCallback>> FrameRequestCallbackList;
   /**
    * Put this document's frame request callbacks into the provided
    * list, and forget about them.
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -113,16 +113,22 @@ public:
    */
 
   virtual already_AddRefed<nsPIDOMWindowOuter> GetTop() = 0; // Outer only
   virtual already_AddRefed<nsPIDOMWindowOuter> GetParent() = 0;
   virtual nsPIDOMWindowOuter* GetScriptableTop() = 0;
   virtual nsPIDOMWindowOuter* GetScriptableParent() = 0;
   virtual already_AddRefed<nsPIWindowRoot> GetTopWindowRoot() = 0;
 
+  bool IsRootOuterWindow()
+  {
+    MOZ_ASSERT(IsOuterWindow());
+    return mIsRootOuterWindow;
+  }
+
   /**
    * Behavies identically to GetScriptableParent extept that it returns null
    * if GetScriptableParent would return this window.
    */
   virtual nsPIDOMWindowOuter* GetScriptableParentOrNull() = 0;
 
   // Inner windows only.
   virtual nsresult RegisterIdleObserver(nsIIdleObserver* aIdleObserver) = 0;
@@ -661,16 +667,18 @@ protected:
   bool                   mAudioMuted;
   float                  mAudioVolume;
 
   bool                   mAudioCaptured;
 
   // current desktop mode flag.
   bool                   mDesktopModeViewport;
 
+  bool                   mIsRootOuterWindow;
+
   // And these are the references between inner and outer windows.
   nsPIDOMWindowInner* MOZ_NON_OWNING_REF mInnerWindow;
   nsCOMPtr<nsPIDOMWindowOuter> mOuterWindow;
 
   // the element within the document that is currently focused when this
   // window is active
   nsCOMPtr<nsIContent> mFocusedNode;
 
--- a/dom/base/test/test_bug435425.html
+++ b/dom/base/test/test_bug435425.html
@@ -212,17 +212,17 @@ var tests =
   [
     { method: "GET", withUpload: none, testAbort: false, testRedirectError: false, testNetworkError: false,
       expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                        {target: XHR, type: "progress", optional: false},
                        {target: XHR, type: "load", optional: false},
                        {target: XHR, type: "loadend", optional: false}]},
     { method: "GET", withUpload: none, testAbort: true, testRedirectError: false, testNetworkError: false,
       expectedEvents: [{target: XHR, type: "loadstart", optional: false},
-                       {target: XHR, type: "progress", optional: false},
+                       {target: XHR, type: "progress", optional: true},
                        {target: XHR, type: "abort", optional: false},
                        {target: XHR, type: "loadend", optional: false}]},
     { method: "GET", withUpload: none, testAbort: false, testRedirectError: true, testNetworkError: false,
       expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                        {target: XHR, type: "error", optional: false},
                        {target: XHR, type: "loadend", optional: false}]},
     { method: "GET", withUpload: none, testAbort: false, testRedirectError: false, testNetworkError: true,
       expectedEvents: [{target: XHR, type: "loadstart", optional: false},
@@ -231,17 +231,17 @@ var tests =
 
     { method: "GET", withUpload: small, testAbort: false, testRedirectError: false, testNetworkError: false,
       expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                        {target: XHR, type: "progress", optional: false},
                        {target: XHR, type: "load", optional: false},
                        {target: XHR, type: "loadend", optional: false}]},
     { method: "GET", withUpload: small, testAbort: true, testRedirectError: false, testNetworkError: false,
       expectedEvents: [{target: XHR, type: "loadstart", optional: false},
-                       {target: XHR, type: "progress", optional: false},
+                       {target: XHR, type: "progress", optional: true},
                        {target: XHR, type: "abort", optional: false},
                        {target: XHR, type: "loadend", optional: false}]},
     { method: "GET", withUpload: small, testAbort: false, testRedirectError: true, testNetworkError: false,
       expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                        {target: XHR, type: "error", optional: false},
                        {target: XHR, type: "loadend", optional: false}]},
     { method: "GET", withUpload: small, testAbort: false, testRedirectError: false, testNetworkError: true,
       expectedEvents: [{target: XHR, type: "loadstart", optional: false},
@@ -250,17 +250,17 @@ var tests =
 
     { method: "GET", withUpload: mid, testAbort: false, testRedirectError: false, testNetworkError: false,
       expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                        {target: XHR, type: "progress", optional: false},
                        {target: XHR, type: "load", optional: false},
                        {target: XHR, type: "loadend", optional: false}]},
     { method: "GET", withUpload: mid, testAbort: true, testRedirectError: false, testNetworkError: false,
       expectedEvents: [{target: XHR, type: "loadstart", optional: false},
-                       {target: XHR, type: "progress", optional: false},
+                       {target: XHR, type: "progress", optional: true},
                        {target: XHR, type: "abort", optional: false},
                        {target: XHR, type: "loadend", optional: false}]},
     { method: "GET", withUpload: mid, testAbort: false, testRedirectError: true, testNetworkError: false,
       expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                        {target: XHR, type: "error", optional: false},
                        {target: XHR, type: "loadend", optional: false}]},
     { method: "GET", withUpload: mid, testAbort: false, testRedirectError: false, testNetworkError: true,
       expectedEvents: [{target: XHR, type: "loadstart", optional: false},
@@ -269,17 +269,17 @@ var tests =
 
     { method: "GET", withUpload: large, testAbort: false, testRedirectError: false, testNetworkError: false,
       expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                        {target: XHR, type: "progress", optional: false},
                        {target: XHR, type: "load", optional: false},
                        {target: XHR, type: "loadend", optional: false}]},
     { method: "GET", withUpload: large, testAbort: true, testRedirectError: false, testNetworkError: false,
       expectedEvents: [{target: XHR, type: "loadstart", optional: false},
-                       {target: XHR, type: "progress", optional: false},
+                       {target: XHR, type: "progress", optional: true},
                        {target: XHR, type: "abort", optional: false},
                        {target: XHR, type: "loadend", optional: false}]},
     { method: "GET", withUpload: large, testAbort: false, testRedirectError: true, testNetworkError: false,
       expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                        {target: XHR, type: "error", optional: false},
                        {target: XHR, type: "loadend", optional: false}]},
     { method: "GET", withUpload: large, testAbort: false, testRedirectError: false, testNetworkError: true,
       expectedEvents: [{target: XHR, type: "loadstart", optional: false},
@@ -288,17 +288,17 @@ var tests =
 
     { method: "POST", withUpload: none, testAbort: false, testRedirectError: false, testNetworkError: false,
       expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                        {target: XHR, type: "progress", optional: false},
                        {target: XHR, type: "load", optional: false},
                        {target: XHR, type: "loadend", optional: false}]},
     { method: "POST", withUpload: none, testAbort: true, testRedirectError: false, testNetworkError: false,
       expectedEvents: [{target: XHR, type: "loadstart", optional: false},
-                       {target: XHR, type: "progress", optional: false},
+                       {target: XHR, type: "progress", optional: true},
                        {target: XHR, type: "abort", optional: false},
                        {target: XHR, type: "loadend", optional: false}]},
     { method: "POST", withUpload: none, testAbort: false, testRedirectError: true, testNetworkError: false,
       expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                        {target: XHR, type: "error", optional: false},
                        {target: XHR, type: "loadend", optional: false}]},
     { method: "POST", withUpload: none, testAbort: false, testRedirectError: false, testNetworkError: true,
       expectedEvents: [{target: XHR, type: "loadstart", optional: false},
@@ -312,20 +312,20 @@ var tests =
                        {target: UPLOAD, type: "load", optional: false},
                        {target: UPLOAD, type: "loadend", optional: false},
                        {target: XHR, type: "progress", optional: false},
                        {target: XHR, type: "load", optional: false},
                        {target: XHR, type: "loadend", optional: false}]},
     { method: "POST", withUpload: small, testAbort: true, testRedirectError: false, testNetworkError: false,
       expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                        {target: UPLOAD, type: "loadstart", optional: false},
-                       {target: UPLOAD, type: "progress", optional: false},
+                       {target: UPLOAD, type: "progress", optional: true},
                        {target: UPLOAD, type: "abort", optional: false},
                        {target: UPLOAD, type: "loadend", optional: false},
-                       {target: XHR, type: "progress", optional: false},
+                       {target: XHR, type: "progress", optional: true},
                        {target: XHR, type: "abort", optional: false},
                        {target: XHR, type: "loadend", optional: false}]},
     { method: "POST", withUpload: small, testAbort: false, testRedirectError: true, testNetworkError: false,
       expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                        {target: UPLOAD, type: "loadstart", optional: false},
                        {target: UPLOAD, type: "progress", optional: true},
                        {target: UPLOAD, type: "error", optional: false},
                        {target: UPLOAD, type: "loadend", optional: false},
@@ -346,20 +346,20 @@ var tests =
                        {target: UPLOAD, type: "load", optional: false},
                        {target: UPLOAD, type: "loadend", optional: false},
                        {target: XHR, type: "progress", optional: false},
                        {target: XHR, type: "load", optional: false},
                        {target: XHR, type: "loadend", optional: false}]},
     { method: "POST", withUpload: mid, testAbort: true, testRedirectError: false, testNetworkError: false,
       expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                        {target: UPLOAD, type: "loadstart", optional: false},
-                       {target: UPLOAD, type: "progress", optional: false},
+                       {target: UPLOAD, type: "progress", optional: true},
                        {target: UPLOAD, type: "abort", optional: false},
                        {target: UPLOAD, type: "loadend", optional: false},
-                       {target: XHR, type: "progress", optional: false},
+                       {target: XHR, type: "progress", optional: true},
                        {target: XHR, type: "abort", optional: false},
                        {target: XHR, type: "loadend", optional: false}]},
     { method: "POST", withUpload: mid, testAbort: false, testRedirectError: true, testNetworkError: false,
       expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                        {target: UPLOAD, type: "loadstart", optional: false},
                        {target: UPLOAD, type: "progress", optional: true},
                        {target: UPLOAD, type: "error", optional: false},
                        {target: UPLOAD, type: "loadend", optional: false},
@@ -380,20 +380,20 @@ var tests =
                        {target: UPLOAD, type: "load", optional: false},
                        {target: UPLOAD, type: "loadend", optional: false},
                        {target: XHR, type: "progress", optional: false},
                        {target: XHR, type: "load", optional: false},
                        {target: XHR, type: "loadend", optional: false}]},
     { method: "POST", withUpload: large, testAbort: true, testRedirectError: false, testNetworkError: false,
       expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                        {target: UPLOAD, type: "loadstart", optional: false},
-                       {target: UPLOAD, type: "progress", optional: false},
+                       {target: UPLOAD, type: "progress", optional: true},
                        {target: UPLOAD, type: "abort", optional: false},
                        {target: UPLOAD, type: "loadend", optional: false},
-                       {target: XHR, type: "progress", optional: false},
+                       {target: XHR, type: "progress", optional: true},
                        {target: XHR, type: "abort", optional: false},
                        {target: XHR, type: "loadend", optional: false}]},
     { method: "POST", withUpload: large, testAbort: false, testRedirectError: true, testNetworkError: false,
       expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                        {target: UPLOAD, type: "loadstart", optional: false},
                        {target: UPLOAD, type: "progress", optional: true},
                        {target: UPLOAD, type: "error", optional: false},
                        {target: UPLOAD, type: "loadend", optional: false},
--- a/dom/base/test/test_bug450160.html
+++ b/dom/base/test/test_bug450160.html
@@ -29,18 +29,16 @@ function testHTMLDocuments(ids, isXHTML)
     ok(docType1, "No doctype?");
     ok(docType1.ownerDocument, "docType should have ownerDocument!");
     var doc1 = document.implementation.createDocument(null, null, docType1);
     is(docType1.ownerDocument, doc1, "docType should have ownerDocument!");
     ok(!doc1.documentElement, "Document shouldn't have document element!");
     is(doc1.body, null, "Shouldn't have .body!");
     ok(doc1 instanceof HTMLDocument,
        "Document should be an HTML document!");
-    ok(!(doc1 instanceof SVGDocument),
-       "Document shouldn't be an SVG document!");
 
     var docType2 =
       document.implementation.createDocumentType(isXHTML ? "html" : "HTML",
                                                  ids[i],
                                                  null);
     var doc2 = document.implementation.createDocument(
       "http://www.w3.org/1999/xhtml", "html", docType2);
     is(docType2.ownerDocument, doc2, "docType should have ownerDocument!");
@@ -65,62 +63,57 @@ function testSVGDocument() {
                                                  null);
   ok(docType1, "No doctype?");
   ok(docType1.ownerDocument, "docType should have ownerDocument!");
   var doc1 = document.implementation.createDocument(null, null, docType1);
   is(docType1.ownerDocument, doc1, "docType should have ownerDocument!");
   ok(!doc1.documentElement, "Document shouldn't have document element!");
   ok(!(doc1 instanceof HTMLDocument),
      "Document shouldn't be an HTML document!");
-  ok(doc1 instanceof SVGDocument,
-     "Document should be an SVG document!");
+  ok(doc1 instanceof XMLDocument,
+     "Document should be an XML document!");
 
-  // SVG documents have .rootElement.
-  ok("rootElement" in doc1, "No .rootElement in document");
+  // SVG documents have .documentElement.
+  ok("documentElement" in doc1, "No .documentElement in document");
 
   var docType2 =
       document.implementation.createDocumentType("svg",
                                                  "-//W3C//DTD SVG 1.1//EN",
                                                  null);
   var doc2 = document.implementation.createDocument("http://www.w3.org/2000/svg",
                                                     "svg", docType2);
   ok(doc2.documentElement, "Document should have document element!");
-  ok(doc2.rootElement, "Should have .rootElement in document");
-  is(doc2.rootElement.localName, "svg", "Wrong .rootElement!");
+  is(doc2.documentElement.localName, "svg", "Wrong .documentElement!");
 }
 
 function testFooBarDocument() {
   var docType1 =
       document.implementation.createDocumentType("FooBar", "FooBar", null);
   ok(docType1, "No doctype?");
   ok(docType1.ownerDocument, "docType should have ownerDocument!");
   var doc1 = document.implementation.createDocument(null, null, docType1);
   is(docType1.ownerDocument, doc1, "docType should have ownerDocument!");
   ok(!doc1.documentElement, "Document shouldn't have document element!");
   ok(!(doc1 instanceof HTMLDocument),
      "Document shouldn't be an HTML document!");
-  ok(!(doc1 instanceof SVGDocument),
-     "Document shouldn't be an SVG document!");
 
   var docType2 =
       document.implementation.createDocumentType("FooBar", "FooBar", null);
   var doc2 = document.implementation.createDocument("FooBarNS",
                                                     "FooBar", docType2);
   ok(doc2.documentElement, "Document should have document element!");
   is(doc2.documentElement.namespaceURI, "FooBarNS", "Wrong namespaceURI!");
   is(doc2.documentElement.localName, "FooBar", "Wrong localName!");
 }
 
 function testNullDocTypeDocument() {
   var doc1 = document.implementation.createDocument(null, null, null);
   ok(!doc1.documentElement, "Document shouldn't have document element!");
   ok(!(doc1 instanceof HTMLDocument),
      "Document shouldn't be an HTML document!");
-  ok(!(doc1 instanceof SVGDocument),
-     "Document shouldn't be an SVG document!");
 
   var doc2 = document.implementation.createDocument("FooBarNS",
                                                     "FooBar", null);
   ok(doc2.documentElement, "Document should have document element!");
   is(doc2.documentElement.namespaceURI, "FooBarNS", "Wrong namespaceURI!");
   is(doc2.documentElement.localName, "FooBar", "Wrong localName!");
 }
 
--- a/dom/base/test/test_bug693875.html
+++ b/dom/base/test/test_bug693875.html
@@ -18,22 +18,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <script type="application/javascript">
 
 /** Test for Bug 693875 **/
 
 var dp = new DOMParser();
 var d = dp.parseFromString("<?xml version='1.0'?><svg xmlns='http://www.w3.org/2000/svg'></svg>",
                            "image/svg+xml");
 
-ok(d instanceof SVGDocument, "Should have created an SVG document.");
-ok("rootElement" in d, "Should have created an SVG document, which has .rootElement.");
+ok(d instanceof XMLDocument, "Should have created an XML document.");
+ok("documentElement" in d, "Should have created an XML document, which has .documentElement.");
 is(d.documentElement.localName, "svg", "Root element should be svg.");
 is(d.documentElement.namespaceURI, "http://www.w3.org/2000/svg",
    "Root element should be in svg namespace.");
-
-dp = new DOMParser();
-d = dp.parseFromString("<?xml version='1.0'?><svg xmlns='http://www.w3.org/2000/svg'></svg>",
-                       "text/xml");
-ok(!(d instanceof SVGDocument), "Should not have created an SVG document!");
 </script>
 </pre>
 </body>
 </html>
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -1112,17 +1112,17 @@ QueryInterface(JSContext* cx, unsigned a
     return false;
 
   // Get the object. It might be a security wrapper, in which case we do a checked
   // unwrap.
   JS::Rooted<JSObject*> origObj(cx, &thisv.toObject());
   JS::Rooted<JSObject*> obj(cx, js::CheckedUnwrap(origObj,
                                                   /* stopAtWindowProxy = */ false));
   if (!obj) {
-      JS_ReportError(cx, "Permission denied to access object");
+      JS_ReportErrorASCII(cx, "Permission denied to access object");
       return false;
   }
 
   // Switch this to UnwrapDOMObjectToISupports once our global objects are
   // using new bindings.
   nsCOMPtr<nsISupports> native;
   UnwrapArg<nsISupports>(obj, getter_AddRefs(native));
   if (!native) {
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -1850,18 +1850,16 @@ addExternalIface('MozTreeView', nativeTy
                   headerFile='nsITreeView.h', notflattened=True)
 addExternalIface('MozWakeLockListener', headerFile='nsIDOMWakeLockListener.h')
 addExternalIface('MozXULTemplateBuilder', nativeType='nsIXULTemplateBuilder')
 addExternalIface('nsIBrowserDOMWindow', nativeType='nsIBrowserDOMWindow',
                  notflattened=True)
 addExternalIface('nsIControllers', nativeType='nsIControllers')
 addExternalIface('nsIDOMCrypto', nativeType='nsIDOMCrypto',
                  headerFile='Crypto.h')
-addExternalIface('nsIInputStreamCallback', nativeType='nsIInputStreamCallback',
-                 headerFile='nsIAsyncInputStream.h')
 addExternalIface('nsIFile', nativeType='nsIFile', notflattened=True)
 addExternalIface('nsILoadGroup', nativeType='nsILoadGroup',
                  headerFile='nsILoadGroup.h', notflattened=True)
 addExternalIface('nsIMessageBroadcaster', nativeType='nsIMessageBroadcaster',
                  headerFile='nsIMessageManager.h', notflattened=True)
 addExternalIface('nsISelectionListener', nativeType='nsISelectionListener')
 addExternalIface('nsIStreamListener', nativeType='nsIStreamListener', notflattened=True)
 addExternalIface('nsITransportProvider', nativeType='nsITransportProvider')
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -6831,26 +6831,30 @@ def needScopeObject(returnType, argument
              any(typeNeedsScopeObject(a.type) for a in arguments)))
 
 
 class CGCallGenerator(CGThing):
     """
     A class to generate an actual call to a C++ object.  Assumes that the C++
     object is stored in a variable whose name is given by the |object| argument.
 
+    needsSubjectPrincipal is a boolean indicating whether the call should
+    receive the subject nsIPrincipal as argument.
+
     isFallible is a boolean indicating whether the call should be fallible.
 
     resultVar: If the returnType is not void, then the result of the call is
     stored in a C++ variable named by resultVar. The caller is responsible for
     declaring the result variable. If the caller doesn't care about the result
     value, resultVar can be omitted.
     """
-    def __init__(self, isFallible, arguments, argsPre, returnType,
-                 extendedAttributes, descriptorProvider, nativeMethodName,
-                 static, object="self", argsPost=[], resultVar=None):
+    def __init__(self, isFallible, needsSubjectPrincipal, arguments, argsPre,
+                 returnType, extendedAttributes, descriptorProvider,
+                 nativeMethodName, static, object="self", argsPost=[],
+                 resultVar=None):
         CGThing.__init__(self)
 
         result, resultOutParam, resultRooter, resultArgs, resultConversion = \
             getRetvalDeclarationForType(returnType, descriptorProvider)
 
         args = CGList([CGGeneric(arg) for arg in argsPre], ", ")
         for a, name in arguments:
             arg = CGGeneric(name)
@@ -6897,16 +6901,19 @@ class CGCallGenerator(CGThing):
                 needResultDecl = True
                 resultVar = "result"
             if resultOutParam == "ref":
                 args.append(CGGeneric(resultVar))
             else:
                 assert resultOutParam == "ptr"
                 args.append(CGGeneric("&" + resultVar))
 
+        if needsSubjectPrincipal:
+            args.append(CGGeneric("subjectPrincipal"))
+
         if isFallible:
             args.append(CGGeneric("rv"))
         args.extend(CGGeneric(arg) for arg in argsPost)
 
         # Build up our actual call
         self.cgRoot = CGList([])
 
         call = CGGeneric(nativeMethodName)
@@ -6935,16 +6942,28 @@ class CGCallGenerator(CGThing):
                     call = CGWrapper(call, pre=resultVar + " = ")
         elif result is not None:
             assert resultOutParam is None
             call = CGWrapper(call, pre=resultVar + " = ")
 
         call = CGWrapper(call, post=";\n")
         self.cgRoot.append(call)
 
+        if needsSubjectPrincipal:
+            self.cgRoot.prepend(CGGeneric(dedent(
+               """
+               Maybe<nsIPrincipal*> subjectPrincipal;
+               if (NS_IsMainThread()) {
+                 JSCompartment* compartment = js::GetContextCompartment(cx);
+                 MOZ_ASSERT(compartment);
+                 JSPrincipals* principals = JS_GetCompartmentPrincipals(compartment);
+                 subjectPrincipal.emplace(nsJSPrincipals::get(principals));
+               }
+               """)))
+
         if isFallible:
             self.cgRoot.prepend(CGGeneric("binding_detail::FastErrorResult rv;\n"))
             self.cgRoot.append(CGGeneric(dedent(
                 """
                 if (MOZ_UNLIKELY(rv.MaybeSetPendingException(cx))) {
                   return false;
                 }
                 """)))
@@ -7401,16 +7420,17 @@ class CGPerSignatureCall(CGThing):
                                                                   idlNode.identifier.name))
             else:
                 cgThings.append(CGIterableMethodGenerator(descriptor,
                                                           idlNode.maplikeOrSetlikeOrIterable,
                                                           idlNode.identifier.name))
         else:
             cgThings.append(CGCallGenerator(
                 self.isFallible(),
+                idlNode.getExtendedAttribute('NeedsSubjectPrincipal'),
                 self.getArguments(), argsPre, returnType,
                 self.extendedAttributes, descriptor,
                 nativeMethodName,
                 static, argsPost=argsPost, resultVar=resultVar))
 
         if useCounterName:
             # Generate a telemetry call for when [UseCounter] is used.
             code = "SetDocumentAndPageUseCounter(cx, obj, eUseCounter_%s);\n" % useCounterName
@@ -13807,16 +13827,19 @@ class CGNativeMember(ClassMethod):
             args.append(Argument("%s&" %
                                  CGUnionStruct.unionTypeDecl(returnType, True),
                                  "aRetVal"))
         elif returnType.isAny():
             args.append(Argument("JS::MutableHandle<JS::Value>", "aRetVal"))
         elif returnType.isObject() or returnType.isSpiderMonkeyInterface():
             args.append(Argument("JS::MutableHandle<JSObject*>", "aRetVal"))
 
+        # And the nsIPrincipal
+        if self.member.getExtendedAttribute('NeedsSubjectPrincipal'):
+            args.append(Argument("const Maybe<nsIPrincipal*>&", "aPrincipal"))
         # And the ErrorResult
         if 'infallible' not in self.extendedAttrs:
             # Use aRv so it won't conflict with local vars named "rv"
             args.append(Argument("ErrorResult&", "aRv"))
         # The legacycaller thisval
         if self.member.isMethod() and self.member.isLegacycaller():
             # If it has an identifier, we can't deal with it yet
             assert self.member.isIdentifierLess()
@@ -15752,17 +15775,17 @@ class CGMaplikeOrSetlikeMethodGenerator(
         something that would return an iterator is called via Xray, fail early.
         """
         # TODO: Bug 1173651 - Remove check once bug 1023984 is fixed.
         code = CGGeneric(dedent(
             """
             // TODO (Bug 1173651): Xrays currently cannot wrap iterators. Change
             // after bug 1023984 is fixed.
             if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
-              JS_ReportError(cx, "Xray wrapping of iterators not supported.");
+              JS_ReportErrorASCII(cx, "Xray wrapping of iterators not supported.");
               return false;
             }
             JS::Rooted<JSObject*> result(cx);
             JS::Rooted<JS::Value> v(cx);
             """))
         arguments = "&v"
         setResult = CGGeneric(dedent(
             """
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -4199,16 +4199,17 @@ class IDLAttribute(IDLInterfaceMember):
               identifier == "Throws" or
               identifier == "GetterThrows" or
               identifier == "ChromeOnly" or
               identifier == "Func" or
               identifier == "SecureContext" or
               identifier == "Frozen" or
               identifier == "NewObject" or
               identifier == "UnsafeInPrerendering" or
+              identifier == "NeedsSubjectPrincipal" or
               identifier == "BinaryName"):
             # Known attributes that we don't need to do anything with here
             pass
         else:
             raise WebIDLError("Unknown extended attribute %s on attribute" % identifier,
                               [attr.location])
         IDLInterfaceMember.handleExtendedAttribute(self, attr)
 
@@ -4915,16 +4916,17 @@ class IDLMethod(IDLInterfaceMember, IDLS
               identifier == "NewObject" or
               identifier == "ChromeOnly" or
               identifier == "UnsafeInPrerendering" or
               identifier == "Pref" or
               identifier == "Deprecated" or
               identifier == "Func" or
               identifier == "SecureContext" or
               identifier == "BinaryName" or
+              identifier == "NeedsSubjectPrincipal" or
               identifier == "StaticClassOverride"):
             # Known attributes that we don't need to do anything with here
             pass
         else:
             raise WebIDLError("Unknown extended attribute %s on method" % identifier,
                               [attr.location])
         IDLInterfaceMember.handleExtendedAttribute(self, attr)
 
--- a/dom/bindings/test/TestExampleGen.webidl
+++ b/dom/bindings/test/TestExampleGen.webidl
@@ -768,16 +768,18 @@ interface TestExampleInterface {
   void passRenamedInterface(TestRenamedInterface arg);
   [PutForwards=writableByte] readonly attribute TestExampleInterface putForwardsAttr;
   [PutForwards=writableByte, LenientThis] readonly attribute TestExampleInterface putForwardsAttr2;
   [PutForwards=writableByte, ChromeOnly] readonly attribute TestExampleInterface putForwardsAttr3;
   [Throws] void throwingMethod();
   [Throws] attribute boolean throwingAttr;
   [GetterThrows] attribute boolean throwingGetterAttr;
   [SetterThrows] attribute boolean throwingSetterAttr;
+  [NeedsSubjectPrincipal] void needsSubjectPrincipalMethod();
+  [NeedsSubjectPrincipal] attribute boolean needsSubjectPrincipalAttr;
   legacycaller short(unsigned long arg1, TestInterface arg2);
   void passArgsWithDefaults(optional long arg1,
                             optional TestInterface? arg2 = null,
                             optional Dict arg3, optional double arg4 = 5.0,
                             optional float arg5);
   attribute any jsonifierShouldSkipThis;
   attribute TestParentInterface jsonifierShouldSkipThis2;
   attribute TestCallbackInterface jsonifierShouldSkipThis3;
--- a/dom/canvas/TexUnpackBlob.cpp
+++ b/dom/canvas/TexUnpackBlob.cpp
@@ -390,25 +390,26 @@ TexUnpackBytes::TexOrSubImage(bool isSub
     }
 
     //////
 
     MOZ_ASSERT(webgl->mBoundPixelUnpackBuffer);
 
     if (!isSubImage) {
         // Alloc first to catch OOMs.
-        gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0);
+        AssertUintParamCorrect(gl, LOCAL_GL_PIXEL_UNPACK_BUFFER, 0);
         *out_error = DoTexOrSubImage(false, gl, target, level, dui, xOffset, yOffset,
                                      zOffset, mWidth, mHeight, mDepth, nullptr);
-        gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER,
-                        webgl->mBoundPixelUnpackBuffer->mGLName);
         if (*out_error)
             return false;
     }
 
+    const ScopedLazyBind bindPBO(gl, LOCAL_GL_PIXEL_UNPACK_BUFFER,
+                                 webgl->mBoundPixelUnpackBuffer);
+
     //////
 
     // Make our sometimes-implicit values explicit. Also this keeps them constant when we
     // ask for height=mHeight-1 and such.
     gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, mRowLength);
     gl->fPixelStorei(LOCAL_GL_UNPACK_IMAGE_HEIGHT, mImageHeight);
 
     if (mDepth > 1) {
--- a/dom/canvas/WebGL2ContextBuffers.cpp
+++ b/dom/canvas/WebGL2ContextBuffers.cpp
@@ -80,16 +80,18 @@ WebGL2Context::CopyBufferSubData(GLenum 
                               (readType == WebGLBuffer::Kind::OtherData) ? "other"
                                                                          : "element",
                               (writeType == WebGLBuffer::Kind::OtherData) ? "other"
                                                                           : "element");
         return;
     }
 
     gl->MakeCurrent();
+    const ScopedLazyBind readBind(gl, readTarget, readBuffer);
+    const ScopedLazyBind writeBind(gl, writeTarget, writeBuffer);
     gl->fCopyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size);
 }
 
 void
 WebGL2Context::GetBufferSubData(GLenum target, GLintptr offset,
                                 const dom::ArrayBufferView& data)
 {
     const char funcName[] = "getBufferSubData";
@@ -138,16 +140,17 @@ WebGL2Context::GetBufferSubData(GLenum t
         ErrorInvalidOperation("%s: Currently bound transform feedback is active.",
                               funcName);
         return;
     }
 
     ////
 
     gl->MakeCurrent();
+    const ScopedLazyBind readBind(gl, target, buffer);
 
     const auto ptr = gl->fMapBufferRange(target, offset, data.LengthAllowShared(),
                                          LOCAL_GL_MAP_READ_BIT);
     // Warning: Possibly shared memory.  See bug 1225033.
     memcpy(data.DataAllowShared(), ptr, data.LengthAllowShared());
     gl->fUnmapBuffer(target);
 }
 
--- a/dom/canvas/WebGLBuffer.cpp
+++ b/dom/canvas/WebGLBuffer.cpp
@@ -11,16 +11,17 @@
 #include "WebGLElementArrayCache.h"
 
 namespace mozilla {
 
 WebGLBuffer::WebGLBuffer(WebGLContext* webgl, GLuint buf)
     : WebGLContextBoundObject(webgl)
     , mGLName(buf)
     , mContent(Kind::Undefined)
+    , mUsage(LOCAL_GL_STATIC_DRAW)
     , mByteLength(0)
     , mNumActiveTFOs(0)
     , mBoundForTF(false)
 {
     mContext->mBuffers.insertBack(this);
 }
 
 WebGLBuffer::~WebGLBuffer()
@@ -109,16 +110,17 @@ WebGLBuffer::BufferData(GLenum target, s
         mContext->ErrorInvalidOperation("%s: Buffer is bound to an active transform"
                                         " feedback object.",
                                         funcName);
         return;
     }
 
     const auto& gl = mContext->gl;
     gl->MakeCurrent();
+    const ScopedLazyBind lazyBind(gl, target, this);
     mContext->InvalidateBufferFetching();
 
 #ifdef XP_MACOSX
     // bug 790879
     if (gl->WorkAroundDriverBugs() &&
         size > INT32_MAX)
     {
         mContext->ErrorOutOfMemory("%s: Allocation size too large.", funcName);
@@ -136,16 +138,17 @@ WebGLBuffer::BufferData(GLenum target, s
             MOZ_ASSERT(error == LOCAL_GL_OUT_OF_MEMORY);
             mContext->ErrorOutOfMemory("%s: Error from driver: 0x%04x", funcName, error);
             return;
         }
     } else {
         gl->fBufferData(target, size, data, usage);
     }
 
+    mUsage = usage;
     mByteLength = size;
 
     // Warning: Possibly shared memory.  See bug 1225033.
     if (!ElementArrayCacheBufferData(data, size)) {
         mByteLength = 0;
         mContext->ErrorOutOfMemory("%s: Failed update index buffer cache.", funcName);
     }
 }
--- a/dom/canvas/WebGLBuffer.h
+++ b/dom/canvas/WebGLBuffer.h
@@ -40,16 +40,17 @@ public:
 
     void SetContentAfterBind(GLenum target);
     Kind Content() const { return mContent; }
 
     void Delete();
 
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
 
+    GLenum Usage() const { return mUsage; }
     size_t ByteLength() const { return mByteLength; }
 
     bool ElementArrayCacheBufferData(const void* ptr, size_t bufferSizeInBytes);
 
     void ElementArrayCacheBufferSubData(size_t pos, const void* ptr,
                                         size_t updateSizeInBytes);
 
     bool Validate(GLenum type, uint32_t max_allowed, size_t first, size_t count) const;
@@ -69,16 +70,17 @@ public:
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLBuffer)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLBuffer)
 
 protected:
     ~WebGLBuffer();
 
     Kind mContent;
+    GLenum mUsage;
     size_t mByteLength;
     UniquePtr<WebGLElementArrayCache> mCache;
     size_t mNumActiveTFOs;
     bool mBoundForTF;
 };
 
 } // namespace mozilla
 
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -2154,16 +2154,49 @@ ScopedFBRebinder::UnwrapImpl()
         mGL->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, fnName(mWebGL->mBoundDrawFramebuffer));
         mGL->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, fnName(mWebGL->mBoundReadFramebuffer));
     } else {
         MOZ_ASSERT(mWebGL->mBoundDrawFramebuffer == mWebGL->mBoundReadFramebuffer);
         mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fnName(mWebGL->mBoundDrawFramebuffer));
     }
 }
 
+////////////////////
+
+static GLenum
+TargetIfLazy(GLenum target)
+{
+    switch (target) {
+    case LOCAL_GL_PIXEL_PACK_BUFFER:
+    case LOCAL_GL_PIXEL_UNPACK_BUFFER:
+        return target;
+
+    default:
+        return 0;
+    }
+}
+
+ScopedLazyBind::ScopedLazyBind(gl::GLContext* gl, GLenum target, const WebGLBuffer* buf)
+    : ScopedGLWrapper<ScopedLazyBind>(gl)
+    , mTarget(buf ? TargetIfLazy(target) : 0)
+    , mBuf(buf)
+{
+    if (mTarget) {
+        mGL->fBindBuffer(mTarget, mBuf->mGLName);
+    }
+}
+
+void
+ScopedLazyBind::UnwrapImpl()
+{
+    if (mTarget) {
+        mGL->fBindBuffer(mTarget, 0);
+    }
+}
+
 ////////////////////////////////////////
 
 void
 Intersect(uint32_t srcSize, int32_t dstStartInSrc, uint32_t dstSize,
           uint32_t* const out_intStartInSrc, uint32_t* const out_intStartInDst,
           uint32_t* const out_intSize)
 {
     // Only >0 if dstStartInSrc is >0:
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -1829,16 +1829,31 @@ public:
         : ScopedGLWrapper<ScopedFBRebinder>(webgl->gl)
         , mWebGL(webgl)
     { }
 
 private:
     void UnwrapImpl();
 };
 
+class ScopedLazyBind final
+    : public gl::ScopedGLWrapper<ScopedLazyBind>
+{
+    friend struct gl::ScopedGLWrapper<ScopedLazyBind>;
+
+    const GLenum mTarget;
+    const WebGLBuffer* const mBuf;
+
+public:
+    ScopedLazyBind(gl::GLContext* gl, GLenum target, const WebGLBuffer* buf);
+
+private:
+    void UnwrapImpl();
+};
+
 void
 ComputeLengthAndData(const dom::ArrayBufferViewOrSharedArrayBufferView& view,
                      void** const out_data, size_t* const out_length,
                      js::Scalar::Type* const out_type);
 
 void
 Intersect(uint32_t srcSize, int32_t dstStartInSrc, uint32_t dstSize,
           uint32_t* const out_intStartInSrc, uint32_t* const out_intStartInDst,
--- a/dom/canvas/WebGLContextBuffers.cpp
+++ b/dom/canvas/WebGLContextBuffers.cpp
@@ -132,16 +132,23 @@ WebGLContext::BindBuffer(GLenum target, 
 
     gl->MakeCurrent();
     gl->fBindBuffer(target, buffer ? buffer->mGLName : 0);
 
     *slot = buffer;
     if (buffer) {
         buffer->SetContentAfterBind(target);
     }
+
+    switch (target) {
+    case LOCAL_GL_PIXEL_PACK_BUFFER:
+    case LOCAL_GL_PIXEL_UNPACK_BUFFER:
+        gl->fBindBuffer(target, 0);
+        break;
+    }
 }
 
 ////////////////////////////////////////
 
 bool
 WebGLContext::ValidateIndexedBufferBinding(const char* funcName, GLenum target,
                                            GLuint index,
                                            WebGLRefPtr<WebGLBuffer>** const out_genericBinding,
@@ -424,16 +431,18 @@ WebGLContext::BufferSubDataT(GLenum targ
         ErrorInvalidValue("bufferSubData: Not enough data. Operation requires"
                           " %d bytes, but buffer only has %d bytes.",
                           checked_neededByteLength.value(),
                           buffer->ByteLength());
         return;
     }
 
     MakeContextCurrent();
+    const ScopedLazyBind lazyBind(gl, target, buffer);
+
     // Warning: Possibly shared memory.  See bug 1225033.
     gl->fBufferSubData(target, byteOffset, data.LengthAllowShared(),
                        data.DataAllowShared());
 
     // Warning: Possibly shared memory.  See bug 1225033.
     buffer->ElementArrayCacheBufferSubData(byteOffset, data.DataAllowShared(),
                                            data.LengthAllowShared());
 }
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -625,38 +625,27 @@ WebGLContext::GetBufferParameter(GLenum 
 {
     if (IsContextLost())
         return JS::NullValue();
 
     const auto& buffer = ValidateBufferSelection("getBufferParameter", target);
     if (!buffer)
         return JS::NullValue();
 
-    MakeContextCurrent();
-
     switch (pname) {
-        case LOCAL_GL_BUFFER_SIZE:
-        case LOCAL_GL_BUFFER_USAGE:
-        {
-            GLint i = 0;
-            gl->fGetBufferParameteriv(target, pname, &i);
-            if (pname == LOCAL_GL_BUFFER_SIZE) {
-                return JS::Int32Value(i);
-            }
-
-            MOZ_ASSERT(pname == LOCAL_GL_BUFFER_USAGE);
-            return JS::NumberValue(uint32_t(i));
-        }
-            break;
-
-        default:
-            ErrorInvalidEnumInfo("getBufferParameter: parameter", pname);
+    case LOCAL_GL_BUFFER_SIZE:
+        return JS::NumberValue(buffer->ByteLength());
+
+    case LOCAL_GL_BUFFER_USAGE:
+        return JS::NumberValue(buffer->Usage());
+
+    default:
+        ErrorInvalidEnumInfo("getBufferParameter: parameter", pname);
+        return JS::NullValue();
     }
-
-    return JS::NullValue();
 }
 
 JS::Value
 WebGLContext::GetFramebufferAttachmentParameter(JSContext* cx,
                                                 GLenum target,
                                                 GLenum attachment,
                                                 GLenum pname,
                                                 ErrorResult& rv)
@@ -1523,16 +1512,19 @@ WebGL2Context::ReadPixels(GLint x, GLint
     const auto bytesAvailable = mBoundPixelPackBuffer->ByteLength();
     const auto checkedBytesAfterOffset = CheckedUint32(bytesAvailable) - offset;
 
     uint32_t bytesAfterOffset = 0;
     if (checkedBytesAfterOffset.isValid()) {
         bytesAfterOffset = checkedBytesAfterOffset.value();
     }
 
+    gl->MakeCurrent();
+    const ScopedLazyBind lazyBind(gl, LOCAL_GL_PIXEL_PACK_BUFFER, mBoundPixelPackBuffer);
+
     ReadPixelsImpl(x, y, width, height, format, type, (void*)offset, bytesAfterOffset);
 }
 
 static bool
 ValidateReadPixelsFormatAndType(const webgl::FormatInfo* srcFormat,
                                 const webgl::PackingInfo& pi, gl::GLContext* gl,
                                 WebGLContext* webgl)
 {
--- a/dom/canvas/WebGLTransformFeedback.h
+++ b/dom/canvas/WebGLTransformFeedback.h
@@ -28,19 +28,19 @@ public:
 private:
     // GLES 3.0.4 p267, Table 6.24 "Transform Feedback State"
     WebGLRefPtr<WebGLBuffer> mGenericBufferBinding;
     std::vector<IndexedBufferBinding> mIndexedBindings;
     bool mIsPaused;
     bool mIsActive;
     // Not in state tables:
     WebGLRefPtr<WebGLProgram> mActive_Program;
-    GLenum mActive_PrimMode;
-    size_t mActive_VertPosition;
-    size_t mActive_VertCapacity;
+    MOZ_INIT_OUTSIDE_CTOR GLenum mActive_PrimMode;
+    MOZ_INIT_OUTSIDE_CTOR size_t mActive_VertPosition;
+    MOZ_INIT_OUTSIDE_CTOR size_t mActive_VertCapacity;
 
 public:
     WebGLTransformFeedback(WebGLContext* webgl, GLuint tf);
 private:
     ~WebGLTransformFeedback();
 
 public:
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLTransformFeedback)
--- a/dom/events/DataTransfer.cpp
+++ b/dom/events/DataTransfer.cpp
@@ -444,41 +444,45 @@ DataTransfer::SetData(const nsAString& a
   RefPtr<nsVariantCC> variant = new nsVariantCC();
   variant->SetAsAString(aData);
 
   aRv = SetDataAtInternal(aFormat, variant, 0,
                           nsContentUtils::SubjectPrincipal());
 }
 
 void
-DataTransfer::ClearData(const Optional<nsAString>& aFormat, ErrorResult& aRv)
+DataTransfer::ClearData(const Optional<nsAString>& aFormat,
+                        const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                        ErrorResult& aRv)
 {
+  MOZ_ASSERT(aSubjectPrincipal.isSome());
+
   if (mReadOnly) {
     aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     return;
   }
 
   if (MozItemCount() == 0) {
     return;
   }
 
   if (aFormat.WasPassed()) {
-    MozClearDataAtHelper(aFormat.Value(), 0, aRv);
+    MozClearDataAtHelper(aFormat.Value(), 0, aSubjectPrincipal, aRv);
   } else {
-    MozClearDataAtHelper(EmptyString(), 0, aRv);
+    MozClearDataAtHelper(EmptyString(), 0, aSubjectPrincipal, aRv);
   }
 }
 
 NS_IMETHODIMP
 DataTransfer::ClearData(const nsAString& aFormat)
 {
   Optional<nsAString> format;
   format = &aFormat;
   ErrorResult rv;
-  ClearData(format, rv);
+  ClearData(format, Some(nsContentUtils::SubjectPrincipal()), rv);
   return rv.StealNSResult();
 }
 
 NS_IMETHODIMP
 DataTransfer::GetMozItemCount(uint32_t* aCount)
 {
   *aCount = MozItemCount();
   return NS_OK;
@@ -738,18 +742,21 @@ DataTransfer::MozSetDataAt(JSContext* aC
   if (!aRv.Failed()) {
     aRv = SetDataAtInternal(aFormat, data, aIndex,
                             nsContentUtils::SubjectPrincipal());
   }
 }
 
 void
 DataTransfer::MozClearDataAt(const nsAString& aFormat, uint32_t aIndex,
+                             const Maybe<nsIPrincipal*>& aSubjectPrincipal,
                              ErrorResult& aRv)
 {
+  MOZ_ASSERT(aSubjectPrincipal.isSome());
+
   if (mReadOnly) {
     aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     return;
   }
 
   if (aIndex >= MozItemCount()) {
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return;
@@ -758,51 +765,53 @@ DataTransfer::MozClearDataAt(const nsASt
   // Only the first item is valid for clipboard events
   if (aIndex > 0 &&
       (mEventMessage == eCut || mEventMessage == eCopy ||
        mEventMessage == ePaste)) {
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return;
   }
 
-  MozClearDataAtHelper(aFormat, aIndex, aRv);
+  MozClearDataAtHelper(aFormat, aIndex, aSubjectPrincipal, aRv);
 
   // If we just cleared the 0-th index, and there are still more than 1 indexes
   // remaining, MozClearDataAt should cause the 1st index to become the 0th
   // index. This should _only_ happen when the MozClearDataAt function is
   // explicitly called by script, as this behavior is inconsistent with spec.
   // (however, so is the MozClearDataAt API)
 
   if (aIndex == 0 && mItems->MozItemCount() > 1 &&
       mItems->MozItemsAt(0)->Length() == 0) {
     mItems->PopIndexZero();
   }
 }
 
 void
 DataTransfer::MozClearDataAtHelper(const nsAString& aFormat, uint32_t aIndex,
+                                   const Maybe<nsIPrincipal*>& aSubjectPrincipal,
                                    ErrorResult& aRv)
 {
   MOZ_ASSERT(!mReadOnly);
   MOZ_ASSERT(aIndex < MozItemCount());
   MOZ_ASSERT(aIndex == 0 ||
              (mEventMessage != eCut && mEventMessage != eCopy &&
               mEventMessage != ePaste));
+  MOZ_ASSERT(aSubjectPrincipal.isSome());
 
   nsAutoString format;
   GetRealFormat(aFormat, format);
 
-  mItems->MozRemoveByTypeAt(format, aIndex, aRv);
+  mItems->MozRemoveByTypeAt(format, aIndex, aSubjectPrincipal, aRv);
 }
 
 NS_IMETHODIMP
 DataTransfer::MozClearDataAt(const nsAString& aFormat, uint32_t aIndex)
 {
   ErrorResult rv;
-  MozClearDataAt(aFormat, aIndex, rv);
+  MozClearDataAt(aFormat, aIndex, Some(nsContentUtils::SubjectPrincipal()), rv);
   return rv.StealNSResult();
 }
 
 void
 DataTransfer::SetDragImage(Element& aImage, int32_t aX, int32_t aY,
                            ErrorResult& aRv)
 {
   if (mReadOnly) {
--- a/dom/events/DataTransfer.h
+++ b/dom/events/DataTransfer.h
@@ -139,16 +139,17 @@ public:
   already_AddRefed<DOMStringList> GetTypes(ErrorResult& rv) const;
 
   void GetData(const nsAString& aFormat, nsAString& aData, ErrorResult& aRv);
 
   void SetData(const nsAString& aFormat, const nsAString& aData,
                ErrorResult& aRv);
 
   void ClearData(const mozilla::dom::Optional<nsAString>& aFormat,
+                 const Maybe<nsIPrincipal*>& aSubjectPrincipal,
                  mozilla::ErrorResult& aRv);
 
   already_AddRefed<FileList> GetFiles(mozilla::ErrorResult& aRv);
 
   already_AddRefed<Promise> GetFilesAndDirectories(ErrorResult& aRv);
 
   already_AddRefed<Promise> GetFiles(bool aRecursiveFlag, ErrorResult& aRv);
 
@@ -165,16 +166,17 @@ public:
       aCursor.AssignLiteral("auto");
     }
   }
 
   already_AddRefed<DOMStringList> MozTypesAt(uint32_t aIndex,
                                              mozilla::ErrorResult& aRv) const;
 
   void MozClearDataAt(const nsAString& aFormat, uint32_t aIndex,
+                      const Maybe<nsIPrincipal*>& aSubjectPrincipal,
                       mozilla::ErrorResult& aRv);
 
   void MozSetDataAt(JSContext* aCx, const nsAString& aFormat,
                     JS::Handle<JS::Value> aData, uint32_t aIndex,
                     mozilla::ErrorResult& aRv);
 
   void MozGetDataAt(JSContext* aCx, const nsAString& aFormat,
                     uint32_t aIndex, JS::MutableHandle<JS::Value> aRetval,
@@ -302,16 +304,17 @@ protected:
   void FillAllExternalData();
 
   void FillInExternalCustomTypes(uint32_t aIndex, nsIPrincipal* aPrincipal);
 
   void FillInExternalCustomTypes(nsIVariant* aData, uint32_t aIndex,
                                  nsIPrincipal* aPrincipal);
 
   void MozClearDataAtHelper(const nsAString& aFormat, uint32_t aIndex,
+                            const Maybe<nsIPrincipal*>& aSubjectPrincipal,
                             mozilla::ErrorResult& aRv);
 
   nsCOMPtr<nsISupports> mParent;
 
   // the drop effect and effect allowed
   uint32_t mDropEffect;
   uint32_t mEffectAllowed;
 
--- a/dom/events/DataTransferItem.cpp
+++ b/dom/events/DataTransferItem.cpp
@@ -233,31 +233,28 @@ DataTransferItem::FillInExternalData()
 #ifdef DEBUG
   if (oldKind != Kind()) {
     NS_WARNING("Clipboard data provided by the OS does not match predicted kind");
   }
 #endif
 }
 
 already_AddRefed<File>
-DataTransferItem::GetAsFile(ErrorResult& aRv)
+DataTransferItem::GetAsFile(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                            ErrorResult& aRv)
 {
-  return GetAsFileWithPrincipal(nsContentUtils::SubjectPrincipal(), aRv);
-}
+  MOZ_ASSERT(aSubjectPrincipal.isSome());
 
-already_AddRefed<File>
-DataTransferItem::GetAsFileWithPrincipal(nsIPrincipal* aPrincipal, ErrorResult& aRv)
-{
   if (mKind != KIND_FILE) {
     return nullptr;
   }
 
   // This is done even if we have an mCachedFile, as it performs the necessary
   // permissions checks to ensure that we are allowed to access this type.
-  nsCOMPtr<nsIVariant> data = Data(aPrincipal, aRv);
+  nsCOMPtr<nsIVariant> data = Data(aSubjectPrincipal.value(), aRv);
   if (NS_WARN_IF(!data || aRv.Failed())) {
     return nullptr;
   }
 
   // Generate the dom::File from the stored data, caching it so that the
   // same object is returned in the future.
   if (!mCachedFile) {
     nsCOMPtr<nsISupports> supports;
@@ -281,26 +278,22 @@ DataTransferItem::GetAsFileWithPrincipal
     }
   }
 
   RefPtr<File> file = mCachedFile;
   return file.forget();
 }
 
 already_AddRefed<FileSystemEntry>
-DataTransferItem::GetAsEntry(ErrorResult& aRv)
+DataTransferItem::GetAsEntry(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                             ErrorResult& aRv)
 {
-  return GetAsEntryWithPrincipal(nsContentUtils::SubjectPrincipal(), aRv);
-}
+  MOZ_ASSERT(aSubjectPrincipal.isSome());
 
-already_AddRefed<FileSystemEntry>
-DataTransferItem::GetAsEntryWithPrincipal(nsIPrincipal* aPrincipal,
-                                          ErrorResult& aRv)
-{
-  RefPtr<File> file = GetAsFileWithPrincipal(aPrincipal, aRv);
+  RefPtr<File> file = GetAsFile(aSubjectPrincipal, aRv);
   if (NS_WARN_IF(aRv.Failed()) || !file) {
     return nullptr;
   }
 
   nsCOMPtr<nsIGlobalObject> global;
   // This is annoying, but DataTransfer may have various things as parent.
   nsCOMPtr<EventTarget> target =
     do_QueryInterface(mDataTransfer->GetParentObject());
@@ -388,26 +381,29 @@ DataTransferItem::CreateFileFromInputStr
   }
 
   return File::CreateMemoryFile(mDataTransfer, data, available, fileName,
                                 mType, PR_Now());
 }
 
 void
 DataTransferItem::GetAsString(FunctionStringCallback* aCallback,
+                              const Maybe<nsIPrincipal*>& aSubjectPrincipal,
                               ErrorResult& aRv)
 {
+  MOZ_ASSERT(aSubjectPrincipal.isSome());
+
   if (!aCallback || mKind != KIND_STRING) {
     return;
   }
 
   // Theoretically this should be done inside of the runnable, as it might be an
   // expensive operation on some systems, however we wouldn't get access to the
   // NS_ERROR_DOM_SECURITY_ERROR messages which may be raised by this method.
-  nsCOMPtr<nsIVariant> data = Data(nsContentUtils::SubjectPrincipal(), aRv);
+  nsCOMPtr<nsIVariant> data = Data(aSubjectPrincipal.value(), aRv);
   if (NS_WARN_IF(!data || aRv.Failed())) {
     return;
   }
 
   nsAutoString stringData;
   nsresult rv = data->GetAsAString(stringData);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
--- a/dom/events/DataTransferItem.h
+++ b/dom/events/DataTransferItem.h
@@ -45,17 +45,19 @@ public:
     MOZ_ASSERT(mDataTransfer, "Must be associated with a DataTransfer");
   }
 
   already_AddRefed<DataTransferItem> Clone(DataTransfer* aDataTransfer) const;
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   // NOTE: This accesses the subject principal, and should not be called from C++
-  void GetAsString(FunctionStringCallback* aCallback, ErrorResult& aRv);
+  void GetAsString(FunctionStringCallback* aCallback,
+                   const Maybe<nsIPrincipal*>& aPrincipal,
+                   ErrorResult& aRv);
 
   void GetKind(nsAString& aKind) const
   {
     switch (mKind) {
     case KIND_FILE:
       aKind = NS_LITERAL_STRING("file");
       return;
     case KIND_STRING:
@@ -77,25 +79,21 @@ public:
   {
     return mKind;
   }
   void SetKind(eKind aKind)
   {
     mKind = aKind;
   }
 
-  // NOTE: This accesses the subject principal, and should not be called from C++
-  already_AddRefed<File> GetAsFile(ErrorResult& aRv);
-  already_AddRefed<File> GetAsFileWithPrincipal(nsIPrincipal* aPrincipal,
-                                                ErrorResult& aRv);
+  already_AddRefed<File>
+  GetAsFile(const Maybe<nsIPrincipal*>& aSubjectPrincipal, ErrorResult& aRv);
 
-  // NOTE: This accesses the subject principal, and should not be called from C++
-  already_AddRefed<FileSystemEntry> GetAsEntry(ErrorResult& aRv);
-  already_AddRefed<FileSystemEntry> GetAsEntryWithPrincipal(nsIPrincipal* aPrincipal,
-                                                            ErrorResult& aRv);
+  already_AddRefed<FileSystemEntry>
+  GetAsEntry(const Maybe<nsIPrincipal*>& aSubjectPrincipal, ErrorResult& aRv);
 
   DataTransfer* GetParentObject() const
   {
     return mDataTransfer;
   }
 
   nsIPrincipal* Principal() const
   {
--- a/dom/events/DataTransferItemList.cpp
+++ b/dom/events/DataTransferItemList.cpp
@@ -76,29 +76,33 @@ DataTransferItemList::Clone(DataTransfer
     // Copy over the reference
     list->mItems[i] = list->mIndexedItems[index][subIndex];
   }
 
   return list.forget();
 }
 
 void
-DataTransferItemList::Remove(uint32_t aIndex, ErrorResult& aRv)
+DataTransferItemList::Remove(uint32_t aIndex,
+                             const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                             ErrorResult& aRv)
 {
+  MOZ_ASSERT(aSubjectPrincipal.isSome());
+
   if (mDataTransfer->IsReadOnly()) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   if (aIndex >= Length()) {
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return;
   }
 
-  ClearDataHelper(mItems[aIndex], aIndex, -1, aRv);
+  ClearDataHelper(mItems[aIndex], aIndex, -1, aSubjectPrincipal, aRv);
 }
 
 DataTransferItem*
 DataTransferItemList::IndexedGetter(uint32_t aIndex, bool& aFound, ErrorResult& aRv) const
 {
   if (aIndex >= mItems.Length()) {
     aFound = false;
     return nullptr;
@@ -117,96 +121,104 @@ DataTransferItemList::MozItemCount() con
   // if it is empty, scripts using the moz* APIs should see it as not existing.
   if (length == 1 && mIndexedItems[0].IsEmpty()) {
     return 0;
   }
   return length;
 }
 
 void
-DataTransferItemList::Clear(ErrorResult& aRv)
+DataTransferItemList::Clear(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                            ErrorResult& aRv)
 {
+  MOZ_ASSERT(aSubjectPrincipal.isSome());
+
   if (NS_WARN_IF(mDataTransfer->IsReadOnly())) {
     return;
   }
 
   uint32_t count = Length();
   for (uint32_t i = 0; i < count; i++) {
     // We always remove the last item first, to avoid moving items around in
     // memory as much
-    Remove(Length() - 1, aRv);
+    Remove(Length() - 1, aSubjectPrincipal, aRv);
     ENSURE_SUCCESS_VOID(aRv);
   }
 
   MOZ_ASSERT(Length() == 0);
 }
 
 DataTransferItem*
 DataTransferItemList::Add(const nsAString& aData,
                           const nsAString& aType,
+                          const Maybe<nsIPrincipal*>& aSubjectPrincipal,
                           ErrorResult& aRv)
 {
+  MOZ_ASSERT(aSubjectPrincipal.isSome());
+
   if (NS_WARN_IF(mDataTransfer->IsReadOnly())) {
     return nullptr;
   }
 
   nsCOMPtr<nsIVariant> data(new storage::TextVariant(aData));
 
   nsAutoString format;
   mDataTransfer->GetRealFormat(aType, format);
 
-  nsIPrincipal* subjectPrincipal = nsContentUtils::SubjectPrincipal();
-
-  if (!DataTransfer::PrincipalMaySetData(format, data, subjectPrincipal)) {
+  if (!DataTransfer::PrincipalMaySetData(format, data,
+                                         aSubjectPrincipal.value())) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return nullptr;
   }
 
   // We add the textual data to index 0. We set aInsertOnly to true, as we don't
   // want to update an existing entry if it is already present, as per the spec.
   RefPtr<DataTransferItem> item =
-    SetDataWithPrincipal(format, data, 0, subjectPrincipal,
+    SetDataWithPrincipal(format, data, 0, aSubjectPrincipal.value(),
                          /* aInsertOnly = */ true,
                          /* aHidden = */ false,
                          aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
   MOZ_ASSERT(item->Kind() != DataTransferItem::KIND_FILE);
 
   return item;
 }
 
 DataTransferItem*
-DataTransferItemList::Add(File& aData, ErrorResult& aRv)
+DataTransferItemList::Add(File& aData,
+                          const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                          ErrorResult& aRv)
 {
+  MOZ_ASSERT(aSubjectPrincipal.isSome());
+
   if (mDataTransfer->IsReadOnly()) {
     return nullptr;
   }
 
   nsCOMPtr<nsISupports> supports = do_QueryObject(&aData);
   nsCOMPtr<nsIWritableVariant> data = new nsVariant();
   data->SetAsISupports(supports);
 
   nsAutoString type;
   aData.GetType(type);
 
-  nsIPrincipal* subjectPrincipal = nsContentUtils::SubjectPrincipal();
-
-  if (!DataTransfer::PrincipalMaySetData(type, data, subjectPrincipal)) {
+  if (!DataTransfer::PrincipalMaySetData(type, data,
+                                         aSubjectPrincipal.value())) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return nullptr;
   }
 
   // We need to add this as a new item, as multiple files can't exist in the
   // same item in the Moz DataTransfer layout. It will be appended at the end of
   // the internal specced layout.
   uint32_t index = mIndexedItems.Length();
   RefPtr<DataTransferItem> item =
-    SetDataWithPrincipal(type, data, index, subjectPrincipal,
+    SetDataWithPrincipal(type, data, index, aSubjectPrincipal.value(),
                          /* aInsertOnly = */ true,
                          /* aHidden = */ false,
                          aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
   MOZ_ASSERT(item->Kind() == DataTransferItem::KIND_FILE);
 
@@ -255,51 +267,54 @@ DataTransferItemList::Files(nsIPrincipal
 
   files = mFiles;
   return files.forget();
 }
 
 void
 DataTransferItemList::MozRemoveByTypeAt(const nsAString& aType,
                                         uint32_t aIndex,
+                                        const Maybe<nsIPrincipal*>& aSubjectPrincipal,
                                         ErrorResult& aRv)
 {
+  MOZ_ASSERT(aSubjectPrincipal.isSome());
+
   if (NS_WARN_IF(mDataTransfer->IsReadOnly() ||
                  aIndex >= mIndexedItems.Length())) {
     return;
   }
 
   bool removeAll = aType.IsEmpty();
 
   nsTArray<RefPtr<DataTransferItem>>& items = mIndexedItems[aIndex];
   uint32_t count = items.Length();
   // We remove the last item of the list repeatedly - that way we don't
   // have to worry about modifying the loop iterator
   if (removeAll) {
     for (uint32_t i = 0; i < count; ++i) {
       uint32_t index = items.Length() - 1;
       MOZ_ASSERT(index == count - i - 1);
 
-      ClearDataHelper(items[index], -1, index, aRv);
+      ClearDataHelper(items[index], -1, index, aSubjectPrincipal, aRv);
       if (NS_WARN_IF(aRv.Failed())) {
         return;
       }
     }
 
     // items is no longer a valid reference, as removing the last element from
     // it via ClearDataHelper invalidated it. so we can't MOZ_ASSERT that the
     // length is now 0.
     return;
   }
 
   for (uint32_t i = 0; i < count; ++i) {
     nsAutoString type;
     items[i]->GetType(type);
     if (type == aType) {
-      ClearDataHelper(items[i], -1, i, aRv);
+      ClearDataHelper(items[i], -1, i, aSubjectPrincipal, aRv);
       return;
     }
   }
 }
 
 DataTransferItem*
 DataTransferItemList::MozItemByTypeAt(const nsAString& aType, uint32_t aIndex)
 {
@@ -464,26 +479,26 @@ DataTransferItemList::ClearAllItems()
   // Re-generate files (into an empty list)
   RegenerateFiles();
 }
 
 void
 DataTransferItemList::ClearDataHelper(DataTransferItem* aItem,
                                       uint32_t aIndexHint,
                                       uint32_t aMozOffsetHint,
+                                      const Maybe<nsIPrincipal*>& aSubjectPrincipal,
                                       ErrorResult& aRv)
 {
   MOZ_ASSERT(aItem);
   if (NS_WARN_IF(mDataTransfer->IsReadOnly())) {
     return;
   }
 
-  nsIPrincipal* principal = nsContentUtils::SubjectPrincipal();
-  if (aItem->Principal() && principal &&
-      !principal->Subsumes(aItem->Principal())) {
+  if (aItem->Principal() && aSubjectPrincipal.isSome() &&
+      !aSubjectPrincipal.value()->Subsumes(aItem->Principal())) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return;
   }
 
   // Check if the aIndexHint is actually the index, and then remove the item
   // from aItems
   ErrorResult rv;
   bool found;
@@ -552,17 +567,17 @@ DataTransferItemList::GenerateFiles(File
     ErrorResult rv;
     bool found;
     RefPtr<DataTransferItem> item = IndexedGetter(i, found, rv);
     if (NS_WARN_IF(!found || rv.Failed())) {
       continue;
     }
 
     if (item->Kind() == DataTransferItem::KIND_FILE) {
-      RefPtr<File> file = item->GetAsFileWithPrincipal(aFilesPrincipal, rv);
+      RefPtr<File> file = item->GetAsFile(Some(aFilesPrincipal), rv);
       if (NS_WARN_IF(rv.Failed() || !file)) {
         continue;
       }
       aFiles->Append(file);
     }
   }
 }
 
--- a/dom/events/DataTransferItemList.h
+++ b/dom/events/DataTransferItemList.h
@@ -46,55 +46,63 @@ public:
                                JS::Handle<JSObject*> aGivenProto) override;
 
   uint32_t Length() const
   {
     return mItems.Length();
   };
 
   DataTransferItem* Add(const nsAString& aData, const nsAString& aType,
+                        const Maybe<nsIPrincipal*>& aSubjectPrincipal,
                         ErrorResult& rv);
-  DataTransferItem* Add(File& aData, ErrorResult& aRv);
+  DataTransferItem* Add(File& aData,
+                        const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                        ErrorResult& aRv);
 
-  void Remove(uint32_t aIndex, ErrorResult& aRv);
+  void Remove(uint32_t aIndex,
+              const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+              ErrorResult& aRv);
 
   DataTransferItem* IndexedGetter(uint32_t aIndex, bool& aFound,
                                   ErrorResult& aRv) const;
 
   DataTransfer* GetParentObject() const
   {
     return mDataTransfer;
   }
 
-  void Clear(ErrorResult& aRv);
+  void Clear(const Maybe<nsIPrincipal*>& aSubjectPrincipal, ErrorResult& aRv);
 
   already_AddRefed<DataTransferItem>
   SetDataWithPrincipal(const nsAString& aType, nsIVariant* aData,
                        uint32_t aIndex, nsIPrincipal* aPrincipal,
                        bool aInsertOnly, bool aHidden, ErrorResult& aRv);
 
   already_AddRefed<FileList> Files(nsIPrincipal* aPrincipal);
 
   // Moz-style helper methods for interacting with the stored data
   void MozRemoveByTypeAt(const nsAString& aType, uint32_t aIndex,
+                         const Maybe<nsIPrincipal*>& aSubjectPrincipal,
                          ErrorResult& aRv);
   DataTransferItem* MozItemByTypeAt(const nsAString& aType, uint32_t aIndex);
   const nsTArray<RefPtr<DataTransferItem>>* MozItemsAt(uint32_t aIndex);
   uint32_t MozItemCount() const;
 
   // Causes everything in indexes above 0 to shift down one index.
   void PopIndexZero();
 
   // Delete every item in the DataTransferItemList, without checking for
   // permissions or read-only status (for internal use only).
   void ClearAllItems();
 
 private:
   void ClearDataHelper(DataTransferItem* aItem, uint32_t aIndexHint,
-                       uint32_t aMozOffsetHint, ErrorResult& aRv);
+                       uint32_t aMozOffsetHint,
+                       const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                       ErrorResult& aRv);
   DataTransferItem* AppendNewItem(uint32_t aIndex, const nsAString& aType,
                                   nsIVariant* aData, nsIPrincipal* aPrincipal,
                                   bool aHidden);
   void RegenerateFiles();
   void GenerateFiles(FileList* aFiles, nsIPrincipal* aFilesPrincipal);
 
   ~DataTransferItemList() {}
 
--- a/dom/geolocation/nsGeolocation.cpp
+++ b/dom/geolocation/nsGeolocation.cpp
@@ -5,17 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsXULAppAPI.h"
 
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/Telemetry.h"
 
 #include "nsAutoPtr.h"
-#include "nsISettingsService.h"
 
 #include "nsGeolocation.h"
 #include "nsDOMClassInfoID.h"
 #include "nsComponentManagerUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsContentUtils.h"
 #include "nsContentPermissionHelper.h"
 #include "nsIDocument.h"
@@ -27,17 +26,16 @@
 #include "mozilla/Hal.h"
 #include "mozilla/Services.h"
 #include "mozilla/Unused.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/WeakPtr.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
-#include "mozilla/dom/SettingChangeNotificationBinding.h"
 #include "mozilla/dom/WakeLock.h"
 
 class nsIPrincipal;
 
 #ifdef MOZ_WIDGET_ANDROID
 #include "AndroidLocationProvider.h"
 #endif
 
@@ -57,19 +55,16 @@ class nsIPrincipal;
 #include "WindowsLocationProvider.h"
 #include "mozilla/WindowsVersion.h"
 #endif
 
 // Some limit to the number of get or watch geolocation requests
 // that a window can make.
 #define MAX_GEO_REQUESTS_PER_WINDOW  1500
 
-// The settings key.
-#define GEO_SETTINGS_ENABLED          "geolocation.enabled"
-
 using mozilla::Unused;          // <snicker>
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::hal;
 
 class nsGeolocationRequest final
  : public nsIContentPermissionRequest
  , public nsIGeolocationUpdate
@@ -145,63 +140,16 @@ CreatePositionOptionsCopy(const Position
 
   geoOptions->mEnableHighAccuracy = aOptions.mEnableHighAccuracy;
   geoOptions->mMaximumAge = aOptions.mMaximumAge;
   geoOptions->mTimeout = aOptions.mTimeout;
 
   return geoOptions;
 }
 
-class GeolocationSettingsCallback : public nsISettingsServiceCallback
-{
-  virtual ~GeolocationSettingsCallback() {
-    MOZ_COUNT_DTOR(GeolocationSettingsCallback);
-  }
-
-public:
-  NS_DECL_ISUPPORTS
-
-  GeolocationSettingsCallback() {
-    MOZ_COUNT_CTOR(GeolocationSettingsCallback);
-  }
-
-  NS_IMETHOD Handle(const nsAString& aName, JS::Handle<JS::Value> aResult) override
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    // The geolocation is enabled by default:
-    bool value = true;
-    if (aResult.isBoolean()) {
-        value = aResult.toBoolean();
-    }
-
-    MozSettingValue(value);
-    return NS_OK;
-  }
-
-  NS_IMETHOD HandleError(const nsAString& aName) override
-  {
-    NS_WARNING("Unable to get value for '" GEO_SETTINGS_ENABLED "'");
-
-    // Default it's enabled:
-    MozSettingValue(true);
-    return NS_OK;
-  }
-
-  void MozSettingValue(const bool aValue)
-  {
-    RefPtr<nsGeolocationService> gs = nsGeolocationService::GetGeolocationService();
-    if (gs) {
-      gs->HandleMozsettingValue(aValue);
-    }
-  }
-};
-
-NS_IMPL_ISUPPORTS(GeolocationSettingsCallback, nsISettingsServiceCallback)
-
 class RequestPromptEvent : public Runnable
 {
 public:
   RequestPromptEvent(nsGeolocationRequest* aRequest, nsWeakPtr aWindow)
     : mRequest(aRequest)
     , mWindow(aWindow)
   {
   }
@@ -377,22 +325,24 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
   NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
   NS_INTERFACE_MAP_ENTRY(nsIGeolocationUpdate)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGeolocationRequest)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGeolocationRequest)
 NS_IMPL_CYCLE_COLLECTION(nsGeolocationRequest, mCallback, mErrorCallback, mLocator)
+
 void
 nsGeolocationRequest::Notify()
 {
   SetTimeoutTimer();
   NotifyErrorAndShutdown(nsIDOMGeoPositionError::TIMEOUT);
 }
+
 void
 nsGeolocationRequest::NotifyErrorAndShutdown(uint16_t aErrorCode)
 {
   MOZ_ASSERT(!mShutdown, "timeout after shutdown");
   if (!mIsWatchPositionRequest) {
     Shutdown();
     mLocator->RemoveRequest(this);
   }
@@ -554,16 +504,17 @@ nsGeolocationRequest::Allow(JS::HandleVa
 
 NS_IMETHODIMP
 nsGeolocationRequest::GetRequester(nsIContentPermissionRequester** aRequester)
 {
   NS_ENSURE_ARG_POINTER(aRequester);
 
   nsCOMPtr<nsIContentPermissionRequester> requester = mRequester;
   requester.forget(aRequester);
+
   return NS_OK;
 }
 
 void
 nsGeolocationRequest::SetTimeoutTimer()
 {
   StopTimeoutTimer();
 
@@ -633,32 +584,34 @@ nsGeolocationRequest::SendLocation(nsIDO
     nsIDOMGeoPositionCallback* callback = mCallback.GetXPCOMCallback();
     MOZ_ASSERT(callback);
     callback->HandleEvent(aPosition);
   }
   SetTimeoutTimer();
   MOZ_ASSERT(mShutdown || mIsWatchPositionRequest,
              "non-shutdown getCurrentPosition request after callback!");
 }
+
 nsIPrincipal*
 nsGeolocationRequest::GetPrincipal()
 {
   if (!mLocator) {
     return nullptr;
   }
   return mLocator->GetPrincipal();
 }
 
 NS_IMETHODIMP
 nsGeolocationRequest::Update(nsIDOMGeoPosition* aPosition)
 {
   nsCOMPtr<nsIRunnable> ev = new RequestSendLocationEvent(aPosition, this);
   NS_DispatchToMainThread(ev);
   return NS_OK;
 }
+
 NS_IMETHODIMP
 nsGeolocationRequest::NotifyError(uint16_t aErrorCode)
 {
   MOZ_ASSERT(NS_IsMainThread());
   RefPtr<PositionError> positionError = new PositionError(mLocator, aErrorCode);
   positionError->NotifyCallback(mErrorCallback);
   return NS_OK;
 }
@@ -690,16 +643,17 @@ NS_IMPL_ISUPPORTS(nsGeolocationRequest::
 
 NS_IMETHODIMP
 nsGeolocationRequest::TimerCallbackHolder::Notify(nsITimer*)
 {
   if (mRequest && mRequest->mLocator) {
     RefPtr<nsGeolocationRequest> request(mRequest);
     request->Notify();
   }
+
   return NS_OK;
 }
 
 
 ////////////////////////////////////////////////////
 // nsGeolocationService
 ////////////////////////////////////////////////////
 NS_INTERFACE_MAP_BEGIN(nsGeolocationService)
@@ -708,59 +662,38 @@ NS_INTERFACE_MAP_BEGIN(nsGeolocationServ
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(nsGeolocationService)
 NS_IMPL_RELEASE(nsGeolocationService)
 
 
 static bool sGeoEnabled = true;
-static bool sGeoInitPending = true;
 static int32_t sProviderTimeout = 6000; // Time, in milliseconds, to wait for the location provider to spin up.
 
 nsresult nsGeolocationService::Init()
 {
   Preferences::AddIntVarCache(&sProviderTimeout, "geo.timeout", sProviderTimeout);
   Preferences::AddBoolVarCache(&sGeoEnabled, "geo.enabled", sGeoEnabled);
 
   if (!sGeoEnabled) {
     return NS_ERROR_FAILURE;
   }
 
   if (XRE_IsContentProcess()) {
-    sGeoInitPending = false;
     return NS_OK;
   }
 
-  // check if the geolocation service is enable from settings
-  nsCOMPtr<nsISettingsService> settings =
-    do_GetService("@mozilla.org/settingsService;1");
-
-  if (settings) {
-    nsCOMPtr<nsISettingsServiceLock> settingsLock;
-    nsresult rv = settings->CreateLock(nullptr, getter_AddRefs(settingsLock));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    RefPtr<GeolocationSettingsCallback> callback = new GeolocationSettingsCallback();
-    rv = settingsLock->Get(GEO_SETTINGS_ENABLED, callback);
-    NS_ENSURE_SUCCESS(rv, rv);
-  } else {
-    // If we cannot obtain the settings service, we continue
-    // assuming that the geolocation is enabled:
-    sGeoInitPending = false;
-  }
-
   // geolocation service can be enabled -> now register observer
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   if (!obs) {
     return NS_ERROR_FAILURE;
   }
 
   obs->AddObserver(this, "xpcom-shutdown", false);
-  obs->AddObserver(this, "mozsettings-changed", false);
 
 #ifdef MOZ_WIDGET_ANDROID
   mProvider = new AndroidLocationProvider();
 #endif
 
 #ifdef MOZ_WIDGET_GONK
   // GonkGPSGeolocationProvider can be started at boot up time for initialization reasons.
   // do_getService gets hold of the already initialized component and starts
@@ -811,82 +744,35 @@ nsresult nsGeolocationService::Init()
 
   return NS_OK;
 }
 
 nsGeolocationService::~nsGeolocationService()
 {
 }
 
-void
-nsGeolocationService::HandleMozsettingChanged(nsISupports* aSubject)
-{
-    // The string that we're interested in will be a JSON string that looks like:
-    //  {"key":"gelocation.enabled","value":true}
-
-    RootedDictionary<SettingChangeNotification> setting(RootingCx());
-    if (!WrappedJSToDictionary(aSubject, setting)) {
-      return;
-    }
-    if (!setting.mKey.EqualsASCII(GEO_SETTINGS_ENABLED)) {
-      return;
-    }
-    if (!setting.mValue.isBoolean()) {
-      return;
-    }
-
-    HandleMozsettingValue(setting.mValue.toBoolean());
-}
-
-void
-nsGeolocationService::HandleMozsettingValue(const bool aValue)
-{
-    if (!aValue) {
-      // turn things off
-      StopDevice();
-      Update(nullptr);
-      mLastPosition.position = nullptr;
-      sGeoEnabled = false;
-    } else {
-      sGeoEnabled = true;
-    }
-
-    if (sGeoInitPending) {
-      sGeoInitPending = false;
-      for (uint32_t i = 0, length = mGeolocators.Length(); i < length; ++i) {
-        mGeolocators[i]->ServiceReady();
-      }
-    }
-}
-
 NS_IMETHODIMP
 nsGeolocationService::Observe(nsISupports* aSubject,
                               const char* aTopic,
                               const char16_t* aData)
 {
   if (!strcmp("xpcom-shutdown", aTopic)) {
     nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
     if (obs) {
       obs->RemoveObserver(this, "xpcom-shutdown");
-      obs->RemoveObserver(this, "mozsettings-changed");
     }
 
     for (uint32_t i = 0; i< mGeolocators.Length(); i++) {
       mGeolocators[i]->Shutdown();
     }
     StopDevice();
 
     return NS_OK;
   }
 
-  if (!strcmp("mozsettings-changed", aTopic)) {
-    HandleMozsettingChanged(aSubject);
-    return NS_OK;
-  }
-
   if (!strcmp("timer-callback", aTopic)) {
     // decide if we can close down the service.
     for (uint32_t i = 0; i< mGeolocators.Length(); i++)
       if (mGeolocators[i]->HasActiveCallbacks()) {
         SetDisconnectTimer();
         return NS_OK;
       }
 
@@ -900,21 +786,24 @@ nsGeolocationService::Observe(nsISupport
 }
 
 NS_IMETHODIMP
 nsGeolocationService::Update(nsIDOMGeoPosition *aSomewhere)
 {
   if (aSomewhere) {
     SetCachedPosition(aSomewhere);
   }
+
   for (uint32_t i = 0; i< mGeolocators.Length(); i++) {
     mGeolocators[i]->Update(aSomewhere);
   }
+
   return NS_OK;
 }
+
 NS_IMETHODIMP
 nsGeolocationService::NotifyError(uint16_t aErrorCode)
 {
   for (uint32_t i = 0; i < mGeolocators.Length(); i++) {
     mGeolocators[i]->NotifyError(aErrorCode);
   }
   return NS_OK;
 }
@@ -930,17 +819,17 @@ CachedPositionAndAccuracy
 nsGeolocationService::GetCachedPosition()
 {
   return mLastPosition;
 }
 
 nsresult
 nsGeolocationService::StartDevice(nsIPrincipal *aPrincipal)
 {
-  if (!sGeoEnabled || sGeoInitPending) {
+  if (!sGeoEnabled) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   // we do not want to keep the geolocation devices online
   // indefinitely.  Close them down after a reasonable period of
   // inactivivity
   SetDisconnectTimer();
 
@@ -981,17 +870,16 @@ void
 nsGeolocationService::StopDisconnectTimer()
 {
   if (mDisconnectTimer) {
     mDisconnectTimer->Cancel();
     mDisconnectTimer = nullptr;
   }
 }
 
-
 void
 nsGeolocationService::SetDisconnectTimer()
 {
   if (!mDisconnectTimer) {
     mDisconnectTimer = do_CreateInstance("@mozilla.org/timer;1");
   } else {
     mDisconnectTimer->Cancel();
   }
@@ -1004,51 +892,47 @@ nsGeolocationService::SetDisconnectTimer
 bool
 nsGeolocationService::HighAccuracyRequested()
 {
   for (uint32_t i = 0; i < mGeolocators.Length(); i++) {
     if (mGeolocators[i]->HighAccuracyRequested()) {
       return true;
     }
   }
+
   return false;
 }
 
 void
 nsGeolocationService::UpdateAccuracy(bool aForceHigh)
 {
   bool highRequired = aForceHigh || HighAccuracyRequested();
 
   if (XRE_IsContentProcess()) {
     ContentChild* cpc = ContentChild::GetSingleton();
     if (cpc->IsAlive()) {
       cpc->SendSetGeolocationHigherAccuracy(highRequired);
     }
+
     return;
   }
 
-  if (!mHigherAccuracy && highRequired) {
-      mProvider->SetHighAccuracy(true);
-  }
-
-  if (mHigherAccuracy && !highRequired) {
-      mProvider->SetHighAccuracy(false);
-  }
-
+  mProvider->SetHighAccuracy(!mHigherAccuracy && highRequired);
   mHigherAccuracy = highRequired;
 }
 
 void
 nsGeolocationService::StopDevice()
 {
   StopDisconnectTimer();
 
   if (XRE_IsContentProcess()) {
     ContentChild* cpc = ContentChild::GetSingleton();
     cpc->SendRemoveGeolocationListener();
+
     return; // bail early
   }
 
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   if (!obs) {
     return;
   }
 
@@ -1067,23 +951,25 @@ nsGeolocationService::StopDevice()
 StaticRefPtr<nsGeolocationService> nsGeolocationService::sService;
 
 already_AddRefed<nsGeolocationService>
 nsGeolocationService::GetGeolocationService()
 {
   RefPtr<nsGeolocationService> result;
   if (nsGeolocationService::sService) {
     result = nsGeolocationService::sService;
+
     return result.forget();
   }
 
   result = new nsGeolocationService();
   if (NS_FAILED(result->Init())) {
     return nullptr;
   }
+
   ClearOnShutdown(&nsGeolocationService::sService);
   nsGeolocationService::sService = result;
   return result.forget();
 }
 
 void
 nsGeolocationService::AddLocator(Geolocation* aLocator)
 {
@@ -1178,31 +1064,31 @@ Geolocation::Init(nsPIDOMWindowInner* aC
 
   // If no aContentDom was passed into us, we are being used
   // by chrome/c++ and have no mOwner, no mPrincipal, and no need
   // to prompt.
   mService = nsGeolocationService::GetGeolocationService();
   if (mService) {
     mService->AddLocator(this);
   }
+
   return NS_OK;
 }
 
 bool
 Geolocation::ContainsRequest(nsGeolocationRequest* aRequest)
 {
-  if (aRequest->IsWatch()) {
-    if (mWatchingCallbacks.Contains(aRequest)) {
+  if (aRequest->IsWatch() && mWatchingCallbacks.Contains(aRequest)) {
 	return true;
-    }
-  } else {
-    if (mPendingCallbacks.Contains(aRequest)) {
-        return true;
-    }
   }
+
+  if (mPendingCallbacks.Contains(aRequest)) {
+      return true;
+  }
+
   return false;
 }
 
 
 NS_IMETHODIMP
 Geolocation::HandleEvent(nsIDOMEvent* aEvent)
 {
 
@@ -1216,33 +1102,37 @@ Geolocation::HandleEvent(nsIDOMEvent* aE
   MOZ_ASSERT(doc);
 
   if (doc->Hidden()) {
     WakeLockInformation info;
     GetWakeLockInfo(NS_LITERAL_STRING("gps"), &info);
 
     MOZ_ASSERT(XRE_IsContentProcess());
     ContentChild* cpc = ContentChild::GetSingleton();
+
     if (!info.lockingProcesses().Contains(cpc->GetID())) {
       cpc->SendRemoveGeolocationListener();
       mService->StopDisconnectTimer();
     }
-  } else {
-    mService->SetDisconnectTimer();
+
+    return NS_OK;
+  }
+
+  mService->SetDisconnectTimer();
 
-    // We will unconditionally allow all the requests in the callbacks
-    // because if a request is put into either of these two callbacks,
-    // it means that it has been allowed before.
-    // That's why when we resume them, we unconditionally allow them again.
-    for (uint32_t i = 0, length = mWatchingCallbacks.Length(); i < length; ++i) {
-      mWatchingCallbacks[i]->Allow(JS::UndefinedHandleValue);
-    }
-    for (uint32_t i = 0, length = mPendingCallbacks.Length(); i < length; ++i) {
-      mPendingCallbacks[i]->Allow(JS::UndefinedHandleValue);
-    }
+  // We will unconditionally allow all the requests in the callbacks
+  // because if a request is put into either of these two callbacks,
+  // it means that it has been allowed before.
+  // That's why when we resume them, we unconditionally allow them again.
+  for (uint32_t i = 0, length = mWatchingCallbacks.Length(); i < length; ++i) {
+    mWatchingCallbacks[i]->Allow(JS::UndefinedHandleValue);
+  }
+
+  for (uint32_t i = 0, length = mPendingCallbacks.Length(); i < length; ++i) {
+    mPendingCallbacks[i]->Allow(JS::UndefinedHandleValue);
   }
 
   return NS_OK;
 }
 
 void
 Geolocation::Shutdown()
 {
@@ -1333,25 +1223,28 @@ Geolocation::Update(nsIDOMGeoPosition *a
     mPendingCallbacks[i-1]->Update(aSomewhere);
     RemoveRequest(mPendingCallbacks[i-1]);
   }
 
   // notify everyone that is watching
   for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) {
     mWatchingCallbacks[i]->Update(aSomewhere);
   }
+
   return NS_OK;
 }
+
 NS_IMETHODIMP
 Geolocation::NotifyError(uint16_t aErrorCode)
 {
   if (!WindowOwnerStillExists()) {
     Shutdown();
     return NS_OK;
   }
+
   mozilla::Telemetry::Accumulate(mozilla::Telemetry::GEOLOCATION_ERROR, true);
 
   for (uint32_t i = mPendingCallbacks.Length(); i > 0; i--) {
     mPendingCallbacks[i-1]->NotifyErrorAndShutdown(aErrorCode);
     //NotifyErrorAndShutdown() removes the request from the array
   }
 
   // notify everyone that is watching
@@ -1365,27 +1258,29 @@ Geolocation::NotifyError(uint16_t aError
 bool
 Geolocation::IsAlreadyCleared(nsGeolocationRequest* aRequest)
 {
   for (uint32_t i = 0, length = mClearedWatchIDs.Length(); i < length; ++i) {
     if (mClearedWatchIDs[i] == aRequest->WatchId()) {
       return true;
     }
   }
+
   return false;
 }
 
 bool
 Geolocation::ClearPendingRequest(nsGeolocationRequest* aRequest)
 {
   if (aRequest->IsWatch() && this->IsAlreadyCleared(aRequest)) {
     this->NotifyAllowedRequest(aRequest);
     this->ClearWatch(aRequest->WatchId());
     return true;
   }
+
   return false;
 }
 
 void
 Geolocation::GetCurrentPosition(PositionCallback& aCallback,
                                 PositionErrorCallback* aErrorCallback,
                                 const PositionOptions& aOptions,
                                 ErrorResult& aRv)
@@ -1438,21 +1333,16 @@ Geolocation::GetCurrentPosition(GeoPosit
     NS_DispatchToMainThread(ev);
     return NS_OK;
   }
 
   if (!mOwner && !nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
     return NS_ERROR_FAILURE;
   }
 
-  if (sGeoInitPending) {
-    mPendingRequests.AppendElement(request);
-    return NS_OK;
-  }
-
   return GetCurrentPositionReady(request);
 }
 
 nsresult
 Geolocation::GetCurrentPositionReady(nsGeolocationRequest* aRequest)
 {
   if (mOwner) {
     if (!RegisterRequestWithPrompt(aRequest)) {
@@ -1530,21 +1420,16 @@ Geolocation::WatchPosition(GeoPositionCa
     NS_DispatchToMainThread(ev);
     return NS_OK;
   }
 
   if (!mOwner && !nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
     return NS_ERROR_FAILURE;
   }
 
-  if (sGeoInitPending) {
-    mPendingRequests.AppendElement(request);
-    return NS_OK;
-  }
-
   return WatchPositionReady(request);
 }
 
 nsresult
 Geolocation::WatchPositionReady(nsGeolocationRequest* aRequest)
 {
   if (mOwner) {
     if (!RegisterRequestWithPrompt(aRequest))
--- a/dom/geolocation/nsGeolocation.h
+++ b/dom/geolocation/nsGeolocation.h
@@ -67,19 +67,16 @@ public:
   NS_DECL_NSIOBSERVER
 
   nsGeolocationService() {
       mHigherAccuracy = false;
   }
 
   nsresult Init();
 
-  void HandleMozsettingChanged(nsISupports* aSubject);
-  void HandleMozsettingValue(const bool aValue);
-
   // Management of the Geolocation objects
   void AddLocator(mozilla::dom::Geolocation* locator);
   void RemoveLocator(mozilla::dom::Geolocation* locator);
 
   void SetCachedPosition(nsIDOMGeoPosition* aPosition);
   CachedPositionAndAccuracy GetCachedPosition();
 
   // Find and startup a geolocation device (gps, nmea, etc.)
--- a/dom/html/HTMLImageElement.cpp
+++ b/dom/html/HTMLImageElement.cpp
@@ -112,16 +112,17 @@ private:
   nsCOMPtr<nsIDocument> mDocument;
   bool mAlwaysLoad;
 };
 
 HTMLImageElement::HTMLImageElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
   : nsGenericHTMLElement(aNodeInfo)
   , mForm(nullptr)
   , mInDocResponsiveContent(false)
+  , mCurrentDensity(1.0)
 {
   // We start out broken
   AddStatesSilently(NS_EVENT_STATE_BROKEN);
 }
 
 HTMLImageElement::~HTMLImageElement()
 {
   DestroyImageLoadingContent();
@@ -946,49 +947,79 @@ HTMLImageElement::InResponsiveMode()
   // When we lose srcset or leave a <picture> element, the fallback to img.src
   // will happen from the microtask, and we should behave responsively in the
   // interim
   return mResponsiveSelector ||
          mPendingImageLoadTask ||
          HaveSrcsetOrInPicture();
 }
 
+bool
+HTMLImageElement::SelectedSourceMatchesLast(nsIURI* aSelectedSource, double aSelectedDensity)
+{
+  // If there was no selected source previously, we don't want to short-circuit the load.
+  // Similarly for if there is no newly selected source.
+  if (!mLastSelectedSource || !aSelectedSource) {
+    return false;
+  }
+  bool equal = false;
+  return NS_SUCCEEDED(mLastSelectedSource->Equals(aSelectedSource, &equal)) && equal &&
+      aSelectedDensity == mCurrentDensity;
+}
+
 nsresult
 HTMLImageElement::LoadSelectedImage(bool aForce, bool aNotify, bool aAlwaysLoad)
 {
   nsresult rv = NS_ERROR_FAILURE;
 
   if (aForce) {
     // In responsive mode we generally want to re-run the full
     // selection algorithm whenever starting a new load, per
     // spec. This also causes us to re-resolve the URI as appropriate.
     if (!UpdateResponsiveSource() && !aAlwaysLoad) {
       return NS_OK;
     }
   }
 
+  nsCOMPtr<nsIURI> selectedSource;
+  double currentDensity = 1.0; // default to 1.0 for the src attribute case
   if (mResponsiveSelector) {
     nsCOMPtr<nsIURI> url = mResponsiveSelector->GetSelectedImageURL();
+    selectedSource = url;
+    currentDensity = mResponsiveSelector->GetSelectedImageDensity();
+    if (!aAlwaysLoad && SelectedSourceMatchesLast(selectedSource, currentDensity)) {
+      return NS_OK;
+    }
     if (url) {
       rv = LoadImage(url, aForce, aNotify, eImageLoadType_Imageset);
     }
   } else {
     nsAutoString src;
     if (!GetAttr(kNameSpaceID_None, nsGkAtoms::src, src)) {
       CancelImageRequests(aNotify);
       rv = NS_OK;
     } else {
+      nsIDocument* doc = GetOurOwnerDoc();
+      if (doc) {
+        StringToURI(src, doc, getter_AddRefs(selectedSource));
+        if (!aAlwaysLoad && SelectedSourceMatchesLast(selectedSource, currentDensity)) {
+          return NS_OK;
+        }
+      }
+
       // If we have a srcset attribute or are in a <picture> element,
       // we always use the Imageset load type, even if we parsed no
       // valid responsive sources from either, per spec.
       rv = LoadImage(src, aForce, aNotify,
                      HaveSrcsetOrInPicture() ? eImageLoadType_Imageset
                                              : eImageLoadType_Normal);
     }
   }
+  mLastSelectedSource = selectedSource;
+  mCurrentDensity = currentDensity;
 
   if (NS_FAILED(rv)) {
     CancelImageRequests(aNotify);
   }
   return rv;
 }
 
 void
--- a/dom/html/HTMLImageElement.h
+++ b/dom/html/HTMLImageElement.h
@@ -287,16 +287,19 @@ protected:
   // True if we have a srcset attribute or a <picture> parent, regardless of if
   // any valid responsive sources were parsed from either.
   bool HaveSrcsetOrInPicture();
 
   // True if we are using the newer image loading algorithm. This will be the
   // only mode after Bug 1076583
   bool InResponsiveMode();
 
+  // True if the given URL and density equal the last URL and density that was loaded by this element.
+  bool SelectedSourceMatchesLast(nsIURI* aSelectedSource, double aSelectedDensity);
+
   // Resolve and load the current mResponsiveSelector (responsive mode) or src
   // attr image.
   nsresult LoadSelectedImage(bool aForce, bool aNotify, bool aAlwaysLoad);
 
   // True if this string represents a type we would support on <source type>
   static bool SupportedPictureSourceType(const nsAString& aType);
 
   // Update/create/destroy mResponsiveSelector
@@ -358,14 +361,19 @@ protected:
 private:
   bool SourceElementMatches(nsIContent* aSourceNode);
 
   static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
                                     nsRuleData* aData);
 
   bool mInDocResponsiveContent;
   RefPtr<ImageLoadTask> mPendingImageLoadTask;
+
+  // Last URL that was attempted to load by this element.
+  nsCOMPtr<nsIURI> mLastSelectedSource;
+  // Last pixel density that was selected.
+  double mCurrentDensity;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_HTMLImageElement_h */
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -537,17 +537,17 @@ nsHTMLDocument::StartDocumentLoad(const 
   bool asData = !strcmp(aCommand, kLoadAsData);
   bool import = !strcmp(aCommand, "import");
   if (!(view || viewSource || asData || import)) {
     MOZ_ASSERT(false, "Bad parser command");
     return NS_ERROR_INVALID_ARG;
   }
 
   bool html = contentType.EqualsLiteral(TEXT_HTML);
-  bool xhtml = !html && contentType.EqualsLiteral(APPLICATION_XHTML_XML);
+  bool xhtml = !html && (contentType.EqualsLiteral(APPLICATION_XHTML_XML) || contentType.EqualsLiteral(APPLICATION_WAPXHTML_XML));
   bool plainText = !html && !xhtml && nsContentUtils::IsPlainTextType(contentType);
   if (!(html || xhtml || plainText || viewSource)) {
     MOZ_ASSERT(false, "Channel with bad content type.");
     return NS_ERROR_INVALID_ARG;
   }
 
   bool loadAsHtml5 = true;
 
--- a/dom/html/test/forms/test_input_typing_sanitization.html
+++ b/dom/html/test/forms/test_input_typing_sanitization.html
@@ -113,17 +113,17 @@ function testSubmissions() {
 
 var valueIndex = 0;
 var submitMethod = submitForm;
 
 SimpleTest.waitForExplicitFinish();
 
 function runTest()
 {
-  SimpleTest.requestLongerTimeout(2);
+  SimpleTest.requestLongerTimeout(4);
 
   var data = [
     {
       type: 'number',
       canHaveBadInputValidityState: true,
       validData: [
         "42",
         "-42", // should work for negative values
--- a/dom/icc/IccInfo.h
+++ b/dom/icc/IccInfo.h
@@ -74,18 +74,19 @@ protected:
   // we have all data fields expended here instead of having a data member of
   // |IccInfoData| defined in PIccTypes.h which indirectly includes "windows.h"
   // See 925382 for the restriction of including "windows.h" in UnifiedBindings.cpp.
   nsString mIccType;
   nsString mIccid;
   nsString mMcc;
   nsString mMnc;
   nsString mSpn;
-  bool mIsDisplayNetworkNameRequired;
-  bool mIsDisplaySpnRequired;
+  // The following booleans shall be initialized either in the constructor or in Update
+  MOZ_INIT_OUTSIDE_CTOR bool mIsDisplayNetworkNameRequired;
+  MOZ_INIT_OUTSIDE_CTOR bool mIsDisplaySpnRequired;
 };
 
 class GsmIccInfo final : public IccInfo
                        , public nsIGsmIccInfo
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_FORWARD_NSIICCINFO(IccInfo::)
@@ -135,16 +136,17 @@ public:
 
   int32_t
   PrlVersion() const;
 
 private:
   ~CdmaIccInfo() {}
 
   nsString mPhoneNumber;
-  int32_t mPrlVersion;
+  // The following integer shall be initialized either in the constructor or in Update
+  MOZ_INIT_OUTSIDE_CTOR int32_t mPrlVersion;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_IccInfo_h
 
--- a/dom/interfaces/core/nsIDOMXMLDocument.idl
+++ b/dom/interfaces/core/nsIDOMXMLDocument.idl
@@ -1,26 +1,15 @@
 /* -*- 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/. */
 
-#include "nsIDOMDocument.idl"
+#include "nsISupports.idl"
 
+// The only reason this interface still exists is that we have addon and Firefox
+// code QIing to it and using it for instanceof via Components.interfaces, and
+// the interface needs to exist in order for the shim stuff in
+// xpcom/reflect/xptinfo/ShimInterfaceInfo.cpp to work.
 [uuid(89ab39cb-c568-4d85-bd34-306d5cd5164d)]
-interface nsIDOMXMLDocument : nsIDOMDocument
+interface nsIDOMXMLDocument : nsISupports
 {
-  // DOM Level 3 Load & Save, DocumentLS
-  // http://www.w3.org/TR/DOM-Level-3-LS/load-save.html#LS-DocumentLS
-  /**
-   * Whether to load synchronously or asynchronously.
-   * The default is async==true.
-   */
-  attribute boolean            async;
- 
-  /**
-   * Load an XML document.
-   *
-   * @param  url URL to an XML document.
-   * @return     True if load successfull.
-   */
-  boolean                      load(in DOMString url);
 };
--- a/dom/media/platforms/android/RemoteDataDecoder.cpp
+++ b/dom/media/platforms/android/RemoteDataDecoder.cpp
@@ -194,16 +194,21 @@ public:
   RefPtr<InitPromise> Init() override
   {
     mSurfaceTexture = AndroidSurfaceTexture::Create();
     if (!mSurfaceTexture) {
       NS_WARNING("Failed to create SurfaceTexture for video decode\n");
       return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
     }
 
+    if (!jni::IsFennec()) {
+      NS_WARNING("Remote decoding not supported in non-Fennec environment\n");
+      return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
+    }
+
     // Register native methods.
     JavaCallbacksSupport::Init();
 
     mJavaCallbacks = CodecProxy::NativeCallbacks::New();
     JavaCallbacksSupport::AttachNative(mJavaCallbacks,
                                        mozilla::MakeUnique<CallbacksSupport>(this, mCallback));
 
     mJavaDecoder = CodecProxy::Create(mFormat, mSurfaceTexture->JavaSurface(), mJavaCallbacks);
--- a/dom/media/systemservices/CamerasChild.cpp
+++ b/dom/media/systemservices/CamerasChild.cpp
@@ -588,32 +588,31 @@ CamerasChild::ShutdownChild()
     LOG(("Shutdown called without PBackground thread"));
   }
   LOG(("Erasing sCameras & thread refs (original thread)"));
   CamerasSingleton::Child() = nullptr;
   CamerasSingleton::Thread() = nullptr;
 }
 
 bool
-CamerasChild::RecvDeliverFrame(const int& capEngine,
+CamerasChild::RecvDeliverFrame(const CaptureEngine& capEngine,
                                const int& capId,
                                mozilla::ipc::Shmem&& shmem,
                                const size_t& size,
                                const uint32_t& time_stamp,
                                const int64_t& ntp_time,
                                const int64_t& render_time)
 {
   MutexAutoLock lock(mCallbackMutex);
-  CaptureEngine capEng = static_cast<CaptureEngine>(capEngine);
-  if (Callback(capEng, capId)) {
+  if (Callback(capEngine, capId)) {
     unsigned char* image = shmem.get<unsigned char>();
-    Callback(capEng, capId)->DeliverFrame(image, size,
-                                          time_stamp,
-                                          ntp_time, render_time,
-                                          nullptr);
+    Callback(capEngine, capId)->DeliverFrame(image, size,
+                                             time_stamp,
+                                             ntp_time, render_time,
+                                             nullptr);
   } else {
     LOG(("DeliverFrame called with dead callback"));
   }
   SendReleaseFrame(shmem);
   return true;
 }
 
 bool
@@ -632,25 +631,24 @@ CamerasChild::SetFakeDeviceChangeEvents(
   // we fire a fake devicechange event in Camera IPC thread periodically
   RefPtr<FakeOnDeviceChangeEventRunnable> evt = new FakeOnDeviceChangeEventRunnable(0);
   CamerasSingleton::Thread()->Dispatch(evt.forget(), NS_DISPATCH_NORMAL);
 
   return 0;
 }
 
 bool
-CamerasChild::RecvFrameSizeChange(const int& capEngine,
+CamerasChild::RecvFrameSizeChange(const CaptureEngine& capEngine,
                                   const int& capId,
                                   const int& w, const int& h)
 {
   LOG((__PRETTY_FUNCTION__));
   MutexAutoLock lock(mCallbackMutex);
-  CaptureEngine capEng = static_cast<CaptureEngine>(capEngine);
-  if (Callback(capEng, capId)) {
-    Callback(capEng, capId)->FrameSizeChange(w, h, 0);
+  if (Callback(capEngine, capId)) {
+    Callback(capEngine, capId)->FrameSizeChange(w, h, 0);
   } else {
     LOG(("Frame size change with dead callback"));
   }
   return true;
 }
 
 void
 CamerasChild::ActorDestroy(ActorDestroyReason aWhy)
--- a/dom/media/systemservices/CamerasChild.h
+++ b/dom/media/systemservices/CamerasChild.h
@@ -28,26 +28,16 @@
 namespace mozilla {
 
 namespace ipc {
 class BackgroundChildImpl;
 }
 
 namespace camera {
 
-enum CaptureEngine : int {
-  InvalidEngine = 0,
-  ScreenEngine,
-  BrowserEngine,
-  WinEngine,
-  AppEngine,
-  CameraEngine,
-  MaxEngine
-};
-
 struct CapturerElement {
   CaptureEngine engine;
   int id;
   webrtc::ExternalRenderer* callback;
 };
 
 // Forward declaration so we can work with pointers to it.
 class CamerasChild;
@@ -148,20 +138,20 @@ class CamerasChild final : public PCamer
 
 public:
   // We are owned by the PBackground thread only. CamerasSingleton
   // takes a non-owning reference.
   NS_INLINE_DECL_REFCOUNTING(CamerasChild)
 
   // IPC messages recevied, received on the PBackground thread
   // these are the actual callbacks with data
-  virtual bool RecvDeliverFrame(const int&, const int&, mozilla::ipc::Shmem&&,
+  virtual bool RecvDeliverFrame(const CaptureEngine&, const int&, mozilla::ipc::Shmem&&,
                                 const size_t&, const uint32_t&, const int64_t&,
                                 const int64_t&) override;
-  virtual bool RecvFrameSizeChange(const int&, const int&,
+  virtual bool RecvFrameSizeChange(const CaptureEngine&, const int&,
                                    const int& w, const int& h) override;
 
   virtual bool RecvDeviceChange() override;
   int SetFakeDeviceChangeEvents();
 
   // these are response messages to our outgoing requests
   virtual bool RecvReplyNumberOfCaptureDevices(const int&) override;
   virtual bool RecvReplyNumberOfCapabilities(const int&) override;
--- a/dom/media/systemservices/CamerasParent.cpp
+++ b/dom/media/systemservices/CamerasParent.cpp
@@ -508,17 +508,17 @@ CamerasParent::EnsureInitialized(int aEn
 }
 
 // Dispatch the runnable to do the camera operation on the
 // specific Cameras thread, preventing us from blocking, and
 // chain a runnable to send back the result on the IPC thread.
 // It would be nice to get rid of the code duplication here,
 // perhaps via Promises.
 bool
-CamerasParent::RecvNumberOfCaptureDevices(const int& aCapEngine)
+CamerasParent::RecvNumberOfCaptureDevices(const CaptureEngine& aCapEngine)
 {
   LOG((__PRETTY_FUNCTION__));
 
   RefPtr<CamerasParent> self(this);
   RefPtr<Runnable> webrtc_runnable =
     media::NewRunnableFrom([self, aCapEngine]() -> nsresult {
       int num = -1;
       if (self->EnsureInitialized(aCapEngine)) {
@@ -542,17 +542,17 @@ CamerasParent::RecvNumberOfCaptureDevice
         self->mPBackgroundThread->Dispatch(ipc_runnable, NS_DISPATCH_NORMAL);
       return NS_OK;
     });
   DispatchToVideoCaptureThread(webrtc_runnable);
   return true;
 }
 
 bool
-CamerasParent::RecvNumberOfCapabilities(const int& aCapEngine,
+CamerasParent::RecvNumberOfCapabilities(const CaptureEngine& aCapEngine,
                                         const nsCString& unique_id)
 {
   LOG((__PRETTY_FUNCTION__));
   LOG(("Getting caps for %s", unique_id.get()));
 
   RefPtr<CamerasParent> self(this);
   RefPtr<Runnable> webrtc_runnable =
     media::NewRunnableFrom([self, unique_id, aCapEngine]() -> nsresult {
@@ -581,17 +581,17 @@ CamerasParent::RecvNumberOfCapabilities(
       self->mPBackgroundThread->Dispatch(ipc_runnable, NS_DISPATCH_NORMAL);
       return NS_OK;
     });
   DispatchToVideoCaptureThread(webrtc_runnable);
   return true;
 }
 
 bool
-CamerasParent::RecvGetCaptureCapability(const int &aCapEngine,
+CamerasParent::RecvGetCaptureCapability(const CaptureEngine& aCapEngine,
                                         const nsCString& unique_id,
                                         const int& num)
 {
   LOG((__PRETTY_FUNCTION__));
   LOG(("RecvGetCaptureCapability: %s %d", unique_id.get(), num));
 
   RefPtr<CamerasParent> self(this);
   RefPtr<Runnable> webrtc_runnable =
@@ -631,17 +631,17 @@ CamerasParent::RecvGetCaptureCapability(
       self->mPBackgroundThread->Dispatch(ipc_runnable, NS_DISPATCH_NORMAL);
       return NS_OK;
     });
   DispatchToVideoCaptureThread(webrtc_runnable);
   return true;
 }
 
 bool
-CamerasParent::RecvGetCaptureDevice(const int& aCapEngine,
+CamerasParent::RecvGetCaptureDevice(const CaptureEngine& aCapEngine,
                                     const int& aListNumber)
 {
   LOG((__PRETTY_FUNCTION__));
 
   RefPtr<CamerasParent> self(this);
   RefPtr<Runnable> webrtc_runnable =
     media::NewRunnableFrom([self, aCapEngine, aListNumber]() -> nsresult {
       char deviceName[MediaEngineSource::kMaxDeviceNameLength];
@@ -734,17 +734,17 @@ HasCameraPermission(const nsCString& aOr
         }
       }
     }
   }
   return allowed;
 }
 
 bool
-CamerasParent::RecvAllocateCaptureDevice(const int& aCapEngine,
+CamerasParent::RecvAllocateCaptureDevice(const CaptureEngine& aCapEngine,
                                          const nsCString& unique_id,
                                          const nsCString& aOrigin)
 {
   LOG(("%s: Verifying permissions for %s", __PRETTY_FUNCTION__, aOrigin.get()));
   RefPtr<CamerasParent> self(this);
   RefPtr<Runnable> mainthread_runnable =
     media::NewRunnableFrom([self, aCapEngine, unique_id, aOrigin]() -> nsresult {
       // Verify whether the claimed origin has received permission
@@ -791,28 +791,28 @@ CamerasParent::RecvAllocateCaptureDevice
       self->DispatchToVideoCaptureThread(webrtc_runnable);
       return NS_OK;
     });
   NS_DispatchToMainThread(mainthread_runnable);
   return true;
 }
 
 int
-CamerasParent::ReleaseCaptureDevice(const int& aCapEngine,
+CamerasParent::ReleaseCaptureDevice(const CaptureEngine& aCapEngine,
                                     const int& capnum)
 {
   int error = -1;
   if (EnsureInitialized(aCapEngine)) {
     error = mEngines[aCapEngine].mPtrViECapture->ReleaseCaptureDevice(capnum);
   }
   return error;
 }
 
 bool
-CamerasParent::RecvReleaseCaptureDevice(const int& aCapEngine,
+CamerasParent::RecvReleaseCaptureDevice(const CaptureEngine& aCapEngine,
                                         const int& numdev)
 {
   LOG((__PRETTY_FUNCTION__));
   LOG(("RecvReleaseCamera device nr %d", numdev));
 
   RefPtr<CamerasParent> self(this);
   RefPtr<Runnable> webrtc_runnable =
     media::NewRunnableFrom([self, aCapEngine, numdev]() -> nsresult {
@@ -836,17 +836,17 @@ CamerasParent::RecvReleaseCaptureDevice(
       self->mPBackgroundThread->Dispatch(ipc_runnable, NS_DISPATCH_NORMAL);
       return NS_OK;
     });
   DispatchToVideoCaptureThread(webrtc_runnable);
   return true;
 }
 
 bool
-CamerasParent::RecvStartCapture(const int& aCapEngine,
+CamerasParent::RecvStartCapture(const CaptureEngine& aCapEngine,
                                 const int& capnum,
                                 const CaptureCapability& ipcCaps)
 {
   LOG((__PRETTY_FUNCTION__));
 
   RefPtr<CamerasParent> self(this);
   RefPtr<Runnable> webrtc_runnable =
     media::NewRunnableFrom([self, aCapEngine, capnum, ipcCaps]() -> nsresult {
@@ -898,17 +898,17 @@ CamerasParent::RecvStartCapture(const in
       self->mPBackgroundThread->Dispatch(ipc_runnable, NS_DISPATCH_NORMAL);
       return NS_OK;
     });
   DispatchToVideoCaptureThread(webrtc_runnable);
   return true;
 }
 
 void
-CamerasParent::StopCapture(const int& aCapEngine,
+CamerasParent::StopCapture(const CaptureEngine& aCapEngine,
                            const int& capnum)
 {
   if (EnsureInitialized(aCapEngine)) {
     mEngines[aCapEngine].mPtrViECapture->StopCapture(capnum);
     mEngines[aCapEngine].mPtrViERender->StopRender(capnum);
     mEngines[aCapEngine].mPtrViERender->RemoveRenderer(capnum);
     mEngines[aCapEngine].mEngineIsRunning = false;
 
@@ -919,17 +919,17 @@ CamerasParent::StopCapture(const int& aC
         mCallbacks.RemoveElementAt(i);
         break;
       }
     }
   }
 }
 
 bool
-CamerasParent::RecvStopCapture(const int& aCapEngine,
+CamerasParent::RecvStopCapture(const CaptureEngine& aCapEngine,
                                const int& capnum)
 {
   LOG((__PRETTY_FUNCTION__));
 
   RefPtr<CamerasParent> self(this);
   RefPtr<Runnable> webrtc_runnable =
     media::NewRunnableFrom([self, aCapEngine, capnum]() -> nsresult {
       self->StopCapture(aCapEngine, capnum);
--- a/dom/media/systemservices/CamerasParent.h
+++ b/dom/media/systemservices/CamerasParent.h
@@ -94,24 +94,29 @@ class CamerasParent :  public PCamerasPa
 {
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIOBSERVER
 
 public:
   static already_AddRefed<CamerasParent> Create();
 
   // Messages received form the child. These run on the IPC/PBackground thread.
-  virtual bool RecvAllocateCaptureDevice(const int&, const nsCString&, const nsCString&) override;
-  virtual bool RecvReleaseCaptureDevice(const int&, const int &) override;
-  virtual bool RecvNumberOfCaptureDevices(const int&) override;
-  virtual bool RecvNumberOfCapabilities(const int&, const nsCString&) override;
-  virtual bool RecvGetCaptureCapability(const int&, const nsCString&, const int&) override;
-  virtual bool RecvGetCaptureDevice(const int&, const int&) override;
-  virtual bool RecvStartCapture(const int&, const int&, const CaptureCapability&) override;
-  virtual bool RecvStopCapture(const int&, const int&) override;
+  virtual bool RecvAllocateCaptureDevice(const CaptureEngine&, const nsCString&,
+                                         const nsCString&) override;
+  virtual bool RecvReleaseCaptureDevice(const CaptureEngine&,
+                                        const int&) override;
+  virtual bool RecvNumberOfCaptureDevices(const CaptureEngine&) override;
+  virtual bool RecvNumberOfCapabilities(const CaptureEngine&,
+                                        const nsCString&) override;
+  virtual bool RecvGetCaptureCapability(const CaptureEngine&, const nsCString&,
+                                        const int&) override;
+  virtual bool RecvGetCaptureDevice(const CaptureEngine&, const int&) override;
+  virtual bool RecvStartCapture(const CaptureEngine&, const int&,
+                                const CaptureCapability&) override;
+  virtual bool RecvStopCapture(const CaptureEngine&, const int&) override;
   virtual bool RecvReleaseFrame(mozilla::ipc::Shmem&&) override;
   virtual bool RecvAllDone() override;
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
   nsIThread* GetBackgroundThread() { return mPBackgroundThread; };
   bool IsShuttingDown() { return !mChildIsAlive
                               ||  mDestroyed
                               || !mWebRTCAlive; };
@@ -129,18 +134,18 @@ public:
 
 
   CamerasParent();
 
 protected:
   virtual ~CamerasParent();
 
   // We use these helpers for shutdown and for the respective IPC commands.
-  void StopCapture(const int& aCapEngine, const int& capnum);
-  int ReleaseCaptureDevice(const int& aCapEngine, const int& capnum);
+  void StopCapture(const CaptureEngine& aCapEngine, const int& capnum);
+  int ReleaseCaptureDevice(const CaptureEngine& aCapEngine, const int& capnum);
 
   bool SetupEngine(CaptureEngine aCapEngine);
   bool EnsureInitialized(int aEngine);
   void CloseEngines();
   void StopIPC();
   void StopVideoCapture();
   // Can't take already_AddRefed because it can fail in stupid ways.
   nsresult DispatchToVideoCaptureThread(Runnable* event);
new file mode 100644
--- /dev/null
+++ b/dom/media/systemservices/CamerasTypes.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et ft=cpp : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_CamerasTypes_h
+#define mozilla_CamerasTypes_h
+
+#include "ipc/IPCMessageUtils.h"
+
+namespace mozilla {
+
+namespace camera {
+
+enum CaptureEngine : int {
+  InvalidEngine = 0,
+  ScreenEngine,
+  BrowserEngine,
+  WinEngine,
+  AppEngine,
+  CameraEngine,
+  MaxEngine
+};
+
+} // namespace camera
+} // namespace mozilla
+
+namespace IPC {
+template<>
+struct ParamTraits<mozilla::camera::CaptureEngine> :
+    public ContiguousEnumSerializer<mozilla::camera::CaptureEngine,
+                                    mozilla::camera::CaptureEngine::InvalidEngine,
+                                    mozilla::camera::CaptureEngine::MaxEngine>
+{ };
+}
+
+#endif  // mozilla_CamerasTypes_h
--- a/dom/media/systemservices/PCameras.ipdl
+++ b/dom/media/systemservices/PCameras.ipdl
@@ -1,15 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PContent;
 include protocol PBackground;
 
+using mozilla::camera::CaptureEngine from "CamerasTypes.h";
+
 namespace mozilla {
 namespace camera {
 
 struct CaptureCapability
 {
   int width;
   int height;
   int maxFPS;
@@ -19,42 +21,42 @@ struct CaptureCapability
   bool interlaced;
 };
 
 async protocol PCameras
 {
   manager PBackground;
 
 child:
-  async FrameSizeChange(int capEngine, int cap_id, int w, int h);
+  async FrameSizeChange(CaptureEngine capEngine, int cap_id, int w, int h);
   // transfers ownership of |buffer| from parent to child
-  async DeliverFrame(int capEngine, int cap_id,
+  async DeliverFrame(CaptureEngine capEngine, int cap_id,
                      Shmem buffer, size_t size, uint32_t time_stamp,
                      int64_t ntp_time, int64_t render_time);
   async DeviceChange();
   async ReplyNumberOfCaptureDevices(int numdev);
   async ReplyNumberOfCapabilities(int numdev);
   async ReplyAllocateCaptureDevice(int numdev);
   async ReplyGetCaptureCapability(CaptureCapability cap);
   async ReplyGetCaptureDevice(nsCString device_name, nsCString device_id);
   async ReplyFailure();
   async ReplySuccess();
   async __delete__();
 
 parent:
-  async NumberOfCaptureDevices(int engine);
-  async NumberOfCapabilities(int engine, nsCString deviceUniqueIdUTF8);
+  async NumberOfCaptureDevices(CaptureEngine engine);
+  async NumberOfCapabilities(CaptureEngine engine, nsCString deviceUniqueIdUTF8);
 
-  async GetCaptureCapability(int engine, nsCString unique_idUTF8, int capability_number);
-  async GetCaptureDevice(int engine, int num);
+  async GetCaptureCapability(CaptureEngine engine, nsCString unique_idUTF8, int capability_number);
+  async GetCaptureDevice(CaptureEngine engine, int num);
 
-  async AllocateCaptureDevice(int engine, nsCString unique_idUTF8, nsCString origin);
-  async ReleaseCaptureDevice(int engine, int numdev);
-  async StartCapture(int engine, int numdev, CaptureCapability capability);
-  async StopCapture(int engine, int numdev);
+  async AllocateCaptureDevice(CaptureEngine engine, nsCString unique_idUTF8, nsCString origin);
+  async ReleaseCaptureDevice(CaptureEngine engine, int numdev);
+  async StartCapture(CaptureEngine engine, int numdev, CaptureCapability capability);
+  async StopCapture(CaptureEngine engine, int numdev);
   // transfers frame back
   async ReleaseFrame(Shmem s);
 
   // Ask parent to delete us
   async AllDone();
 };
 
 } // namespace camera
--- a/dom/media/systemservices/moz.build
+++ b/dom/media/systemservices/moz.build
@@ -3,16 +3,17 @@
 # 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/.
 
 if CONFIG['MOZ_WEBRTC']:
     EXPORTS += [
         'CamerasChild.h',
         'CamerasParent.h',
+        'CamerasTypes.h',
         'LoadManager.h',
         'LoadManagerFactory.h',
         'LoadMonitor.h',
     ]
     UNIFIED_SOURCES += [
         'CamerasChild.cpp',
         'CamerasParent.cpp',
         'LoadManager.cpp',
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -605,17 +605,17 @@ JSValToNPVariant(NPP npp, JSContext *cx,
 
   // Pass over ownership of npobj to *variant
   OBJECT_TO_NPVARIANT(npobj, *variant);
 
   return true;
 }
 
 static void
-ThrowJSException(JSContext *cx, const char *message)
+ThrowJSExceptionASCII(JSContext *cx, const char *message)
 {
   const char *ex = PeekException();
 
   if (ex) {
     nsAutoString ucex;
 
     if (message) {
       AppendASCIItoUTF16(message, ucex);
@@ -633,30 +633,30 @@ ThrowJSException(JSContext *cx, const ch
 
     if (str) {
       JS::Rooted<JS::Value> exn(cx, JS::StringValue(str));
       ::JS_SetPendingException(cx, exn);
     }
 
     PopException();
   } else {
-    ::JS_ReportError(cx, message);
+    ::JS_ReportErrorASCII(cx, message);
   }
 }
 
 static bool
 ReportExceptionIfPending(JSContext *cx)
 {
   const char *ex = PeekException();
 
   if (!ex) {
     return true;
   }
 
-  ThrowJSException(cx, nullptr);
+  ThrowJSExceptionASCII(cx, nullptr);
 
   return false;
 }
 
 nsJSObjWrapper::nsJSObjWrapper(NPP npp)
   : mJSObj(nullptr), mNpp(npp), mDestroyPending(false)
 {
   MOZ_COUNT_CTOR(nsJSObjWrapper);
@@ -731,18 +731,18 @@ nsJSObjWrapper::NP_HasMethod(NPObject *n
   if (NS_WARN_IF(!globalObject)) {
     return false;
   }
 
   dom::AutoEntryScript aes(globalObject, "NPAPI HasMethod");
   JSContext *cx = aes.cx();
 
   if (!npobj) {
-    ThrowJSException(cx,
-                     "Null npobj in nsJSObjWrapper::NP_HasMethod!");
+    ThrowJSExceptionASCII(cx,
+                          "Null npobj in nsJSObjWrapper::NP_HasMethod!");
 
     return false;
   }
 
   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
 
   JSAutoCompartment ac(cx, npjsobj->mJSObj);
 
@@ -767,17 +767,17 @@ doInvoke(NPObject *npobj, NPIdentifier m
   }
 
   // We're about to run script via JS_CallFunctionValue, so we need an
   // AutoEntryScript. NPAPI plugins are Gecko-specific and not in any spec.
   dom::AutoEntryScript aes(globalObject, "NPAPI doInvoke");
   JSContext *cx = aes.cx();
 
   if (!npobj || !result) {
-    ThrowJSException(cx, "Null npobj, or result in doInvoke!");
+    ThrowJSExceptionASCII(cx, "Null npobj, or result in doInvoke!");
 
     return false;
   }
 
   // Initialize *result
   VOID_TO_NPVARIANT(*result);
 
   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
@@ -859,18 +859,18 @@ nsJSObjWrapper::NP_HasProperty(NPObject 
   if (NS_WARN_IF(!globalObject)) {
     return false;
   }
 
   dom::AutoEntryScript aes(globalObject, "NPAPI HasProperty");
   JSContext *cx = aes.cx();
 
   if (!npobj) {
-    ThrowJSException(cx,
-                     "Null npobj in nsJSObjWrapper::NP_HasProperty!");
+    ThrowJSExceptionASCII(cx,
+                          "Null npobj in nsJSObjWrapper::NP_HasProperty!");
 
     return false;
   }
 
   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
   bool found, ok = false;
 
   AutoJSExceptionSuppressor suppressor(aes, npjsobj);
@@ -897,18 +897,18 @@ nsJSObjWrapper::NP_GetProperty(NPObject 
   }
 
   // We're about to run script via JS_CallFunctionValue, so we need an
   // AutoEntryScript. NPAPI plugins are Gecko-specific and not in any spec.
   dom::AutoEntryScript aes(globalObject, "NPAPI get");
   JSContext *cx = aes.cx();
 
   if (!npobj) {
-    ThrowJSException(cx,
-                     "Null npobj in nsJSObjWrapper::NP_GetProperty!");
+    ThrowJSExceptionASCII(cx,
+                          "Null npobj in nsJSObjWrapper::NP_GetProperty!");
 
     return false;
   }
 
   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
 
   AutoJSExceptionSuppressor suppressor(aes, npjsobj);
   JSAutoCompartment ac(cx, npjsobj->mJSObj);
@@ -931,18 +931,18 @@ nsJSObjWrapper::NP_SetProperty(NPObject 
   }
 
   // We're about to run script via JS_CallFunctionValue, so we need an
   // AutoEntryScript. NPAPI plugins are Gecko-specific and not in any spec.
   dom::AutoEntryScript aes(globalObject, "NPAPI set");
   JSContext *cx = aes.cx();
 
   if (!npobj) {
-    ThrowJSException(cx,
-                     "Null npobj in nsJSObjWrapper::NP_SetProperty!");
+    ThrowJSExceptionASCII(cx,
+                          "Null npobj in nsJSObjWrapper::NP_SetProperty!");
 
     return false;
   }
 
   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
   bool ok = false;
 
   AutoJSExceptionSuppressor suppressor(aes, npjsobj);
@@ -968,18 +968,18 @@ nsJSObjWrapper::NP_RemoveProperty(NPObje
   if (NS_WARN_IF(!globalObject)) {
     return false;
   }
 
   dom::AutoEntryScript aes(globalObject, "NPAPI RemoveProperty");
   JSContext *cx = aes.cx();
 
   if (!npobj) {
-    ThrowJSException(cx,
-                     "Null npobj in nsJSObjWrapper::NP_RemoveProperty!");
+    ThrowJSExceptionASCII(cx,
+                          "Null npobj in nsJSObjWrapper::NP_RemoveProperty!");
 
     return false;
   }
 
   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
 
   AutoJSExceptionSuppressor suppressor(aes, npjsobj);
   JS::ObjectOpResult result;
@@ -1022,18 +1022,18 @@ nsJSObjWrapper::NP_Enumerate(NPObject *n
 
   dom::AutoEntryScript aes(globalObject, "NPAPI Enumerate");
   JSContext *cx = aes.cx();
 
   *idarray = 0;
   *count = 0;
 
   if (!npobj) {
-    ThrowJSException(cx,
-                     "Null npobj in nsJSObjWrapper::NP_Enumerate!");
+    ThrowJSExceptionASCII(cx,
+                          "Null npobj in nsJSObjWrapper::NP_Enumerate!");
 
     return false;
   }
 
   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
 
   AutoJSExceptionSuppressor suppressor(aes, npjsobj);
   JS::Rooted<JSObject*> jsobj(cx, npjsobj->mJSObj);
@@ -1042,17 +1042,17 @@ nsJSObjWrapper::NP_Enumerate(NPObject *n
   JS::Rooted<JS::IdVector> ida(cx, JS::IdVector(cx));
   if (!JS_Enumerate(cx, jsobj, &ida)) {
     return false;
   }
 
   *count = ida.length();
   *idarray = (NPIdentifier *)PR_Malloc(*count * sizeof(NPIdentifier));
   if (!*idarray) {
-    ThrowJSException(cx, "Memory allocation failed for NPIdentifier!");
+    ThrowJSExceptionASCII(cx, "Memory allocation failed for NPIdentifier!");
     return false;
   }
 
   for (uint32_t i = 0; i < *count; i++) {
     JS::Rooted<JS::Value> v(cx);
     if (!JS_IdToValue(cx, ida[i], &v)) {
       PR_Free(*idarray);
       return false;
@@ -1211,17 +1211,17 @@ GetNPObject(JSContext *cx, JSObject *obj
 // SetProperty call.
 static bool
 NPObjWrapper_AddProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, JS::Handle<JS::Value> v)
 {
   NPObject *npobj = GetNPObject(cx, obj);
 
   if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
       !npobj->_class->hasMethod) {
-    ThrowJSException(cx, "Bad NPObject as private data!");
+    ThrowJSExceptionASCII(cx, "Bad NPObject as private data!");
 
     return false;
   }
 
   if (NPObjectIsOutOfProcessProxy(npobj)) {
     return true;
   }
 
@@ -1237,33 +1237,33 @@ NPObjWrapper_AddProperty(JSContext *cx, 
 
   // We must permit methods here since JS_DefineUCFunction() will add
   // the function as a property
   bool hasMethod = npobj->_class->hasMethod(npobj, identifier);
   if (!ReportExceptionIfPending(cx))
     return false;
 
   if (!hasMethod) {
-    ThrowJSException(cx, "Trying to add unsupported property on NPObject!");
+    ThrowJSExceptionASCII(cx, "Trying to add unsupported property on NPObject!");
 
     return false;
   }
 
   return true;
 }
 
 static bool
 NPObjWrapper_DelProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
                          JS::ObjectOpResult &result)
 {
   NPObject *npobj = GetNPObject(cx, obj);
 
   if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
       !npobj->_class->removeProperty) {
-    ThrowJSException(cx, "Bad NPObject as private data!");
+    ThrowJSExceptionASCII(cx, "Bad NPObject as private data!");
 
     return false;
   }
 
   PluginDestructionGuard pdg(LookupNPP(npobj));
 
   NPIdentifier identifier = JSIdToNPIdentifier(id);
 
@@ -1289,76 +1289,76 @@ NPObjWrapper_DelProperty(JSContext *cx, 
 static bool
 NPObjWrapper_SetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
                          JS::MutableHandle<JS::Value> vp, JS::ObjectOpResult &result)
 {
   NPObject *npobj = GetNPObject(cx, obj);
 
   if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
       !npobj->_class->setProperty) {
-    ThrowJSException(cx, "Bad NPObject as private data!");
+    ThrowJSExceptionASCII(cx, "Bad NPObject as private data!");
 
     return false;
   }
 
   // Find out what plugin (NPP) is the owner of the object we're
   // manipulating, and make it own any JSObject wrappers created here.
   NPP npp = LookupNPP(npobj);
 
   if (!npp) {
-    ThrowJSException(cx, "No NPP found for NPObject!");
+    ThrowJSExceptionASCII(cx, "No NPP found for NPObject!");
 
     return false;
   }
 
   PluginDestructionGuard pdg(npp);
 
   NPIdentifier identifier = JSIdToNPIdentifier(id);
 
   if (!NPObjectIsOutOfProcessProxy(npobj)) {
     bool hasProperty = npobj->_class->hasProperty(npobj, identifier);
     if (!ReportExceptionIfPending(cx))
       return false;
 
     if (!hasProperty) {
-      ThrowJSException(cx, "Trying to set unsupported property on NPObject!");
+      ThrowJSExceptionASCII(cx, "Trying to set unsupported property on NPObject!");
 
       return false;
     }
   }
 
   NPVariant npv;
   if (!JSValToNPVariant(npp, cx, vp, &npv)) {
-    ThrowJSException(cx, "Error converting jsval to NPVariant!");
+    ThrowJSExceptionASCII(cx, "Error converting jsval to NPVariant!");
 
     return false;
   }
 
   bool ok = npobj->_class->setProperty(npobj, identifier, &npv);
   _releasevariantvalue(&npv); // Release the variant
   if (!ReportExceptionIfPending(cx))
     return false;
 
   if (!ok) {
-    ThrowJSException(cx, "Error setting property on NPObject!");
+    ThrowJSExceptionASCII(cx, "Error setting property on NPObject!");
 
     return false;
   }
 
   return result.succeed();
 }
 
 static bool
 NPObjWrapper_GetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp)
 {
   NPObject *npobj = GetNPObject(cx, obj);
 
   if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
       !npobj->_class->hasMethod || !npobj->_class->getProperty) {
-    ThrowJSException(cx, "Bad NPObject as private data!");
+    ThrowJSExceptionASCII(cx, "Bad NPObject as private data!");
 
     return false;
   }
 
   if (JSID_IS_SYMBOL(id)) {
     JS::RootedSymbol sym(cx, JSID_TO_SYMBOL(id));
     if (JS::GetSymbolCode(sym) == JS::SymbolCode::toPrimitive) {
       JS::RootedObject obj(cx, JS_GetFunctionObject(
@@ -1384,17 +1384,17 @@ NPObjWrapper_GetProperty(JSContext *cx, 
     vp.setUndefined();
     return true;
   }
 
   // Find out what plugin (NPP) is the owner of the object we're
   // manipulating, and make it own any JSObject wrappers created here.
   NPP npp = LookupNPP(npobj);
   if (!npp) {
-    ThrowJSException(cx, "No NPP found for NPObject!");
+    ThrowJSExceptionASCII(cx, "No NPP found for NPObject!");
 
     return false;
   }
 
   PluginDestructionGuard pdg(npp);
 
   bool hasProperty, hasMethod;
 
@@ -1470,53 +1470,53 @@ NPObjWrapper_GetProperty(JSContext *cx, 
 
 static bool
 CallNPMethodInternal(JSContext *cx, JS::Handle<JSObject*> obj, unsigned argc,
                      JS::Value *argv, JS::Value *rval, bool ctorCall)
 {
   NPObject *npobj = GetNPObject(cx, obj);
 
   if (!npobj || !npobj->_class) {
-    ThrowJSException(cx, "Bad NPObject as private data!");
+    ThrowJSExceptionASCII(cx, "Bad NPObject as private data!");
 
     return false;
   }
 
   // Find out what plugin (NPP) is the owner of the object we're
   // manipulating, and make it own any JSObject wrappers created here.
   NPP npp = LookupNPP(npobj);
 
   if (!npp) {
-    ThrowJSException(cx, "Error finding NPP for NPObject!");
+    ThrowJSExceptionASCII(cx, "Error finding NPP for NPObject!");
 
     return false;
   }
 
   PluginDestructionGuard pdg(npp);
 
   NPVariant npargs_buf[8];
   NPVariant *npargs = npargs_buf;
 
   if (argc > (sizeof(npargs_buf) / sizeof(NPVariant))) {
     // Our stack buffer isn't large enough to hold all arguments,
     // malloc a buffer.
     npargs = (NPVariant *)PR_Malloc(argc * sizeof(NPVariant));
 
     if (!npargs) {
-      ThrowJSException(cx, "Out of memory!");
+      ThrowJSExceptionASCII(cx, "Out of memory!");
 
       return false;
     }
   }
 
   // Convert arguments
   uint32_t i;
   for (i = 0; i < argc; ++i) {
     if (!JSValToNPVariant(npp, cx, argv[i], npargs + i)) {
-      ThrowJSException(cx, "Error converting jsvals to NPVariants!");
+      ThrowJSExceptionASCII(cx, "Error converting jsvals to NPVariants!");
 
       if (npargs != npargs_buf) {
         PR_Free(npargs);
       }
 
       return false;
     }
   }
@@ -1578,17 +1578,17 @@ CallNPMethodInternal(JSContext *cx, JS::
   if (npargs != npargs_buf) {
     PR_Free(npargs);
   }
 
   if (!ok) {
     // ReportExceptionIfPending returns a return value, which is true
     // if no exception was thrown. In that case, throw our own.
     if (ReportExceptionIfPending(cx))
-      ThrowJSException(cx, msg);
+      ThrowJSExceptionASCII(cx, msg);
 
     return false;
   }
 
   *rval = NPVariantToJSVal(npp, cx, &v);
 
   // *rval now owns the value, release our reference.
   _releasevariantvalue(&v);
@@ -1608,35 +1608,35 @@ CallNPMethod(JSContext *cx, unsigned arg
 }
 
 static bool
 NPObjWrapper_Enumerate(JSContext *cx, JS::Handle<JSObject*> obj,
                        JS::AutoIdVector &properties, bool enumerableOnly)
 {
   NPObject *npobj = GetNPObject(cx, obj);
   if (!npobj || !npobj->_class) {
-    ThrowJSException(cx, "Bad NPObject as private data!");
+    ThrowJSExceptionASCII(cx, "Bad NPObject as private data!");
     return false;
   }
 
   PluginDestructionGuard pdg(LookupNPP(npobj));
 
   if (!NP_CLASS_STRUCT_VERSION_HAS_ENUM(npobj->_class) ||
       !npobj->_class->enumerate) {
     return true;
   }
 
   NPIdentifier *identifiers;
   uint32_t length;
   if (!npobj->_class->enumerate(npobj, &identifiers, &length)) {
     if (ReportExceptionIfPending(cx)) {
       // ReportExceptionIfPending returns a return value, which is true
       // if no exception was thrown. In that case, throw our own.
-      ThrowJSException(cx, "Error enumerating properties on scriptable "
-                           "plugin object");
+      ThrowJSExceptionASCII(cx, "Error enumerating properties on scriptable "
+                            "plugin object");
     }
     return false;
   }
 
   if (!properties.reserve(length))
     return false;
 
   JS::Rooted<jsid> id(cx);
@@ -1657,17 +1657,17 @@ NPObjWrapper_Resolve(JSContext *cx, JS::
     return true;
 
   PROFILER_LABEL_FUNC(js::ProfileEntry::Category::JS);
 
   NPObject *npobj = GetNPObject(cx, obj);
 
   if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
       !npobj->_class->hasMethod) {
-    ThrowJSException(cx, "Bad NPObject as private data!");
+    ThrowJSExceptionASCII(cx, "Bad NPObject as private data!");
 
     return false;
   }
 
   PluginDestructionGuard pdg(LookupNPP(npobj));
 
   NPIdentifier identifier = JSIdToNPIdentifier(id);
 
@@ -2055,17 +2055,17 @@ LookupNPP(NPObject *npobj)
 
 static bool
 CreateNPObjectMember(NPP npp, JSContext *cx, JSObject *obj, NPObject* npobj,
                      JS::Handle<jsid> id,  NPVariant* getPropertyResult,
                      JS::MutableHandle<JS::Value> vp)
 {
   if (!npobj || !npobj->_class || !npobj->_class->getProperty ||
       !npobj->_class->invoke) {
-    ThrowJSException(cx, "Bad NPObject");
+    ThrowJSExceptionASCII(cx, "Bad NPObject");
 
     return false;
   }
 
   NPObjectMemberPrivate *memberPrivate =
     (NPObjectMemberPrivate *)PR_Malloc(sizeof(NPObjectMemberPrivate));
   if (!memberPrivate)
     return false;
@@ -2165,40 +2165,40 @@ NPObjectMember_Call(JSContext *cx, unsig
     (NPObjectMemberPrivate *)::JS_GetInstancePrivate(cx, memobj,
                                                      &sNPObjectMemberClass,
                                                      &args);
   if (!memberPrivate || !memberPrivate->npobjWrapper)
     return false;
 
   NPObject *npobj = GetNPObject(cx, memberPrivate->npobjWrapper);
   if (!npobj) {
-    ThrowJSException(cx, "Call on invalid member object");
+    ThrowJSExceptionASCII(cx, "Call on invalid member object");
 
     return false;
   }
 
   NPVariant npargs_buf[8];
   NPVariant *npargs = npargs_buf;
 
   if (args.length() > (sizeof(npargs_buf) / sizeof(NPVariant))) {
     // Our stack buffer isn't large enough to hold all arguments,
     // malloc a buffer.
     npargs = (NPVariant *)PR_Malloc(args.length() * sizeof(NPVariant));
 
     if (!npargs) {
-      ThrowJSException(cx, "Out of memory!");
+      ThrowJSExceptionASCII(cx, "Out of memory!");
 
       return false;
     }
   }
 
   // Convert arguments
   for (uint32_t i = 0; i < args.length(); ++i) {
     if (!JSValToNPVariant(memberPrivate->npp, cx, args[i], npargs + i)) {
-      ThrowJSException(cx, "Error converting jsvals to NPVariants!");
+      ThrowJSExceptionASCII(cx, "Error converting jsvals to NPVariants!");
 
       if (npargs != npargs_buf) {
         PR_Free(npargs);
       }
 
       return false;
     }
   }
@@ -2217,17 +2217,17 @@ NPObjectMember_Call(JSContext *cx, unsig
   if (npargs != npargs_buf) {
     PR_Free(npargs);
   }
 
   if (!ok) {
     // ReportExceptionIfPending returns a return value, which is true
     // if no exception was thrown. In that case, throw our own.
     if (ReportExceptionIfPending(cx))
-      ThrowJSException(cx, "Error calling method on NPObject!");
+      ThrowJSExceptionASCII(cx, "Error calling method on NPObject!");
 
     return false;
   }
 
   args.rval().set(NPVariantToJSVal(memberPrivate->npp, cx, &npv));
 
   // *vp now owns the value, release our reference.
   _releasevariantvalue(&npv);
@@ -2294,18 +2294,18 @@ nsJSObjWrapper::HasOwnProperty(NPObject 
   if (NS_WARN_IF(!globalObject)) {
     return false;
   }
 
   dom::AutoEntryScript aes(globalObject, "NPAPI HasOwnProperty");
   JSContext *cx = aes.cx();
 
   if (!npobj) {
-    ThrowJSException(cx,
-                     "Null npobj in nsJSObjWrapper::NP_HasOwnProperty!");
+    ThrowJSExceptionASCII(cx,
+                          "Null npobj in nsJSObjWrapper::NP_HasOwnProperty!");
 
     return false;
   }
 
   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
   bool found, ok = false;
 
   AutoJSExceptionSuppressor suppressor(aes, npjsobj);
--- a/dom/security/test/cors/test_CrossSiteXHR.html
+++ b/dom/security/test/cors/test_CrossSiteXHR.html
@@ -754,30 +754,28 @@ function runTest() {
       is(res.didFail, true,
         "should have failed in test for " + test.toSource());
       is(res.status, 0, "wrong status in test for " + test.toSource());
       is(res.statusText, "", "wrong status text for " + test.toSource());
       is(res.responseXML, null,
          "wrong responseXML in test for " + test.toSource());
       is(res.responseText, "",
          "wrong responseText in test for " + test.toSource());
-      var expectedProgressCount = 0;
       if (!res.sendThrew) {
         if (test.username) {
-          expectedProgressCount = 1;
           is(res.events.join(","),
              "opening,rs1,sending,loadstart,rs4,error,loadend",
              "wrong events in test for " + test.toSource());
         } else {
           is(res.events.join(","),
              "opening,rs1,sending,loadstart,rs2,rs4,error,loadend",
              "wrong events in test for " + test.toSource());
         }
       }
-      is(res.progressEvents, expectedProgressCount,
+      is(res.progressEvents, 0,
          "wrong events in test for " + test.toSource());
       if (test.responseHeaders) {
         for (header in test.responseHeaders) {
           is(res.responseHeaders[header], null,
              "wrong response header (" + header + ") in test for " +
              test.toSource());
         }
       }
--- a/dom/svg/SVGDocument.cpp
+++ b/dom/svg/SVGDocument.cpp
@@ -14,65 +14,31 @@
 #include "nsLayoutStylesheetCache.h"
 #include "nsNetUtil.h"
 #include "nsServiceManagerUtils.h"
 #include "nsString.h"
 #include "nsLiteralString.h"
 #include "nsIDOMSVGElement.h"
 #include "mozilla/dom/Element.h"
 #include "nsSVGElement.h"
-#include "mozilla/dom/SVGDocumentBinding.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/StyleSheetInlines.h"
 
 using namespace mozilla::css;
 using namespace mozilla::dom;
 
 namespace mozilla {
 namespace dom {
 
 //----------------------------------------------------------------------
 // Implementation
 
 //----------------------------------------------------------------------
 // nsISupports methods:
 
-void
-SVGDocument::GetDomain(nsAString& aDomain, ErrorResult& aRv)
-{
-  SetDOMStringToNull(aDomain);
-
-  if (mDocumentURI) {
-    nsAutoCString domain;
-    nsresult rv = mDocumentURI->GetHost(domain);
-    if (NS_FAILED(rv)) {
-      aRv.Throw(rv);
-      return;
-    }
-    if (domain.IsEmpty()) {
-      return;
-    }
-    CopyUTF8toUTF16(domain, aDomain);
-  }
-}
-
-nsSVGElement*
-SVGDocument::GetRootElement(ErrorResult& aRv)
-{
-  Element* root = nsDocument::GetRootElement();
-  if (!root) {
-    return nullptr;
-  }
-  if (!root->IsSVGElement()) {
-    aRv.Throw(NS_NOINTERFACE);
-    return nullptr;
-  }
-  return static_cast<nsSVGElement*>(root);
-}
-
 nsresult
 SVGDocument::InsertChildAt(nsIContent* aKid, uint32_t aIndex, bool aNotify)
 {
   if (aKid->IsElement() && !aKid->IsSVGElement()) {
     // We can get here when well formed XML with a non-SVG root element is
     // served with the SVG MIME type, for example. In that case we need to load
     // the non-SVG UA sheets or else we can get bugs like bug 1016145.  Note
     // that we have to do this _before_ the XMLDocument::InsertChildAt call,
@@ -187,22 +153,16 @@ SVGDocument::EnsureNonSVGUserAgentStyleS
   if (nsLayoutUtils::ShouldUseNoScriptSheet(this)) {
     EnsureOnDemandBuiltInUASheet(cache->NoScriptSheet());
   }
   EnsureOnDemandBuiltInUASheet(cache->UASheet());
 
   EndUpdate(UPDATE_STYLE);
 }
 
-JSObject*
-SVGDocument::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
-{
-  return SVGDocumentBinding::Wrap(aCx, this, aGivenProto);
-}
-
 } // namespace dom
 } // namespace mozilla
 
 ////////////////////////////////////////////////////////////////////////
 // Exported creation functions
 
 nsresult
 NS_NewSVGDocument(nsIDocument** aInstancePtrResult)
--- a/dom/svg/SVGDocument.h
+++ b/dom/svg/SVGDocument.h
@@ -28,28 +28,22 @@ public:
   {
     mType = eSVG;
   }
 
   virtual nsresult InsertChildAt(nsIContent* aKid, uint32_t aIndex,
                                  bool aNotify) override;
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override;
 
-  // WebIDL API
-  void GetDomain(nsAString& aDomain, ErrorResult& aRv);
-  nsSVGElement* GetRootElement(ErrorResult& aRv);
-
   virtual SVGDocument* AsSVGDocument() override {
     return this;
   }
 
 private:
   void EnsureNonSVGUserAgentStyleSheetsLoaded();
 
-  virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
-
   bool mHasLoadedNonSVGUserAgentStyleSheets;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_SVGDocument_h
--- a/dom/svg/test/test_fragments.html
+++ b/dom/svg/test/test_fragments.html
@@ -25,17 +25,17 @@ function Test(svgFragmentIdentifier, val
     this.svgFragmentIdentifier = svgFragmentIdentifier;
     this.valid = valid;
 }
 
 function runTests()
 {
   var svg = $("svg");
   var doc = svg.contentWindow.document;
-  var rootElement = doc.rootElement;
+  var rootElement = doc.documentElement;
 
   var tests = [
       new Test("unknown", false),
       new Test("svgView(viewBox(0,0,200,200))", true),
       new Test("svgView(preserveAspectRatio(xMaxYMin slice))", true),
       new Test("svgView(viewBox(1,2,3,4);preserveAspectRatio(xMinYMax))", true),
       new Test("svgView(viewBox(none))", true),
       new Test("svgView(zoomAndPan(disable))", true),
--- a/dom/tests/mochitest/bugs/test_onerror_message.html
+++ b/dom/tests/mochitest/bugs/test_onerror_message.html
@@ -24,17 +24,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 var expected = [
 { name: "Error", message: "foo", filename: String(location), lineNumber: 45 },
 { name: "Error", message: "foo", filename: "bar", lineNumber: 123 },
 { name: "", message: "uncaught exception: [object Object]" },
 { name: "DuckError", message: "foo", filename: "bar", lineNumber: 123 },
 { name: "", message: "uncaught exception: [object Object]" },
 { name: "", message: "foo", filename: "baz", lineNumber: 123 },
 { name: "", message: "uncaught exception: [object Object]" },
-{ name: "InvalidStateError", message: "An attempt was made to use an object that is not, or is no longer, usable", filename: String(location), lineNumber: 62 },
+{ name: "InvalidStateError", message: "XMLHttpRequest state must not be LOADING or DONE.", filename: String(location), lineNumber: 62 },
 { name: "ReferenceError", message: "xxx is not defined", filename: String(location), lineNumber: 64 },
 { name: "ReferenceError", message: "xxx is not defined", filename: String(location), lineNumber: 66 }
 ];
 
 var counter = 0;
 var origin = location.protocol + "//" + location.host;
 postMessage(counter, origin);
 window.onmessage = function(e) {
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -1119,18 +1119,16 @@ var interfaceNamesInGlobalScope =
     "SVGClipPathElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "SVGComponentTransferFunctionElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "SVGDefsElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "SVGDescElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    "SVGDocument",
-// IMPORTANT: Do not change this list without review from a DOM peer!
     "SVGElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "SVGEllipseElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "SVGFEBlendElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "SVGFEColorMatrixElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
deleted file mode 100644
--- a/dom/tests/mochitest/geolocation/chrome.ini
+++ /dev/null
@@ -1,9 +0,0 @@
-[DEFAULT]
-support-files =
-  geolocation.html
-  geolocation_common.js
-
-[test_mozsettings.html]
-skip-if = buildapp == 'b2g' || toolkit == 'android'
-[test_mozsettingsWatch.html]
-skip-if = buildapp == 'b2g' || toolkit == 'android'
--- a/dom/tests/mochitest/geolocation/geolocation_common.js
+++ b/dom/tests/mochitest/geolocation/geolocation_common.js
@@ -84,20 +84,8 @@ function check_geolocation(location) {
   // optional ok("speed" in coords, "Check to see if there is a speed");
 
   ok (Math.abs(location.coords.latitude - 37.41857) < 0.001, "lat matches known value");
   ok (Math.abs(location.coords.longitude + 122.08769) < 0.001, "lon matches known value");
   // optional  ok(location.coords.altitude == 42, "alt matches known value");
   // optional  ok(location.coords.altitudeAccuracy == 42, "alt acc matches known value");
 }
 
-function toggleGeolocationSetting(value, callback) {
-  var mozSettings = window.navigator.mozSettings;
-  var lock = mozSettings.createLock();
-
-  var geoenabled = {"geolocation.enabled": value};
-
-  req = lock.set(geoenabled);
-  req.onsuccess = function () {
-    ok(true, "set done");
-    callback();
-  }
-}
deleted file mode 100644
--- a/dom/tests/mochitest/geolocation/test_mozsettings.html
+++ /dev/null
@@ -1,83 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=478911
--->
-<head>
-  <title>Test for getCurrentPosition </title>
-  <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="text/javascript" src="geolocation_common.js"></script>
-
-<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
-</head>
-<body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=777594">Mozilla Bug 777594</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
-<pre id="test">
-<script class="testbody" type="text/javascript">
-
-SimpleTest.waitForExplicitFinish();
-SimpleTest.requestFlakyTimeout("untriaged");
-
-var timeToWaitMs = 1000;
-
-resume_geolocationProvider(function() {
-  force_prompt(true, test2);
-});
-
-SpecialPowers.importInMainProcess("resource://gre/modules/SettingsRequestManager.jsm");
-
-function test2() {
-  ok(navigator.geolocation, "get geolocation object");
-
-  toggleGeolocationSetting(false, function() {
-      ok(true, "turned off geolocation via mozSettings");
-      setTimeout(function() {
-          navigator.geolocation.getCurrentPosition(successCallbackAfterMozsettingOff,
-                                                   failureCallbackAfterMozsettingOff);
-        }, timeToWaitMs); // need to wait a bit for all of these async callbacks to finish
-  });
-}
-
-function successCallbackAfterMozsettingOff(position) {
-  ok(false, "Success callback should not have been called after setting geolocation.enabled to false.");
-
-  toggleGeolocationSetting(true, function() {
-      ok(true, "turned on geolocation via mozSettings");
-      setTimeout(function() {
-         navigator.geolocation.getCurrentPosition(successCallbackAfterMozsettingOn,
-                                                  failureCallbackAfterMozsettingOn);
-        }, timeToWaitMs); // need to wait a bit for all of these async callbacks to finish
-    });
-}
-
-function failureCallbackAfterMozsettingOff(error) {
-  ok(true, "Geolocation didn't work after setting geolocation.enabled to false.");
-
-  toggleGeolocationSetting(true, function() {
-      ok(true, "turned on geolocation via mozSettings");
-      setTimeout(function() {
-         navigator.geolocation.getCurrentPosition(successCallbackAfterMozsettingOn,
-                                                  failureCallbackAfterMozsettingOn);
-        }, timeToWaitMs); // need to wait a bit for all of these async callbacks to finish
-    });
-}
-
-function successCallbackAfterMozsettingOn(position) {
-  ok(true, "Geolocation worked after setting geolocation.enabled to true.");
-  SimpleTest.finish();
-}
-
-function failureCallbackAfterMozsettingOn(error) {
-  ok(false, "Geolocation didn't work after setting geolocation.enabled to true.");
-  SimpleTest.finish();
-}
-
-</script>
-</pre>
-</body>
-</html>
-
deleted file mode 100644
--- a/dom/tests/mochitest/geolocation/test_mozsettingsWatch.html
+++ /dev/null
@@ -1,88 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=478911
--->
-<head>
-  <title>Test for getCurrentPosition </title>
-  <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="text/javascript" src="geolocation_common.js"></script>
-
-<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
-</head>
-<body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=777594">Mozilla Bug 777594</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
-<pre id="test">
-<script class="testbody" type="text/javascript">
-
-SimpleTest.waitForExplicitFinish();
-SimpleTest.requestFlakyTimeout("untriaged");
-
-resume_geolocationProvider(function() {
-  force_prompt(true, test2);
-});
-
-SpecialPowers.importInMainProcess("resource://gre/modules/SettingsRequestManager.jsm");
-
-var watchId;
-function test2() {
-  ok(navigator.geolocation, "get geolocation object");
-
-  toggleGeolocationSetting(false, function() {
-      ok(true, "turned off geolocation via mozSettings");
-      setTimeout(function() {
-          watchId = navigator.geolocation.watchPosition(successCallbackAfterMozsettingOff,
-                                                        failureCallbackAfterMozsettingOff);
-      }, 500); // need to wait a bit for all of these async callbacks to finish
-  });
-}
-
-function successCallbackAfterMozsettingOff(position) {
-  ok(false, "Success callback should not have been called after setting geolocation.enabled to false.");
-
-  navigator.geolocation.clearWatch(watchId);
-  toggleGeolocationSetting(true, function() {
-      ok(true, "turned on geolocation via mozSettings");
-      setTimeout(function() {
-          watchId = navigator.geolocation.watchPosition(successCallbackAfterMozsettingOn,
-                                                        failureCallbackAfterMozsettingOn);
-        }, 500); // need to wait a bit for all of these async callbacks to finish
-    });
-}
-
-function failureCallbackAfterMozsettingOff(error) {
-  ok(true, "Geolocation didn't work after setting geolocation.enabled to false.");
-
-  navigator.geolocation.clearWatch(watchId);
-  toggleGeolocationSetting(true, function() {
-      ok(true, "turned on geolocation via mozSettings");
-      setTimeout(function() {
-          watchId = navigator.geolocation.watchPosition(successCallbackAfterMozsettingOn,
-                                                        failureCallbackAfterMozsettingOn);
-        }, 500); // need to wait a bit for all of these async callbacks to finish
-    });
-}
-
-function successCallbackAfterMozsettingOn(position) {
-  ok(true, "Geolocation worked after setting geolocation.enabled to true.");
-
-  navigator.geolocation.clearWatch(watchId);
-  SimpleTest.finish();
-}
-
-function failureCallbackAfterMozsettingOn(error) {
-  ok(false, "Geolocation didn't work after setting geolocation.enabled to true.");
-
-  navigator.geolocation.clearWatch(watchId);
-  SimpleTest.finish();
-}
-
-</script>
-</pre>
-</body>
-</html>
-
--- a/dom/tests/moz.build
+++ b/dom/tests/moz.build
@@ -29,17 +29,16 @@ MOCHITEST_MANIFESTS += [
     'mochitest/webcomponents/mochitest.ini',
     'mochitest/whatwg/mochitest.ini',
 ]
 
 MOCHITEST_CHROME_MANIFESTS += [
     'mochitest/beacon/chrome.ini',
     'mochitest/chrome/chrome.ini',
     'mochitest/general/chrome.ini',
-    'mochitest/geolocation/chrome.ini',
     'mochitest/localstorage/chrome.ini',
     'mochitest/notification/chrome.ini',
     'mochitest/sessionstorage/chrome.ini',
     'mochitest/whatwg/chrome.ini',
 ]
 
 if CONFIG['MOZ_GAMEPAD']:
     MOCHITEST_MANIFESTS += [
--- a/dom/webidl/CSSStyleSheet.webidl
+++ b/dom/webidl/CSSStyleSheet.webidl
@@ -13,17 +13,17 @@ enum CSSStyleSheetParsingMode {
   "author",
   "user",
   "agent"
 };
 
 interface CSSStyleSheet : StyleSheet {
   [Pure]
   readonly attribute CSSRule? ownerRule;
-  [Throws]
+  [Throws, NeedsSubjectPrincipal]
   readonly attribute CSSRuleList cssRules;
   [ChromeOnly, BinaryName="parsingModeDOM"]
   readonly attribute CSSStyleSheetParsingMode parsingMode;
-  [Throws]
+  [Throws, NeedsSubjectPrincipal]
   unsigned long insertRule(DOMString rule, unsigned long index);
-  [Throws]
+  [Throws, NeedsSubjectPrincipal]
   void deleteRule(unsigned long index);
 };
--- a/dom/webidl/DataTransfer.webidl
+++ b/dom/webidl/DataTransfer.webidl
@@ -18,17 +18,17 @@ interface DataTransfer {
   void setDragImage(Element image, long x, long y);
 
   [Throws]
   readonly attribute DOMStringList types;
   [Throws]
   DOMString getData(DOMString format);
   [Throws]
   void setData(DOMString format, DOMString data);
-  [Throws]
+  [Throws, NeedsSubjectPrincipal]
   void clearData(optional DOMString format);
   [Throws]
   readonly attribute FileList? files;
 };
 
 partial interface DataTransfer {
   [Throws, Pref="dom.input.dirpicker"]
   Promise<sequence<(File or Directory)>> getFilesAndDirectories();
@@ -86,17 +86,17 @@ partial interface DataTransfer {
    *
    * If format is empty, then the data associated with all formats is removed.
    * If the format is not found, then this method has no effect.
    *
    * @param format the format to remove
    * @throws NS_ERROR_DOM_INDEX_SIZE_ERR if index is greater or equal than itemCount
    * @throws NO_MODIFICATION_ALLOWED_ERR if the item cannot be modified
    */
-  [Throws]
+  [Throws, NeedsSubjectPrincipal]
   void mozClearDataAt(DOMString format, unsigned long index);
 
   /*
    * A data transfer may store multiple items, each at a given zero-based
    * index. setDataAt may only be called with an index argument less than
    * itemCount in which case an existing item is modified, or equal to
    * itemCount in which case a new item is added, and the itemCount is
    * incremented by one.
--- a/dom/webidl/DataTransferItem.webidl
+++ b/dom/webidl/DataTransferItem.webidl
@@ -5,20 +5,21 @@
  *
  * The origin of this IDL file is:
  * https://html.spec.whatwg.org/multipage/interaction.html#the-datatransferitem-interface
  */
 
 interface DataTransferItem {
   readonly attribute DOMString kind;
   readonly attribute DOMString type;
-  [Throws]
+  [Throws, NeedsSubjectPrincipal]
   void getAsString(FunctionStringCallback? _callback);
-  [Throws]
+  [Throws, NeedsSubjectPrincipal]
   File? getAsFile();
 };
 
 callback FunctionStringCallback = void (DOMString data);
 
 partial interface DataTransferItem {
-  [Pref="dom.webkitBlink.filesystem.enabled", BinaryName="getAsEntry", Throws]
+  [Pref="dom.webkitBlink.filesystem.enabled", BinaryName="getAsEntry", Throws,
+   NeedsSubjectPrincipal]
   FileSystemEntry? webkitGetAsEntry();
 };
--- a/dom/webidl/DataTransferItemList.webidl
+++ b/dom/webidl/DataTransferItemList.webidl
@@ -6,17 +6,17 @@
  * The origin of this IDL file is:
  * https://html.spec.whatwg.org/multipage/interaction.html#the-datatransferitemlist-interface
  */
 
 interface DataTransferItemList {
   readonly attribute unsigned long length;
   [Throws]
   getter DataTransferItem (unsigned long index);
-  [Throws]
+  [Throws, NeedsSubjectPrincipal]
   DataTransferItem? add(DOMString data, DOMString type);
-  [Throws]
+  [Throws, NeedsSubjectPrincipal]
   DataTransferItem? add(File data);
-  [Throws]
+  [Throws, NeedsSubjectPrincipal]
   void remove(unsigned long index);
-  [Throws]
+  [Throws, NeedsSubjectPrincipal]
   void clear();
 };
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -322,16 +322,22 @@ partial interface Document {
 // http://w3c.github.io/web-animations/#extensions-to-the-document-interface
 partial interface Document {
   [Func="nsDocument::IsWebAnimationsEnabled"]
   readonly attribute DocumentTimeline timeline;
   [Func="nsDocument::IsWebAnimationsEnabled"]
   sequence<Animation> getAnimations();
 };
 
+// https://svgwg.org/svg2-draft/struct.html#InterfaceDocumentExtensions
+partial interface Document {
+  [BinaryName="SVGRootElement"]
+  readonly attribute SVGSVGElement? rootElement;
+};
+
 //  Mozilla extensions of various sorts
 partial interface Document {
   // nsIDOMDocumentXBL.  Wish we could make these [ChromeOnly], but
   // that would likely break bindings running with the page principal.
   [Func="IsChromeOrXBL"]
   NodeList? getAnonymousNodes(Element elt);
   [Func="IsChromeOrXBL"]
   Element? getAnonymousElementByAttribute(Element elt, DOMString attrName,
--- a/dom/webidl/HTMLCanvasElement.webidl
+++ b/dom/webidl/HTMLCanvasElement.webidl
@@ -5,17 +5,16 @@
  *
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/#the-canvas-element
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
-interface nsIInputStreamCallback;
 interface nsISupports;
 interface Variant;
 
 interface HTMLCanvasElement : HTMLElement {
   [Pure, SetterThrows]
            attribute unsigned long width;
   [Pure, SetterThrows]
            attribute unsigned long height;
--- a/dom/webidl/Location.webidl
+++ b/dom/webidl/Location.webidl
@@ -12,42 +12,42 @@
  */
 
 [Unforgeable, NonOrdinaryGetPrototypeOf]
 interface Location {
   // Bug 824857: no support for stringifier attributes yet.
   //  stringifier attribute USVString href;
 
   // Bug 824857 should remove this.
-  [Throws]
+  [Throws, NeedsSubjectPrincipal]
   stringifier;
 
-  [Throws, CrossOriginWritable]
+  [Throws, CrossOriginWritable, NeedsSubjectPrincipal]
            attribute USVString href;
-  [Throws]
+  [Throws, NeedsSubjectPrincipal]
   readonly attribute USVString origin;
-  [Throws]
+  [Throws, NeedsSubjectPrincipal]
            attribute USVString protocol;
-  [Throws]
+  [Throws, NeedsSubjectPrincipal]
            attribute USVString host;
-  [Throws]
+  [Throws, NeedsSubjectPrincipal]
            attribute USVString hostname;
-  [Throws]
+  [Throws, NeedsSubjectPrincipal]
            attribute USVString port;
-  [Throws]
+  [Throws, NeedsSubjectPrincipal]
            attribute USVString pathname;
-  [Throws]
+  [Throws, NeedsSubjectPrincipal]
            attribute USVString search;
-  [Throws]
+  [Throws, NeedsSubjectPrincipal]
            attribute USVString hash;
 
-  [Throws, UnsafeInPrerendering]
+  [Throws, UnsafeInPrerendering, NeedsSubjectPrincipal]
   void assign(USVString url);
 
-  [Throws, CrossOriginCallable, UnsafeInPrerendering]
+  [Throws, CrossOriginCallable, UnsafeInPrerendering, NeedsSubjectPrincipal]
   void replace(USVString url);
 
   // XXXbz there is no forceget argument in the spec!  See bug 1037721.
-  [Throws, UnsafeInPrerendering]
+  [Throws, UnsafeInPrerendering, NeedsSubjectPrincipal]
   void reload(optional boolean forceget = false);
 
   // Bug 1085214 [SameObject] readonly attribute USVString[] ancestorOrigins;
 };
deleted file mode 100644
--- a/dom/webidl/SVGDocument.webidl
+++ /dev/null
@@ -1,15 +0,0 @@
-/* -*- 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/.
- *
- * The origin of this IDL file is:
- * dom/interfaces/svg/nsIDOMSVGDocument.idl
- */
-
-interface SVGDocument : Document {
-  [Throws]
-  readonly attribute DOMString domain;
-  [Pure, Throws]
-  readonly attribute SVGElement? rootElement;
-};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -467,17 +467,16 @@ WEBIDL_FILES = [
     'SVGAnimateMotionElement.webidl',
     'SVGAnimateTransformElement.webidl',
     'SVGAnimationElement.webidl',
     'SVGCircleElement.webidl',
     'SVGClipPathElement.webidl',
     'SVGComponentTransferFunctionElement.webidl',
     'SVGDefsElement.webidl',
     'SVGDescElement.webidl',
-    'SVGDocument.webidl',
     'SVGElement.webidl',
     'SVGEllipseElement.webidl',
     'SVGFEBlendElement.webidl',
     'SVGFEColorMatrixElement.webidl',
     'SVGFEComponentTransferElement.webidl',
     'SVGFECompositeElement.webidl',
     'SVGFEConvolveMatrixElement.webidl',
     'SVGFEDiffuseLightingElement.webidl',
--- a/dom/workers/ChromeWorkerScope.cpp
+++ b/dom/workers/ChromeWorkerScope.cpp
@@ -23,17 +23,17 @@ namespace {
 
 char*
 UnicodeToNative(JSContext* aCx, const char16_t* aSource, size_t aSourceLen)
 {
   nsDependentString unicode(aSource, aSourceLen);
 
   nsAutoCString native;
   if (NS_FAILED(NS_CopyUnicodeToNative(unicode, native))) {
-    JS_ReportError(aCx, "Could not convert string to native charset!");
+    JS_ReportErrorASCII(aCx, "Could not convert string to native charset!");
     return nullptr;
   }
 
   char* result = static_cast<char*>(JS_malloc(aCx, native.Length() + 1));
   if (!result) {
     return nullptr;
   }
 
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -6136,17 +6136,17 @@ WorkerPrivate::RescheduleTimeoutTimer(JS
     (mTimeouts[0]->mTargetTime - TimeStamp::Now()).ToMilliseconds();
   uint32_t delay = delta > 0 ? std::min(delta, double(UINT32_MAX)) : 0;
 
   LOG(TimeoutsLog(), ("Worker %p scheduled timer for %d ms, %d pending timeouts\n",
                       this, delay, mTimeouts.Length()));
 
   nsresult rv = mTimer->InitWithCallback(mTimerRunnable, delay, nsITimer::TYPE_ONE_SHOT);
   if (NS_FAILED(rv)) {
-    JS_ReportError(aCx, "Failed to start timer!");
+    JS_ReportErrorASCII(aCx, "Failed to start timer!");
     return false;
   }
 
   return true;
 }
 
 void
 WorkerPrivate::UpdateContextOptionsInternal(
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -11,16 +11,17 @@
 #include <unistd.h>
 #endif
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/dom/BlobSet.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FetchUtil.h"
 #include "mozilla/dom/FormData.h"
+#include "mozilla/dom/MutableBlobStorage.h"
 #include "mozilla/dom/XMLDocument.h"
 #include "mozilla/dom/URLSearchParams.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/LoadInfo.h"
 #include "mozilla/LoadContext.h"
 #include "mozilla/MemoryReporting.h"
 #include "nsIDOMDocument.h"
@@ -66,16 +67,17 @@
 #include "nsIFileChannel.h"
 #include "mozilla/Telemetry.h"
 #include "jsfriendapi.h"
 #include "GeckoProfiler.h"
 #include "mozilla/dom/EncodingUtils.h"
 #include "nsIUnicodeDecoder.h"
 #include "mozilla/dom/XMLHttpRequestBinding.h"
 #include "mozilla/Attributes.h"
+#include "MultipartBlobImpl.h"
 #include "nsIPermissionManager.h"
 #include "nsMimeTypes.h"
 #include "nsIHttpChannelInternal.h"
 #include "nsIClassOfService.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsStreamListenerWrapper.h"
 #include "xpcjsid.h"
 #include "nsITimedChannel.h"
@@ -286,16 +288,17 @@ XMLHttpRequestMainThread::InitParameters
 void
 XMLHttpRequestMainThread::ResetResponse()
 {
   mResponseXML = nullptr;
   mResponseBody.Truncate();
   TruncateResponseText();
   mResponseBlob = nullptr;
   mDOMBlob = nullptr;
+  mBlobStorage = nullptr;
   mBlobSet = nullptr;
   mResultArrayBuffer = nullptr;
   mArrayBufferBuilder.reset();
   mResultJSON.setUndefined();
   mDataAvailable = 0;
   mLoadTransferred = 0;
   mResponseBodyDecodedPos = 0;
 }
@@ -468,17 +471,17 @@ XMLHttpRequestMainThread::GetResponseXML
   return CallQueryInterface(responseXML, aResponseXML);
 }
 
 nsIDocument*
 XMLHttpRequestMainThread::GetResponseXML(ErrorResult& aRv)
 {
   if (mResponseType != XMLHttpRequestResponseType::_empty &&
       mResponseType != XMLHttpRequestResponseType::Document) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_WRONG_RESPONSETYPE_FOR_RESPONSEXML);
     return nullptr;
   }
   if (mWarnAboutSyncHtml) {
     mWarnAboutSyncHtml = false;
     LogMessage("HTMLSyncXHRWarning", GetOwner());
   }
   if (mState != State::done) {
     return nullptr;
@@ -590,17 +593,17 @@ void
 XMLHttpRequestMainThread::GetResponseText(XMLHttpRequestStringSnapshot& aSnapshot,
                                           ErrorResult& aRv)
 {
   aSnapshot.Reset();
 
   if (mResponseType != XMLHttpRequestResponseType::_empty &&
       mResponseType != XMLHttpRequestResponseType::Text &&
       mResponseType != XMLHttpRequestResponseType::Moz_chunked_text) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_WRONG_RESPONSETYPE_FOR_RESPONSETEXT);
     return;
   }
 
   if (mResponseType == XMLHttpRequestResponseType::Moz_chunked_text &&
       !mInLoadProgressEvent) {
     aSnapshot.SetVoid();
     return;
   }
@@ -682,17 +685,26 @@ XMLHttpRequestMainThread::CreatePartialB
     return;
   }
 
   nsAutoCString contentType;
   if (mLoadTotal == mLoadTransferred) {
     mChannel->GetContentType(contentType);
   }
 
-  mResponseBlob = mBlobSet->GetBlobInternal(GetOwner(), contentType, aRv);
+  nsTArray<RefPtr<BlobImpl>> subImpls(mBlobSet->GetBlobImpls());
+  RefPtr<BlobImpl> blobImpl =
+    MultipartBlobImpl::Create(Move(subImpls),
+                              NS_ConvertASCIItoUTF16(contentType),
+                              aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return;
+  }
+
+  mResponseBlob = Blob::Create(GetOwner(), blobImpl);
 }
 
 NS_IMETHODIMP XMLHttpRequestMainThread::GetResponseType(nsAString& aResponseType)
 {
   MOZ_ASSERT(mResponseType < XMLHttpRequestResponseType::EndGuard_);
   const EnumEntry& entry =
     XMLHttpRequestResponseTypeValues::strings[static_cast<uint32_t>(mResponseType)];
   aResponseType.AssignASCII(entry.value, entry.length);
@@ -713,34 +725,32 @@ NS_IMETHODIMP XMLHttpRequestMainThread::
 
   return NS_OK;
 }
 
 void
 XMLHttpRequestMainThread::SetResponseType(XMLHttpRequestResponseType aResponseType,
                                           ErrorResult& aRv)
 {
-  // If the state is LOADING or DONE raise an INVALID_STATE_ERR exception
-  // and terminate these steps.
   if (mState == State::loading || mState == State::done) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_LOADING_OR_DONE);
     return;
   }
 
   // sync request is not allowed setting responseType in window context
   if (HasOrHasHadOwner() && mState != State::unsent && mFlagSynchronous) {
     LogMessage("ResponseTypeSyncXHRWarning", GetOwner());
-    aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+    aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_XHR_TIMEOUT_AND_RESPONSETYPE_UNSUPPORTED_FOR_SYNC);
     return;
   }
 
   if (mFlagSynchronous &&
       (aResponseType == XMLHttpRequestResponseType::Moz_chunked_text ||
        aResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer)) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_CHUNKED_RESPONSETYPES_UNSUPPORTED_FOR_SYNC);
     return;
   }
 
   // Set the responseType attribute's value to the given value.
   mResponseType = aResponseType;
 }
 
 NS_IMETHODIMP
@@ -1033,20 +1043,18 @@ XMLHttpRequestMainThread::CloseRequestWi
   if (mState != State::unsent &&
       !(mState == State::opened && !mFlagSend) &&
       mState != State::done) {
     ChangeState(State::done, true);
 
     if (!mFlagSyncLooping) {
       if (mUpload && !mUploadComplete) {
         mUploadComplete = true;
-        DispatchProgressEvent(mUpload, ProgressEventType::progress, 0, 0);
         DispatchProgressEvent(mUpload, aType, 0, 0);
       }
-      DispatchProgressEvent(this, ProgressEventType::progress, 0, 0);
       DispatchProgressEvent(this, aType, 0, 0);
     }
   }
 
   // The ChangeState call above calls onreadystatechange handlers which
   // if they load a new url will cause XMLHttpRequestMainThread::Open to clear
   // the abort state bit. If this occurs we're not uninitialized (bug 361773).
   if (mFlagAborted) {
@@ -1436,17 +1444,17 @@ XMLHttpRequestMainThread::Open(const nsA
 
   // Step 1
   nsCOMPtr<nsIDocument> responsibleDocument = GetDocumentIfCurrent();
   if (!responsibleDocument) {
     // This could be because we're no longer current or because we're in some
     // non-window context...
     nsresult rv = CheckInnerWindowCorrectness();
     if (NS_WARN_IF(NS_FAILED(rv))) {
-      return NS_ERROR_DOM_INVALID_STATE_ERR;
+      return NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT;
     }
   }
   NS_ENSURE_TRUE(mPrincipal, NS_ERROR_NOT_INITIALIZED);
 
   // Steps 2-4
   nsAutoCString method;
   nsresult rv = FetchUtil::GetValidRequestMethod(aMethod, method);
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -1459,22 +1467,22 @@ XMLHttpRequestMainThread::Open(const nsA
     baseURI = mBaseURI;
   } else if (responsibleDocument) {
     baseURI = responsibleDocument->GetBaseURI();
   }
   nsCOMPtr<nsIURI> parsedURL;
   rv = NS_NewURI(getter_AddRefs(parsedURL), aUrl, nullptr, baseURI);
   if (NS_FAILED(rv)) {
     if (rv ==  NS_ERROR_MALFORMED_URI) {
-      return NS_ERROR_DOM_SYNTAX_ERR;
+      return NS_ERROR_DOM_MALFORMED_URI;
     }
     return rv;
   }
   if (NS_WARN_IF(NS_FAILED(CheckInnerWindowCorrectness()))) {
-    return NS_ERROR_DOM_INVALID_STATE_ERR;
+    return NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT;
   }
 
   // Step 7
   // This is already handled by the other Open() method, which passes
   // username and password in as NullStrings.
 
   // Step 8
   nsAutoCString host;
@@ -1495,17 +1503,17 @@ XMLHttpRequestMainThread::Open(const nsA
   if (!aAsync && HasOrHasHadOwner() && (mTimeoutMilliseconds ||
        mResponseType != XMLHttpRequestResponseType::_empty)) {
     if (mTimeoutMilliseconds) {
       LogMessage("TimeoutSyncXHRWarning", GetOwner());
     }
     if (mResponseType != XMLHttpRequestResponseType::_empty) {
       LogMessage("ResponseTypeSyncXHRWarning", GetOwner());
     }
-    return NS_ERROR_DOM_INVALID_ACCESS_ERR;
+    return NS_ERROR_DOM_INVALID_ACCESS_XHR_TIMEOUT_AND_RESPONSETYPE_UNSUPPORTED_FOR_SYNC;
   }
 
   // Step 10
   CloseRequest();
 
   // Step 11
   // timeouts are handled without a flag
   mFlagSend = false;
@@ -1576,28 +1584,30 @@ XMLHttpRequestMainThread::StreamReaderFu
   XMLHttpRequestMainThread* xmlHttpRequest = static_cast<XMLHttpRequestMainThread*>(closure);
   if (!xmlHttpRequest || !writeCount) {
     NS_WARNING("XMLHttpRequest cannot read from stream: no closure or writeCount");
     return NS_ERROR_FAILURE;
   }
 
   nsresult rv = NS_OK;
 
-  if (xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Blob ||
-      xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Moz_blob) {
+  if (xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Blob) {
+    if (!xmlHttpRequest->mDOMBlob) {
+      xmlHttpRequest->MaybeCreateBlobStorage();
+      rv = xmlHttpRequest->mBlobStorage->Append(fromRawSegment, count);
+    }
+  } else if (xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Moz_blob) {
     if (!xmlHttpRequest->mDOMBlob) {
       if (!xmlHttpRequest->mBlobSet) {
         xmlHttpRequest->mBlobSet = new BlobSet();
       }
       rv = xmlHttpRequest->mBlobSet->AppendVoidPtr(fromRawSegment, count);
     }
     // Clear the cache so that the blob size is updated.
-    if (xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Moz_blob) {
-      xmlHttpRequest->mResponseBlob = nullptr;
-    }
+    xmlHttpRequest->mResponseBlob = nullptr;
   } else if ((xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Arraybuffer &&
               !xmlHttpRequest->mIsMappedArrayBuffer) ||
              xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer) {
     // get the initial capacity to something reasonable to avoid a bunch of reallocs right
     // at the start
     if (xmlHttpRequest->mArrayBufferBuilder.capacity() == 0)
       xmlHttpRequest->mArrayBufferBuilder.setCapacity(std::max(count, XML_HTTP_REQUEST_ARRAYBUFFER_MIN_SIZE));
 
@@ -1663,16 +1673,17 @@ bool XMLHttpRequestMainThread::CreateDOM
     return false;
 
   nsAutoCString contentType;
   mChannel->GetContentType(contentType);
 
   mDOMBlob = File::CreateFromFile(GetOwner(), file, EmptyString(),
                                   NS_ConvertASCIItoUTF16(contentType));
 
+  mBlobStorage = nullptr;
   mBlobSet = nullptr;
   NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
   return true;
 }
 
 NS_IMETHODIMP
 XMLHttpRequestMainThread::OnDataAvailable(nsIRequest *request,
                                           nsISupports *ctxt,
@@ -1903,17 +1914,17 @@ XMLHttpRequestMainThread::OnStartRequest
     nsCOMPtr<nsIURI> chromeXHRDocURI, chromeXHRDocBaseURI;
     if (doc) {
       chromeXHRDocURI = doc->GetDocumentURI();
       chromeXHRDocBaseURI = doc->GetBaseURI();
     } else {
       // If we're no longer current, just kill the load, though it really should
       // have been killed already.
       if (NS_WARN_IF(NS_FAILED(CheckInnerWindowCorrectness()))) {
-        return NS_ERROR_DOM_INVALID_STATE_ERR;
+        return NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT;
       }
     }
 
     // Create an empty document from it.
     const nsAString& emptyStr = EmptyString();
     nsCOMPtr<nsIDOMDocument> responseDoc;
     nsIGlobalObject* global = DOMEventTargetHelper::GetParentObject();
 
@@ -2018,54 +2029,76 @@ XMLHttpRequestMainThread::OnStopRequest(
   // Is this good enough here?
   if (mXMLParserStreamListener && mFlagParseBody) {
     mXMLParserStreamListener->OnStopRequest(request, ctxt, status);
   }
 
   mXMLParserStreamListener = nullptr;
   mContext = nullptr;
 
+  bool waitingForBlobCreation = false;
+
   if (NS_SUCCEEDED(status) &&
       (mResponseType == XMLHttpRequestResponseType::_empty ||
        mResponseType == XMLHttpRequestResponseType::Text)) {
     mLoadTotal = mResponseBody.Length();
   } else if (NS_SUCCEEDED(status) &&
       (mResponseType == XMLHttpRequestResponseType::Blob ||
        mResponseType == XMLHttpRequestResponseType::Moz_blob)) {
     ErrorResult rv;
     if (!mDOMBlob) {
       CreateDOMBlob(request);
     }
     if (mDOMBlob) {
       mResponseBlob = mDOMBlob;
       mDOMBlob = nullptr;
+
+      mLoadTotal = mResponseBlob->GetSize(rv);
+      if (NS_WARN_IF(rv.Failed())) {
+        status = rv.StealNSResult();
+      }
     } else {
-      // mBlobSet can be null if the channel is non-file non-cacheable
-      // 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, rv);
-      mBlobSet = nullptr;
-
-      if (NS_WARN_IF(rv.Failed())) {
-        return rv.StealNSResult();
+      if (mResponseType == XMLHttpRequestResponseType::Blob) {
+        // mBlobStorage can be null if the channel is non-file non-cacheable
+        // and if the response length is zero.
+        MaybeCreateBlobStorage();
+        mLoadTotal =
+          mBlobStorage->GetBlobWhenReady(GetOwner(), contentType, this);
+        waitingForBlobCreation = true;
+      } else {
+        // mBlobSet can be null if the channel is non-file non-cacheable
+        // and if the response length is zero.
+        if (!mBlobSet) {
+          mBlobSet = new BlobSet();
+        }
+
+        nsTArray<RefPtr<BlobImpl>> subImpls(mBlobSet->GetBlobImpls());
+        RefPtr<BlobImpl> blobImpl =
+          MultipartBlobImpl::Create(Move(subImpls),
+                                    NS_ConvertASCIItoUTF16(contentType),
+                                    rv);
+        mBlobSet = nullptr;
+
+        if (NS_WARN_IF(rv.Failed())) {
+          return rv.StealNSResult();
+        }
+
+        mResponseBlob = Blob::Create(GetOwner(), blobImpl);
+        mLoadTotal = mResponseBlob->GetSize(rv);
+        if (NS_WARN_IF(rv.Failed())) {
+          status = rv.StealNSResult();
+        }
       }
     }
 
-    mLoadTotal = mResponseBlob->GetSize(rv);
-    if (NS_WARN_IF(rv.Failed())) {
-      status = rv.StealNSResult();
-    }
-
     NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
     NS_ASSERTION(mResponseText.IsEmpty(), "mResponseText should be empty");
   } else if (NS_SUCCEEDED(status) &&
              ((mResponseType == XMLHttpRequestResponseType::Arraybuffer &&
                !mIsMappedArrayBuffer) ||
               mResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer)) {
     // set the capacity down to the actual length, to realloc back
     // down to the actual size
@@ -2102,17 +2135,22 @@ XMLHttpRequestMainThread::OnStopRequest(
   // earlier and listeners have already been notified. Also we do
   // not want to do this if we already completed.
   if (mState == State::unsent || mState == State::done) {
     return NS_OK;
   }
 
   if (!mResponseXML) {
     mFlagParseBody = false;
-    ChangeStateToDone();
+
+    //We postpone the 'done' until the creation of the Blob is completed.
+    if (!waitingForBlobCreation) {
+      ChangeStateToDone();
+    }
+
     return NS_OK;
   }
 
   if (mIsHtml) {
     NS_ASSERTION(!mFlagSyncLooping,
       "We weren't supposed to support HTML parsing with XHR!");
     nsCOMPtr<EventTarget> eventTarget = do_QueryInterface(mResponseXML);
     EventListenerManager* manager =
@@ -2179,20 +2217,19 @@ XMLHttpRequestMainThread::ChangeStateToD
     DispatchProgressEvent(this, ProgressEventType::progress,
                           mLoadTransferred, mLoadTotal);
     mProgressSinceLastProgressEvent = false;
   }
 
   // Per spec, fire readystatechange=4/done before final error events.
   ChangeState(State::done, true);
 
-  // Per spec, if we failed in the upload phase, fire a final progress, error,
-  // and loadend event for the upload after readystatechange=4/done.
+  // Per spec, if we failed in the upload phase, fire a final error
+  // and loadend events for the upload after readystatechange=4/done.
   if (!mFlagSynchronous && mUpload && !mUploadComplete) {
-    DispatchProgressEvent(mUpload, ProgressEventType::progress, 0, 0);
     DispatchProgressEvent(mUpload, ProgressEventType::error, 0, 0);
   }
 
   // Per spec, fire download's load/error and loadend events after
   // readystatechange=4/done (and of course all upload events).
   DispatchProgressEvent(this,
                         mErrorLoad ? ProgressEventType::error :
                                      ProgressEventType::load,
@@ -2752,24 +2789,29 @@ XMLHttpRequestMainThread::Send(nsIVarian
   return SendInternal(&body);
 }
 
 nsresult
 XMLHttpRequestMainThread::SendInternal(const RequestBodyBase* aBody)
 {
   NS_ENSURE_TRUE(mPrincipal, NS_ERROR_NOT_INITIALIZED);
 
-  // Steps 1 and 2
-  if (mState != State::opened || mFlagSend) {
-    return NS_ERROR_DOM_INVALID_STATE_ERR;
+  // Step 1
+  if (mState != State::opened) {
+    return NS_ERROR_DOM_INVALID_STATE_XHR_MUST_BE_OPENED;
+  }
+
+  // Step 2
+  if (mFlagSend) {
+    return NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_SENDING;
   }
 
   nsresult rv = CheckInnerWindowCorrectness();
   if (NS_FAILED(rv)) {
-    return NS_ERROR_DOM_INVALID_STATE_ERR;
+    return NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT;
   }
 
   // If open() failed to create the channel, then throw a network error
   // as per spec. We really should create the channel here in send(), but
   // we have internal code relying on the channel being created in open().
   if (!mChannel) {
     return NS_ERROR_DOM_NETWORK_ERR;
   }
@@ -2947,29 +2989,34 @@ XMLHttpRequestMainThread::SendInternal(c
   return rv;
 }
 
 // http://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html#dom-xmlhttprequest-setrequestheader
 NS_IMETHODIMP
 XMLHttpRequestMainThread::SetRequestHeader(const nsACString& aName,
                                            const nsACString& aValue)
 {
-  // Steps 1 and 2
-  if (mState != State::opened || mFlagSend) {
-    return NS_ERROR_DOM_INVALID_STATE_ERR;
+  // Step 1
+  if (mState != State::opened) {
+    return NS_ERROR_DOM_INVALID_STATE_XHR_MUST_BE_OPENED;
+  }
+
+  // Step 2
+  if (mFlagSend) {
+    return NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_SENDING;
   }
 
   // Step 3
   nsAutoCString value(aValue);
   static const char kHTTPWhitespace[] = "\n\t\r ";
   value.Trim(kHTTPWhitespace);
 
   // Step 4
   if (!NS_IsValidHTTPToken(aName) || !NS_IsReasonableHTTPHeaderValue(value)) {
-    return NS_ERROR_DOM_SYNTAX_ERR;
+    return NS_ERROR_DOM_INVALID_HEADER_NAME;
   }
 
   // Step 5
   bool isPrivilegedCaller = IsSystemXHR();
   bool isForbiddenHeader = nsContentUtils::IsForbiddenRequestHeader(aName);
   if (!isPrivilegedCaller && isForbiddenHeader) {
     const char16_t* params[] = { NS_ConvertUTF8toUTF16(aName).get() };
     LogMessage("ForbiddenHeaderWarning", GetOwner(), params, ArrayLength(params));
@@ -3009,17 +3056,17 @@ XMLHttpRequestMainThread::SetTimeout(uin
 
 void
 XMLHttpRequestMainThread::SetTimeout(uint32_t aTimeout, ErrorResult& aRv)
 {
   if (mFlagSynchronous && mState != State::unsent && HasOrHasHadOwner()) {
     /* Timeout is not supported for synchronous requests with an owning window,
        per XHR2 spec. */
     LogMessage("TimeoutSyncXHRWarning", GetOwner());
-    aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+    aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_XHR_TIMEOUT_AND_RESPONSETYPE_UNSUPPORTED_FOR_SYNC);
     return;
   }
 
   mTimeoutMilliseconds = aTimeout;
   if (mRequestSentTime) {
     StartTimeoutTimer();
   }
 }
@@ -3081,17 +3128,17 @@ XMLHttpRequestMainThread::ReadyState() c
   }
   return 0;
 }
 
 void XMLHttpRequestMainThread::OverrideMimeType(const nsAString& aMimeType, ErrorResult& aRv)
 {
   if (mState == State::loading || mState == State::done) {
     ResetResponse();
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_LOADING_OR_DONE);
     return;
   }
 
   mOverrideMimeType = aMimeType;
 }
 
 NS_IMETHODIMP
 XMLHttpRequestMainThread::SlowOverrideMimeType(const nsAString& aMimeType)
@@ -3118,17 +3165,17 @@ NS_IMETHODIMP
 XMLHttpRequestMainThread::SetMozBackgroundRequest(bool aMozBackgroundRequest)
 {
   if (!IsSystemXHR()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   if (mState != State::unsent) {
     // Can't change this while we're in the middle of something.
-     return NS_ERROR_IN_PROGRESS;
+    return NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_SENDING;
   }
 
   mFlagBackgroundRequest = aMozBackgroundRequest;
 
   return NS_OK;
 }
 
 void
@@ -3161,19 +3208,20 @@ XMLHttpRequestMainThread::SetWithCredent
 }
 
 void
 XMLHttpRequestMainThread::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 != State::unsent && mState != State::opened) ||
       mFlagSend || mIsAnon) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_SENDING);
     return;
   }
 
   mFlagACwithCredentials = aWithCredentials;
 }
 
 nsresult
 XMLHttpRequestMainThread::ChangeState(State aState, bool aBroadcast)
@@ -3578,16 +3626,56 @@ nsHeaderVisitor::VisitHeader(const nsACS
     mHeaders.Append(header);
     mHeaders.AppendLiteral(": ");
     mHeaders.Append(value);
     mHeaders.AppendLiteral("\r\n");
   }
   return NS_OK;
 }
 
+void
+XMLHttpRequestMainThread::MaybeCreateBlobStorage()
+{
+  MOZ_ASSERT(mResponseType == XMLHttpRequestResponseType::Blob);
+
+  if (mBlobStorage) {
+    return;
+  }
+
+  MutableBlobStorage::MutableBlobStorageType storageType =
+    BasePrincipal::Cast(mPrincipal)->PrivateBrowsingId() == 0
+      ? MutableBlobStorage::eCouldBeInTemporaryFile
+      : MutableBlobStorage::eOnlyInMemory;
+
+  mBlobStorage = new MutableBlobStorage(storageType);
+}
+
+void
+XMLHttpRequestMainThread::BlobStoreCompleted(MutableBlobStorage* aBlobStorage,
+                                             Blob* aBlob, nsresult aRv)
+{
+  // Ok, the state is changed...
+  if (mBlobStorage != aBlobStorage || NS_FAILED(aRv)) {
+    return;
+  }
+
+  MOZ_ASSERT(mState != State::done);
+
+  mResponseBlob = aBlob;
+  mBlobStorage = nullptr;
+
+  ErrorResult rv;
+  mLoadTotal = mResponseBlob->GetSize(rv);
+  if (NS_WARN_IF(rv.Failed())) {
+    rv.SuppressException();
+  }
+
+  ChangeStateToDone();
+}
+
 // 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)
--- a/dom/xhr/XMLHttpRequestMainThread.h
+++ b/dom/xhr/XMLHttpRequestMainThread.h
@@ -29,16 +29,17 @@
 #include "nsISizeOfEventTarget.h"
 #include "nsIXPConnect.h"
 #include "nsIInputStream.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/NotNull.h"
+#include "mozilla/dom/MutableBlobStorage.h"
 #include "mozilla/dom/TypedArray.h"
 #include "mozilla/dom/XMLHttpRequest.h"
 #include "mozilla/dom/XMLHttpRequestBinding.h"
 #include "mozilla/dom/XMLHttpRequestEventTarget.h"
 #include "mozilla/dom/XMLHttpRequestString.h"
 
 #ifdef Status
 /* Xlib headers insist on this for some reason... Nuke it because
@@ -157,17 +158,18 @@ class XMLHttpRequestMainThread final : p
                                        public nsIXMLHttpRequest,
                                        public nsIJSXMLHttpRequest,
                                        public nsIStreamListener,
                                        public nsIChannelEventSink,
                                        public nsIProgressEventSink,
                                        public nsIInterfaceRequestor,
                                        public nsSupportsWeakReference,
                                        public nsITimerCallback,
-                                       public nsISizeOfEventTarget
+                                       public nsISizeOfEventTarget,
+                                       public MutableBlobStorageCallback
 {
   friend class nsXHRParseEndListener;
   friend class nsXMLHttpRequestXPCOMifier;
 
 public:
   enum class ProgressEventType : uint8_t {
     loadstart,
     progress,
@@ -531,16 +533,20 @@ public:
   static bool DontWarnAboutSyncXHR()
   {
     return sDontWarnAboutSyncXHR;
   }
 
   virtual void
   SetOriginAttributes(const mozilla::dom::OriginAttributesDictionary& aAttrs) override;
 
+  void BlobStoreCompleted(MutableBlobStorage* aBlobStorage,
+                          Blob* aBlob,
+                          nsresult aResult) override;
+
 protected:
   // XHR states are meant to mirror the XHR2 spec:
   //   https://xhr.spec.whatwg.org/#states
   enum class State : uint8_t {
     unsent,           // object has been constructed.
     opened,           // open() has been successfully invoked.
     headers_received, // redirects followed and response headers received.
     loading,          // response body is being received.
@@ -573,16 +579,18 @@ protected:
   bool InUploadPhase() const;
 
   void OnBodyParseEnd();
   void ChangeStateToDone();
 
   void StartProgressEventTimer();
   void StopProgressEventTimer();
 
+  void MaybeCreateBlobStorage();
+
   nsresult OnRedirectVerifyCallback(nsresult result);
 
   already_AddRefed<nsXMLHttpRequestXPCOMifier> EnsureXPCOMifier();
 
   nsCOMPtr<nsISupports> mContext;
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsCOMPtr<nsIChannel> mChannel;
   nsCString mRequestMethod;
@@ -639,18 +647,21 @@ protected:
   XMLHttpRequestResponseType mResponseType;
 
   // It is either a cached blob-response from the last call to GetResponse,
   // but is also explicitly set in OnStopRequest.
   RefPtr<Blob> mResponseBlob;
   // Non-null only when we are able to get a os-file representation of the
   // response, i.e. when loading from a file.
   RefPtr<Blob> mDOMBlob;
-  // We stream data to mBlobSet when response type is "blob" or "moz-blob"
-  // and mDOMBlob is null.
+  // We stream data to mBlobStorage when response type is "blob" and mDOMBlob is
+  // null.
+  RefPtr<MutableBlobStorage> mBlobStorage;
+  // We stream data to mBlobStorage when response type is "moz-blob" and
+  // mDOMBlob is null.
   nsAutoPtr<BlobSet> mBlobSet;
 
   nsString mOverrideMimeType;
 
   /**
    * The notification callbacks the channel had when Send() was
    * called.  We want to forward things here as needed.
    */
new file mode 100644
--- /dev/null
+++ b/dom/xhr/tests/common_temporaryFileBlob.js
@@ -0,0 +1,100 @@
+var data = new Array(256).join("1234567890ABCDEF");
+
+function createXHR() {
+  var xhr = new XMLHttpRequest();
+  xhr.open("POST", "temporaryFileBlob.sjs");
+  xhr.responseType = 'blob';
+  xhr.send({toString: function() { return data; }});
+  return xhr;
+}
+
+function test_simple() {
+  info("Simple test");
+
+  var xhr = createXHR();
+
+  xhr.onloadend = function() {
+    ok(xhr.response instanceof Blob, "We have a blob!");
+    is(xhr.response.size, data.length, "Data length matches");
+
+    var fr = new FileReader();
+    fr.readAsText(xhr.response);
+    fr.onload = function() {
+      is(fr.result, data, "Data content matches");
+      next();
+    }
+  }
+}
+
+function test_abort() {
+  info("Aborting during onloading");
+
+  var xhr = createXHR();
+
+  xhr.onprogress = function() {
+    xhr.abort();
+  }
+
+  xhr.onloadend = function() {
+    ok(!xhr.response, "We should not have a Blob!");
+    next();
+  }
+}
+
+function test_reuse() {
+  info("Reuse test");
+
+  var xhr = createXHR();
+
+  var count = 0;
+  xhr.onloadend = function() {
+    ok(xhr.response instanceof Blob, "We have a blob!");
+    is(xhr.response.size, data.length, "Data length matches");
+
+    var fr = new FileReader();
+    fr.readAsText(xhr.response);
+    fr.onload = function() {
+      is(fr.result, data, "Data content matches");
+      if (++count > 2) {
+        next();
+        return;
+      }
+
+      xhr.open("POST", "temporaryFileBlob.sjs");
+      xhr.responseType = 'blob';
+      xhr.send({toString: function() { return data; }});
+    }
+  }
+}
+
+function test_worker_generic(test) {
+  var w = new Worker('worker_temporaryFileBlob.js');
+  w.onmessage = function(e) {
+    if (e.data.type == 'info') {
+      info(e.data.msg);
+    } else if (e.data.type == 'check') {
+      ok(e.data.what, e.data.msg);
+    } else if (e.data.type == 'finish') {
+      next();
+    } else {
+      ok(false, 'Something wrong happened');
+    }
+  }
+
+  w.postMessage(test);
+}
+
+function test_worker() {
+  info("XHR in workers");
+  test_worker_generic('simple');
+}
+
+function test_worker_abort() {
+  info("XHR in workers");
+  test_worker_generic('abort');
+}
+
+function test_worker_reuse() {
+  info("XHR in workers");
+  test_worker_generic('reuse');
+}
--- a/dom/xhr/tests/mochitest.ini
+++ b/dom/xhr/tests/mochitest.ini
@@ -1,11 +1,12 @@
 [DEFAULT]
 support-files =
   echo.sjs
+  temporaryFileBlob.sjs
   file_html_in_xhr.html
   file_html_in_xhr.sjs
   file_html_in_xhr2.html
   file_html_in_xhr3.html
   file_XHRDocURI.text
   file_XHRDocURI.text^headers^
   file_XHRDocURI.xml
   file_XHRDocURI.xml^headers^
@@ -51,16 +52,18 @@ support-files =
   xhr_implicit_cancel_worker.js
   relativeLoad_import.js
   relativeLoad_worker.js
   relativeLoad_worker2.js
   responseIdentical.sjs
   subdir/relativeLoad_sub_worker.js
   subdir/relativeLoad_sub_worker2.js
   subdir/relativeLoad_sub_import.js
+  common_temporaryFileBlob.js
+  worker_temporaryFileBlob.js
 
 [test_xhr_overridemimetype_throws_on_invalid_state.html]
 skip-if = buildapp == 'b2g' # Requires webgl support
 [test_html_in_xhr.html]
 [test_sync_xhr_timer.xhtml]
 skip-if = toolkit == 'android'
 [test_xhr_abort_after_load.html]
 skip-if = toolkit == 'android'
@@ -96,8 +99,9 @@ skip-if = buildapp == 'b2g'
 [test_worker_xhr_parameters.html]
 skip-if = buildapp == 'b2g'
 [test_worker_xhr_responseURL.html]
 [test_worker_xhr_system.html]
 [test_worker_xhr_timeout.html]
 skip-if = (os == "win") || (os == "mac") || toolkit == 'android' #bug 798220
 [test_relativeLoad.html]
 skip-if = buildapp == 'b2g' # b2g(Failed to load script: relativeLoad_import.js) b2g-debug(Failed to load script: relativeLoad_import.js) b2g-desktop(Failed to load script: relativeLoad_import.js)
+[test_temporaryFileBlob.html]
new file mode 100644
--- /dev/null
+++ b/dom/xhr/tests/temporaryFileBlob.sjs
@@ -0,0 +1,33 @@
+const CC = Components.Constructor;
+
+const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
+                             "nsIBinaryInputStream",
+                             "setInputStream");
+const BinaryOutputStream = CC("@mozilla.org/binaryoutputstream;1",
+                              "nsIBinaryOutputStream",
+                              "setOutputStream");
+const Timer = CC("@mozilla.org/timer;1",
+                 "nsITimer",
+                 "initWithCallback");
+
+function handleRequest(request, response) {
+  var bodyStream = new BinaryInputStream(request.bodyInputStream);
+  var bodyBytes = [];
+  while ((bodyAvail = bodyStream.available()) > 0)
+    Array.prototype.push.apply(bodyBytes, bodyStream.readByteArray(bodyAvail));
+
+  var bos = new BinaryOutputStream(response.bodyOutputStream);
+
+  response.processAsync();
+
+  var part = bodyBytes.splice(0, 256);
+  bos.writeByteArray(part, part.length);
+
+  response.timer1 = new Timer(function(timer) {
+    bos.writeByteArray(bodyBytes, bodyBytes.length);
+  }, 1000, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
+
+  response.timer2 = new Timer(function(timer) {
+    response.finish();
+  }, 2000, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
+}
new file mode 100644
--- /dev/null
+++ b/dom/xhr/tests/test_temporaryFileBlob.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1202006</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="common_temporaryFileBlob.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+  <script type="application/javascript">
+
+var tests = [
+  // from common_temporaryFileBlob.js:
+  test_simple,
+  test_reuse,
+  test_abort,
+
+  test_worker,
+  test_worker_reuse,
+  test_worker_abort,
+];
+
+function next() {
+  if (!tests.length) {
+    SimpleTest.finish();
+    return;
+  }
+
+  var test = tests.shift();
+  test();
+}
+
+SpecialPowers.pushPrefEnv({ "set" : [[ "dom.blob.memoryToTemporaryFile", 1 ]] },
+                          next);
+SimpleTest.waitForExplicitFinish();
+
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/xhr/tests/worker_temporaryFileBlob.js
@@ -0,0 +1,29 @@
+importScripts('common_temporaryFileBlob.js');
+
+function info(msg) {
+  postMessage({type: 'info', msg: msg});
+}
+
+function ok(a, msg) {
+  postMessage({type: 'check', what: !!a, msg: msg});
+}
+
+function is(a, b, msg) {
+  ok(a === b, msg);
+}
+
+function next() {
+  postMessage({type: 'finish'});
+}
+
+onmessage = function(e) {
+  if (e.data == 'simple') {
+    test_simple();
+  } else if (e.data == 'abort') {
+    test_abort();
+  } else if (e.data == 'reuse') {
+    test_reuse();
+  } else {
+    ok(false, 'Something wrong happened');
+  }
+}
--- a/dom/xml/XMLDocument.cpp
+++ b/dom/xml/XMLDocument.cpp
@@ -261,66 +261,47 @@ XMLDocument::ResetToURI(nsIURI *aURI, ns
     StopDocumentLoad();
     mChannel->Cancel(NS_BINDING_ABORTED);
     mChannelIsPending = false;
   }
 
   nsDocument::ResetToURI(aURI, aLoadGroup, aPrincipal);
 }
 
-NS_IMETHODIMP
-XMLDocument::GetAsync(bool *aAsync)
-{
-  NS_ENSURE_ARG_POINTER(aAsync);
-  *aAsync = mAsync;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-XMLDocument::SetAsync(bool aAsync)
-{
-  mAsync = aAsync;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-XMLDocument::Load(const nsAString& aUrl, bool *aReturn)
-{
-  ErrorResult rv;
-  *aReturn = Load(aUrl, rv);
-  return rv.StealNSResult();
-}
-
 bool
 XMLDocument::Load(const nsAString& aUrl, ErrorResult& aRv)
 {
   bool hasHadScriptObject = true;
   nsIScriptGlobalObject* scriptObject =
     GetScriptHandlingObject(hasHadScriptObject);
   if (!scriptObject && hasHadScriptObject) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return false;
   }
 
-  WarnOnceAbout(nsIDocument::eUseOfDOM3LoadMethod);
-
   nsCOMPtr<nsIDocument> callingDoc = GetEntryDocument();
   nsCOMPtr<nsIPrincipal> principal = NodePrincipal();
 
   // The callingDoc's Principal and doc's Principal should be the same
   if (callingDoc && (callingDoc->NodePrincipal() != principal)) {
     nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
                                     NS_LITERAL_CSTRING("DOM"),
                                     callingDoc,
                                     nsContentUtils::eDOM_PROPERTIES,
                                     "XMLDocumentLoadPrincipalMismatch");
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return false;
   }
 
+  if (nsContentUtils::IsCallerChrome()) {
+    WarnOnceAbout(nsIDocument::eChromeUseOfDOM3LoadMethod);
+  } else {
+    WarnOnceAbout(nsIDocument::eUseOfDOM3LoadMethod);
+  } 
+
   nsIURI *baseURI = mDocumentURI;
   nsAutoCString charset;
 
   if (callingDoc) {
     baseURI = callingDoc->GetDocBaseURI();
     charset = callingDoc->GetDocumentCharacterSet();
   }
 
--- a/dom/xml/XMLDocument.h
+++ b/dom/xml/XMLDocument.h
@@ -13,17 +13,18 @@
 #include "nsIScriptContext.h"
 
 class nsIURI;
 class nsIChannel;
 
 namespace mozilla {
 namespace dom {
 
-class XMLDocument : public nsDocument
+class XMLDocument : public nsDocument,
+                    public nsIDOMXMLDocument
 {
 public:
   explicit XMLDocument(const char* aContentType = "application/xml");
 
   NS_DECL_ISUPPORTS_INHERITED
 
   virtual void Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup) override;
   virtual void ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,
@@ -56,17 +57,20 @@ public:
 
 
   // WebIDL API
   bool Load(const nsAString& aUrl, mozilla::ErrorResult& aRv);
   bool Async() const
   {
     return mAsync;
   }
-  // The XPCOM SetAsync is ok for us
+  void SetAsync(bool aAsync)
+  {
+    mAsync = aAsync;
+  }
 
   // .location is [Unforgeable], so we have to make it clear that the
   // nsIDocument version applies to us (it's shadowed by the XPCOM thing on
   // nsDocument).
   using nsIDocument::GetLocation;
   // But then we need to also pull in the nsDocument XPCOM version
   // because nsXULDocument tries to forward to it.
   using nsDocument::GetLocation;
--- a/dom/xul/templates/nsXULTemplateBuilder.cpp
+++ b/dom/xul/templates/nsXULTemplateBuilder.cpp
@@ -19,17 +19,16 @@
 
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsCRT.h"
 #include "nsIContent.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMNode.h"
 #include "nsIDOMDocument.h"
-#include "nsIDOMXMLDocument.h"
 #include "nsIDOMXULElement.h"
 #include "nsIDocument.h"
 #include "nsBindingManager.h"
 #include "nsIDOMNodeList.h"
 #include "nsIObserverService.h"
 #include "nsIRDFCompositeDataSource.h"
 #include "nsIRDFInferDataSource.h"
 #include "nsIRDFContainerUtils.h"
--- a/extensions/cookie/nsPermission.cpp
+++ b/extensions/cookie/nsPermission.cpp
@@ -22,16 +22,34 @@ nsPermission::nsPermission(nsIPrincipal*
  : mPrincipal(aPrincipal)
  , mType(aType)
  , mCapability(aCapability)
  , mExpireType(aExpireType)
  , mExpireTime(aExpireTime)
 {
 }
 
+already_AddRefed<nsPermission>
+nsPermission::Create(nsIPrincipal* aPrincipal,
+                     const nsACString &aType,
+                     uint32_t aCapability,
+                     uint32_t aExpireType,
+                     int64_t aExpireTime)
+{
+  NS_ENSURE_TRUE(aPrincipal, nullptr);
+  nsCOMPtr<nsIPrincipal> principal =
+    mozilla::BasePrincipal::Cast(aPrincipal)->CloneStrippingUserContextIdAndFirstPartyDomain();
+
+  NS_ENSURE_TRUE(principal, nullptr);
+
+  RefPtr<nsPermission> permission =
+    new nsPermission(principal, aType, aCapability, aExpireType, aExpireTime);
+  return permission.forget();
+}
+
 NS_IMETHODIMP
 nsPermission::GetPrincipal(nsIPrincipal** aPrincipal)
 {
   nsCOMPtr<nsIPrincipal> copy = mPrincipal;
   copy.forget(aPrincipal);
   return NS_OK;
 }
 
@@ -66,38 +84,46 @@ nsPermission::GetExpireTime(int64_t *aEx
 NS_IMETHODIMP
 nsPermission::Matches(nsIPrincipal* aPrincipal, bool aExactHost, bool* aMatches)
 {
   NS_ENSURE_ARG_POINTER(aPrincipal);
   NS_ENSURE_ARG_POINTER(aMatches);
 
   *aMatches = false;
 
+  nsCOMPtr<nsIPrincipal> principal =
+    mozilla::BasePrincipal::Cast(aPrincipal)->CloneStrippingUserContextIdAndFirstPartyDomain();
+
+  if (!principal) {
+    *aMatches = false;
+    return NS_OK;
+  }
+
   // If the principals are equal, then they match.
-  if (mPrincipal->Equals(aPrincipal)) {
+  if (mPrincipal->Equals(principal)) {
     *aMatches = true;
     return NS_OK;
   }
 
   // If we are matching with an exact host, we're done now - the permissions don't match
   // otherwise, we need to start comparing subdomains!
   if (aExactHost) {
       return NS_OK;
   }
 
   // Compare their OriginAttributes
-  const mozilla::PrincipalOriginAttributes& theirAttrs = mozilla::BasePrincipal::Cast(aPrincipal)->OriginAttributesRef();
+  const mozilla::PrincipalOriginAttributes& theirAttrs = mozilla::BasePrincipal::Cast(principal)->OriginAttributesRef();
   const mozilla::PrincipalOriginAttributes& ourAttrs = mozilla::BasePrincipal::Cast(mPrincipal)->OriginAttributesRef();
 
   if (theirAttrs != ourAttrs) {
       return NS_OK;
   }
 
   nsCOMPtr<nsIURI> theirURI;
-  nsresult rv = aPrincipal->GetURI(getter_AddRefs(theirURI));
+  nsresult rv = principal->GetURI(getter_AddRefs(theirURI));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIURI> ourURI;
   rv = mPrincipal->GetURI(getter_AddRefs(ourURI));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Compare schemes
   nsAutoCString theirScheme;
--- a/extensions/cookie/nsPermission.h
+++ b/extensions/cookie/nsPermission.h
@@ -13,23 +13,29 @@
 
 class nsPermission : public nsIPermission
 {
 public:
   // nsISupports
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPERMISSION
 
+  static already_AddRefed<nsPermission> Create(nsIPrincipal* aPrincipal,
+                                               const nsACString &aType,
+                                               uint32_t aCapability,
+                                               uint32_t aExpireType,
+                                               int64_t aExpireTime);
+
+protected:
   nsPermission(nsIPrincipal* aPrincipal,
                const nsACString &aType,
                uint32_t aCapability,
                uint32_t aExpireType,
                int64_t aExpireTime);
 
-protected:
   virtual ~nsPermission() {};
 
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsCString mType;
   uint32_t  mCapability;
   uint32_t  mExpireType;
   int64_t   mExpireTime;
 };
--- a/extensions/cookie/nsPermissionManager.cpp
+++ b/extensions/cookie/nsPermissionManager.cpp
@@ -117,45 +117,35 @@ GetOriginFromPrincipal(nsIPrincipal* aPr
   if (!attrs.PopulateFromSuffix(suffix)) {
     return NS_ERROR_FAILURE;
   }
 
   // mPrivateBrowsingId must be set to false because PermissionManager is not supposed to have
   // any knowledge of private browsing. Allowing it to be true changes the suffix being hashed.
   attrs.mPrivateBrowsingId = 0;
 
-  // TODO: Bug 1302047 - Ignore userContextId and firstPartyDomain when matching permissions.
-
-  // set to default to disable user context isolation for permissions
-  attrs.mUserContextId = nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID;
-
-  // set to default to disable firstParty isolation for permissions.
-  attrs.mFirstPartyDomain.Truncate();
+  // Disable userContext and firstParty isolation for permissions.
+  attrs.StripUserContextIdAndFirstPartyDomain();
 
   attrs.CreateSuffix(suffix);
   aOrigin.Append(suffix);
   return NS_OK;
 }
 
 nsresult
 GetPrincipalFromOrigin(const nsACString& aOrigin, nsIPrincipal** aPrincipal)
 {
   nsAutoCString originNoSuffix;
   mozilla::PrincipalOriginAttributes attrs;
   if (!attrs.PopulateFromOrigin(aOrigin, originNoSuffix)) {
     return NS_ERROR_FAILURE;
   }
 
-  // TODO: Bug 1302047 - Ignore userContextId and firstPartyDomain when matching permissions.
-
-  // set to default to disable user context isolation for permissions
-  attrs.mUserContextId = nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID;
-
-  // set to default to disable firstParty isolation for permissions.
-  attrs.mFirstPartyDomain.Truncate();
+  // Disable userContext and firstParty isolation for permissions.
+  attrs.StripUserContextIdAndFirstPartyDomain();
 
   nsCOMPtr<nsIURI> uri;
   nsresult rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIPrincipal> principal = mozilla::BasePrincipal::CreateCodebasePrincipal(uri, attrs);
   principal.forget(aPrincipal);
   return NS_OK;
@@ -2060,21 +2050,24 @@ nsPermissionManager::GetPermissionObject
     return NS_OK;
   }
 
   nsCOMPtr<nsIPrincipal> principal;
   nsresult rv = GetPrincipalFromOrigin(entry->GetKey()->mOrigin, getter_AddRefs(principal));
   NS_ENSURE_SUCCESS(rv, rv);
 
   PermissionEntry& perm = entry->GetPermissions()[idx];
-  nsCOMPtr<nsIPermission> r = new nsPermission(principal,
-                                               mTypeArray.ElementAt(perm.mType),
-                                               perm.mPermission,
-                                               perm.mExpireType,
-                                               perm.mExpireTime);
+  nsCOMPtr<nsIPermission> r = nsPermission::Create(principal,
+                                                   mTypeArray.ElementAt(perm.mType),
+                                                   perm.mPermission,
+                                                   perm.mExpireType,
+                                                   perm.mExpireTime);
+  if (NS_WARN_IF(!r)) {
+    return NS_ERROR_FAILURE;
+  }
   r.forget(aResult);
   return NS_OK;
 }
 
 nsresult
 nsPermissionManager::CommonTestPermission(nsIPrincipal* aPrincipal,
                                           const char *aType,
                                           uint32_t   *aPermission,
@@ -2203,23 +2196,18 @@ nsPermissionManager::GetPermissionHashKe
     if (NS_FAILED(rv)) {
       return nullptr;
     }
 
     // Copy the attributes over
     mozilla::PrincipalOriginAttributes attrs =
       mozilla::BasePrincipal::Cast(aPrincipal)->OriginAttributesRef();
 
-    // TODO: Bug 1302047 - Ignore userContextId and firstPartyDomain when matching permissions.
-
-    // ensure that the user context isolation is disabled
-    attrs.mUserContextId = nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID;
-
-    // ensure firstPartyIsolation is disabled.
-    attrs.mFirstPartyDomain.Truncate();
+    // Disable userContext and firstParty isolation for permissions.
+    attrs.StripUserContextIdAndFirstPartyDomain();
 
     nsCOMPtr<nsIPrincipal> principal =
       mozilla::BasePrincipal::CreateCodebasePrincipal(newURI, attrs);
 
     return GetPermissionHashKey(principal, aType, aExactHostMatch);
   }
 
   // No entry, really...
@@ -2243,22 +2231,26 @@ NS_IMETHODIMP nsPermissionManager::GetEn
 
       nsCOMPtr<nsIPrincipal> principal;
       nsresult rv = GetPrincipalFromOrigin(entry->GetKey()->mOrigin,
                                            getter_AddRefs(principal));
       if (NS_FAILED(rv)) {
         continue;
       }
 
-      array.AppendObject(
-        new nsPermission(principal,
-                         mTypeArray.ElementAt(permEntry.mType),
-                         permEntry.mPermission,
-                         permEntry.mExpireType,
-                         permEntry.mExpireTime));
+      nsCOMPtr<nsIPermission> permission =
+        nsPermission::Create(principal,
+                             mTypeArray.ElementAt(permEntry.mType),
+                             permEntry.mPermission,
+                             permEntry.mExpireType,
+                             permEntry.mExpireTime);
+      if (NS_WARN_IF(!permission)) {
+        continue;
+      }
+      array.AppendObject(permission);
     }
   }
 
   return NS_NewArrayEnumerator(aEnum, array);
 }
 
 NS_IMETHODIMP nsPermissionManager::GetAllForURI(nsIURI* aURI, nsISimpleEnumerator **aEnum)
 {
@@ -2273,22 +2265,26 @@ NS_IMETHODIMP nsPermissionManager::GetAl
 
   if (entry) {
     for (const auto& permEntry : entry->GetPermissions()) {
       // Only return custom permissions
       if (permEntry.mPermission == nsIPermissionManager::UNKNOWN_ACTION) {
         continue;
       }
 
-      array.AppendObject(
-        new nsPermission(principal,
-                         mTypeArray.ElementAt(permEntry.mType),
-                         permEntry.mPermission,
-                         permEntry.mExpireType,
-                         permEntry.mExpireTime));
+      nsCOMPtr<nsIPermission> permission =
+        nsPermission::Create(principal,
+                             mTypeArray.ElementAt(permEntry.mType),
+                             permEntry.mPermission,
+                             permEntry.mExpireType,
+                             permEntry.mExpireTime);
+      if (NS_WARN_IF(!permission)) {
+        continue;
+      }
+      array.AppendObject(permission);
     }
   }
 
   return NS_NewArrayEnumerator(aEnum, array);
 }
 
 NS_IMETHODIMP nsPermissionManager::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *someData)
 {
@@ -2323,22 +2319,26 @@ nsPermissionManager::RemoveAllModifiedSi
 
       nsCOMPtr<nsIPrincipal> principal;
       nsresult rv = GetPrincipalFromOrigin(entry->GetKey()->mOrigin,
                                            getter_AddRefs(principal));
       if (NS_FAILED(rv)) {
         continue;
       }
 
-      array.AppendObject(
-        new nsPermission(principal,
-                         mTypeArray.ElementAt(permEntry.mType),
-                         permEntry.mPermission,
-                         permEntry.mExpireType,
-                         permEntry.mExpireTime));
+      nsCOMPtr<nsIPermission> permission =
+        nsPermission::Create(principal,
+                             mTypeArray.ElementAt(permEntry.mType),
+                             permEntry.mPermission,
+                             permEntry.mExpireType,
+                             permEntry.mExpireTime);
+      if (NS_WARN_IF(!permission)) {
+        continue;
+      }
+      array.AppendObject(permission);
     }
   }
 
   for (int32_t i = 0; i<array.Count(); ++i) {
     nsCOMPtr<nsIPrincipal> principal;
     nsAutoCString type;
 
     nsresult rv = array[i]->GetPrincipal(getter_AddRefs(principal));
@@ -2395,22 +2395,26 @@ nsPermissionManager::RemovePermissionsWi
       continue;
     }
 
     if (!aPattern.Matches(mozilla::BasePrincipal::Cast(principal)->OriginAttributesRef())) {
       continue;
     }
 
     for (const auto& permEntry : entry->GetPermissions()) {
-      permissions.AppendObject(
-        new nsPermission(principal,
-                         mTypeArray.ElementAt(permEntry.mType),
-                         permEntry.mPermission,
-                         permEntry.mExpireType,
-                         permEntry.mExpireTime));
+      nsCOMPtr<nsIPermission> permission =
+        nsPermission::Create(principal,
+                             mTypeArray.ElementAt(permEntry.mType),
+                             permEntry.mPermission,
+                             permEntry.mExpireType,
+                             permEntry.mExpireTime);
+      if (NS_WARN_IF(!permission)) {
+        continue;
+      }
+      permissions.AppendObject(permission);
     }
   }
 
   for (int32_t i = 0; i < permissions.Count(); ++i) {
     nsCOMPtr<nsIPrincipal> principal;
     nsAutoCString type;
 
     permissions[i]->GetPrincipal(getter_AddRefs(principal));
@@ -2531,18 +2535,18 @@ void
 nsPermissionManager::NotifyObserversWithPermission(nsIPrincipal*     aPrincipal,
                                                    const nsCString  &aType,
                                                    uint32_t          aPermission,
                                                    uint32_t          aExpireType,
                                                    int64_t           aExpireTime,
                                                    const char16_t  *aData)
 {
   nsCOMPtr<nsIPermission> permission =
-    new nsPermission(aPrincipal, aType, aPermission,
-                     aExpireType, aExpireTime);
+    nsPermission::Create(aPrincipal, aType, aPermission,
+                         aExpireType, aExpireTime);
   if (permission)
     NotifyObservers(permission, aData);
 }
 
 // notify observers that the permission list changed. there are four possible
 // values for aData:
 // "deleted" means a permission was deleted. aPermission is the deleted permission.
 // "added"   means a permission was added. aPermission is the added permission.
--- a/extensions/cookie/test/unit/test_permmanager_matches.js
+++ b/extensions/cookie/test/unit/test_permmanager_matches.js
@@ -72,62 +72,112 @@ function run_test() {
   attrs = {appId: 2000, inIsolatedMozBrowser: true};
   let uri0_2000_y = secMan.createCodebasePrincipal(uri0, attrs);
   let uri1_2000_y = secMan.createCodebasePrincipal(uri1, attrs);
   let uri2_2000_y = secMan.createCodebasePrincipal(uri2, attrs);
   let uri3_2000_y = secMan.createCodebasePrincipal(uri3, attrs);
   let uri4_2000_y = secMan.createCodebasePrincipal(uri4, attrs);
   let uri5_2000_y = secMan.createCodebasePrincipal(uri5, attrs);
 
+  attrs = {userContextId: 1};
+  let uri0_1 = secMan.createCodebasePrincipal(uri0, attrs);
+  let uri1_1 = secMan.createCodebasePrincipal(uri1, attrs);
+  let uri2_1 = secMan.createCodebasePrincipal(uri2, attrs);
+  let uri3_1 = secMan.createCodebasePrincipal(uri3, attrs);
+  let uri4_1 = secMan.createCodebasePrincipal(uri4, attrs);
+  let uri5_1 = secMan.createCodebasePrincipal(uri5, attrs);
+
+  attrs = {firstPartyDomain: "cnn.com"};
+  let uri0_cnn = secMan.createCodebasePrincipal(uri0, attrs);
+  let uri1_cnn = secMan.createCodebasePrincipal(uri1, attrs);
+  let uri2_cnn = secMan.createCodebasePrincipal(uri2, attrs);
+  let uri3_cnn = secMan.createCodebasePrincipal(uri3, attrs);
+  let uri4_cnn = secMan.createCodebasePrincipal(uri4, attrs);
+  let uri5_cnn = secMan.createCodebasePrincipal(uri5, attrs);
+
   pm.addFromPrincipal(uri0_n_n, "test/matches", pm.ALLOW_ACTION);
   let perm_n_n = pm.getPermissionObject(uri0_n_n, "test/matches", true);
   pm.addFromPrincipal(uri0_1000_n, "test/matches", pm.ALLOW_ACTION);
   let perm_1000_n = pm.getPermissionObject(uri0_1000_n, "test/matches", true);
   pm.addFromPrincipal(uri0_1000_y, "test/matches", pm.ALLOW_ACTION);
   let perm_1000_y = pm.getPermissionObject(uri0_1000_y, "test/matches", true);
   pm.addFromPrincipal(uri0_2000_n, "test/matches", pm.ALLOW_ACTION);
   let perm_2000_n = pm.getPermissionObject(uri0_2000_n, "test/matches", true);
   pm.addFromPrincipal(uri0_2000_y, "test/matches", pm.ALLOW_ACTION);
   let perm_2000_y = pm.getPermissionObject(uri0_2000_y, "test/matches", true);
+  pm.addFromPrincipal(uri0_1, "test/matches", pm.ALLOW_ACTION);
+  let perm_1 = pm.getPermissionObject(uri0_n_n, "test/matches", true);
+  pm.addFromPrincipal(uri0_cnn, "test/matches", pm.ALLOW_ACTION);
+  let perm_cnn = pm.getPermissionObject(uri0_n_n, "test/matches", true);
 
-  matches_always(perm_n_n, [uri0_n_n]);
-  matches_weak(perm_n_n, [uri1_n_n]);
+  matches_always(perm_n_n, [uri0_n_n, uri0_1, uri0_cnn]);
+  matches_weak(perm_n_n, [uri1_n_n, uri1_1, uri1_cnn]);
   matches_never(perm_n_n, [uri2_n_n, uri3_n_n, uri4_n_n, uri5_n_n,
                            uri0_1000_n, uri1_1000_n, uri2_1000_n, uri3_1000_n, uri4_1000_n, uri5_1000_n,
                            uri0_1000_y, uri1_1000_y, uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y,
                            uri0_2000_n, uri1_2000_n, uri2_2000_n, uri3_2000_n, uri4_2000_n, uri5_2000_n,
-                           uri0_2000_y, uri1_2000_y, uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y]);
+                           uri0_2000_y, uri1_2000_y, uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y,
+                           uri2_1, uri3_1, uri4_1, uri5_1,
+                           uri2_cnn, uri3_cnn, uri4_cnn, uri5_cnn]);
 
   matches_always(perm_1000_n, [uri0_1000_n]);
   matches_weak(perm_1000_n, [uri1_1000_n]);
   matches_never(perm_1000_n, [uri2_1000_n, uri3_1000_n, uri4_1000_n, uri5_1000_n,
                               uri0_n_n, uri1_n_n, uri2_n_n, uri3_n_n, uri4_n_n, uri5_n_n,
                               uri0_1000_y, uri1_1000_y, uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y,
                               uri0_2000_n, uri1_2000_n, uri2_2000_n, uri3_2000_n, uri4_2000_n, uri5_2000_n,
-                              uri0_2000_y, uri1_2000_y, uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y]);
+                              uri0_2000_y, uri1_2000_y, uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y,
+                              uri0_1, uri1_1, uri2_1, uri3_1, uri4_1, uri5_1,
+                              uri0_cnn, uri1_cnn, uri2_cnn, uri3_cnn, uri4_cnn, uri5_cnn]);
 
   matches_always(perm_1000_y, [uri0_1000_y]);
   matches_weak(perm_1000_y, [uri1_1000_y]);
   matches_never(perm_1000_y, [uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y,
                               uri0_n_n, uri1_n_n, uri2_n_n, uri3_n_n, uri4_n_n, uri5_n_n,
                               uri0_1000_n, uri1_1000_n, uri2_1000_n, uri3_1000_n, uri4_1000_n, uri5_1000_n,
                               uri0_2000_n, uri1_2000_n, uri2_2000_n, uri3_2000_n, uri4_2000_n, uri5_2000_n,
-                              uri0_2000_y, uri1_2000_y, uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y]);
+                              uri0_2000_y, uri1_2000_y, uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y,
+                              uri0_1, uri1_1, uri2_1, uri3_1, uri4_1, uri5_1,
+                              uri0_cnn, uri1_cnn, uri2_cnn, uri3_cnn, uri4_cnn, uri5_cnn]);
 
   matches_always(perm_2000_n, [uri0_2000_n]);
   matches_weak(perm_2000_n, [uri1_2000_n]);
   matches_never(perm_2000_n, [uri2_2000_n, uri3_2000_n, uri4_2000_n, uri5_2000_n,
                               uri0_n_n, uri1_n_n, uri2_n_n, uri3_n_n, uri4_n_n, uri5_n_n,
                               uri0_2000_y, uri1_2000_y, uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y,
                               uri0_1000_n, uri1_1000_n, uri2_1000_n, uri3_1000_n, uri4_1000_n, uri5_1000_n,
-                              uri0_1000_y, uri1_1000_y, uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y]);
+                              uri0_1000_y, uri1_1000_y, uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y,
+                              uri0_1, uri1_1, uri2_1, uri3_1, uri4_1, uri5_1,
+                              uri0_cnn, uri1_cnn, uri2_cnn, uri3_cnn, uri4_cnn, uri5_cnn]);
 
   matches_always(perm_2000_y, [uri0_2000_y]);
   matches_weak(perm_2000_y, [uri1_2000_y]);
   matches_never(perm_2000_y, [uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y,
                               uri0_n_n, uri1_n_n, uri2_n_n, uri3_n_n, uri4_n_n, uri5_n_n,
                               uri0_2000_n, uri1_2000_n, uri2_2000_n, uri3_2000_n, uri4_2000_n, uri5_2000_n,
                               uri0_1000_n, uri1_1000_n, uri2_1000_n, uri3_1000_n, uri4_1000_n, uri5_1000_n,
-                              uri0_1000_y, uri1_1000_y, uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y]);
+                              uri0_1000_y, uri1_1000_y, uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y,
+                              uri0_1, uri1_1, uri2_1, uri3_1, uri4_1, uri5_1,
+                              uri0_cnn, uri1_cnn, uri2_cnn, uri3_cnn, uri4_cnn, uri5_cnn]);
+
+  matches_always(perm_1, [uri0_n_n, uri0_1, uri0_cnn]);
+  matches_weak(perm_1, [uri1_n_n, uri1_1, uri1_cnn]);
+  matches_never(perm_1, [uri2_n_n, uri3_n_n, uri4_n_n, uri5_n_n,
+                         uri0_1000_n, uri1_1000_n, uri2_1000_n, uri3_1000_n, uri4_1000_n, uri5_1000_n,
+                         uri0_1000_y, uri1_1000_y, uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y,
+                         uri0_2000_n, uri1_2000_n, uri2_2000_n, uri3_2000_n, uri4_2000_n, uri5_2000_n,
+                         uri0_2000_y, uri1_2000_y, uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y,
+                         uri2_1, uri3_1, uri4_1, uri5_1,
+                         uri2_cnn, uri3_cnn, uri4_cnn, uri5_cnn]);
+
+  matches_always(perm_cnn, [uri0_n_n, uri0_1, uri0_cnn]);
+  matches_weak(perm_cnn, [uri1_n_n, uri1_1, uri1_cnn]);
+  matches_never(perm_cnn, [uri2_n_n, uri3_n_n, uri4_n_n, uri5_n_n,
+                           uri0_1000_n, uri1_1000_n, uri2_1000_n, uri3_1000_n, uri4_1000_n, uri5_1000_n,
+                           uri0_1000_y, uri1_1000_y, uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y,
+                           uri0_2000_n, uri1_2000_n, uri2_2000_n, uri3_2000_n, uri4_2000_n, uri5_2000_n,
+                           uri0_2000_y, uri1_2000_y, uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y,
+                           uri2_1, uri3_1, uri4_1, uri5_1,
+                           uri2_cnn, uri3_cnn, uri4_cnn, uri5_cnn]);
 
   // Clean up!
   pm.removeAll();
 }
--- a/gfx/2d/DrawTargetSkia.cpp
+++ b/gfx/2d/DrawTargetSkia.cpp
@@ -175,38 +175,38 @@ VerifyRGBXCorners(uint8_t* aData, const 
   }
 
   int height = aSize.height;
   int width = aSize.width;
   const int pixelSize = 4;
   const int strideDiff = aStride - (width * pixelSize);
   MOZ_ASSERT(width * pixelSize <= aStride);
 
-  const int topLeft = kARGBAlphaOffset;
-  const int topRight = width * pixelSize + kARGBAlphaOffset - pixelSize;
-  const int bottomRight = aStride * height - strideDiff + kARGBAlphaOffset - pixelSize;
-  const int bottomLeft = aStride * height - aStride + kARGBAlphaOffset;
+  const int topLeft = 0;
+  const int topRight = width * pixelSize - pixelSize;
+  const int bottomRight = aStride * height - strideDiff - pixelSize;
+  const int bottomLeft = aStride * height - aStride;
 
   // Lastly the center pixel
   int middleRowHeight = height / 2;
   int middleRowWidth = (width / 2) * pixelSize;
-  const int middle = aStride * middleRowHeight + middleRowWidth + kARGBAlphaOffset;
+  const int middle = aStride * middleRowHeight + middleRowWidth;
 
   const int offsets[] = { topLeft, topRight, bottomRight, bottomLeft, middle };
   for (size_t i = 0; i < MOZ_ARRAY_LENGTH(offsets); i++) {
     int offset = offsets[i];
-    if (aData[offset] != 0xFF) {
+    if (aData[offset + kARGBAlphaOffset] != 0xFF) {
         int row = offset / aStride;
         int column = (offset % aStride) / pixelSize;
         gfxCriticalError() << "RGBX corner pixel at (" << column << "," << row << ") in "
                            << width << "x" << height << " surface is not opaque: "
-                           << int(aData[column]) << ","
-                           << int(aData[column+1]) << ","
-                           << int(aData[column+2]) << ","
-                           << int(aData[column+3]);
+                           << int(aData[offset]) << ","
+                           << int(aData[offset+1]) << ","
+                           << int(aData[offset+2]) << ","
+                           << int(aData[offset+3]);
     }
   }
 
   return true;
 }
 #endif
 
 static SkBitmap
--- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
+++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
@@ -2549,16 +2549,20 @@ void Renderer11::releaseDeviceResources(
     SafeRelease(mSyncQuery);
 }
 
 // set notify to true to broadcast a message to all contexts of the device loss
 bool Renderer11::testDeviceLost()
 {
     bool isLost = false;
 
+    if (!mDevice) {
+        return true;
+    }
+
     // GetRemovedReason is used to test if the device is removed
     HRESULT result = mDevice->GetDeviceRemovedReason();
     isLost = d3d11::isDeviceLostError(result);
 
     if (isLost)
     {
         ERR("The D3D11 device was removed: 0x%08X", result);
     }
--- a/gfx/ots/README.mozilla
+++ b/gfx/ots/README.mozilla
@@ -1,11 +1,11 @@
 This is the Sanitiser for OpenType project, from http://code.google.com/p/ots/.
 
 Our reference repository is https://github.com/khaledhosny/ots/.
 
-Current revision: 8d70cffebbfa58f67a5c3ed0e9bc84dccdbc5bc0
+Current revision: ba8417620956a920ed1f05a2f666fb6317fb10cb
 
 Upstream files included: LICENSE, src/, include/
 
 Additional files: README.mozilla, src/moz.build
 
 Additional patch: ots-visibility.patch (bug 711079).
--- a/gfx/ots/include/opentype-sanitiser.h
+++ b/gfx/ots/include/opentype-sanitiser.h
@@ -39,16 +39,18 @@ typedef unsigned __int64 uint64_t;
 #define ntohs(x) _byteswap_ushort (x)
 #define htonl(x) _byteswap_ulong (x)
 #define htons(x) _byteswap_ushort (x)
 #else
 #include <arpa/inet.h>
 #include <stdint.h>
 #endif
 
+#include <sys/types.h>
+
 #include <algorithm>
 #include <cassert>
 #include <cstddef>
 #include <cstring>
 
 #define OTS_TAG(c1,c2,c3,c4) ((uint32_t)((((uint8_t)(c1))<<24)|(((uint8_t)(c2))<<16)|(((uint8_t)(c3))<<8)|((uint8_t)(c4))))
 #define OTS_UNTAG(tag)       ((uint8_t)((tag)>>24)), ((uint8_t)((tag)>>16)), ((uint8_t)((tag)>>8)), ((uint8_t)(tag))
 
--- a/gfx/ots/src/ots.cc
+++ b/gfx/ots/src/ots.cc
@@ -473,27 +473,28 @@ bool ProcessWOFF2(ots::OpenTypeFile *hea
   if (decompressed_size == 0) {
     return OTS_FAILURE_MSG_HDR("Size of decompressed WOFF 2.0 is set to 0");
   }
   // decompressed font must be <= 30MB
   if (decompressed_size > 30 * 1024 * 1024) {
     return OTS_FAILURE_MSG_HDR("Size of decompressed WOFF 2.0 font exceeds 30MB");
   }
 
-  std::vector<uint8_t> decompressed_buffer(decompressed_size);
-  if (!woff2::ConvertWOFF2ToTTF(&decompressed_buffer[0], decompressed_size,
-                                data, length)) {
+  std::string buf(decompressed_size, 0);
+  woff2::WOFF2StringOut out(&buf);
+  if (!woff2::ConvertWOFF2ToTTF(data, length, &out)) {
     return OTS_FAILURE_MSG_HDR("Failed to convert WOFF 2.0 font to SFNT");
   }
+  const uint8_t *decompressed = reinterpret_cast<const uint8_t*>(buf.data());
 
   if (data[4] == 't' && data[5] == 't' && data[6] == 'c' && data[7] == 'f') {
-    return ProcessTTC(header, output, &decompressed_buffer[0], decompressed_size, index);
+    return ProcessTTC(header, output, decompressed, out.Size(), index);
   } else {
     ots::Font font(header);
-    return ProcessTTF(header, &font, output, &decompressed_buffer[0], decompressed_size);
+    return ProcessTTF(header, &font, output, decompressed, out.Size());
   }
 }
 
 ots::TableAction GetTableAction(ots::OpenTypeFile *header, uint32_t tag) {
   ots::TableAction action = header->context->GetTableAction(tag);
 
   if (action == ots::TABLE_ACTION_DEFAULT) {
     action = ots::TABLE_ACTION_DROP;
--- a/gfx/skia/generate_mozbuild.py
+++ b/gfx/skia/generate_mozbuild.py
@@ -424,20 +424,22 @@ def write_mozbuild(sources):
   f.write("if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':\n")
   # Windows-specific files don't get unification because of nasty headers.
   # Luckily there are not many files in this.
   write_list(f, "SOURCES", sources['win'], 4)
 
   f.write("if CONFIG['INTEL_ARCHITECTURE']:\n")
   write_sources(f, sources['intel'], 4)
 
-  f.write("elif CONFIG['CPU_ARCH'] == 'arm' and CONFIG['GNU_CC']:\n")
+  f.write("elif CONFIG['CPU_ARCH'] in ('arm', 'aarch64') and CONFIG['GNU_CC']:\n")
   write_sources(f, sources['arm'], 4)
 
-  f.write("    if CONFIG['BUILD_ARM_NEON']:\n")
+  f.write("    if CONFIG['CPU_ARCH'] == 'aarch64':\n")
+  write_sources(f, sources['neon'], 8)
+  f.write("    elif CONFIG['BUILD_ARM_NEON']:\n")
   write_list(f, 'SOURCES', sources['neon'], 8)
   write_cflags(f, sources['neon'], 'neon', "CONFIG['NEON_FLAGS']", 8)
 
   f.write("else:\n")
   write_sources(f, sources['none'], 4)
 
   f.write(footer)
 
--- a/gfx/skia/moz.build
+++ b/gfx/skia/moz.build
@@ -514,26 +514,34 @@ if CONFIG['INTEL_ARCHITECTURE']:
         'skia/src/opts/SkBitmapFilter_opts_SSE2.cpp',
         'skia/src/opts/SkBitmapProcState_opts_SSE2.cpp',
         'skia/src/opts/SkBitmapProcState_opts_SSSE3.cpp',
         'skia/src/opts/SkBlitRow_opts_SSE2.cpp',
         'skia/src/opts/SkOpts_sse2.cpp',
         'skia/src/opts/SkOpts_sse41.cpp',
         'skia/src/opts/SkOpts_ssse3.cpp',
     ]
-elif CONFIG['CPU_ARCH'] == 'arm' and CONFIG['GNU_CC']:
+elif CONFIG['CPU_ARCH'] in ('arm', 'aarch64') and CONFIG['GNU_CC']:
     UNIFIED_SOURCES += [
         'skia/src/core/SkUtilsArm.cpp',
         'skia/src/opts/SkBitmapProcState_opts_arm.cpp',
         'skia/src/opts/SkBlitMask_opts_arm.cpp',
     ]
     SOURCES += [
         'skia/src/opts/SkBlitRow_opts_arm.cpp',
     ]
-    if CONFIG['BUILD_ARM_NEON']:
+    if CONFIG['CPU_ARCH'] == 'aarch64':
+        SOURCES += [
+            'skia/src/opts/SkBitmapProcState_arm_neon.cpp',
+            'skia/src/opts/SkBitmapProcState_matrixProcs_neon.cpp',
+            'skia/src/opts/SkBlitMask_opts_arm_neon.cpp',
+            'skia/src/opts/SkBlitRow_opts_arm_neon.cpp',
+            'skia/src/opts/SkOpts_neon.cpp',
+        ]
+    elif CONFIG['BUILD_ARM_NEON']:
         SOURCES += [
             'skia/src/opts/SkBitmapProcState_arm_neon.cpp',
             'skia/src/opts/SkBitmapProcState_matrixProcs_neon.cpp',
             'skia/src/opts/SkBlitMask_opts_arm_neon.cpp',
             'skia/src/opts/SkBlitRow_opts_arm_neon.cpp',
             'skia/src/opts/SkOpts_neon.cpp',
         ]
         SOURCES['skia/src/opts/SkBitmapProcState_arm_neon.cpp'].flags += CONFIG['NEON_FLAGS']
--- a/gfx/tests/mochitest/mochitest.ini
+++ b/gfx/tests/mochitest/mochitest.ini
@@ -1,11 +1,12 @@
 [DEFAULT]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g'
 
 [test_acceleration.html]
 subsuite = gpu
 fail-if = (os == "win" && os_version == "5.1" && e10s) # Bug 1253862
 [test_bug509244.html]
 [test_bug513439.html]
+[test_font_whitelist.html]
 [test_overdraw.html]
 # Disable test until bug 1064136 is fixed
 skip-if = true
new file mode 100644
--- /dev/null
+++ b/gfx/tests/mochitest/test_font_whitelist.html
@@ -0,0 +1,85 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1121643
+-->
+<head>
+  <title>Test for Bug 1121643</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SpawnTask.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=1121643">Mozilla Bug 1121643</a>
+<span id="mono" style="font-family: monospace; font-size: 64px;">M</span>
+<span id="sans" style="font-family: sans-serif; font-size: 64px;">M</span>
+<span id="serif" style="font-family: serif; font-size: 64px;">M</span>
+<div id="content" style="display: none">
+
+</div>
+<script class="testbody" type="application/javascript;version=1.7">
+
+/** Test for Bug 1121643 **/
+
+const DOMUtils =  SpecialPowers.Cc["@mozilla.org/inspector/dom-utils;1"]
+                               .getService(SpecialPowers.Ci.inIDOMUtils);
+
+// Given an element id, returns the first font face name encountered.
+let fontUsed = id => {
+  let element = document.getElementById(id),
+      range = document.createRange();
+  range.selectNode(element);
+  return DOMUtils.getUsedFontFaces(range).item(0).CSSFamilyName;
+}
+
+// A map of the default mono, sans and serif fonts, obtained when
+// whitelisting is disabled.
+const fonts = { mono : fontUsed("mono"),
+                sans : fontUsed("sans"),
+                serif : fontUsed("serif") };
+
+// Set the font whitelist to contain none, some, or all of the
+// default mono, sans, and serif fonts. Check that the rendering
+// of our three test elements uses only fonts present in the
+// whitelist.
+let testFontWhitelist = function* (useMono, useSans, useSerif) {
+  let whitelist = [];
+  if (useMono) {
+    whitelist.push(fonts.mono);
+  }
+  if (useSans) {
+    whitelist.push(fonts.sans);
+  }
+  if (useSerif) {
+    whitelist.push(fonts.serif);
+  }
+  yield SpecialPowers.pushPrefEnv({"set": [["font.system.whitelist",
+                                            whitelist.join(", ")]]});
+  // If whitelist is empty, then whitelisting is considered disabled
+  // and all fonts are allowed.
+  info("font whitelist: " + JSON.stringify(whitelist));
+  let whitelistEmpty = whitelist.length === 0;
+  is(useMono || whitelistEmpty, fontUsed("mono") === fonts.mono,
+     "Correct mono whitelisting state; got " + fontUsed("mono") + ", requested " + fonts.mono);
+  is(useSans || whitelistEmpty, fontUsed("sans") === fonts.sans,
+     "Correct sans whitelisting state; got " + fontUsed("sans") + ", requested " + fonts.sans);
+  is(useSerif || whitelistEmpty, fontUsed("serif") === fonts.serif,
+     "Correct serif whitelisting state; got " + fontUsed("serif") + ", requested " + fonts.serif);
+}
+
+// Run tests to confirm that only whitelisting fonts are present in a
+// rendered page. Try turning mono, sans, and serif off and on in
+// every combination.
+add_task(function* () {
+  for (let useMono of [false, true]) {
+    for (let useSans of [false, true]) {
+      for (let useSerif of [false, true]) {
+        yield testFontWhitelist(useMono, useSans, useSerif);
+      }
+    }
+  }
+});
+
+</script>
+</body>
+</html>
--- a/gfx/thebes/gfxDWriteFontList.cpp
+++ b/gfx/thebes/gfxDWriteFontList.cpp
@@ -734,17 +734,17 @@ gfxDWriteFontList::gfxDWriteFontList()
 {
 }
 
 // bug 602792 - CJK systems default to large CJK fonts which cause excessive
 //   I/O strain during cold startup due to dwrite caching bugs.  Default to
 //   Arial to avoid this.
 
 gfxFontFamily *
-gfxDWriteFontList::GetDefaultFont(const gfxFontStyle *aStyle)
+gfxDWriteFontList::GetDefaultFontForPlatform(const gfxFontStyle *aStyle)
 {
     nsAutoString resolvedName;
 
     // try Arial first
     gfxFontFamily *ff;
     if ((ff = FindFamily(NS_LITERAL_STRING("Arial")))) {
         return ff;
     }
@@ -850,17 +850,17 @@ gfxDWriteFontList::MakePlatformFont(cons
 
 enum DWriteInitError {
     errGDIInterop = 1,
     errSystemFontCollection = 2,
     errNoFonts = 3
 };
 
 nsresult
-gfxDWriteFontList::InitFontList()
+gfxDWriteFontList::InitFontListForPlatform()
 {
     LARGE_INTEGER frequency;          // ticks per second
     LARGE_INTEGER t1, t2, t3, t4, t5; // ticks
     double elapsedTime, upTime;
     char nowTime[256], nowDate[256];
 
     if (LOG_FONTINIT_ENABLED()) {
         GetTimeFormatA(LOCALE_INVARIANT, TIME_FORCE24HOURFORMAT,
@@ -871,18 +871,16 @@ gfxDWriteFontList::InitFontList()
     QueryPerformanceFrequency(&frequency);
     QueryPerformanceCounter(&t1); // start
 
     HRESULT hr;
     mGDIFontTableAccess =
         Preferences::GetBool("gfx.font_rendering.directwrite.use_gdi_table_loading",
                              false);
 
-    gfxPlatformFontList::InitFontList();
-
     mFontSubstitutes.Clear();
     mNonExistingFonts.Clear();
 
     hr = gfxWindowsPlatform::GetPlatform()->GetDWriteFactory()->
         GetGdiInterop(getter_AddRefs(mGDIInterop));
     if (FAILED(hr)) {
         Telemetry::Accumulate(Telemetry::DWRITEFONT_INIT_PROBLEM,
                               uint32_t(errGDIInterop));
@@ -1404,17 +1402,18 @@ IFACEMETHODIMP DWriteFontFallbackRendere
 
 gfxFontEntry*
 gfxDWriteFontList::GlobalFontFallback(const uint32_t aCh,
                                       Script aRunScript,
                                       const gfxFontStyle* aMatchStyle,
                                       uint32_t& aCmapCount,
                                       gfxFontFamily** aMatchedFamily)
 {
-    bool useCmaps = gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
+    bool useCmaps = IsFontFamilyWhitelistActive() ||
+                    gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
 
     if (useCmaps) {
         return gfxPlatformFontList::GlobalFontFallback(aCh,
                                                        aRunScript,
                                                        aMatchStyle,
                                                        aCmapCount,
                                                        aMatchedFamily);
     }
--- a/gfx/thebes/gfxDWriteFontList.h
+++ b/gfx/thebes/gfxDWriteFontList.h
@@ -352,19 +352,17 @@ class gfxDWriteFontList : public gfxPlat
 public:
     gfxDWriteFontList();
 
     static gfxDWriteFontList* PlatformFontList() {
         return static_cast<gfxDWriteFontList*>(sPlatformFontList);
     }
 
     // initialize font lists
-    virtual nsresult InitFontList();
-
-    virtual gfxFontFamily* GetDefaultFont(const gfxFontStyle* aStyle);
+    virtual nsresult InitFontListForPlatform() override;
 
     virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
                                           uint16_t aWeight,
                                           int16_t aStretch,
                                           uint8_t aStyle);
 
     virtual gfxFontEntry* MakePlatformFont(const nsAString& aFontName,
                                            uint16_t aWeight,
@@ -386,16 +384,20 @@ public:
 
     gfxFloat GetForceGDIClassicMaxFontSize() { return mForceGDIClassicMaxFontSize; }
 
     virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontListSizes* aSizes) const;
     virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontListSizes* aSizes) const;
 
+protected:
+    virtual gfxFontFamily*
+    GetDefaultFontForPlatform(const gfxFontStyle* aStyle) override;
+
 private:
     friend class gfxDWriteFontFamily;
 
     nsresult GetFontSubstitutes();
 
     void GetDirectWriteSubstitutes();
 
     // search fonts system-wide for a given character, null otherwise
--- a/gfx/thebes/gfxFT2FontList.cpp
+++ b/gfx/thebes/gfxFT2FontList.cpp
@@ -27,18 +27,20 @@
 #include "cairo-ft.h"
 
 #include "gfxFT2FontList.h"
 #include "gfxFT2Fonts.h"
 #include "gfxUserFontSet.h"
 #include "gfxFontUtils.h"
 
 #include "nsServiceManagerUtils.h"
+#include "nsIObserverService.h"
 #include "nsTArray.h"
 #include "nsUnicharUtils.h"
+#include "nsCRT.h"
 
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsISimpleEnumerator.h"
 #include "nsIMemory.h"
 #include "gfxFontConstants.h"
 
@@ -605,16 +607,18 @@ FT2FontFamily::AddFacesToFontList(Infall
  * faces without instantiating Freetype faces for each font file (in order to
  * find their attributes), leading to significantly quicker startup.
  */
 
 #define CACHE_KEY "font.cached-list"
 
 class FontNameCache {
 public:
+    // Creates the object but does NOT load the cached data from the startup
+    // cache; call Init() after creation to do that.
     FontNameCache()
         : mMap(&mOps, sizeof(FNCMapEntry), 0)
         , mWriteNeeded(false)
     {
         // HACK ALERT: it's weird to assign |mOps| after we passed a pointer to
         // it to |mMap|'s constructor. A more normal approach here would be to
         // have a static |sOps| member. Unfortunately, this mysteriously but
         // consistently makes Fennec start-up slower, so we take this
@@ -625,21 +629,18 @@ public:
             StringHash,
             HashMatchEntry,
             MoveEntry,
             PLDHashTable::ClearEntryStub,
             nullptr
         };
 
         MOZ_ASSERT(XRE_IsParentProcess(),
-                   "StartupCacheFontNameCache should only be used in chrome "
-                   "process");
+                   "FontNameCache should only be used in chrome process");
         mCache = mozilla::scache::StartupCache::GetSingleton();
-
-        Init();
     }
 
     ~FontNameCache()
     {
         if (!mWriteNeeded || !mCache) {
             return;
         }
 
@@ -657,29 +658,34 @@ public:
             buf.AppendInt(entry->mTimestamp);
             buf.Append(';');
             buf.AppendInt(entry->mFilesize);
             buf.Append(';');
         }
         mCache->PutBuffer(CACHE_KEY, buf.get(), buf.Length() + 1);
     }
 
+    // This may be called more than once (if we re-load the font list).
     void Init()
     {
         if (!mCache) {
             return;
         }
+
         uint32_t size;
         UniquePtr<char[]> buf;
         if (NS_FAILED(mCache->GetBuffer(CACHE_KEY, &buf, &size))) {
             return;
         }
 
         LOG(("got: %s from the cache", nsDependentCString(buf.get(), size).get()));
 
+        mMap.Clear();
+        mWriteNeeded = false;
+
         const char* beginning = buf.get();
         const char* end = strchr(beginning, ';');
         while (end) {
             nsCString filename(beginning, end - beginning);
             beginning = end + 1;
             if (!(end = strchr(beginning, ';'))) {
                 break;
             }
@@ -707,33 +713,33 @@ public:
                 mapEntry->mFileExists = false;
             }
 
             beginning = end + 1;
             end = strchr(beginning, ';');
         }
     }
 
-    virtual void
+    void
     GetInfoForFile(const nsCString& aFileName, nsCString& aFaceList,
                    uint32_t *aTimestamp, uint32_t *aFilesize)
     {
         auto entry = static_cast<FNCMapEntry*>(mMap.Search(aFileName.get()));
         if (entry) {
             *aTimestamp = entry->mTimestamp;
             *aFilesize = entry->mFilesize;
             aFaceList.Assign(entry->mFaces);
             // this entry does correspond to an existing file
             // (although it might not be up-to-date, in which case
             // it will get overwritten via CacheFileInfo)
             entry->mFileExists = true;
         }
     }
 
-    virtual void
+    void
     CacheFileInfo(const nsCString& aFileName, const nsCString& aFaceList,
                   uint32_t aTimestamp, uint32_t aFilesize)
     {
         auto entry =
             static_cast<FNCMapEntry*>(mMap.Add(aFileName.get(), fallible));
         if (entry) {
             entry->mFilename.Assign(aFileName);
             entry->mTimestamp = aTimestamp;
@@ -789,18 +795,69 @@ private:
  *
  * gfxFT2FontList
  *
  */
 
 // For Mobile, we use gfxFT2Fonts, and we build the font list by directly
 // scanning the system's Fonts directory for OpenType and TrueType files.
 
+#define JAR_LAST_MODIFED_TIME "jar-last-modified-time"
+
+class WillShutdownObserver : public nsIObserver
+{
+public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIOBSERVER
+
+    explicit WillShutdownObserver(gfxFT2FontList* aFontList)
+        : mFontList(aFontList)
+    { }
+
+protected:
+    virtual ~WillShutdownObserver()
+    { }
+
+    gfxFT2FontList *mFontList;
+};
+
+NS_IMPL_ISUPPORTS(WillShutdownObserver, nsIObserver)
+
+NS_IMETHODIMP
+WillShutdownObserver::Observe(nsISupports *aSubject,
+                              const char *aTopic,
+                              const char16_t *aData)
+{
+    if (!nsCRT::strcmp(aTopic, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID)) {
+        mFontList->WillShutdown();
+    } else {
+        NS_NOTREACHED("unexpected notification topic");
+    }
+    return NS_OK;
+}
+
 gfxFT2FontList::gfxFT2FontList()
+    : mJarModifiedTime(0)
 {
+    nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+    if (obs) {
+        mObserver = new WillShutdownObserver(this);
+        obs->AddObserver(mObserver, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID, false);
+    }
+}
+
+gfxFT2FontList::~gfxFT2FontList()
+{
+    if (mObserver) {
+        nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+        if (obs) {
+            obs->RemoveObserver(mObserver, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID);
+        }
+        mObserver = nullptr;
+    }
 }
 
 void
 gfxFT2FontList::AppendFacesFromCachedFaceList(
     const nsCString& aFileName,
     const nsCString& aFaceList,
     StandardFile aStdFile,
     FT2FontFamily::Visibility aVisibility)
@@ -934,37 +991,34 @@ gfxFT2FontList::AppendFacesFromFontFile(
         }
         FT_Done_Face(dummy);
         if (aCache && 0 == statRetval && !newFaceList.IsEmpty()) {
             aCache->CacheFileInfo(aFileName, newFaceList, timestamp, filesize);
         }
     }
 }
 
-#define JAR_LAST_MODIFED_TIME "jar-last-modified-time"
-
 void
 gfxFT2FontList::FindFontsInOmnijar(FontNameCache *aCache)
 {
     bool jarChanged = false;
 
     mozilla::scache::StartupCache* cache =
         mozilla::scache::StartupCache::GetSingleton();
     UniquePtr<char[]> cachedModifiedTimeBuf;
     uint32_t longSize;
-    int64_t jarModifiedTime;
     if (cache &&
         NS_SUCCEEDED(cache->GetBuffer(JAR_LAST_MODIFED_TIME,
                                       &cachedModifiedTimeBuf,
                                       &longSize)) &&
         longSize == sizeof(int64_t))
     {
         nsCOMPtr<nsIFile> jarFile = Omnijar::GetPath(Omnijar::Type::GRE);
-        jarFile->GetLastModifiedTime(&jarModifiedTime);
-        if (jarModifiedTime > *(int64_t*)cachedModifiedTimeBuf.get()) {
+        jarFile->GetLastModifiedTime(&mJarModifiedTime);
+        if (mJarModifiedTime > *(int64_t*)cachedModifiedTimeBuf.get()) {
             jarChanged = true;
         }
     }
 
     static const char* sJarSearchPaths[] = {
         "res/fonts/*.ttf$",
     };
     RefPtr<nsZipArchive> reader = Omnijar::GetReader(Omnijar::Type::GRE);
@@ -976,21 +1030,16 @@ gfxFT2FontList::FindFontsInOmnijar(FontN
             while (NS_SUCCEEDED(find->FindNext(&path, &len))) {
                 nsCString entryName(path, len);
                 AppendFacesFromOmnijarEntry(reader, entryName, aCache,
                                             jarChanged);
             }
             delete find;
         }
     }
-
-    if (cache) {
-        cache->PutBuffer(JAR_LAST_MODIFED_TIME, (char*)&jarModifiedTime,
-                         sizeof(jarModifiedTime));
-    }
 }
 
 // Given the freetype face corresponding to an entryName and face index,
 // add the face to the available font list and to the faceList string
 void
 gfxFT2FontList::AddFaceToList(const nsCString& aEntryName, uint32_t aIndex,
                               StandardFile aStdFile,
                               FT2FontFamily::Visibility aVisibility,
@@ -999,27 +1048,27 @@ gfxFT2FontList::AddFaceToList(const nsCS
 {
     if (FT_Err_Ok != FT_Select_Charmap(aFace, FT_ENCODING_UNICODE)) {
         // ignore faces that don't support a Unicode charmap
         return;
     }
 
     // build the font entry name and create an FT2FontEntry,
     // but do -not- keep a reference to the FT_Face
-    FT2FontEntry* fe =
+    RefPtr<FT2FontEntry> fe =
         CreateNamedFontEntry(aFace, aEntryName.get(), aIndex);
 
     auto& fontFamilies =
         (aVisibility == FT2FontFamily::kHidden) ? mHiddenFontFamilies :
                                                   mFontFamilies;
 
     if (fe) {
         NS_ConvertUTF8toUTF16 name(aFace->family_name);
         BuildKeyNameFromFontName(name);
-        gfxFontFamily *family = fontFamilies.GetWeak(name);
+        RefPtr<gfxFontFamily> family = fontFamilies.GetWeak(name);
         if (!family) {
             family = new FT2FontFamily(name);
             fontFamilies.Put(name, family);
             if (mSkipSpaceLookupCheckFamilies.Contains(name)) {
                 family->SetSkipSpaceFeatureCheck(true);
             }
             if (mBadUnderlineFamilyNames.Contains(name)) {
                 family->SetBadUnderlineFamily();
@@ -1157,80 +1206,85 @@ gfxFT2FontList::FindFonts()
         LOG(("got font list from chrome process: %d faces in %d families "
              "and %d in hidden families",
             fonts.Length(), mFontFamilies.Count(),
             mHiddenFontFamilies.Count()));
         return;
     }
 
     // Chrome process: get the cached list (if any)
-    FontNameCache fnc;
+    if (!mFontNameCache) {
+        mFontNameCache = MakeUnique<FontNameCache>();
+    }
+    mFontNameCache->Init();
 
     // ANDROID_ROOT is the root of the android system, typically /system;
     // font files are in /$ANDROID_ROOT/fonts/
     nsCString root;
     char *androidRoot = PR_GetEnv("ANDROID_ROOT");
     if (androidRoot) {
         root = androidRoot;
     } else {
         root = NS_LITERAL_CSTRING("/system");
     }
     root.AppendLiteral("/fonts");
 
-    FindFontsInDir(root, &fnc, FT2FontFamily::kVisible);
+    FindFontsInDir(root, mFontNameCache.get(), FT2FontFamily::kVisible);
 
     if (mFontFamilies.Count() == 0) {
         // if we can't find/read the font directory, we are doomed!
         NS_RUNTIMEABORT("Could not read the system fonts directory");
     }
 
 #ifdef MOZ_WIDGET_GONK
     // Look for fonts in /system/fonts/hidden and preload them to the
     // user-font cache as data: URIs
     root.AppendLiteral("/hidden");
-    FindFontsInDir(root, &fnc, FT2FontFamily::kHidden);
+    FindFontsInDir(root, mFontNameCache.get(), FT2FontFamily::kHidden);
 #endif
 
     // Look for fonts stored in omnijar, unless we're on a low-memory
     // device where we don't want to spend the RAM to decompress them.
     // (Prefs may disable this, or force-enable it even with low memory.)
     bool lowmem;
     nsCOMPtr<nsIMemory> mem = nsMemory::GetGlobalMemoryService();
     if ((NS_SUCCEEDED(mem->IsLowMemoryPlatform(&lowmem)) && !lowmem &&
          Preferences::GetBool("gfx.bundled_fonts.enabled")) ||
         Preferences::GetBool("gfx.bundled_fonts.force-enabled")) {
-        FindFontsInOmnijar(&fnc);
+        FindFontsInOmnijar(mFontNameCache.get());
     }
 
     // Look for downloaded fonts in a profile-agnostic "fonts" directory.
     nsCOMPtr<nsIProperties> dirSvc =
       do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
     if (dirSvc) {
         nsCOMPtr<nsIFile> appDir;
         nsresult rv = dirSvc->Get(NS_XPCOM_CURRENT_PROCESS_DIR,
                          NS_GET_IID(nsIFile), getter_AddRefs(appDir));
         if (NS_SUCCEEDED(rv)) {
             appDir->AppendNative(NS_LITERAL_CSTRING("fonts"));
             nsCString localPath;
             if (NS_SUCCEEDED(appDir->GetNativePath(localPath))) {
-                FindFontsInDir(localPath, &fnc, FT2FontFamily::kVisible);
+                FindFontsInDir(localPath, mFontNameCache.get(),
+                               FT2FontFamily::kVisible);
             }
         }
     }
 
     // look for locally-added fonts in a "fonts" subdir of the profile
     nsCOMPtr<nsIFile> localDir;
     nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
                                          getter_AddRefs(localDir));
     if (NS_SUCCEEDED(rv) &&
         NS_SUCCEEDED(localDir->Append(NS_LITERAL_STRING("fonts")))) {
         nsCString localPath;
         rv = localDir->GetNativePath(localPath);
         if (NS_SUCCEEDED(rv)) {
-            FindFontsInDir(localPath, &fnc, FT2FontFamily::kVisible);
+            FindFontsInDir(localPath, mFontNameCache.get(),
+                           FT2FontFamily::kVisible);
         }
     }
 
     // Finalize the families by sorting faces into standard order
     // and marking "simple" families.
     // Passing non-null userData here says that we want faces to be sorted.
     for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
         nsStringHashKey::KeyType key = iter.Key();
@@ -1309,17 +1363,17 @@ gfxFT2FontList::AppendFaceFromFontListEn
                                             StandardFile aStdFile)
 {
     FT2FontEntry* fe = FT2FontEntry::CreateFontEntry(aFLE);
     if (fe) {
         auto& fontFamilies =
             aFLE.isHidden() ? mHiddenFontFamilies : mFontFamilies;
         fe->mStandardFace = (aStdFile == kStandard);
         nsAutoString name(aFLE.familyName());
-        gfxFontFamily *family = fontFamilies.GetWeak(name);
+        RefPtr<gfxFontFamily> family = fontFamilies.GetWeak(name);
         if (!family) {
             family = new FT2FontFamily(name);
             fontFamilies.Put(name, family);
             if (mSkipSpaceLookupCheckFamilies.Contains(name)) {
                 family->SetSkipSpaceFeatureCheck(true);
             }
             if (mBadUnderlineFamilyNames.Contains(name)) {
                 family->SetBadUnderlineFamily();
@@ -1411,20 +1465,19 @@ PreloadAsUserFontFaces(nsStringHashKey::
 
         // Stash it persistently in the user-font cache.
         gfxUserFontSet::UserFontCache::CacheFont(
             fe, gfxUserFontSet::UserFontCache::kPersistent);
     }
 }
 
 nsresult
-gfxFT2FontList::InitFontList()
+gfxFT2FontList::InitFontListForPlatform()
 {
-    // reset font lists
-    gfxPlatformFontList::InitFontList();
+    // reset hidden font list
     mHiddenFontFamilies.Clear();
 
     LoadSkipSpaceLookupCheck(mSkipSpaceLookupCheckFamilies);
 
     FindFonts();
 
     for (auto iter = mHiddenFontFamilies.Iter(); !iter.Done(); iter.Next()) {
         nsStringHashKey::KeyType key = iter.Key();
@@ -1503,17 +1556,17 @@ searchDone:
         fe->mStretch = aStretch;
         fe->mIsLocalUserFont = true;
     }
 
     return fe;
 }
 
 gfxFontFamily*
-gfxFT2FontList::GetDefaultFont(const gfxFontStyle* aStyle)
+gfxFT2FontList::GetDefaultFontForPlatform(const gfxFontStyle* aStyle)
 {
     gfxFontFamily *ff = nullptr;
 #ifdef MOZ_WIDGET_GONK
     ff = FindFamily(NS_LITERAL_STRING("Fira Sans"));
 #elif defined(MOZ_WIDGET_ANDROID)
     ff = FindFamily(NS_LITERAL_STRING("Roboto"));
     if (!ff) {
         ff = FindFamily(NS_LITERAL_STRING("Droid Sans"));
@@ -1545,8 +1598,20 @@ gfxFT2FontList::GetFontFamilyList(nsTArr
         RefPtr<gfxFontFamily>& family = iter.Data();
         aFamilyArray.AppendElement(family);
     }
     for (auto iter = mHiddenFontFamilies.Iter(); !iter.Done(); iter.Next()) {
         RefPtr<gfxFontFamily>& family = iter.Data();
         aFamilyArray.AppendElement(family);
     }
 }
+
+void
+gfxFT2FontList::WillShutdown()
+{
+    mozilla::scache::StartupCache* cache =
+        mozilla::scache::StartupCache::GetSingleton();
+    if (cache && mJarModifiedTime > 0) {
+        cache->PutBuffer(JAR_LAST_MODIFED_TIME,
+                         (char*)&mJarModifiedTime, sizeof(mJarModifiedTime));
+    }
+    mFontNameCache = nullptr;
+}
--- a/gfx/thebes/gfxFT2FontList.h
+++ b/gfx/thebes/gfxFT2FontList.h
@@ -114,18 +114,17 @@ public:
     void AddFacesToFontList(InfallibleTArray<FontListEntry>* aFontList,
                             Visibility aVisibility);
 };
 
 class gfxFT2FontList : public gfxPlatformFontList
 {
 public:
     gfxFT2FontList();
-
-    virtual gfxFontFamily* GetDefaultFont(const gfxFontStyle* aStyle);
+    virtual ~gfxFT2FontList();
 
     virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
                                           uint16_t aWeight,
                                           int16_t aStretch,
                                           uint8_t aStyle);
 
     virtual gfxFontEntry* MakePlatformFont(const nsAString& aFontName,
                                            uint16_t aWeight,
@@ -137,23 +136,26 @@ public:
     void GetSystemFontList(InfallibleTArray<FontListEntry>* retValue);
 
     static gfxFT2FontList* PlatformFontList() {
         return static_cast<gfxFT2FontList*>(gfxPlatformFontList::PlatformFontList());
     }
 
     virtual void GetFontFamilyList(nsTArray<RefPtr<gfxFontFamily> >& aFamilyArray);
 
+    void WillShutdown();
+
 protected:
     typedef enum {
         kUnknown,
         kStandard
     } StandardFile;
 
-    virtual nsresult InitFontList();
+    // initialize font lists
+    virtual nsresult InitFontListForPlatform() override;
 
     void AppendFaceFromFontListEntry(const FontListEntry& aFLE,
                                      StandardFile aStdFile);
 
     void AppendFacesFromFontFile(const nsCString& aFileName,
                                  FontNameCache *aCache,
                                  StandardFile aStdFile,
                                  FT2FontFamily::Visibility aVisibility);
@@ -177,15 +179,22 @@ protected:
 
     void FindFonts();
 
     void FindFontsInOmnijar(FontNameCache *aCache);
 
     void FindFontsInDir(const nsCString& aDir, FontNameCache* aFNC,
                         FT2FontFamily::Visibility aVisibility);
 
+    virtual gfxFontFamily*
+    GetDefaultFontForPlatform(const gfxFontStyle* aStyle) override;
+
     nsTHashtable<nsStringHashKey> mSkipSpaceLookupCheckFamilies;
 
 private:
     FontFamilyTable mHiddenFontFamilies;
+
+    mozilla::UniquePtr<FontNameCache> mFontNameCache;
+    int64_t mJarModifiedTime;
+    nsCOMPtr<nsIObserver> mObserver;
 };
 
 #endif /* GFX_FT2FONTLIST_H */
--- a/gfx/thebes/gfxFcPlatformFontList.cpp
+++ b/gfx/thebes/gfxFcPlatformFontList.cpp
@@ -980,17 +980,17 @@ gfxFcPlatformFontList::AddFontSetFamilie
     // Chrome Skia/Webkit code does also.
 
     if (!aFontSet) {
         NS_WARNING("AddFontSetFamilies called with a null font set.");
         return;
     }
 
     FcChar8* lastFamilyName = (FcChar8*)"";
-    gfxFontconfigFontFamily* fontFamily = nullptr;
+    RefPtr<gfxFontconfigFontFamily> fontFamily;
     nsAutoString familyName;
     for (int f = 0; f < aFontSet->nfont; f++) {
         FcPattern* font = aFontSet->fonts[f];
 
         // not scalable? skip...
         FcBool scalable;
         if (FcPatternGetBool(font, FC_SCALABLE, 0, &scalable) != FcResultMatch ||
             !scalable) {
@@ -1056,23 +1056,20 @@ gfxFcPlatformFontList::AddFontSetFamilie
         if (!fullname.IsEmpty()) {
             ToLowerCase(fullname);
             mLocalNames.Put(fullname, font);
         }
     }
 }
 
 nsresult
-gfxFcPlatformFontList::InitFontList()
+gfxFcPlatformFontList::InitFontListForPlatform()
 {
     mLastConfig = FcConfigGetCurrent();
 
-    // reset font lists
-    gfxPlatformFontList::InitFontList();
-
     mLocalNames.Clear();
     mFcSubstituteCache.Clear();
 
     // iterate over available fonts
     FcFontSet* systemFonts = FcConfigGetFonts(nullptr, FcSetSystem);
     AddFontSetFamilies(systemFonts, /* aAppFonts = */ false);
     mAlwaysUseFontconfigGenerics = PrefFontListsUseOnlyGenerics();
 
@@ -1177,17 +1174,17 @@ gfxFcPlatformFontList::GetFontList(nsIAt
         aListOfFonts.InsertElementAt(0, NS_LITERAL_STRING("monospace"));
     if (sansSerif)
         aListOfFonts.InsertElementAt(0, NS_LITERAL_STRING("sans-serif"));
     if (serif)
         aListOfFonts.InsertElementAt(0, NS_LITERAL_STRING("serif"));
 }
 
 gfxFontFamily*
-gfxFcPlatformFontList::GetDefaultFont(const gfxFontStyle* aStyle)
+gfxFcPlatformFontList::GetDefaultFontForPlatform(const gfxFontStyle* aStyle)
 {
     // Get the default font by using a fake name to retrieve the first
     // scalable font that fontconfig suggests for the given language.
     PrefFontList* prefFonts =
         FindGenericFamilies(NS_LITERAL_STRING("-moz-default"), aStyle->language);
     NS_ASSERTION(prefFonts, "null list of generic fonts");
     if (prefFonts && !prefFonts->IsEmpty()) {
         return (*prefFonts)[0];
--- a/gfx/thebes/gfxFcPlatformFontList.h
+++ b/gfx/thebes/gfxFcPlatformFontList.h
@@ -213,26 +213,23 @@ class gfxFcPlatformFontList : public gfx
 public:
     gfxFcPlatformFontList();
 
     static gfxFcPlatformFontList* PlatformFontList() {
         return static_cast<gfxFcPlatformFontList*>(sPlatformFontList);
     }
 
     // initialize font lists
-    nsresult InitFontList() override;
+    virtual nsresult InitFontListForPlatform() override;
 
     void GetFontList(nsIAtom *aLangGroup,
                      const nsACString& aGenericFamily,
                      nsTArray<nsString>& aListOfFonts) override;
 
 
-    gfxFontFamily*
-    GetDefaultFont(const gfxFontStyle* aStyle) override;
-
     gfxFontEntry*
     LookupLocalFont(const nsAString& aFontName, uint16_t aWeight,
                     int16_t aStretch, uint8_t aStyle) override;
 
     gfxFontEntry*
     MakePlatformFont(const nsAString& aFontName, uint16_t aWeight,
                      int16_t aStretch,
                      uint8_t aStyle,
@@ -275,16 +272,19 @@ protected:
     PrefFontList* FindGenericFamilies(const nsAString& aGeneric,
                                       nsIAtom* aLanguage);
 
     // are all pref font settings set to use fontconfig generics?
     bool PrefFontListsUseOnlyGenerics();
 
     static void CheckFontUpdates(nsITimer *aTimer, void *aThis);
 
+    virtual gfxFontFamily*
+    GetDefaultFontForPlatform(const gfxFontStyle* aStyle) override;
+
 #ifdef MOZ_BUNDLED_FONTS
     void ActivateBundledFonts();
     nsCString mBundledFontsPath;
     bool mBundledFontsInitialized;
 #endif
 
     // to avoid enumerating all fonts, maintain a mapping of local font
     // names to family
--- a/gfx/thebes/gfxGDIFontList.cpp
+++ b/gfx/thebes/gfxGDIFontList.cpp
@@ -647,23 +647,20 @@ gfxGDIFontList::GetFontSubstitutes()
         if (ff) {
             mFontSubstitutes.Put(substituteName, ff);
         }
     }
     return NS_OK;
 }
 
 nsresult
-gfxGDIFontList::InitFontList()
+gfxGDIFontList::InitFontListForPlatform()
 {
     Telemetry::AutoTimer<Telemetry::GDI_INITFONTLIST_TOTAL> timer;
 
-    // reset font lists
-    gfxPlatformFontList::InitFontList();
-    
     mFontSubstitutes.Clear();
     mNonExistingFonts.Clear();
 
     // iterate over available families
     LOGFONTW logfont;
     memset(&logfont, 0, sizeof(logfont));
     logfont.lfCharSet = DEFAULT_CHARSET;
 
@@ -915,17 +912,17 @@ gfxGDIFontList::FindAndAddFamilies(const
         return false;
     }
 
     return gfxPlatformFontList::FindAndAddFamilies(aFamily, aOutput, aStyle,
                                                    aDevToCssSize);
 }
 
 gfxFontFamily*
-gfxGDIFontList::GetDefaultFont(const gfxFontStyle* aStyle)
+gfxGDIFontList::GetDefaultFontForPlatform(const gfxFontStyle* aStyle)
 {
     gfxFontFamily *ff = nullptr;
 
     // this really shouldn't fail to find a font....
     NONCLIENTMETRICSW ncm;
     ncm.cbSize = sizeof(ncm);
     BOOL status = ::SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, 
                                           sizeof(ncm), &ncm, 0);
--- a/gfx/thebes/gfxGDIFontList.h
+++ b/gfx/thebes/gfxGDIFontList.h
@@ -296,19 +296,17 @@ private:
 
 class gfxGDIFontList : public gfxPlatformFontList {
 public:
     static gfxGDIFontList* PlatformFontList() {
         return static_cast<gfxGDIFontList*>(sPlatformFontList);
     }
 
     // initialize font lists
-    virtual nsresult InitFontList();
-
-    virtual gfxFontFamily* GetDefaultFont(const gfxFontStyle* aStyle);
+    virtual nsresult InitFontListForPlatform() override;
 
     bool FindAndAddFamilies(const nsAString& aFamily,
                             nsTArray<gfxFontFamily*>* aOutput,
                             gfxFontStyle* aStyle = nullptr,
                             gfxFloat aDevToCssSize = 1.0) override;
 
     virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
                                           uint16_t aWeight,
@@ -322,16 +320,20 @@ public:
                                            const uint8_t* aFontData,
                                            uint32_t aLength);
 
     virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontListSizes* aSizes) const;
     virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontListSizes* aSizes) const;
 
+protected:
+    virtual gfxFontFamily*
+    GetDefaultFontForPlatform(const gfxFontStyle* aStyle) override;
+
 private:
     friend class gfxWindowsPlatform;
 
     gfxGDIFontList();
 
     nsresult GetFontSubstitutes();
 
     static int CALLBACK EnumFontFamExProc(ENUMLOGFONTEXW *lpelfe,
--- a/gfx/thebes/gfxMacPlatformFontList.h
+++ b/gfx/thebes/gfxMacPlatformFontList.h
@@ -77,18 +77,16 @@ protected:
 class gfxMacPlatformFontList : public gfxPlatformFontList {
 public:
     static gfxMacPlatformFontList* PlatformFontList() {
         return static_cast<gfxMacPlatformFontList*>(sPlatformFontList);
     }
 
     static int32_t AppleWeightToCSSWeight(int32_t aAppleWeight);
 
-    gfxFontFamily* GetDefaultFont(const gfxFontStyle* aStyle) override;
-
     bool GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName) override;
 
     gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
                                   uint16_t aWeight,
                                   int16_t aStretch,
                                   uint8_t aStyle) override;
 
     gfxFontEntry* MakePlatformFont(const nsAString& aFontName,
@@ -105,24 +103,28 @@ public:
 
     // lookup the system font for a particular system font type and set
     // the name and style characteristics
     void LookupSystemFont(mozilla::LookAndFeel::FontID aSystemFontID,
                           nsAString& aSystemFontName,
                           gfxFontStyle &aFontStyle,
                           float aDevPixPerCSSPixel);
 
+protected:
+    virtual gfxFontFamily*
+    GetDefaultFontForPlatform(const gfxFontStyle* aStyle) override;
+
 private:
     friend class gfxPlatformMac;
 
     gfxMacPlatformFontList();
     virtual ~gfxMacPlatformFontList();
 
     // initialize font lists
-    nsresult InitFontList() override;
+    virtual nsresult InitFontListForPlatform() override;
 
     // special case font faces treated as font families (set via prefs)
     void InitSingleFaceList();
 
     // initialize system fonts
     void InitSystemFontNames();
 
     // helper function to lookup in both hidden system fonts and normal fonts
--- a/gfx/thebes/gfxMacPlatformFontList.mm
+++ b/gfx/thebes/gfxMacPlatformFontList.mm
@@ -698,34 +698,33 @@ gfxMacPlatformFontList::AddFamily(CFStri
     if (hiddenSystemFont && mUseSizeSensitiveSystemFont &&
         mSystemDisplayFontFamilyName.Equals(familyName)) {
         sizeHint = 128.0;
     }
 
     nsAutoString key;
     ToLowerCase(familyName, key);
 
-    gfxFontFamily* familyEntry = new gfxMacFontFamily(familyName, sizeHint);
+    RefPtr<gfxFontFamily> familyEntry = new gfxMacFontFamily(familyName, sizeHint);
     table.Put(key, familyEntry);
 
     // check the bad underline blacklist
     if (mBadUnderlineFamilyNames.Contains(key)) {
         familyEntry->SetBadUnderlineFamily();
     }
 }
 
 nsresult
-gfxMacPlatformFontList::InitFontList()
+gfxMacPlatformFontList::InitFontListForPlatform()
 {
     nsAutoreleasePool localPool;
 
     Telemetry::AutoTimer<Telemetry::MAC_INITFONTLIST_TOTAL> timer;
 
-    // reset font lists
-    gfxPlatformFontList::InitFontList();
+    // reset system font list
     mSystemFontFamilies.Clear();
     
     // iterate over available families
 
     InitSystemFontNames();
 
     CFArrayRef familyNames = CTFontManagerCopyAvailableFontFamilyNames();
 
@@ -775,17 +774,17 @@ gfxMacPlatformFontList::InitSingleFaceLi
             nsAutoString key;
             GenerateFontListKey(familyName, key);
             LOG_FONTLIST(("(fontlist-singleface) family name: %s, key: %s\n",
                           NS_ConvertUTF16toUTF8(familyName).get(),
                           NS_ConvertUTF16toUTF8(key).get()));
 
             // add only if doesn't exist already
             if (!mFontFamilies.GetWeak(key)) {
-                gfxFontFamily *familyEntry =
+                RefPtr<gfxFontFamily> familyEntry =
                     new gfxSingleFaceMacFontFamily(familyName);
                 // LookupLocalFont sets this, need to clear
                 fontEntry->mIsLocalUserFont = false;
                 familyEntry->AddFontEntry(fontEntry);
                 familyEntry->SetHasStyles(true);
                 mFontFamilies.Put(key, familyEntry);
                 LOG_FONTLIST(("(fontlist-singleface) added new family\n",
                               NS_ConvertUTF16toUTF8(familyName).get(),
@@ -922,17 +921,18 @@ gfxMacPlatformFontList::RegisteredFontsC
 
 gfxFontEntry*
 gfxMacPlatformFontList::GlobalFontFallback(const uint32_t aCh,
                                            Script aRunScript,
                                            const gfxFontStyle* aMatchStyle,
                                            uint32_t& aCmapCount,
                                            gfxFontFamily** aMatchedFamily)
 {
-    bool useCmaps = gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
+    bool useCmaps = IsFontFamilyWhitelistActive() ||
+                    gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
 
     if (useCmaps) {
         return gfxPlatformFontList::GlobalFontFallback(aCh,
                                                        aRunScript,
                                                        aMatchStyle,
                                                        aCmapCount,
                                                        aMatchedFamily);
     }
@@ -1012,17 +1012,17 @@ gfxMacPlatformFontList::GlobalFontFallba
     }
 
     ::CFRelease(str);
 
     return fontEntry;
 }
 
 gfxFontFamily*
-gfxMacPlatformFontList::GetDefaultFont(const gfxFontStyle* aStyle)
+gfxMacPlatformFontList::GetDefaultFontForPlatform(const gfxFontStyle* aStyle)
 {
     nsAutoreleasePool localPool;
 
     NSString *defaultFamily = [[NSFont userFontOfSize:aStyle->size] familyName];
     nsAutoString familyName;
 
     GetStringForNSString(defaultFamily, familyName);
     return FindFamily(familyName);
--- a/gfx/thebes/gfxPlatformFontList.cpp
+++ b/gfx/thebes/gfxPlatformFontList.cpp
@@ -95,16 +95,18 @@ const gfxFontEntry::ScriptRange gfxPlatf
 
 static const char* kObservedPrefs[] = {
     "font.",
     "font.name-list.",
     "intl.accept_languages",  // hmmmm...
     nullptr
 };
 
+static const char kFontSystemWhitelistPref[] = "font.system.whitelist";
+
 // xxx - this can probably be eliminated by reworking pref font handling code
 static const char *gPrefLangNames[] = {
     #define FONT_PREF_LANG(enum_id_, str_, atom_id_) str_
     #include "gfxFontPrefLangList.h"
     #undef FONT_PREF_LANG
 };
 
 static_assert(MOZ_ARRAY_LENGTH(gPrefLangNames) == uint32_t(eFontPrefLang_Count),
@@ -168,17 +170,18 @@ gfxPlatformFontList::MemoryReporter::Col
     }
 
     return NS_OK;
 }
 
 gfxPlatformFontList::gfxPlatformFontList(bool aNeedFullnamePostscriptNames)
     : mFontFamilies(64), mOtherFamilyNames(16),
       mBadUnderlineFamilyNames(8), mSharedCmaps(8),
-      mStartIndex(0), mIncrement(1), mNumFamilies(0), mFontlistInitCount(0)
+      mStartIndex(0), mIncrement(1), mNumFamilies(0), mFontlistInitCount(0),
+      mFontFamilyWhitelistActive(false)
 {
     mOtherFamilyNamesInitialized = false;
 
     if (aNeedFullnamePostscriptNames) {
         mExtraNames = MakeUnique<ExtraNames>();
     }
     mFaceNameListsInitialized = false;
 
@@ -186,31 +189,65 @@ gfxPlatformFontList::gfxPlatformFontList
 
     // pref changes notification setup
     NS_ASSERTION(!gFontListPrefObserver,
                  "There has been font list pref observer already");
     gFontListPrefObserver = new gfxFontListPrefObserver();
     NS_ADDREF(gFontListPrefObserver);
     Preferences::AddStrongObservers(gFontListPrefObserver, kObservedPrefs);
 
+    Preferences::RegisterCallback(FontWhitelistPrefChanged,
+                                  kFontSystemWhitelistPref);
+
     RegisterStrongMemoryReporter(new MemoryReporter());
 }
 
 gfxPlatformFontList::~gfxPlatformFontList()
 {
     mSharedCmaps.Clear();
     ClearLangGroupPrefFonts();
     NS_ASSERTION(gFontListPrefObserver, "There is no font list pref observer");
     Preferences::RemoveObservers(gFontListPrefObserver, kObservedPrefs);
+    Preferences::UnregisterCallback(FontWhitelistPrefChanged,
+                                    kFontSystemWhitelistPref);
     NS_RELEASE(gFontListPrefObserver);
 }
 
 // number of CSS generic font families
 const uint32_t kNumGenerics = 5;
 
+void
+gfxPlatformFontList::ApplyWhitelist()
+{
+    nsTArray<nsString> list;
+    gfxFontUtils::GetPrefsFontList(kFontSystemWhitelistPref, list);
+    uint32_t numFonts = list.Length();
+    mFontFamilyWhitelistActive = (numFonts > 0);
+    if (!mFontFamilyWhitelistActive) {
+        return;
+    }
+    nsTHashtable<nsStringHashKey> familyNamesWhitelist;
+    for (uint32_t i = 0; i < numFonts; i++) {
+        nsString key;
+        ToLowerCase(list[i], key);
+        familyNamesWhitelist.PutEntry(key);
+    }
+    for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+        // Don't continue if we only have one font left.
+        if (mFontFamilies.Count() == 1) {
+            break;
+        }
+        nsString fontFamilyName(iter.Key());
+        ToLowerCase(fontFamilyName);
+        if (!familyNamesWhitelist.Contains(fontFamilyName)) {
+            iter.Remove();
+        }
+    }
+}
+
 nsresult
 gfxPlatformFontList::InitFontList()
 {
     mFontlistInitCount++;
 
     if (LOG_FONTINIT_ENABLED()) {
         LOG_FONTINIT(("(fontinit) system fontlist initialization\n"));
     }
@@ -238,16 +275,22 @@ gfxPlatformFontList::InitFontList()
 
     // initialize ranges of characters for which system-wide font search should be skipped
     mCodepointsWithNoFonts.reset();
     mCodepointsWithNoFonts.SetRange(0,0x1f);     // C0 controls
     mCodepointsWithNoFonts.SetRange(0x7f,0x9f);  // C1 controls
 
     sPlatformFontList = this;
 
+    nsresult rv = InitFontListForPlatform();
+    if (NS_FAILED(rv)) {
+        return rv;
+    }
+
+    ApplyWhitelist();
     return NS_OK;
 }
 
 void
 gfxPlatformFontList::GenerateFontListKey(const nsAString& aKeyName, nsAString& aResult)
 {
     aResult = aKeyName;
     ToLowerCase(aResult);
@@ -1142,16 +1185,31 @@ gfxPlatformFontList::GetDefaultGeneric(e
     }
 
     if (uint32_t(aLang) < ArrayLength(gPrefLangNames)) {
         return mDefaultGenericsLangGroup[uint32_t(aLang)];
     }
     return eFamily_serif;
 }
 
+
+gfxFontFamily*
+gfxPlatformFontList::GetDefaultFont(const gfxFontStyle* aStyle)
+{
+    gfxFontFamily* family = GetDefaultFontForPlatform(aStyle);
+    if (family) {
+        return family;
+    }
+    // Something has gone wrong and we were unable to retrieve a default font
+    // from the platform. (Likely the whitelist has blocked all potential
+    // default fonts.) As a last resort, we return the first font listed in
+    // mFontFamilies.
+    return mFontFamilies.Iter().Data();
+}
+
 void
 gfxPlatformFontList::GetFontFamilyNames(nsTArray<nsString>& aFontFamilyNames)
 {
     for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
         RefPtr<gfxFontFamily>& family = iter.Data();
         aFontFamilyNames.AppendElement(family->Name());
     }
 }
@@ -1593,10 +1651,16 @@ gfxPlatformFontList::AddSizeOfExcludingT
 void
 gfxPlatformFontList::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
                                             FontListSizes* aSizes) const
 {
     aSizes->mFontListSize += aMallocSizeOf(this);
     AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
 }
 
+bool
+gfxPlatformFontList::IsFontFamilyWhitelistActive()
+{
+    return mFontFamilyWhitelistActive;
+}
+
 #undef LOG
 #undef LOG_ENABLED
--- a/gfx/thebes/gfxPlatformFontList.h
+++ b/gfx/thebes/gfxPlatformFontList.h
@@ -109,17 +109,17 @@ public:
     static void Shutdown() {
         delete sPlatformFontList;
         sPlatformFontList = nullptr;
     }
 
     virtual ~gfxPlatformFontList();
 
     // initialize font lists
-    virtual nsresult InitFontList();
+    nsresult InitFontList();
 
     virtual void GetFontList(nsIAtom *aLangGroup,
                              const nsACString& aGenericFamily,
                              nsTArray<nsString>& aListOfFonts);
 
     void UpdateFontList();
 
     virtual void ClearLangGroupPrefFonts();
@@ -150,17 +150,17 @@ public:
 
     void AddPostscriptName(gfxFontEntry *aFontEntry, nsAString& aPostscriptName);
 
     bool NeedFullnamePostscriptNames() { return mExtraNames != nullptr; }
 
     // pure virtual functions, to be provided by concrete subclasses
 
     // get the system default font family
-    virtual gfxFontFamily* GetDefaultFont(const gfxFontStyle* aStyle) = 0;
+    gfxFontFamily* GetDefaultFont(const gfxFontStyle* aStyle);
 
     // look up a font by name on the host platform
     virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
                                           uint16_t aWeight,
                                           int16_t aStretch,
                                           uint8_t aStyle) = 0;
 
     // create a new platform font from downloaded data (@font-face)
@@ -243,16 +243,23 @@ public:
     // default serif/sans-serif choice based on font.default.xxx prefs
     mozilla::FontFamilyType
     GetDefaultGeneric(eFontPrefLang aLang);
 
     // map lang group ==> lang string
     void GetSampleLangForGroup(nsIAtom* aLanguage, nsACString& aLangStr,
                                bool aCheckEnvironment = true);
 
+    // Returns true if the font family whitelist is not empty.
+    bool IsFontFamilyWhitelistActive();
+
+    static void FontWhitelistPrefChanged(const char *aPref, void *aClosure) {
+        gfxPlatformFontList::PlatformFontList()->UpdateFontList();
+    }
+
 protected:
     class MemoryReporter final : public nsIMemoryReporter
     {
         ~MemoryReporter() {}
     public:
         NS_DECL_ISUPPORTS
         NS_DECL_NSIMEMORYREPORTER
     };
@@ -356,27 +363,35 @@ protected:
 
     void RebuildLocalFonts();
 
     void
     ResolveGenericFontNames(mozilla::FontFamilyType aGenericType,
                             eFontPrefLang aPrefLang,
                             nsTArray<RefPtr<gfxFontFamily>>* aGenericFamilies);
 
+    virtual nsresult InitFontListForPlatform() = 0;
+
+    void ApplyWhitelist();
+
     typedef nsRefPtrHashtable<nsStringHashKey, gfxFontFamily> FontFamilyTable;
     typedef nsRefPtrHashtable<nsStringHashKey, gfxFontEntry> FontEntryTable;
 
     // used by memory reporter to accumulate sizes of family names in the table
     static size_t
     SizeOfFontFamilyTableExcludingThis(const FontFamilyTable& aTable,
                                        mozilla::MallocSizeOf aMallocSizeOf);
     static size_t
     SizeOfFontEntryTableExcludingThis(const FontEntryTable& aTable,
                                       mozilla::MallocSizeOf aMallocSizeOf);
 
+    // Platform-specific helper for GetDefaultFont(...).
+    virtual gfxFontFamily*
+    GetDefaultFontForPlatform(const gfxFontStyle* aStyle) = 0;
+
     // canonical family name ==> family entry (unique, one name per family entry)
     FontFamilyTable mFontFamilies;
 
     // other family name ==> family entry (not unique, can have multiple names per
     // family entry, only names *other* than the canonical names are stored here)
     FontFamilyTable mOtherFamilyNames;
 
     // flag set after InitOtherFamilyNames is called upon first name lookup miss
@@ -432,11 +447,13 @@ protected:
     // see bugs 636957, 1070983, 1189129
     uint32_t mFontlistInitCount; // num times InitFontList called
 
     nsTHashtable<nsPtrHashKey<gfxUserFontSet> > mUserFontSetList;
 
     nsCOMPtr<nsILanguageAtomService> mLangService;
     nsTArray<uint32_t> mCJKPrefLangs;
     nsTArray<mozilla::FontFamilyType> mDefaultGenericsLangGroup;
+
+    bool mFontFamilyWhitelistActive;
 };
 
 #endif /* GFXPLATFORMFONTLIST_H_ */
--- a/gfx/thebes/gfxUserFontSet.cpp
+++ b/gfx/thebes/gfxUserFontSet.cpp
@@ -417,17 +417,20 @@ gfxUserFontEntry::LoadNextSrc()
     // load each src entry in turn, until a local face is found
     // or a download begins successfully
     while (mSrcIndex < numSrc) {
         gfxFontFaceSrc& currSrc = mSrcList[mSrcIndex];
 
         // src local ==> lookup and load immediately
 
         if (currSrc.mSourceType == gfxFontFaceSrc::eSourceType_Local) {
-            gfxFontEntry* fe =
+            // Don't look up local fonts if the font whitelist is being used.
+            gfxFontEntry* fe = gfxPlatformFontList::PlatformFontList()->
+                                 IsFontFamilyWhitelistActive() ?
+                nullptr :
                 gfxPlatform::GetPlatform()->LookupLocalFont(currSrc.mLocalName,
                                                             mWeight,
                                                             mStretch,
                                                             mStyle);
             nsTArray<gfxUserFontSet*> fontSets;
             GetUserFontSets(fontSets);
             for (gfxUserFontSet* fontSet : fontSets) {
                 // We need to note on each gfxUserFontSet that contains the user
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -360,29 +360,29 @@ gfxWindowsPlatform::InitAcceleration()
   mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_11_0);
   mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_10_1);
   mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_10_0);
   mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_9_3);
 
   DeviceManagerDx::Init();
   DeviceManagerD3D9::Init();
 
-  // CanUseHardwareVideoDecoding depends on DeviceManagerDx state,
-  // so update the cached value now.
-  UpdateCanUseHardwareVideoDecoding();
-
   InitializeConfig();
   InitializeDevices();
   UpdateANGLEConfig();
   UpdateRenderMode();
 
   // If we have Skia and we didn't init dwrite already, do it now.
   if (!mDWriteFactory && GetDefaultContentBackend() == BackendType::SKIA) {
     InitDWriteSupport();
   }
+
+  // CanUseHardwareVideoDecoding depends on DeviceManagerDx state,
+  // so update the cached value now.
+  UpdateCanUseHardwareVideoDecoding();
 }
 
 bool
 gfxWindowsPlatform::CanUseHardwareVideoDecoding()
 {
   DeviceManagerDx* dm = DeviceManagerDx::Get();
   if (!dm) {
     return false;
--- a/intl/unicharutil/tools/genUnicodePropertyData.pl
+++ b/intl/unicharutil/tools/genUnicodePropertyData.pl
@@ -768,17 +768,18 @@ sub sprintFullWidthInverse
 }
 &genTables("", "", "FullWidthInverse", "", "uint16_t", 10, 6, \&sprintFullWidthInverse, 0, 2, 1);
 
 sub sprintCasemap
 {
   my $usv = shift;
   return sprintf("0x%08x,", $casemap[$usv]);
 }
-&genTables("", "", "CaseMap", "", "uint32_t", 11, 5, \&sprintCasemap, 1, 4, 1);
+&genTables("#if !ENABLE_INTL_API", "#endif",
+           "CaseMap", "", "uint32_t", 11, 5, \&sprintCasemap, 1, 4, 1);
 
 print STDERR "Total data = $totalData\n";
 
 printf DATA_TABLES "const uint32_t kTitleToUpper = 0x%08x;\n", $kTitleToUpper;
 printf DATA_TABLES "const uint32_t kUpperToLower = 0x%08x;\n", $kUpperToLower;
 printf DATA_TABLES "const uint32_t kLowerToTitle = 0x%08x;\n", $kLowerToTitle;
 printf DATA_TABLES "const uint32_t kLowerToUpper = 0x%08x;\n", $kLowerToUpper;
 printf DATA_TABLES "const uint32_t kCaseMapCharMask = 0x%08x;\n\n", $kCaseMapCharMask;
--- a/intl/unicharutil/util/nsUnicodeProperties.cpp
+++ b/intl/unicharutil/util/nsUnicodeProperties.cpp
@@ -5,21 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsUnicodeProperties.h"
 #include "nsUnicodePropertyData.cpp"
 
 #include "mozilla/ArrayUtils.h"
 #include "nsCharTraits.h"
 
-#if ENABLE_INTL_API
-#include "unicode/uchar.h"
-#include "unicode/uscript.h"
-#endif
-
 #define UNICODE_BMP_LIMIT 0x10000
 #define UNICODE_LIMIT     0x110000
 
 #ifndef ENABLE_INTL_API
 static const nsCharProps1&
 GetCharProps1(uint32_t aCh)
 {
     if (aCh < UNICODE_BMP_LIMIT) {
@@ -168,128 +163,78 @@ const hb_unicode_general_category_t sICU
   HB_UNICODE_GENERAL_CATEGORY_CURRENCY_SYMBOL, // U_CURRENCY_SYMBOL = 25,
   HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL, // U_MODIFIER_SYMBOL = 26,
   HB_UNICODE_GENERAL_CATEGORY_OTHER_SYMBOL, // U_OTHER_SYMBOL = 27,
   HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION, // U_INITIAL_PUNCTUATION = 28,
   HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION, // U_FINAL_PUNCTUATION = 29,
 };
 #endif
 
+#if !ENABLE_INTL_API
 uint8_t GetGeneralCategory(uint32_t aCh) {
-#if ENABLE_INTL_API
-  return sICUtoHBcategory[u_charType(aCh)];
-#else
   return GetCharProps2(aCh).mCategory;
-#endif
 }
 
 nsCharType GetBidiCat(uint32_t aCh) {
-#if ENABLE_INTL_API
-  return nsCharType(u_charDirection(aCh));
-#else
   return nsCharType(GetCharProps2(aCh).mBidiCategory);
-#endif
 }
 
 int8_t GetNumericValue(uint32_t aCh) {
-#if ENABLE_INTL_API
-  UNumericType type =
-    UNumericType(u_getIntPropertyValue(aCh, UCHAR_NUMERIC_TYPE));
-  return type == U_NT_DECIMAL || type == U_NT_DIGIT
-         ? int8_t(u_getNumericValue(aCh))
-         : -1;  
-#else
   return GetCharProps2(aCh).mNumericValue;
-#endif
 }
 
 uint32_t
 GetMirroredChar(uint32_t aCh)
 {
-#if ENABLE_INTL_API
-    return u_charMirror(aCh);
-#else
     return aCh + sMirrorOffsets[GetCharProps1(aCh).mMirrorOffsetIndex];
-#endif
 }
 
 bool
 HasMirroredChar(uint32_t aCh)
 {
-#if ENABLE_INTL_API
-    return u_isMirrored(aCh);
-#else
     return GetCharProps1(aCh).mMirrorOffsetIndex != 0;
-#endif
 }
 
 uint8_t
 GetCombiningClass(uint32_t aCh)
 {
-#if ENABLE_INTL_API
-    return u_getCombiningClass(aCh);
-#else
     return GetCharProps1(aCh).mCombiningClass;
-#endif
 }
 
 uint8_t
 GetLineBreakClass(uint32_t aCh)
 {
-#if ENABLE_INTL_API
-    return u_getIntPropertyValue(aCh, UCHAR_LINE_BREAK);
-#else
     return GetCharProps2(aCh).mLineBreak;
-#endif
 }
 
 Script
 GetScriptCode(uint32_t aCh)
 {
-#if ENABLE_INTL_API
-    UErrorCode err = U_ZERO_ERROR;
-    return Script(uscript_getScript(aCh, &err));
-#else
     return Script(GetCharProps2(aCh).mScriptCode);
-#endif
 }
 
 uint32_t
 GetScriptTagForCode(Script aScriptCode)
 {
-#if ENABLE_INTL_API
-    const char* tag = uscript_getShortName(UScriptCode(aScriptCode));
-    return HB_TAG(tag[0], tag[1], tag[2], tag[3]);
-#else
     // this will safely return 0 for negative script codes, too :)
     if (static_cast<uint32_t>(aScriptCode) > ArrayLength(sScriptCodeToTag)) {
         return 0;
     }
     return sScriptCodeToTag[static_cast<uint32_t>(aScriptCode)];
-#endif
 }
 
 PairedBracketType GetPairedBracketType(uint32_t aCh)
 {
-#if ENABLE_INTL_API
-  return PairedBracketType
-           (u_getIntPropertyValue(aCh, UCHAR_BIDI_PAIRED_BRACKET_TYPE));
-#else
   return PairedBracketType(GetCharProps2(aCh).mPairedBracketType);
-#endif
 }
 
 uint32_t GetPairedBracket(uint32_t aCh)
 {
-#if ENABLE_INTL_API
-  return u_getBidiPairedBracket(aCh);
-#else
   return GetPairedBracketType(aCh) != PAIRED_BRACKET_TYPE_NONE
          ? GetMirroredChar(aCh) : aCh;
-#endif
 }
 
 static inline uint32_t
 GetCaseMapValue(uint32_t aCh)
 {
     if (aCh < UNICODE_BMP_LIMIT) {
         return sCaseMapValues[sCaseMapPages[0][aCh >> kCaseMapCharBits]]
                              [aCh & ((1 << kCaseMapCharBits) - 1)];
@@ -345,36 +290,16 @@ GetTitlecaseForAll(uint32_t aCh)
     if (mapValue & (kLowerToTitle | kLowerToUpper)) {
         return aCh ^ (mapValue & kCaseMapCharMask);
     }
     if (mapValue & kUpperToLower) {
         return GetTitlecaseForLower(aCh ^ (mapValue & kCaseMapCharMask));
     }
     return aCh;
 }
-
-#if 0 // currently unused - bug 857481
-HanVariantType
-GetHanVariant(uint32_t aCh)
-{
-    // In the sHanVariantValues array, data for 4 successive characters
-    // (2 bits each) is packed in to each uint8_t entry, with the value
-    // for the lowest character stored in the least significant bits.
-    uint8_t v = 0;
-    if (aCh < UNICODE_BMP_LIMIT) {
-        v = sHanVariantValues[sHanVariantPages[0][aCh >> kHanVariantCharBits]]
-                             [(aCh & ((1 << kHanVariantCharBits) - 1)) >> 2];
-    } else if (aCh < (kHanVariantMaxPlane + 1) * 0x10000) {
-        v = sHanVariantValues[sHanVariantPages[sHanVariantPlanes[(aCh >> 16) - 1]]
-                                              [(aCh & 0xffff) >> kHanVariantCharBits]]
-                             [(aCh & ((1 << kHanVariantCharBits) - 1)) >> 2];
-    }
-    // extract the appropriate 2-bit field from the value
-    return HanVariantType((v >> ((aCh & 3) * 2)) & 3);
-}
 #endif
 
 #define DEFINE_BMP_1PLANE_MAPPING_GET_FUNC(prefix_) \
   uint32_t Get##prefix_(uint32_t aCh) \
   { \
     if (aCh >= UNICODE_BMP_LIMIT) { \
       return aCh; \
     } \
--- a/intl/unicharutil/util/nsUnicodeProperties.h
+++ b/intl/unicharutil/util/nsUnicodeProperties.h
@@ -5,130 +5,226 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef NS_UNICODEPROPERTIES_H
 #define NS_UNICODEPROPERTIES_H
 
 #include "nsBidiUtils.h"
 #include "nsIUGenCategory.h"
 #include "nsUnicodeScriptCodes.h"
+#include "harfbuzz/hb.h"
+
+#if ENABLE_INTL_API
+#include "unicode/uchar.h"
+#include "unicode/uscript.h"
+#endif
 
 const nsCharProps2& GetCharProps2(uint32_t aCh);
 
 namespace mozilla {
 
 namespace unicode {
 
 extern const nsIUGenCategory::nsUGenCategory sDetailedToGeneralCategory[];
 
-// Return whether the char has a mirrored-pair counterpart.
-uint32_t GetMirroredChar(uint32_t aCh);
-
-bool HasMirroredChar(uint32_t aChr);
-
-uint8_t GetCombiningClass(uint32_t aCh);
-
-// returns the detailed General Category in terms of HB_UNICODE_* values
-uint8_t GetGeneralCategory(uint32_t aCh);
-
-// returns the simplified Gen Category as defined in nsIUGenCategory
-inline nsIUGenCategory::nsUGenCategory GetGenCategory(uint32_t aCh) {
-  return sDetailedToGeneralCategory[GetGeneralCategory(aCh)];
-}
-
-nsCharType GetBidiCat(uint32_t aCh);
-
-uint8_t GetLineBreakClass(uint32_t aCh);
-
-Script GetScriptCode(uint32_t aCh);
-
-uint32_t GetScriptTagForCode(Script aScriptCode);
-
 /* This MUST match the values assigned by genUnicodePropertyData.pl! */
 enum VerticalOrientation {
   VERTICAL_ORIENTATION_U  = 0,
   VERTICAL_ORIENTATION_R  = 1,
   VERTICAL_ORIENTATION_Tu = 2,
   VERTICAL_ORIENTATION_Tr = 3
 };
 
-inline VerticalOrientation GetVerticalOrientation(uint32_t aCh) {
-  return VerticalOrientation(GetCharProps2(aCh).mVertOrient);
-}
-
 /* This MUST match the values assigned by genUnicodePropertyData.pl! */
 enum PairedBracketType {
   PAIRED_BRACKET_TYPE_NONE = 0,
   PAIRED_BRACKET_TYPE_OPEN = 1,
   PAIRED_BRACKET_TYPE_CLOSE = 2
 };
 
-PairedBracketType GetPairedBracketType(uint32_t aCh);
-uint32_t GetPairedBracket(uint32_t aCh);
-
 enum XidmodType {
   XIDMOD_RECOMMENDED,
   XIDMOD_INCLUSION,
   XIDMOD_UNCOMMON_USE,
   XIDMOD_TECHNICAL,
   XIDMOD_OBSOLETE,
   XIDMOD_ASPIRATIONAL,
   XIDMOD_LIMITED_USE,
   XIDMOD_EXCLUSION,
   XIDMOD_NOT_XID,
   XIDMOD_NOT_NFKC,
   XIDMOD_DEFAULT_IGNORABLE,
   XIDMOD_DEPRECATED,
   XIDMOD_NOT_CHARS
 };
 
-inline XidmodType GetIdentifierModification(uint32_t aCh) {
-  return XidmodType(GetCharProps2(aCh).mXidmod);
+#if ENABLE_INTL_API // ICU is available, so simply forward to its API
+
+extern const hb_unicode_general_category_t sICUtoHBcategory[];
+
+inline uint32_t