merge mozilla-inbound to mozilla-central. r=merge a=merge
authorSebastian Hengst <archaeopteryx@coole-files.de>
Sat, 02 Sep 2017 10:54:44 +0200
changeset 378438 a46a5879b8781ae9ea99f37b5d34a891f0f75047
parent 378437 b01a7e57425b5fe791ab091f5c33e069890753fb (current diff)
parent 378352 59ea29d58ab0b297fd57c3ac1595d770d1f389d6 (diff)
child 378439 5cc81a8efe158d3ea9504251cb3563d1a474e4b6
child 378442 66d3a14132a12d5a871ecffbab5a9f3775b02989
child 378456 1fdc9b1dbb9e5b4f4ef03934dfa1827f3581d725
push id94474
push userarchaeopteryx@coole-files.de
push dateSat, 02 Sep 2017 09:00:26 +0000
treeherdermozilla-inbound@a46a5879b878 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone57.0a1
first release with
nightly linux32
a46a5879b878 / 57.0a1 / 20170902100317 / files
nightly linux64
a46a5879b878 / 57.0a1 / 20170902100317 / files
nightly mac
a46a5879b878 / 57.0a1 / 20170902100317 / files
nightly win32
a46a5879b878 / 57.0a1 / 20170902100317 / files
nightly win64
a46a5879b878 / 57.0a1 / 20170902100317 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central. r=merge a=merge MozReview-Commit-ID: Gsw48p934sI
dom/base/Element.cpp
dom/base/nsDocument.cpp
dom/base/nsDocument.h
layout/base/nsCSSFrameConstructor.cpp
layout/style/Loader.cpp
mobile/android/components/extensions/ext-utils.js
taskcluster/taskgraph/try_option_syntax.py
third_party/rust/bindgen/.github/ISSUE_TEMPLATE.md
third_party/rust/bindgen/.travis.yml
third_party/rust/bindgen/CONTRIBUTING.md
third_party/rust/bindgen/LICENSE
third_party/rust/bindgen/README.md
third_party/rust/bindgen/appveyor.yml
third_party/rust/bindgen/book/book.toml
third_party/rust/bindgen/book/src/SUMMARY.md
third_party/rust/bindgen/book/src/blacklisting.md
third_party/rust/bindgen/book/src/chapter_1.md
third_party/rust/bindgen/book/src/command-line-usage.md
third_party/rust/bindgen/book/src/cpp.md
third_party/rust/bindgen/book/src/customizing-generated-bindings.md
third_party/rust/bindgen/book/src/introduction.md
third_party/rust/bindgen/book/src/library-usage.md
third_party/rust/bindgen/book/src/nocopy.md
third_party/rust/bindgen/book/src/opaque.md
third_party/rust/bindgen/book/src/replacing-types.md
third_party/rust/bindgen/book/src/requirements.md
third_party/rust/bindgen/book/src/tutorial-0.md
third_party/rust/bindgen/book/src/tutorial-1.md
third_party/rust/bindgen/book/src/tutorial-2.md
third_party/rust/bindgen/book/src/tutorial-3.md
third_party/rust/bindgen/book/src/tutorial-4.md
third_party/rust/bindgen/book/src/tutorial-5.md
third_party/rust/bindgen/book/src/tutorial-6.md
third_party/rust/bindgen/book/src/using-unions.md
third_party/rust/bindgen/book/src/whitelisting.md
third_party/rust/bindgen/ci/assert-docs.sh
third_party/rust/bindgen/ci/assert-no-diff.bat
third_party/rust/bindgen/ci/assert-no-diff.sh
third_party/rust/bindgen/ci/assert-rustfmt.sh
third_party/rust/bindgen/ci/before_install.sh
third_party/rust/bindgen/ci/deploy-book.sh
third_party/rust/bindgen/ci/no-includes.sh
third_party/rust/bindgen/ci/test-book.sh
third_party/rust/bindgen/ci/test.bat
third_party/rust/bindgen/ci/test.sh
third_party/rust/bindgen/example-graphviz-ir.png
third_party/rust/bindgen/rustfmt.toml
third_party/rust/bindgen/src/uses.rs
third_party/rust/clap/appveyor.yml
third_party/rust/thread-id/license
--- a/accessible/windows/msaa/Compatibility.cpp
+++ b/accessible/windows/msaa/Compatibility.cpp
@@ -19,16 +19,35 @@
 #include "mozilla/Preferences.h"
 
 #include <shlobj.h>
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 /**
+ * String versions of consumer flags. See GetHumanReadableConsumersStr.
+ */
+static const wchar_t* ConsumerStringMap[CONSUMERS_ENUM_LEN+1] = {
+  L"NVDA",
+  L"JAWS",
+  L"OLDJAWS",
+  L"WE",
+  L"DOLPHIN",
+  L"SEROTEK",
+  L"COBRA",
+  L"ZOOMTEXT",
+  L"KAZAGURU",
+  L"YOUDAO",
+  L"UNKNOWN",
+  L"UIAUTOMATION",
+  L"\0"
+};
+
+/**
  * Return true if module version is lesser than the given version.
  */
 bool
 IsModuleVersionLessThan(HMODULE aModuleHandle, DWORD aMajor, DWORD aMinor)
 {
   wchar_t fileName[MAX_PATH];
   ::GetModuleFileNameW(aModuleHandle, fileName, MAX_PATH);
 
@@ -393,8 +412,27 @@ Compatibility::GetActCtxResourceId()
       UseIAccessibleProxyStub()) {
     return 64;
   }
 
   return 32;
 #endif // defined(HAVE_64BIT_BUILD)
 }
 
+// static
+void
+Compatibility::GetHumanReadableConsumersStr(nsAString &aResult)
+{
+  bool appened = false;
+  uint32_t index = 0;
+  for (uint32_t consumers = sConsumers; consumers; consumers = consumers >> 1) {
+    if (consumers & 0x1) {
+      if (appened) {
+        aResult.AppendLiteral(",");
+      }
+      aResult.Append(ConsumerStringMap[index]);
+      appened = true;
+    }
+    if (++index > CONSUMERS_ENUM_LEN) {
+      break;
+    }
+  }
+}
--- a/accessible/windows/msaa/Compatibility.h
+++ b/accessible/windows/msaa/Compatibility.h
@@ -2,16 +2,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef COMPATIBILITY_MANAGER_H
 #define COMPATIBILITY_MANAGER_H
 
+#include "nsString.h"
 #include <stdint.h>
 
 namespace mozilla {
 namespace a11y {
 
 /**
  * Used to get compatibility modes. Note, modes are computed at accessibility
  * start up time and aren't changed during lifetime.
@@ -40,16 +41,22 @@ public:
   static bool IsDolphin() { return !!(sConsumers & DOLPHIN); }
 
   /**
    * @return ID of a11y manifest resource to be passed to
    * mscom::ActivationContext
    */
   static uint16_t GetActCtxResourceId();
 
+  /**
+   * Return a string describing sConsumers suitable for about:support.
+   * Exposed through nsIXULRuntime.accessibilityInstantiator.
+   */
+  static void GetHumanReadableConsumersStr(nsAString &aResult);
+
 private:
   Compatibility();
   Compatibility(const Compatibility&);
   Compatibility& operator = (const Compatibility&);
 
   /**
    * Initialize compatibility mode. Called by platform (see Platform.h) during
    * accessibility initialization.
@@ -69,16 +76,17 @@ private:
     SEROTEK = 1 << 5,
     COBRA = 1 << 6,
     ZOOMTEXT = 1 << 7,
     KAZAGURU = 1 << 8,
     YOUDAO = 1 << 9,
     UNKNOWN = 1 << 10,
     UIAUTOMATION = 1 << 11
   };
+  #define CONSUMERS_ENUM_LEN 12
 
 private:
   static uint32_t sConsumers;
 };
 
 } // a11y namespace
 } // mozilla namespace
 
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -2322,16 +2322,17 @@
               });
             }
           ]]>
         </body>
       </method>
 
       <method name="_insertBrowser">
         <parameter name="aTab"/>
+        <parameter name="aInsertedOnTabCreation"/>
         <body>
           <![CDATA[
             "use strict";
 
             // If browser is already inserted or window is closed don't do anything.
             if (aTab.linkedPanel || window.closed) {
               return;
             }
@@ -2383,17 +2384,18 @@
             // set the "nodefaultsrc" attribute that prevents a frameLoader
             // from being created as soon as the linked <browser> is inserted
             // into the DOM. We thus have to register the new outerWindowID
             // for non-remote browsers after we have called browser.loadURI().
             if (remoteType == E10SUtils.NOT_REMOTE) {
               this._outerWindowIDBrowserMap.set(browser.outerWindowID, browser);
             }
 
-            var evt = new CustomEvent("TabBrowserInserted", { bubbles: true, detail: {} });
+            var evt = new CustomEvent("TabBrowserInserted",
+              { bubbles: true, detail: { insertedOnTabCreation: aInsertedOnTabCreation } });
             aTab.dispatchEvent(evt);
           ]]>
         </body>
       </method>
 
       <method name="addTab">
         <parameter name="aURI"/>
         <parameter name="aReferrerURI"/>
@@ -2634,17 +2636,17 @@
 
               if (lazyBrowserURI) {
                 // Lazy browser must be explicitly registered so tab will appear as
                 // a switch-to-tab candidate in autocomplete.
                 this._unifiedComplete.registerOpenPage(lazyBrowserURI, aUserContextId);
                 b.registeredOpenURI = lazyBrowserURI;
               }
             } else {
-              this._insertBrowser(t);
+              this._insertBrowser(t, true);
             }
 
             // Dispatch a new tab notification.  We do this once we're
             // entirely done, so that things are in a consistent state
             // even if the event listener opens or closes tabs.
             var detail = aEventDetail || {};
             var evt = new CustomEvent("TabOpen", { bubbles: true, detail });
             t.dispatchEvent(evt);
--- a/browser/components/extensions/ext-browser.js
+++ b/browser/components/extensions/ext-browser.js
@@ -564,16 +564,20 @@ class Tab extends TabBase {
   get audible() {
     return this.nativeTab.soundPlaying;
   }
 
   get browser() {
     return this.nativeTab.linkedBrowser;
   }
 
+  get discarded() {
+    return !this.nativeTab.linkedPanel;
+  }
+
   get frameLoader() {
     // If we don't have a frameLoader yet, just return a dummy with no width and
     // height.
     return super.frameLoader || {lazyWidth: 0, lazyHeight: 0};
   }
 
   get cookieStoreId() {
     return getCookieStoreIdForTab(this, this.nativeTab);
--- a/browser/components/extensions/ext-tabs.js
+++ b/browser/components/extensions/ext-tabs.js
@@ -257,16 +257,19 @@ this.tabs = class extends ExtensionAPI {
               }
               if (changed.includes("label")) {
                 needed.push("title");
               }
             } else if (event.type == "TabPinned") {
               needed.push("pinned");
             } else if (event.type == "TabUnpinned") {
               needed.push("pinned");
+            } else if (event.type == "TabBrowserInserted" &&
+                       !event.detail.insertedOnTabCreation) {
+              needed.push("discarded");
             }
 
             let tab = tabManager.getWrapper(event.originalTarget);
             let changeInfo = {};
             for (let prop of needed) {
               changeInfo[prop] = tab[prop];
             }
 
@@ -285,22 +288,24 @@ this.tabs = class extends ExtensionAPI {
               fireForTab(tabManager.wrapTab(tabElem), changed);
             }
           };
 
           windowTracker.addListener("status", statusListener);
           windowTracker.addListener("TabAttrModified", listener);
           windowTracker.addListener("TabPinned", listener);
           windowTracker.addListener("TabUnpinned", listener);
+          windowTracker.addListener("TabBrowserInserted", listener);
 
           return () => {
             windowTracker.removeListener("status", statusListener);
             windowTracker.removeListener("TabAttrModified", listener);
             windowTracker.removeListener("TabPinned", listener);
             windowTracker.removeListener("TabUnpinned", listener);
+            windowTracker.removeListener("TabBrowserInserted", listener);
           };
         }).api(),
 
         create(createProperties) {
           return new Promise((resolve, reject) => {
             let window = createProperties.windowId !== null ?
               windowTracker.getWindow(createProperties.windowId, context) :
               windowTracker.topWindow;
--- a/browser/components/extensions/schemas/tabs.json
+++ b/browser/components/extensions/schemas/tabs.json
@@ -66,16 +66,17 @@
           "pinned": {"type": "boolean", "description": "Whether the tab is pinned."},
           "lastAccessed": {"type": "integer", "optional": true, "description": "The last time the tab was accessed as the number of milliseconds since epoch."},
           "audible": {"type": "boolean", "optional": true, "description": "Whether the tab has produced sound over the past couple of seconds (but it might not be heard if also muted). Equivalent to whether the speaker audio indicator is showing."},
           "mutedInfo": {"$ref": "MutedInfo", "optional": true, "description": "Current tab muted state and the reason for the last state change."},
           "url": {"type": "string", "optional": true, "permissions": ["tabs"], "description": "The URL the tab is displaying. This property is only present if the extension's manifest includes the <code>\"tabs\"</code> permission."},
           "title": {"type": "string", "optional": true, "permissions": ["tabs"], "description": "The title of the tab. This property is only present if the extension's manifest includes the <code>\"tabs\"</code> permission."},
           "favIconUrl": {"type": "string", "optional": true, "permissions": ["tabs"], "description": "The URL of the tab's favicon. This property is only present if the extension's manifest includes the <code>\"tabs\"</code> permission. It may also be an empty string if the tab is loading."},
           "status": {"type": "string", "optional": true, "description": "Either <em>loading</em> or <em>complete</em>."},
+          "discarded": {"type": "boolean", "optional": true, "description": "True while the tab is not loaded with content."},
           "incognito": {"type": "boolean", "description": "Whether the tab is in an incognito window."},
           "width": {"type": "integer", "optional": true, "description": "The width of the tab in pixels."},
           "height": {"type": "integer", "optional": true, "description": "The height of the tab in pixels."},
           "sessionId": {"type": "string", "optional": true, "description": "The session ID used to uniquely identify a Tab obtained from the $(ref:sessions) API."},
           "cookieStoreId": {"type": "string", "optional": true, "description": "The CookieStoreId used for the tab."}
         }
       },
       {
@@ -584,16 +585,21 @@
                 "optional": true,
                 "description": "Whether the tabs are in the last focused window."
               },
               "status": {
                 "$ref": "TabStatus",
                 "optional": true,
                 "description": "Whether the tabs have completed loading."
               },
+              "discarded": {
+                "type": "boolean",
+                "optional": true,
+                "description": "True while the tabs are not loaded with content."
+              },
               "title": {
                 "type": "string",
                 "optional": true,
                 "description": "Match page titles against a pattern."
               },
               "url": {
                 "choices": [
                   {"type": "string"},
@@ -1190,16 +1196,21 @@
             "name": "changeInfo",
             "description": "Lists the changes to the state of the tab that was updated.",
             "properties": {
               "status": {
                 "type": "string",
                 "optional": true,
                 "description": "The status of the tab. Can be either <em>loading</em> or <em>complete</em>."
               },
+              "discarded": {
+                "type": "boolean",
+                "optional": true,
+                "description": "True while the tab is not loaded with content."
+              },
               "url": {
                 "type": "string",
                 "optional": true,
                 "description": "The tab's URL if it has changed."
               },
               "pinned": {
                 "type": "boolean",
                 "optional": true,
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -122,16 +122,17 @@ skip-if = debug || asan # Bug 1354681
 [browser_ext_sidebarAction_windows.js]
 [browser_ext_simple.js]
 [browser_ext_tab_runtimeConnect.js]
 [browser_ext_tabs_audio.js]
 [browser_ext_tabs_captureVisibleTab.js]
 [browser_ext_tabs_create.js]
 [browser_ext_tabs_create_invalid_url.js]
 [browser_ext_tabs_detectLanguage.js]
+[browser_ext_tabs_discarded.js]
 [browser_ext_tabs_duplicate.js]
 [browser_ext_tabs_events.js]
 [browser_ext_tabs_executeScript.js]
 [browser_ext_tabs_executeScript_good.js]
 [browser_ext_tabs_executeScript_bad.js]
 [browser_ext_tabs_executeScript_multiple.js]
 [browser_ext_tabs_executeScript_no_create.js]
 [browser_ext_tabs_executeScript_runAt.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_discarded.js
@@ -0,0 +1,66 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+/* global gBrowser SessionStore */
+"use strict";
+
+let lazyTabState = {entries: [{url: "http://example.com/", title: "Example Domain"}]};
+
+add_task(async function test_discarded() {
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "permissions": ["tabs"],
+    },
+
+    background: async function() {
+      let onCreatedTabData = [];
+      let discardedEventData = [];
+
+      async function finishTest() {
+        browser.test.assertEq(0, discardedEventData.length, "number of discarded events fired");
+
+        onCreatedTabData.sort((data1, data2) => data1.index - data2.index);
+        browser.test.assertEq(false, onCreatedTabData[0].discarded, "non-lazy tab onCreated discard property");
+        browser.test.assertEq(true, onCreatedTabData[1].discarded, "lazy tab onCreated discard property");
+
+        let tabs = await browser.tabs.query({currentWindow: true});
+        tabs.sort((tab1, tab2) => tab1.index - tab2.index);
+
+        browser.test.assertEq(false, tabs[1].discarded, "non-lazy tab query discard property");
+        browser.test.assertEq(true, tabs[2].discarded, "lazy tab query discard property");
+
+        let updatedTab = await browser.tabs.update(tabs[2].id, {active: true});
+        browser.test.assertEq(false, updatedTab.discarded, "lazy to non-lazy update discard property");
+        browser.test.assertEq(false, discardedEventData[0], "lazy to non-lazy onUpdated discard property");
+
+        browser.test.notifyPass("test-finished");
+      }
+
+      browser.tabs.onUpdated.addListener(function(tabId, updatedInfo) {
+        if ("discarded" in updatedInfo) {
+          discardedEventData.push(updatedInfo.discarded);
+        }
+      });
+
+      browser.tabs.onCreated.addListener(function(tab) {
+        onCreatedTabData.push({discarded: tab.discarded, index: tab.index});
+        if (onCreatedTabData.length == 2) {
+          finishTest();
+        }
+      });
+    },
+  });
+
+  await extension.startup();
+
+  let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com");
+
+  let tab2 = BrowserTestUtils.addTab(gBrowser, "about:blank", {createLazyBrowser: true});
+  SessionStore.setTabState(tab2, JSON.stringify(lazyTabState));
+
+  await extension.awaitFinish("test-finished");
+  await extension.unload();
+
+  await BrowserTestUtils.removeTab(tab1);
+  await BrowserTestUtils.removeTab(tab2);
+});
+
--- a/dom/base/Attr.cpp
+++ b/dom/base/Attr.cpp
@@ -314,23 +314,16 @@ Attr::GetChildCount() const
 }
 
 nsIContent *
 Attr::GetChildAt(uint32_t aIndex) const
 {
   return nullptr;
 }
 
-nsIContent * const *
-Attr::GetChildArray(uint32_t* aChildCount) const
-{
-  *aChildCount = 0;
-  return nullptr;
-}
-
 int32_t
 Attr::IndexOf(const nsINode* aPossibleChild) const
 {
   return -1;
 }
 
 nsresult
 Attr::InsertChildAt(nsIContent* aKid, uint32_t aIndex,
--- a/dom/base/Attr.h
+++ b/dom/base/Attr.h
@@ -60,17 +60,16 @@ public:
   void SetMap(nsDOMAttributeMap *aMap) override;
   Element* GetElement() const;
   nsresult SetOwnerDocument(nsIDocument* aDocument) override;
 
   // nsINode interface
   virtual bool IsNodeOfType(uint32_t aFlags) const override;
   virtual uint32_t GetChildCount() const override;
   virtual nsIContent *GetChildAt(uint32_t aIndex) const override;
-  virtual nsIContent * const * GetChildArray(uint32_t* aChildCount) const override;
   virtual int32_t IndexOf(const nsINode* aPossibleChild) const override;
   virtual nsresult InsertChildAt(nsIContent* aKid, uint32_t aIndex,
                                  bool aNotify) override;
   virtual void RemoveChildAt(uint32_t aIndex, bool aNotify) override;
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
                          bool aPreallocateChildren) const override;
   virtual already_AddRefed<nsIURI> GetBaseURI(bool aTryUseXHRDocBaseURI = false) const override;
 
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -3039,19 +3039,25 @@ Element::List(FILE* out, int32_t aIndent
   fprintf(out, "@%p", (void *)this);
 
   ListAttributes(out);
 
   fprintf(out, " state=[%llx]",
           static_cast<unsigned long long>(State().GetInternalValue()));
   fprintf(out, " flags=[%08x]", static_cast<unsigned int>(GetFlags()));
   if (IsCommonAncestorForRangeInSelection()) {
-    const nsTHashtable<nsPtrHashKey<nsRange>>* ranges =
-      GetExistingCommonAncestorRanges();
-    fprintf(out, " ranges:%d", ranges ? ranges->Count() : 0);
+    const LinkedList<nsRange>* ranges = GetExistingCommonAncestorRanges();
+    int32_t count = 0;
+    if (ranges) {
+      // Can't use range-based iteration on a const LinkedList, unfortunately.
+      for (const nsRange* r = ranges->getFirst(); r; r = r->getNext()) {
+        ++count;
+      }
+    }
+    fprintf(out, " ranges:%d", count);
   }
   fprintf(out, " primaryframe=%p", static_cast<void*>(GetPrimaryFrame()));
   fprintf(out, " refcount=%" PRIuPTR "<", mRefCnt.get());
 
   nsIContent* child = GetFirstChild();
   if (child) {
     fputs("\n", out);
 
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -2322,22 +2322,16 @@ FragmentOrElement::GetChildCount() const
 }
 
 nsIContent *
 FragmentOrElement::GetChildAt(uint32_t aIndex) const
 {
   return mAttrsAndChildren.GetSafeChildAt(aIndex);
 }
 
-nsIContent * const *
-FragmentOrElement::GetChildArray(uint32_t* aChildCount) const
-{
-  return mAttrsAndChildren.GetChildArray(aChildCount);
-}
-
 int32_t
 FragmentOrElement::IndexOf(const nsINode* aPossibleChild) const
 {
   return mAttrsAndChildren.IndexOfChild(aPossibleChild);
 }
 
 static inline bool
 IsVoidTag(nsIAtom* aTag)
--- a/dom/base/FragmentOrElement.h
+++ b/dom/base/FragmentOrElement.h
@@ -115,17 +115,16 @@ public:
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 
   NS_DECL_ADDSIZEOFEXCLUDINGTHIS
 
   // nsINode interface methods
   virtual uint32_t GetChildCount() const override;
   virtual nsIContent *GetChildAt(uint32_t aIndex) const override;
-  virtual nsIContent * const * GetChildArray(uint32_t* aChildCount) const override;
   virtual int32_t IndexOf(const nsINode* aPossibleChild) const override;
   virtual nsresult InsertChildAt(nsIContent* aKid, uint32_t aIndex,
                                  bool aNotify) override;
   virtual void RemoveChildAt(uint32_t aIndex, bool aNotify) override;
   virtual void GetTextContentInternal(nsAString& aTextContent,
                                       mozilla::OOMReporter& aError) override;
   virtual void SetTextContentInternal(const nsAString& aTextContent,
                                       mozilla::ErrorResult& aError) override;
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -4349,23 +4349,16 @@ nsDocument::IndexOf(const nsINode* aPoss
 }
 
 uint32_t
 nsDocument::GetChildCount() const
 {
   return mChildren.ChildCount();
 }
 
-nsIContent * const *
-nsDocument::GetChildArray(uint32_t* aChildCount) const
-{
-  return mChildren.GetChildArray(aChildCount);
-}
-
-
 nsresult
 nsDocument::InsertChildAt(nsIContent* aKid, uint32_t aIndex,
                           bool aNotify)
 {
   if (aKid->IsElement() && GetRootElement()) {
     NS_WARNING("Inserting root element when we already have one");
     return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
   }
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -593,17 +593,16 @@ public:
   virtual void OnPageHide(bool aPersisted, mozilla::dom::EventTarget* aDispatchStartTarget) override;
 
   virtual void WillDispatchMutationEvent(nsINode* aTarget) override;
   virtual void MutationEventDispatched(nsINode* aTarget) override;
 
   // nsINode
   virtual bool IsNodeOfType(uint32_t aFlags) const override;
   virtual nsIContent *GetChildAt(uint32_t aIndex) const override;
-  virtual nsIContent * const * GetChildArray(uint32_t* aChildCount) const override;
   virtual int32_t IndexOf(const nsINode* aPossibleChild) const override;
   virtual uint32_t GetChildCount() const override;
   virtual nsresult InsertChildAt(nsIContent* aKid, uint32_t aIndex,
                                  bool aNotify) override;
   virtual void RemoveChildAt(uint32_t aIndex, bool aNotify) override;
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
                          bool aPreallocateChildren) const override
   {
--- a/dom/base/nsGenericDOMDataNode.cpp
+++ b/dom/base/nsGenericDOMDataNode.cpp
@@ -676,23 +676,16 @@ nsGenericDOMDataNode::GetChildCount() co
 }
 
 nsIContent *
 nsGenericDOMDataNode::GetChildAt(uint32_t aIndex) const
 {
   return nullptr;
 }
 
-nsIContent * const *
-nsGenericDOMDataNode::GetChildArray(uint32_t* aChildCount) const
-{
-  *aChildCount = 0;
-  return nullptr;
-}
-
 int32_t
 nsGenericDOMDataNode::IndexOf(const nsINode* aPossibleChild) const
 {
   return -1;
 }
 
 nsresult
 nsGenericDOMDataNode::InsertChildAt(nsIContent* aKid, uint32_t aIndex,
--- a/dom/base/nsGenericDOMDataNode.h
+++ b/dom/base/nsGenericDOMDataNode.h
@@ -95,17 +95,16 @@ public:
   nsresult InsertData(uint32_t aOffset, const nsAString& aArg);
   nsresult DeleteData(uint32_t aOffset, uint32_t aCount);
   nsresult ReplaceData(uint32_t aOffset, uint32_t aCount,
                        const nsAString& aArg);
 
   // nsINode methods
   virtual uint32_t GetChildCount() const override;
   virtual nsIContent *GetChildAt(uint32_t aIndex) const override;
-  virtual nsIContent * const * GetChildArray(uint32_t* aChildCount) const override;
   virtual int32_t IndexOf(const nsINode* aPossibleChild) const override;
   virtual nsresult InsertChildAt(nsIContent* aKid, uint32_t aIndex,
                                  bool aNotify) override;
   virtual void RemoveChildAt(uint32_t aIndex, bool aNotify) override;
   virtual void GetTextContentInternal(nsAString& aTextContent,
                                       mozilla::OOMReporter& aError) override
   {
     GetNodeValue(aTextContent);
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -14,16 +14,17 @@
 #include "nsIDOMNode.h"
 #include "mozilla/dom/NodeInfo.h"            // member (in nsCOMPtr)
 #include "nsIVariant.h"             // for use in GetUserData()
 #include "nsNodeInfoManager.h"      // for use in NodePrincipal()
 #include "nsPropertyTable.h"        // for typedefs
 #include "nsTObserverArray.h"       // for member
 #include "nsWindowSizes.h"          // for nsStyleSizes
 #include "mozilla/ErrorResult.h"
+#include "mozilla/LinkedList.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/EventTarget.h" // for base class
 #include "js/TypeDecls.h"     // for Handle, Value, JSObject, JSContext
 #include "mozilla/dom/DOMString.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "nsTHashtable.h"
 #include <iosfwd>
 
@@ -496,27 +497,16 @@ public:
   /**
    * Get a child by index
    * @param aIndex the index of the child to get
    * @return the child, or null if index out of bounds
    */
   virtual nsIContent* GetChildAt(uint32_t aIndex) const = 0;
 
   /**
-   * Get a raw pointer to the child array.  This should only be used if you
-   * plan to walk a bunch of the kids, promise to make sure that nothing ever
-   * mutates (no attribute changes, not DOM tree changes, no script execution,
-   * NOTHING), and will never ever peform an out-of-bounds access here.  This
-   * method may return null if there are no children, or it may return a
-   * garbage pointer.  In all cases the out param will be set to the number of
-   * children.
-   */
-  virtual nsIContent * const * GetChildArray(uint32_t* aChildCount) const = 0;
-
-  /**
    * Get the index of a child within this content
    * @param aPossibleChild the child to get the index of.
    * @return the index of the child, or -1 if not a child
    *
    * If the return value is not -1, then calling GetChildAt() with that value
    * will return aPossibleChild.
    */
   virtual int32_t IndexOf(const nsINode* aPossibleChild) const = 0;
@@ -1127,20 +1117,22 @@ public:
 
     /**
      * Weak reference to this node.  This is cleared by the destructor of
      * nsNodeWeakReference.
      */
     nsNodeWeakReference* MOZ_NON_OWNING_REF mWeakReference;
 
     /**
-     * A set of ranges in the common ancestor for the selection to which
-     * this node belongs to.
+     * A set of ranges which are in the selection and which have this node as
+     * their endpoints' common ancestor.  This is a UniquePtr instead of just a
+     * LinkedList, because that prevents us from pushing DOMSlots up to the next
+     * allocation bucket size, at the cost of some complexity.
      */
-    mozilla::UniquePtr<nsTHashtable<nsPtrHashKey<nsRange>>> mCommonAncestorRanges;
+    mozilla::UniquePtr<mozilla::LinkedList<nsRange>> mCommonAncestorRanges;
 
     /**
      * Number of descendant nodes in the uncomposed document that have been
      * explicitly set as editable.
      */
     uint32_t mEditableDescendantCount;
   };
 
@@ -1283,20 +1275,19 @@ public:
    * not in same subtree, this returns the root content of the closeset subtree.
    */
   nsIContent* GetSelectionRootContent(nsIPresShell* aPresShell);
 
   virtual nsINodeList* ChildNodes();
   nsIContent* GetFirstChild() const { return mFirstChild; }
   nsIContent* GetLastChild() const
   {
-    uint32_t count;
-    nsIContent* const* children = GetChildArray(&count);
+    uint32_t count = GetChildCount();
 
-    return count > 0 ? children[count - 1] : nullptr;
+    return count > 0 ? GetChildAt(count - 1) : nullptr;
   }
 
   /**
    * Implementation is in nsIDocument.h, because it needs to cast from
    * nsIDocument* to nsINode*.
    */
   nsIDocument* GetOwnerDocument() const;
 
@@ -1938,33 +1929,33 @@ public:
                                                 CallerType aCallerType,
                                                 ErrorResult& aRv);
   already_AddRefed<DOMPoint> ConvertPointFromNode(const DOMPointInit& aPoint,
                                                   const TextOrElementOrDocument& aFrom,
                                                   const ConvertCoordinateOptions& aOptions,
                                                   CallerType aCallerType,
                                                   ErrorResult& aRv);
 
-  const nsTHashtable<nsPtrHashKey<nsRange>>* GetExistingCommonAncestorRanges() const
+  const mozilla::LinkedList<nsRange>* GetExistingCommonAncestorRanges() const
   {
     if (!HasSlots()) {
       return nullptr;
     }
-    mozilla::UniquePtr<nsTHashtable<nsPtrHashKey<nsRange>>>& ranges =
-      GetExistingSlots()->mCommonAncestorRanges;
-    return ranges.get();
+    return GetExistingSlots()->mCommonAncestorRanges.get();
   }
 
-  nsTHashtable<nsPtrHashKey<nsRange>>* GetExistingCommonAncestorRanges()
+  mozilla::LinkedList<nsRange>* GetExistingCommonAncestorRanges()
   {
-    nsINode::nsSlots* slots = GetExistingSlots();
-    return slots ? slots->mCommonAncestorRanges.get() : nullptr;
+    if (!HasSlots()) {
+      return nullptr;
+    }
+    return GetExistingSlots()->mCommonAncestorRanges.get();
   }
 
-  mozilla::UniquePtr<nsTHashtable<nsPtrHashKey<nsRange>>>& GetCommonAncestorRangesPtr()
+  mozilla::UniquePtr<mozilla::LinkedList<nsRange>>& GetCommonAncestorRangesPtr()
   {
     return Slots()->mCommonAncestorRanges;
   }
 
 protected:
 
   // Override this function to create a custom slots class.
   // Must not return null.
--- a/dom/base/nsRange.cpp
+++ b/dom/base/nsRange.cpp
@@ -200,24 +200,24 @@ nsRange::IsNodeSelected(nsINode* aNode, 
   NS_ASSERTION(n || !aNode->IsSelectionDescendant(),
                "orphan selection descendant");
 
   // Collect the potential ranges and their selection objects.
   RangeHashTable ancestorSelectionRanges;
   nsTHashtable<nsPtrHashKey<Selection>> ancestorSelections;
   uint32_t maxRangeCount = 0;
   for (; n; n = GetNextRangeCommonAncestor(n->GetParentNode())) {
-    nsTHashtable<nsPtrHashKey<nsRange>>* ranges =
-      n->GetExistingCommonAncestorRanges();
+    LinkedList<nsRange>* ranges = n->GetExistingCommonAncestorRanges();
     if (!ranges) {
       continue;
     }
-    for (auto iter = ranges->ConstIter(); !iter.Done(); iter.Next()) {
-      nsRange* range = iter.Get()->GetKey();
-      if (range->IsInSelection() && !range->Collapsed()) {
+    for (nsRange* range : *ranges) {
+      MOZ_ASSERT(range->IsInSelection(),
+                 "Why is this range registeed with a node?");
+      if (!range->Collapsed()) {
         ancestorSelectionRanges.PutEntry(range);
         Selection* selection = range->mSelection;
         ancestorSelections.PutEntry(selection);
         maxRangeCount = std::max(maxRangeCount, selection->RangeCount());
       }
     }
   }
 
@@ -332,16 +332,24 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMRange)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsRange)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsRange)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner);
+
+  // We _could_ just rely on Reset() to UnregisterCommonAncestor(),
+  // but it wouldn't know we're calling it from Unlink and so would do
+  // more work than it really needs to.
+  if (tmp->mRegisteredCommonAncestor) {
+    tmp->UnregisterCommonAncestor(tmp->mRegisteredCommonAncestor, true);
+  }
+
   tmp->Reset();
 
   // This needs to be unlinked after Reset() is called, as it controls
   // the result of IsInSelection() which is used by tmp->Reset().
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelection);
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsRange)
@@ -405,56 +413,55 @@ nsRange::RegisterCommonAncestor(nsINode*
 {
   NS_PRECONDITION(aNode, "bad arg");
   NS_ASSERTION(IsInSelection(), "registering range not in selection");
 
   mRegisteredCommonAncestor = aNode;
 
   MarkDescendants(aNode);
 
-  UniquePtr<nsTHashtable<nsPtrHashKey<nsRange>>>& ranges =
-    aNode->GetCommonAncestorRangesPtr();
+  UniquePtr<LinkedList<nsRange>>& ranges = aNode->GetCommonAncestorRangesPtr();
   if (!ranges) {
-    ranges = MakeUnique<nsRange::RangeHashTable>();
+    ranges = MakeUnique<LinkedList<nsRange>>();
   }
-  ranges->PutEntry(this);
+  ranges->insertBack(this);
   aNode->SetCommonAncestorForRangeInSelection();
 }
 
 void
-nsRange::UnregisterCommonAncestor(nsINode* aNode)
+nsRange::UnregisterCommonAncestor(nsINode* aNode, bool aIsUnlinking)
 {
   NS_PRECONDITION(aNode, "bad arg");
   NS_ASSERTION(aNode->IsCommonAncestorForRangeInSelection(), "wrong node");
   MOZ_DIAGNOSTIC_ASSERT(aNode == mRegisteredCommonAncestor, "wrong node");
-  nsTHashtable<nsPtrHashKey<nsRange>>* ranges =
-    aNode->GetExistingCommonAncestorRanges();
+  LinkedList<nsRange>* ranges = aNode->GetExistingCommonAncestorRanges();
   MOZ_ASSERT(ranges);
-  NS_ASSERTION(ranges->GetEntry(this), "unknown range");
 
   mRegisteredCommonAncestor = nullptr;
 
-  bool isNativeAnon = aNode->IsInNativeAnonymousSubtree();
-  bool removed = false;
-
-  if (ranges->Count() == 1) {
+#ifdef DEBUG
+  bool found = false;
+  for (nsRange* range : *ranges) {
+    if (range == this) {
+      found = true;
+      break;
+    }
+  }
+  MOZ_ASSERT(found,
+             "We should be in the list on our registered common ancestor");
+#endif // DEBUG
+
+  remove();
+
+  // We don't want to waste time unmarking flags on nodes that are
+  // being unlinked anyway.
+  if (!aIsUnlinking && ranges->isEmpty()) {
     aNode->ClearCommonAncestorForRangeInSelection();
-    if (!isNativeAnon) {
-      // For nodes which are in native anonymous subtrees, we optimize away the
-      // cost of deallocating the hashtable here because we may need to create
-      // it again shortly afterward.  We don't do this for all nodes in order
-      // to avoid the additional memory usage unconditionally.
-      aNode->GetCommonAncestorRangesPtr().reset();
-      removed = true;
-    }
     UnmarkDescendants(aNode);
   }
-  if (!removed) {
-    ranges->RemoveEntry(this);
-  }
 }
 
 /******************************************************
  * nsIMutationObserver implementation
  ******************************************************/
 void
 nsRange::CharacterDataChanged(nsIDocument* aDocument,
                               nsIContent* aContent,
@@ -507,17 +514,17 @@ nsRange::CharacterDataChanged(nsIDocumen
       newStart.Set(aInfo->mDetails->mNextSibling, newStartOffset);
       if (MOZ_UNLIKELY(aContent == mRoot)) {
         newRoot = IsValidBoundary(newStart.Container());
       }
 
       bool isCommonAncestor =
         IsInSelection() && mStart.Container() == mEnd.Container();
       if (isCommonAncestor) {
-        UnregisterCommonAncestor(mStart.Container());
+        UnregisterCommonAncestor(mStart.Container(), false);
         RegisterCommonAncestor(newStart.Container());
       }
       if (mStart.Container()->IsDescendantOfCommonAncestorForRangeInSelection()) {
         newStart.Container()->SetDescendantOfCommonAncestorForRangeInSelection();
       }
     } else {
       // If boundary is inside changed text, position it before change
       // else adjust start offset for the change in length.
@@ -541,17 +548,17 @@ nsRange::CharacterDataChanged(nsIDocumen
       NS_ASSERTION(mEnd.Offset() <= aInfo->mChangeEnd + 1,
                    "mEnd.Offset() is beyond the end of this node");
       newEnd.Set(aInfo->mDetails->mNextSibling, mEnd.Offset() - aInfo->mChangeStart);
 
       bool isCommonAncestor =
         IsInSelection() && mStart.Container() == mEnd.Container();
       if (isCommonAncestor && !newStart.Container()) {
         // The split occurs inside the range.
-        UnregisterCommonAncestor(mStart.Container());
+        UnregisterCommonAncestor(mStart.Container(), false);
         RegisterCommonAncestor(mStart.Container()->GetParentNode());
         newEnd.Container()->SetDescendantOfCommonAncestorForRangeInSelection();
       } else if (mEnd.Container()->
                    IsDescendantOfCommonAncestorForRangeInSelection()) {
         newEnd.Container()->SetDescendantOfCommonAncestorForRangeInSelection();
       }
     } else {
       int32_t newEndOffset = mEnd.Offset() <= aInfo->mChangeEnd ?
@@ -969,25 +976,29 @@ nsRange::DoSetRange(const RawRangeBounda
     }
     if (aRoot) {
       aRoot->AddMutationObserver(this);
     }
   }
   bool checkCommonAncestor =
     (mStart.Container() != aStart.Container() || mEnd.Container() != aEnd.Container()) &&
     IsInSelection() && !aNotInsertedYet;
-  nsINode* oldCommonAncestor = checkCommonAncestor ? GetCommonAncestor() : nullptr;
+
+  // GetCommonAncestor is unreliable while we're unlinking (could
+  // return null if our start/end have already been unlinked), so make
+  // sure to not use it here to determine our "old" current ancestor.
   mStart = aStart;
   mEnd = aEnd;
   mIsPositioned = !!mStart.Container();
   if (checkCommonAncestor) {
+    nsINode* oldCommonAncestor = mRegisteredCommonAncestor;
     nsINode* newCommonAncestor = GetCommonAncestor();
     if (newCommonAncestor != oldCommonAncestor) {
       if (oldCommonAncestor) {
-        UnregisterCommonAncestor(oldCommonAncestor);
+        UnregisterCommonAncestor(oldCommonAncestor, false);
       }
       if (newCommonAncestor) {
         RegisterCommonAncestor(newCommonAncestor);
       } else {
         NS_ASSERTION(!mIsPositioned, "unexpected disconnected nodes");
         mSelection = nullptr;
       }
     }
@@ -1029,22 +1040,22 @@ nsRange::SetSelection(mozilla::dom::Sele
   }
   // At least one of aSelection and mSelection must be null
   // aSelection will be null when we are removing from a selection
   // and a range can't be in more than one selection at a time,
   // thus mSelection must be null too.
   MOZ_ASSERT(!aSelection || !mSelection);
 
   mSelection = aSelection;
-  nsINode* commonAncestor = GetCommonAncestor();
-  NS_ASSERTION(commonAncestor, "unexpected disconnected nodes");
   if (mSelection) {
+    nsINode* commonAncestor = GetCommonAncestor();
+    NS_ASSERTION(commonAncestor, "unexpected disconnected nodes");
     RegisterCommonAncestor(commonAncestor);
   } else {
-    UnregisterCommonAncestor(commonAncestor);
+    UnregisterCommonAncestor(mRegisteredCommonAncestor, false);
   }
 }
 
 nsINode*
 nsRange::GetCommonAncestor() const
 {
   return mIsPositioned ?
     nsContentUtils::GetCommonAncestor(mStart.Container(), mEnd.Container()) :
--- a/dom/base/nsRange.h
+++ b/dom/base/nsRange.h
@@ -17,31 +17,34 @@
 #include "nsIDocument.h"
 #include "nsIDOMNode.h"
 #include "nsLayoutUtils.h"
 #include "prmon.h"
 #include "nsStubMutationObserver.h"
 #include "nsWrapperCache.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/GuardObjects.h"
+#include "mozilla/LinkedList.h"
 
 namespace mozilla {
 class ErrorResult;
 namespace dom {
 struct ClientRectsAndTexts;
 class DocumentFragment;
 class DOMRect;
 class DOMRectList;
 class Selection;
 } // namespace dom
 } // namespace mozilla
 
 class nsRange final : public nsIDOMRange,
                       public nsStubMutationObserver,
-                      public nsWrapperCache
+                      public nsWrapperCache,
+                      // For linking together selection-associated ranges.
+                      public mozilla::LinkedListElement<nsRange>
 {
   typedef mozilla::ErrorResult ErrorResult;
   typedef mozilla::dom::DOMRect DOMRect;
   typedef mozilla::dom::DOMRectList DOMRectList;
 
   virtual ~nsRange();
 
 public:
@@ -585,17 +588,17 @@ protected:
 
     mutable mozilla::Maybe<uint32_t> mOffset;
   };
 
   typedef RangeBoundaryBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent>> RangeBoundary;
   typedef RangeBoundaryBase<nsINode*, nsIContent*> RawRangeBoundary;
 
   void RegisterCommonAncestor(nsINode* aNode);
-  void UnregisterCommonAncestor(nsINode* aNode);
+  void UnregisterCommonAncestor(nsINode* aNode, bool aIsUnlinking);
   nsINode* IsValidBoundary(nsINode* aNode) const
   {
     return ComputeRootNode(aNode, mMaySpanAnonymousSubtrees);
   }
 
   /**
    * XXX nsRange should accept 0 - UINT32_MAX as offset.  However, users of
    *     nsRange treat offset as int32_t.  Additionally, some other internal
--- a/dom/base/nsTextNode.cpp
+++ b/dom/base/nsTextNode.cpp
@@ -160,19 +160,25 @@ void
 nsTextNode::List(FILE* out, int32_t aIndent) const
 {
   int32_t index;
   for (index = aIndent; --index >= 0; ) fputs("  ", out);
 
   fprintf(out, "Text@%p", static_cast<const void*>(this));
   fprintf(out, " flags=[%08x]", static_cast<unsigned int>(GetFlags()));
   if (IsCommonAncestorForRangeInSelection()) {
-    const nsTHashtable<nsPtrHashKey<nsRange>>* ranges =
-      GetExistingCommonAncestorRanges();
-    fprintf(out, " ranges:%d", ranges ? ranges->Count() : 0);
+    const LinkedList<nsRange>* ranges = GetExistingCommonAncestorRanges();
+    int32_t count = 0;
+    if (ranges) {
+      // Can't use range-based iteration on a const LinkedList, unfortunately.
+      for (const nsRange* r = ranges->getFirst(); r; r = r->getNext()) {
+        ++count;
+      }
+    }
+    fprintf(out, " ranges:%d", count);
   }
   fprintf(out, " primaryframe=%p", static_cast<void*>(GetPrimaryFrame()));
   fprintf(out, " refcount=%" PRIuPTR "<", mRefCnt.get());
 
   nsAutoString tmp;
   ToCString(tmp, 0, mText.GetLength());
   fputs(NS_LossyConvertUTF16toASCII(tmp).get(), out);
 
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -31,18 +31,16 @@
 #include "nsIDOMGeoPositionCallback.h"
 #include "nsIDOMGeoPositionErrorCallback.h"
 #include "nsRefPtrHashtable.h"
 #include "PermissionMessageUtils.h"
 #include "DriverCrashGuard.h"
 
 #define CHILD_PROCESS_SHUTDOWN_MESSAGE NS_LITERAL_STRING("child-process-shutdown")
 
-#define NO_REMOTE_TYPE ""
-
 // These must match the similar ones in E10SUtils.jsm.
 // Process names as reported by about:memory are defined in
 // ContentChild:RecvRemoteType.  Add your value there too or it will be called
 // "Web Content".
 #define DEFAULT_REMOTE_TYPE "web"
 #define FILE_REMOTE_TYPE "file"
 #define EXTENSION_REMOTE_TYPE "extension"
 
@@ -164,17 +162,17 @@ public:
 
   /**
    * Get or create a content process for:
    * 1. browser iframe
    * 2. remote xul <browser>
    * 3. normal iframe
    */
   static already_AddRefed<ContentParent>
-  GetNewOrUsedBrowserProcess(const nsAString& aRemoteType = NS_LITERAL_STRING(NO_REMOTE_TYPE),
+  GetNewOrUsedBrowserProcess(const nsAString& aRemoteType,
                              hal::ProcessPriority aPriority =
                              hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND,
                              ContentParent* aOpener = nullptr,
                              bool aPreferUsed = false);
 
   /**
    * Get or create a content process for a JS plugin. aPluginID is the id of the JS plugin
    * (@see nsFakePlugin::mId). There is a maximum of one process per JS plugin.
--- a/dom/plugins/ipc/PluginModuleChild.cpp
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -107,16 +107,19 @@ static WindowsDllInterceptor sComDlg32In
 typedef BOOL (WINAPI *GetOpenFileNameWPtr)(LPOPENFILENAMEW lpofn);
 static GetOpenFileNameWPtr sGetOpenFileNameWPtrStub = nullptr;
 typedef BOOL (WINAPI *GetSaveFileNameWPtr)(LPOPENFILENAMEW lpofn);
 static GetSaveFileNameWPtr sGetSaveFileNameWPtrStub = nullptr;
 
 typedef BOOL (WINAPI *SetCursorPosPtr)(int x, int y);
 static SetCursorPosPtr sSetCursorPosPtrStub = nullptr;
 
+typedef BOOL (WINAPI *PrintDlgWPtr)(LPPRINTDLGW aDlg);
+static PrintDlgWPtr sPrintDlgWPtrStub = nullptr;
+
 #endif
 
 /* static */
 bool
 PluginModuleChild::CreateForContentProcess(Endpoint<PPluginModuleChild>&& aEndpoint)
 {
     auto* child = new PluginModuleChild(false);
     return child->InitForContent(Move(aEndpoint));
@@ -2182,16 +2185,29 @@ PMCGetSaveFileNameW(LPOPENFILENAMEW aLpo
 }
 // static
 BOOL WINAPI
 PMCGetOpenFileNameW(LPOPENFILENAMEW aLpofn)
 {
     return PMCGetFileNameW(OPEN_FUNC, aLpofn);
 }
 
+//static
+BOOL WINAPI
+PMCPrintDlgW(LPPRINTDLGW aDlg)
+{
+  // Zero out the HWND supplied by the plugin.  We are sacrificing window
+  // parentage for the ability to run in the NPAPI sandbox.
+  HWND hwnd = aDlg->hwndOwner;
+  aDlg->hwndOwner = 0;
+  BOOL ret = sPrintDlgWPtrStub(aDlg);
+  aDlg->hwndOwner = hwnd;
+  return ret;
+}
+
 BOOL WINAPI PMCSetCursorPos(int x, int y);
 
 class SetCursorPosTaskData : public PluginThreadTaskData
 {
 public:
     SetCursorPosTaskData(int x, int y) : mX(x), mY(y) {}
     bool RunTask() { return PMCSetCursorPos(mX, mY); }
 private:
@@ -2260,16 +2276,22 @@ PluginModuleChild::AllocPPluginInstanceC
         sComDlg32Intercept.AddHook("GetSaveFileNameW", reinterpret_cast<intptr_t>(PMCGetSaveFileNameW),
                                  (void**) &sGetSaveFileNameWPtrStub);
     }
 
     if (!sGetOpenFileNameWPtrStub) {
         sComDlg32Intercept.AddHook("GetOpenFileNameW", reinterpret_cast<intptr_t>(PMCGetOpenFileNameW),
                                  (void**) &sGetOpenFileNameWPtrStub);
     }
+
+    if ((mQuirks & QUIRK_FLASH_HOOK_PRINTDLGW) &&
+        !sPrintDlgWPtrStub) {
+        sComDlg32Intercept.AddHook("PrintDlgW", reinterpret_cast<intptr_t>(PMCPrintDlgW),
+                                 (void**) &sPrintDlgWPtrStub);
+    }
 #endif
 
     return new PluginInstanceChild(&mFunctions, aMimeType, aNames,
                                    aValues);
 }
 
 void
 PluginModuleChild::InitQuirksModes(const nsCString& aMimeType)
--- a/dom/plugins/ipc/PluginQuirks.cpp
+++ b/dom/plugins/ipc/PluginQuirks.cpp
@@ -24,16 +24,17 @@ int GetQuirksFromMimeTypeAndFilename(con
         quirks |= QUIRK_WINLESS_TRACKPOPUP_HOOK;
         quirks |= QUIRK_FLASH_THROTTLE_WMUSER_EVENTS;
         quirks |= QUIRK_FLASH_HOOK_SETLONGPTR;
         quirks |= QUIRK_FLASH_HOOK_GETWINDOWINFO;
         quirks |= QUIRK_FLASH_FIXUP_MOUSE_CAPTURE;
         quirks |= QUIRK_WINLESS_HOOK_IME;
 #if defined(_M_X64) || defined(__x86_64__)
         quirks |= QUIRK_FLASH_HOOK_GETKEYSTATE;
+        quirks |= QUIRK_FLASH_HOOK_PRINTDLGW;
 #endif
 #endif
     }
 
 #ifdef XP_MACOSX
     // Whitelist Flash to support offline renderer.
     if (specialType == nsPluginHost::eSpecialType_Flash) {
         quirks |= QUIRK_ALLOW_OFFLINE_RENDERER;
--- a/dom/plugins/ipc/PluginQuirks.h
+++ b/dom/plugins/ipc/PluginQuirks.h
@@ -40,16 +40,18 @@ enum PluginQuirks {
   // Work around a Flash bug where it fails to check the error code of a
   // NPN_GetValue(NPNVdocumentOrigin) call before trying to dereference
   // its char* output.
   QUIRK_FLASH_RETURN_EMPTY_DOCUMENT_ORIGIN        = 1 << 10,
   // Win: Hook IMM32 API to handle IME event on windowless plugin
   QUIRK_WINLESS_HOOK_IME                          = 1 << 12,
   // Win: Hook GetKeyState to get keyboard state on sandbox process
   QUIRK_FLASH_HOOK_GETKEYSTATE                    = 1 << 13,
+  // Win: Hook PrintDlgW to show print settings dialog on sandbox process
+  QUIRK_FLASH_HOOK_PRINTDLGW                      = 1 << 14,
 };
 
 int GetQuirksFromMimeTypeAndFilename(const nsCString& aMimeType,
                                      const nsCString& aPluginFilename);
 
 } /* namespace plugins */
 } /* namespace mozilla */
 
--- a/dom/push/PushNotifier.cpp
+++ b/dom/push/PushNotifier.cpp
@@ -103,16 +103,23 @@ PushNotifier::Dispatch(PushDispatcher& a
     Unused << NS_WARN_IF(NS_FAILED(aDispatcher.NotifyObservers()));
 
     nsTArray<ContentParent*> contentActors;
     ContentParent::GetAll(contentActors);
     if (!contentActors.IsEmpty()) {
       // At least one content process is active, so e10s must be enabled.
       // Broadcast a message to notify observers and service workers.
       for (uint32_t i = 0; i < contentActors.Length(); ++i) {
+        // We need to filter based on process type, only "web" AKA the default
+        // remote type is acceptable.
+        if (!contentActors[i]->GetRemoteType().EqualsLiteral(
+               DEFAULT_REMOTE_TYPE)) {
+          continue;
+        }
+
         // Ensure that the content actor has the permissions avaliable for the
         // principal the push is being sent for before sending the push message
         // down.
         Unused << contentActors[i]->
           TransmitPermissionsForPrincipal(aDispatcher.GetPrincipal());
         if (aDispatcher.SendToChild(contentActors[i])) {
           // Only send the push message to the first content process to avoid
           // multiple SWs showing the same notification. See bug 1300112.
--- a/dom/storage/StorageDBThread.cpp
+++ b/dom/storage/StorageDBThread.cpp
@@ -1086,18 +1086,20 @@ StorageDBThread::DBOperation::Perform(St
       nsAutoString value;
       rv = stmt->GetString(1, value);
       NS_ENSURE_SUCCESS(rv, rv);
 
       if (!mCache->LoadItem(key, value)) {
         break;
       }
     }
-
-    mCache->LoadDone(NS_OK);
+    // The loop condition's call to ExecuteStep() may have terminated because
+    // !NS_SUCCEEDED(), we need an early return to cover that case.  This also
+    // covers success cases as well, but that's inductively safe.
+    NS_ENSURE_SUCCESS(rv, rv);
     break;
   }
 
   case opGetUsage:
   {
     nsCOMPtr<mozIStorageStatement> stmt = aThread->mWorkerStatements.GetCachedStatement(
       "SELECT SUM(LENGTH(key) + LENGTH(value)) FROM webappsstore2 "
       "WHERE (originAttributes || ':' || originKey) LIKE :usageOrigin"
--- a/dom/storage/StorageIPC.cpp
+++ b/dom/storage/StorageIPC.cpp
@@ -664,32 +664,37 @@ public:
   }
   virtual const nsCString& OriginNoSuffix() const { return mOrigin; }
   virtual const nsCString& OriginSuffix() const { return mSuffix; }
   virtual bool Loaded() { return mLoaded; }
   virtual uint32_t LoadedCount() { return mLoadedCount; }
   virtual bool LoadItem(const nsAString& aKey, const nsString& aValue)
   {
     // Called on the aCache background thread
+    MOZ_ASSERT(!mLoaded);
     if (mLoaded) {
       return false;
     }
 
     ++mLoadedCount;
     mKeys->AppendElement(aKey);
     mValues->AppendElement(aValue);
     return true;
   }
 
   virtual void LoadDone(nsresult aRv)
   {
     // Called on the aCache background thread
     MonitorAutoLock monitor(mMonitor);
+    MOZ_ASSERT(!mLoaded && mRv);
     mLoaded = true;
-    *mRv = aRv;
+    if (mRv) {
+      *mRv = aRv;
+      mRv = nullptr;
+    }
     monitor.Notify();
   }
 
   virtual void LoadWait()
   {
     // Called on the main thread, exits after LoadDone() call
     MonitorAutoLock monitor(mMonitor);
     while (!mLoaded) {
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -548,21 +548,18 @@ GLContext::InitWithPrefixImpl(const char
     uint32_t majorVer, minorVer;
     if (!ParseVersion(versionStr, &majorVer, &minorVer)) {
         MOZ_ASSERT(false, "Failed to parse GL_VERSION");
         return false;
     }
     MOZ_ASSERT(majorVer < 10);
     MOZ_ASSERT(minorVer < 10);
     mVersion = majorVer*100 + minorVer*10;
-    if (mVersion < 200) {
-        // Mac OSX 10.6/10.7 machines with Intel GPUs claim only OpenGL 1.4 but
-        // have all the GL2+ extensions that we need.
-        mVersion = 200;
-    }
+    if (mVersion < 200)
+        return false;
 
     ////
 
     const auto glslVersionStr = (const char*)fGetString(LOCAL_GL_SHADING_LANGUAGE_VERSION);
     if (!glslVersionStr) {
         // This happens on the Android emulators. We'll just return 100
         mShadingLanguageVersion = 100;
     } else if (ParseVersion(glslVersionStr, &majorVer, &minorVer)) {
--- a/intl/strres/nsStringBundle.cpp
+++ b/intl/strres/nsStringBundle.cpp
@@ -20,16 +20,19 @@
 #include "nsIInputStream.h"
 #include "nsIURI.h"
 #include "nsIObserverService.h"
 #include "nsCOMArray.h"
 #include "nsTextFormatter.h"
 #include "nsIErrorService.h"
 #include "nsICategoryManager.h"
 #include "nsContentUtils.h"
+#include "nsStringStream.h"
+#include "mozilla/ResultExtensions.h"
+#include "mozilla/URLPreloader.h"
 
 // for async loading
 #ifdef ASYNC_LOADING
 #include "nsIBinaryInputStream.h"
 #include "nsIStringStream.h"
 #endif
 
 using namespace mozilla;
@@ -88,31 +91,37 @@ nsStringBundle::LoadProperties()
   nsCString scheme;
   uri->GetScheme(scheme);
   if (!scheme.EqualsLiteral("chrome") && !scheme.EqualsLiteral("jar") &&
       !scheme.EqualsLiteral("resource") && !scheme.EqualsLiteral("file") &&
       !scheme.EqualsLiteral("data")) {
     return NS_ERROR_ABORT;
   }
 
-  nsCOMPtr<nsIChannel> channel;
-  rv = NS_NewChannel(getter_AddRefs(channel),
-                     uri,
-                     nsContentUtils::GetSystemPrincipal(),
-                     nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
-                     nsIContentPolicy::TYPE_OTHER);
+  nsCOMPtr<nsIInputStream> in;
 
-  if (NS_FAILED(rv)) return rv;
+  auto result = URLPreloader::ReadURI(uri);
+  if (result.isOk()) {
+    MOZ_TRY(NS_NewCStringInputStream(getter_AddRefs(in), result.unwrap()));
+  } else {
+    nsCOMPtr<nsIChannel> channel;
+    rv = NS_NewChannel(getter_AddRefs(channel),
+                       uri,
+                       nsContentUtils::GetSystemPrincipal(),
+                       nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
+                       nsIContentPolicy::TYPE_OTHER);
 
-  // It's a string bundle.  We expect a text/plain type, so set that as hint
-  channel->SetContentType(NS_LITERAL_CSTRING("text/plain"));
+    if (NS_FAILED(rv)) return rv;
 
-  nsCOMPtr<nsIInputStream> in;
-  rv = channel->Open2(getter_AddRefs(in));
-  if (NS_FAILED(rv)) return rv;
+    // It's a string bundle.  We expect a text/plain type, so set that as hint
+    channel->SetContentType(NS_LITERAL_CSTRING("text/plain"));
+
+    rv = channel->Open2(getter_AddRefs(in));
+    if (NS_FAILED(rv)) return rv;
+  }
 
   NS_ASSERTION(NS_SUCCEEDED(rv) && in, "Error in OpenBlockingStream");
   NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && in, NS_ERROR_FAILURE);
 
   static NS_DEFINE_CID(kPersistentPropertiesCID, NS_IPERSISTENTPROPERTIES_CID);
   mProps = do_CreateInstance(kPersistentPropertiesCID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
--- a/ipc/mscom/Interceptor.cpp
+++ b/ipc/mscom/Interceptor.cpp
@@ -300,16 +300,17 @@ Interceptor::DisconnectObject(DWORD dwRe
 {
   return mStdMarshal->DisconnectObject(dwReserved);
 }
 
 Interceptor::MapEntry*
 Interceptor::Lookup(REFIID aIid)
 {
   mMutex.AssertCurrentThreadOwns();
+
   for (uint32_t index = 0, len = mInterceptorMap.Length(); index < len; ++index) {
     if (mInterceptorMap[index].mIID == aIid) {
       return &mInterceptorMap[index];
     }
   }
   return nullptr;
 }
 
@@ -362,25 +363,67 @@ Interceptor::CreateInterceptor(REFIID aI
   // If this assert fires then the interceptor doesn't like something about
   // the format of the typelib. One thing in particular that it doesn't like
   // is complex types that contain unions.
   MOZ_ASSERT(SUCCEEDED(hr));
   return hr;
 }
 
 HRESULT
-Interceptor::GetInitialInterceptorForIID(detail::LiveSetAutoLock& aLock,
+Interceptor::PublishTarget(detail::LiveSetAutoLock& aLiveSetLock,
+                           RefPtr<IUnknown> aInterceptor,
+                           REFIID aTargetIid,
+                           STAUniquePtr<IUnknown> aTarget)
+{
+  RefPtr<IWeakReference> weakRef;
+  HRESULT hr = GetWeakReference(getter_AddRefs(weakRef));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  // mTarget is a weak reference to aTarget. This is safe because we transfer
+  // ownership of aTarget into mInterceptorMap which remains live for the
+  // lifetime of this Interceptor.
+  mTarget = ToInterceptorTargetPtr(aTarget);
+  GetLiveSet().Put(mTarget.get(), weakRef.forget());
+
+  // Now we transfer aTarget's ownership into mInterceptorMap.
+  mInterceptorMap.AppendElement(MapEntry(aTargetIid,
+                                         aInterceptor,
+                                         aTarget.release()));
+
+  // Release the live set lock because subsequent operations may post work to
+  // the main thread, creating potential for deadlocks.
+  aLiveSetLock.Unlock();
+  return S_OK;
+}
+
+HRESULT
+Interceptor::GetInitialInterceptorForIID(detail::LiveSetAutoLock& aLiveSetLock,
                                          REFIID aTargetIid,
                                          STAUniquePtr<IUnknown> aTarget,
                                          void** aOutInterceptor)
 {
   MOZ_ASSERT(aOutInterceptor);
-  MOZ_ASSERT(aTargetIid != IID_IUnknown && aTargetIid != IID_IMarshal);
+  MOZ_ASSERT(aTargetIid != IID_IMarshal);
   MOZ_ASSERT(!IsProxy(aTarget.get()));
 
+  if (aTargetIid == IID_IUnknown) {
+    // We must lock ourselves so that nothing can race with us once we have been
+    // published to the live set.
+    AutoLock lock(*this);
+
+    HRESULT hr = PublishTarget(aLiveSetLock, nullptr, aTargetIid, Move(aTarget));
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    return QueryInterface(aTargetIid, aOutInterceptor);
+  }
+
   // Raise the refcount for stabilization purposes during aggregation
   RefPtr<IUnknown> kungFuDeathGrip(static_cast<IUnknown*>(
         static_cast<WeakReferenceSupport*>(this)));
 
   RefPtr<IUnknown> unkInterceptor;
   HRESULT hr = CreateInterceptor(aTargetIid, kungFuDeathGrip,
                                  getter_AddRefs(unkInterceptor));
   if (FAILED(hr)) {
@@ -394,37 +437,25 @@ Interceptor::GetInitialInterceptorForIID
     return hr;
   }
 
   hr = interceptor->RegisterSink(mEventSink);
   if (FAILED(hr)) {
     return hr;
   }
 
-  RefPtr<IWeakReference> weakRef;
-  hr = GetWeakReference(getter_AddRefs(weakRef));
+  // We must lock ourselves so that nothing can race with us once we have been
+  // published to the live set.
+  AutoLock lock(*this);
+
+  hr = PublishTarget(aLiveSetLock, unkInterceptor, aTargetIid, Move(aTarget));
   if (FAILED(hr)) {
     return hr;
   }
 
-  // mTarget is a weak reference to aTarget. This is safe because we transfer
-  // ownership of aTarget into mInterceptorMap which remains live for the
-  // lifetime of this Interceptor.
-  mTarget = ToInterceptorTargetPtr(aTarget);
-  GetLiveSet().Put(mTarget.get(), weakRef.forget());
-
-  // Release the live set lock because GetInterceptorForIID will post work to
-  // the main thread, creating potential for deadlocks.
-  aLock.Unlock();
-
-  // Now we transfer aTarget's ownership into mInterceptorMap.
-  mInterceptorMap.AppendElement(MapEntry(aTargetIid,
-                                         unkInterceptor,
-                                         aTarget.release()));
-
   if (mEventSink->MarshalAs(aTargetIid) == aTargetIid) {
     return unkInterceptor->QueryInterface(aTargetIid, aOutInterceptor);
   }
 
   return GetInterceptorForIID(aTargetIid, aOutInterceptor);
 }
 
 /**
--- a/ipc/mscom/Interceptor.h
+++ b/ipc/mscom/Interceptor.h
@@ -115,25 +115,29 @@ private:
     IID               mIID;
     RefPtr<IUnknown>  mInterceptor;
     IUnknown*         mTargetInterface;
   };
 
 private:
   explicit Interceptor(IInterceptorSink* aSink);
   ~Interceptor();
-  HRESULT GetInitialInterceptorForIID(detail::LiveSetAutoLock& aLock,
+  HRESULT GetInitialInterceptorForIID(detail::LiveSetAutoLock& aLiveSetLock,
                                       REFIID aTargetIid,
                                       STAUniquePtr<IUnknown> aTarget,
                                       void** aOutInterface);
   MapEntry* Lookup(REFIID aIid);
   HRESULT QueryInterfaceTarget(REFIID aIid, void** aOutput);
   HRESULT ThreadSafeQueryInterface(REFIID aIid,
                                    IUnknown** aOutInterface) override;
   HRESULT CreateInterceptor(REFIID aIid, IUnknown* aOuter, IUnknown** aOutput);
+  HRESULT PublishTarget(detail::LiveSetAutoLock& aLiveSetLock,
+                        RefPtr<IUnknown> aInterceptor,
+                        REFIID aTargetIid,
+                        STAUniquePtr<IUnknown> aTarget);
 
 private:
   InterceptorTargetPtr<IUnknown>  mTarget;
   RefPtr<IInterceptorSink>  mEventSink;
   mozilla::Mutex            mMutex; // Guards mInterceptorMap
   // Using a nsTArray since the # of interfaces is not going to be very high
   nsTArray<MapEntry>        mInterceptorMap;
   RefPtr<IUnknown>          mStdMarshalUnk;
--- a/ipc/mscom/WeakRef.cpp
+++ b/ipc/mscom/WeakRef.cpp
@@ -104,16 +104,28 @@ WeakReferenceSupport::WeakReferenceSuppo
   ::InitializeCS(mCSForQI);
 }
 
 WeakReferenceSupport::~WeakReferenceSupport()
 {
   ::DeleteCriticalSection(&mCSForQI);
 }
 
+void
+WeakReferenceSupport::Lock()
+{
+  ::EnterCriticalSection(&mCSForQI);
+}
+
+void
+WeakReferenceSupport::Unlock()
+{
+  ::LeaveCriticalSection(&mCSForQI);
+}
+
 HRESULT
 WeakReferenceSupport::QueryInterface(REFIID riid, void** ppv)
 {
   RefPtr<IUnknown> punk;
   if (!ppv) {
     return E_INVALIDARG;
   }
   *ppv = nullptr;
--- a/ipc/mscom/WeakRef.h
+++ b/ipc/mscom/WeakRef.h
@@ -7,16 +7,17 @@
 #ifndef mozilla_mscom_WeakRef_h
 #define mozilla_mscom_WeakRef_h
 
 #include <guiddef.h>
 #include <unknwn.h>
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Atomics.h"
+#include "mozilla/Mutex.h"
 #include "mozilla/RefPtr.h"
 #include "nsISupportsImpl.h"
 
 /**
  * Thread-safe weak references for COM that works pre-Windows 8 and do not
  * require WinRT.
  */
 
@@ -96,16 +97,22 @@ public:
 
 protected:
   explicit WeakReferenceSupport(Flags aFlags);
   virtual ~WeakReferenceSupport();
 
   virtual HRESULT ThreadSafeQueryInterface(REFIID aIid,
                                            IUnknown** aOutInterface) = 0;
 
+  void Lock();
+  void Unlock();
+
+  typedef BaseAutoLock<WeakReferenceSupport> AutoLock;
+  friend class BaseAutoLock<WeakReferenceSupport>;
+
 private:
   RefPtr<detail::SharedRef> mSharedRef;
   ULONG                     mRefCnt;
   Flags                     mFlags;
   CRITICAL_SECTION          mCSForQI;
 };
 
 class WeakRef final : public IWeakReference
--- a/js/src/build/Makefile.in
+++ b/js/src/build/Makefile.in
@@ -76,19 +76,26 @@ install::
 # END SpiderMonkey header installation
 #############################################
 
 # Install versioned script, for parallel installability in Linux distributions
 install:: js-config
 	cp $^ js$(MOZJS_MAJOR_VERSION)-config
 	$(SYSINSTALL) js$(MOZJS_MAJOR_VERSION)-config $(DESTDIR)$(bindir)
 
+# Use install_name_tool to set the install_name properly for standalone
+# installed libraries on macOS
 install:: $(REAL_LIBRARY) $(SHARED_LIBRARY) $(IMPORT_LIBRARY)
 ifneq (,$(REAL_LIBRARY))
 	$(SYSINSTALL) $(REAL_LIBRARY) $(DESTDIR)$(libdir)
 	mv -f $(DESTDIR)$(libdir)/$(REAL_LIBRARY) $(subst $(STATIC_LIBRARY_NAME),$(LIBRARY_NAME),$(DESTDIR)$(libdir)/$(REAL_LIBRARY))
 endif
 ifneq (,$(SHARED_LIBRARY))
 	$(SYSINSTALL) $(SHARED_LIBRARY) $(DESTDIR)$(libdir)
+ifeq ($(OS_ARCH),Darwin)
+	install_name_tool -id $(abspath $(libdir)/$(SHARED_LIBRARY)) $(DESTDIR)$(libdir)/$(SHARED_LIBRARY)
+endif
 endif
 ifneq (,$(IMPORT_LIBRARY))
+ifneq ($(IMPORT_LIBRARY),$(SHARED_LIBRARY))
 	$(SYSINSTALL) $(IMPORT_LIBRARY) $(DESTDIR)$(libdir)
 endif
+endif
--- a/js/xpconnect/idl/xpccomponents.idl
+++ b/js/xpconnect/idl/xpccomponents.idl
@@ -9,16 +9,17 @@
 #include "jspubtd.h"
 %}
 
 interface xpcIJSWeakReference;
 interface nsIAddonInterposition;
 interface nsIClassInfo;
 interface nsIComponentManager;
 interface nsICycleCollectorListener;
+interface nsIFile;
 interface nsIJSCID;
 interface nsIJSIID;
 interface nsIPrincipal;
 interface nsIStackFrame;
 
 /**
 * interface of Components.interfacesByID
 * (interesting stuff only reflected into JavaScript)
@@ -688,16 +689,23 @@ interface nsIXPCComponents_Utils : nsISu
     [implicit_jscontext]
     void allowCPOWsInAddon(in ACString addonId, in bool allow);
 
     /*
      * Return a fractional number of milliseconds from process
      * startup, measured with a monotonic clock.
      */
     double now();
+
+    /*
+     * Reads the given file and returns its contents. If called during early
+     * startup, the file will be pre-read on a background thread during profile
+     * startup so its contents will be available the next time they're read.
+     */
+    ACString readFile(in nsIFile file);
 };
 
 /**
 * Interface for the 'Components' object.
 *
 * The first interface contains things that are available to non-chrome XBL code
 * that runs in a scope with an ExpandedPrincipal. The second interface
 * includes members that are only exposed to chrome.
--- a/js/xpconnect/loader/ScriptPreloader-inl.h
+++ b/js/xpconnect/loader/ScriptPreloader-inl.h
@@ -20,16 +20,25 @@
 #include <prio.h>
 
 namespace mozilla {
 
 namespace loader {
 
 using mozilla::dom::AutoJSAPI;
 
+static inline Result<Ok, nsresult>
+Write(PRFileDesc* fd, const void* data, int32_t len)
+{
+    if (PR_Write(fd, data, len) != len) {
+        return Err(NS_ERROR_FAILURE);
+    }
+    return Ok();
+}
+
 struct MOZ_RAII AutoSafeJSAPI : public AutoJSAPI
 {
     AutoSafeJSAPI() { Init(); }
 };
 
 
 class OutputBuffer
 {
@@ -254,18 +263,25 @@ public:
         ElemType get()
         {
           if (done_) {
             return nullptr;
           }
           return iter().Data();
         }
 
+        const ElemType get() const
+        {
+          return const_cast<Elem*>(this)->get();
+        }
+
         ElemType operator->() { return get(); }
 
+        const ElemType operator->() const { return get(); }
+
         operator ElemType() { return get(); }
 
         void Remove() { iter().Remove(); }
 
         Elem& operator++()
         {
             MOZ_ASSERT(!done_);
 
--- a/js/xpconnect/loader/ScriptPreloader.cpp
+++ b/js/xpconnect/loader/ScriptPreloader.cpp
@@ -3,16 +3,18 @@
 /* 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 "ScriptPreloader-inl.h"
 #include "mozilla/ScriptPreloader.h"
 #include "mozilla/loader/ScriptCacheActors.h"
 
+#include "mozilla/URLPreloader.h"
+
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/FileUtils.h"
 #include "mozilla/Logging.h"
 #include "mozilla/ScopeExit.h"
 #include "mozilla/Services.h"
 #include "mozilla/Unused.h"
 #include "mozilla/dom/ContentChild.h"
@@ -408,16 +410,20 @@ ScriptPreloader::InitCache(const nsAStri
     mBaseName = basePath;
 
     RegisterWeakMemoryReporter(this);
 
     if (!XRE_IsParentProcess()) {
         return Ok();
     }
 
+    // Note: Code on the main thread *must not access Omnijar in any way* until
+    // this AutoBeginReading guard is destroyed.
+    URLPreloader::AutoBeginReading abr;
+
     MOZ_TRY(OpenCache());
 
     return InitCacheInternal();
 }
 
 Result<Ok, nsresult>
 ScriptPreloader::InitCache(const Maybe<ipc::FileDescriptor>& cacheFile, ScriptCacheChild* cacheChild)
 {
@@ -512,25 +518,16 @@ ScriptPreloader::InitCacheInternal()
         mPendingScripts = Move(scripts);
         cleanup.release();
     }
 
     DecodeNextBatch(OFF_THREAD_FIRST_CHUNK_SIZE);
     return Ok();
 }
 
-static inline Result<Ok, nsresult>
-Write(PRFileDesc* fd, const void* data, int32_t len)
-{
-    if (PR_Write(fd, data, len) != len) {
-        return Err(NS_ERROR_FAILURE);
-    }
-    return Ok();
-}
-
 void
 ScriptPreloader::PrepareCacheWriteInternal()
 {
     MOZ_ASSERT(NS_IsMainThread());
 
     mMonitor.AssertCurrentThreadOwns();
 
     auto cleanup = MakeScopeExit([&] () {
@@ -598,16 +595,18 @@ ScriptPreloader::PrepareCacheWrite()
 //
 // - A block of XDR data for the encoded scripts, with each script's data at
 //   an offset from the start of the block, as specified above.
 Result<Ok, nsresult>
 ScriptPreloader::WriteCache()
 {
     MOZ_ASSERT(!NS_IsMainThread());
 
+    Unused << URLPreloader::GetSingleton().WriteCache();
+
     if (!mDataPrepared && !mSaveComplete) {
         MOZ_ASSERT(!mBlockedOnSyncDispatch);
         mBlockedOnSyncDispatch = true;
 
         MonitorAutoUnlock mau(mSaveMonitor);
 
         NS_DispatchToMainThread(
           NewRunnableMethod("ScriptPreloader::PrepareCacheWrite",
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/loader/URLPreloader.cpp
@@ -0,0 +1,691 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=4 et sw=4 tw=99: */
+/* 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 "ScriptPreloader-inl.h"
+#include "mozilla/URLPreloader.h"
+#include "mozilla/loader/AutoMemMap.h"
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/FileUtils.h"
+#include "mozilla/Logging.h"
+#include "mozilla/ScopeExit.h"
+#include "mozilla/Services.h"
+#include "mozilla/Unused.h"
+#include "mozilla/Vector.h"
+
+#include "MainThreadUtils.h"
+#include "nsPrintfCString.h"
+#include "nsDebug.h"
+#include "nsIFile.h"
+#include "nsIFileURL.h"
+#include "nsIObserverService.h"
+#include "nsNetUtil.h"
+#include "nsPromiseFlatString.h"
+#include "nsProxyRelease.h"
+#include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
+#include "nsZipArchive.h"
+#include "xpcpublic.h"
+
+#include "mozilla/dom/ContentChild.h"
+
+#undef DELAYED_STARTUP_TOPIC
+#define DELAYED_STARTUP_TOPIC "sessionstore-windows-restored"
+
+namespace mozilla {
+namespace {
+static LazyLogModule gURLLog("URLPreloader");
+
+#define LOG(level, ...) MOZ_LOG(gURLLog, LogLevel::level, (__VA_ARGS__))
+
+template<typename T>
+bool
+StartsWith(const T& haystack, const T& needle)
+{
+    return StringHead(haystack, needle.Length()) == needle;
+}
+} // anonymous namespace
+
+using namespace mozilla::loader;
+
+nsresult
+URLPreloader::CollectReports(nsIHandleReportCallback* aHandleReport,
+                             nsISupports* aData, bool aAnonymize)
+{
+    MOZ_COLLECT_REPORT(
+        "explicit/url-preloader/other", KIND_HEAP, UNITS_BYTES,
+        ShallowSizeOfIncludingThis(MallocSizeOf),
+        "Memory used by the URL preloader service itself.");
+
+    for (const auto& elem : IterHash(mCachedURLs)) {
+        nsAutoCString pathName;
+        pathName.Append(elem->mPath);
+        // The backslashes will automatically be replaced with slashes in
+        // about:memory, without splitting each path component into a separate
+        // branch in the memory report tree.
+        pathName.ReplaceChar('/', '\\');
+
+        nsPrintfCString path("explicit/url-preloader/cached-urls/%s/[%s]",
+                             elem->TypeString(), pathName.get());
+
+        aHandleReport->Callback(
+            EmptyCString(), path, KIND_HEAP, UNITS_BYTES,
+            elem->SizeOfIncludingThis(MallocSizeOf),
+            NS_LITERAL_CSTRING("Memory used to hold cache data for files which "
+                               "have been read or pre-loaded during this session."),
+            aData);
+    }
+
+    return NS_OK;
+}
+
+
+URLPreloader&
+URLPreloader::GetSingleton()
+{
+    static RefPtr<URLPreloader> singleton;
+
+    if (!singleton) {
+        singleton = new URLPreloader();
+        ClearOnShutdown(&singleton);
+    }
+
+    return *singleton;
+}
+
+
+bool URLPreloader::sInitialized = false;
+
+URLPreloader::URLPreloader()
+{
+    if (InitInternal().isOk()) {
+        sInitialized = true;
+        RegisterWeakMemoryReporter(this);
+    }
+}
+
+URLPreloader::~URLPreloader()
+{
+    if (sInitialized) {
+        UnregisterWeakMemoryReporter(this);
+    }
+}
+
+Result<Ok, nsresult>
+URLPreloader::InitInternal()
+{
+    MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+    if (Omnijar::HasOmnijar(Omnijar::GRE)) {
+        MOZ_TRY(Omnijar::GetURIString(Omnijar::GRE, mGREPrefix));
+    }
+    if (Omnijar::HasOmnijar(Omnijar::APP)) {
+        MOZ_TRY(Omnijar::GetURIString(Omnijar::APP, mAppPrefix));
+    }
+
+    nsresult rv;
+    nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
+    MOZ_TRY(rv);
+
+    nsCOMPtr<nsIProtocolHandler> ph;
+    MOZ_TRY(ios->GetProtocolHandler("resource", getter_AddRefs(ph)));
+
+    mResProto = do_QueryInterface(ph, &rv);
+    MOZ_TRY(rv);
+
+    mChromeReg = services::GetChromeRegistryService();
+    if (!mChromeReg) {
+        return Err(NS_ERROR_UNEXPECTED);
+    }
+
+    if (XRE_IsParentProcess()) {
+        nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+
+        obs->AddObserver(this, DELAYED_STARTUP_TOPIC, false);
+
+        MOZ_TRY(NS_GetSpecialDirectory("ProfLDS", getter_AddRefs(mProfD)));
+    } else {
+        mStartupFinished = true;
+        mReaderInitialized = true;
+    }
+
+    return Ok();
+}
+
+Result<nsCOMPtr<nsIFile>, nsresult>
+URLPreloader::GetCacheFile(const nsAString& suffix)
+{
+    if (!mProfD) {
+        return Err(NS_ERROR_NOT_INITIALIZED);
+    }
+
+    nsCOMPtr<nsIFile> cacheFile;
+    MOZ_TRY(mProfD->Clone(getter_AddRefs(cacheFile)));
+
+    MOZ_TRY(cacheFile->AppendNative(NS_LITERAL_CSTRING("startupCache")));
+    Unused << cacheFile->Create(nsIFile::DIRECTORY_TYPE, 0777);
+
+    MOZ_TRY(cacheFile->Append(NS_LITERAL_STRING("urlCache") + suffix));
+
+    return Move(cacheFile);
+}
+
+static const uint8_t URL_MAGIC[] = "mozURLcachev001";
+
+Result<nsCOMPtr<nsIFile>, nsresult>
+URLPreloader::FindCacheFile()
+{
+    nsCOMPtr<nsIFile> cacheFile;
+    MOZ_TRY_VAR(cacheFile, GetCacheFile(NS_LITERAL_STRING(".bin")));
+
+    bool exists;
+    MOZ_TRY(cacheFile->Exists(&exists));
+    if (exists) {
+        MOZ_TRY(cacheFile->MoveTo(nullptr, NS_LITERAL_STRING("urlCache-current.bin")));
+    } else {
+        MOZ_TRY(cacheFile->SetLeafName(NS_LITERAL_STRING("urlCache-current.bin")));
+        MOZ_TRY(cacheFile->Exists(&exists));
+        if (!exists) {
+            return Err(NS_ERROR_FILE_NOT_FOUND);
+        }
+    }
+
+    return Move(cacheFile);
+}
+
+Result<Ok, nsresult>
+URLPreloader::WriteCache()
+{
+    MOZ_ASSERT(!NS_IsMainThread());
+
+    nsCOMPtr<nsIFile> cacheFile;
+    MOZ_TRY_VAR(cacheFile, GetCacheFile(NS_LITERAL_STRING("-new.bin")));
+
+    bool exists;
+    MOZ_TRY(cacheFile->Exists(&exists));
+    if (exists) {
+        MOZ_TRY(cacheFile->Remove(false));
+    }
+
+    {
+        AutoFDClose fd;
+        MOZ_TRY(cacheFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE, 0644, &fd.rwget()));
+
+        nsTArray<URLEntry*> entries;
+        for (auto& entry : IterHash(mCachedURLs)) {
+            if (entry->mReadTime) {
+                entries.AppendElement(entry);
+            }
+        }
+
+        entries.Sort(URLEntry::Comparator());
+
+        OutputBuffer buf;
+        for (auto entry : entries) {
+            entry->Code(buf);
+        }
+
+        uint8_t headerSize[4];
+        LittleEndian::writeUint32(headerSize, buf.cursor());
+
+        MOZ_TRY(Write(fd, URL_MAGIC, sizeof(URL_MAGIC)));
+        MOZ_TRY(Write(fd, headerSize, sizeof(headerSize)));
+        MOZ_TRY(Write(fd, buf.Get(), buf.cursor()));
+    }
+
+    MOZ_TRY(cacheFile->MoveTo(nullptr, NS_LITERAL_STRING("urlCache.bin")));
+
+    NS_DispatchToMainThread(
+        NewRunnableMethod("URLPreloader::Cleanup",
+                          this,
+                          &URLPreloader::Cleanup));
+
+    return Ok();
+}
+
+void
+URLPreloader::Cleanup()
+{
+    mCachedURLs.Clear();
+}
+
+Result<Ok, nsresult>
+URLPreloader::ReadCache(LinkedList<URLEntry>& pendingURLs)
+{
+    nsCOMPtr<nsIFile> cacheFile;
+    MOZ_TRY_VAR(cacheFile, FindCacheFile());
+
+    AutoMemMap cache;
+    MOZ_TRY(cache.init(cacheFile));
+
+    auto size = cache.size();
+
+    uint32_t headerSize;
+    if (size < sizeof(URL_MAGIC) + sizeof(headerSize)) {
+        return Err(NS_ERROR_UNEXPECTED);
+    }
+
+    auto data = cache.get<uint8_t>();
+    auto end = data + size;
+
+    if (memcmp(URL_MAGIC, data.get(), sizeof(URL_MAGIC))) {
+        return Err(NS_ERROR_UNEXPECTED);
+    }
+    data += sizeof(URL_MAGIC);
+
+    headerSize = LittleEndian::readUint32(data.get());
+    data += sizeof(headerSize);
+
+    if (data + headerSize > end) {
+        return Err(NS_ERROR_UNEXPECTED);
+    }
+
+    {
+        mMonitor.AssertCurrentThreadOwns();
+
+        auto cleanup = MakeScopeExit([&] () {
+            while (auto* elem = pendingURLs.getFirst()) {
+                elem->remove();
+            }
+            mCachedURLs.Clear();
+        });
+
+        Range<uint8_t> header(data, data + headerSize);
+        data += headerSize;
+
+        InputBuffer buf(header);
+        while (!buf.finished()) {
+            CacheKey key(buf);
+
+            auto entry = mCachedURLs.LookupOrAdd(key, key);
+            entry->mResultCode = NS_ERROR_NOT_INITIALIZED;
+
+            pendingURLs.insertBack(entry);
+        }
+
+        if (buf.error()) {
+            return Err(NS_ERROR_UNEXPECTED);
+        }
+
+        cleanup.release();
+    }
+
+    return Ok();
+}
+
+void
+URLPreloader::BackgroundReadFiles()
+{
+    Vector<nsZipCursor> cursors;
+    LinkedList<URLEntry> pendingURLs;
+
+    {
+        MonitorAutoLock mal(mMonitor);
+
+        if (ReadCache(pendingURLs).isErr()) {
+            mReaderInitialized = true;
+            mal.NotifyAll();
+            return;
+        }
+
+        int numZipEntries = 0;
+        for (auto entry : pendingURLs) {
+            if (entry->mType != entry->TypeFile) {
+                numZipEntries++;
+            }
+        }
+        MOZ_RELEASE_ASSERT(cursors.reserve(numZipEntries));
+
+        // Initialize the zip cursors for all files in Omnijar while the monitor
+        // is locked. Omnijar is not threadsafe, so the caller of
+        // AutoBeginReading guard must ensure that no code accesses Omnijar
+        // until this segment is done. Once the cursors have been initialized,
+        // the actual reading and decompression can safely be done off-thread,
+        // as is the case for thread-retargeted jar: channels.
+        for (auto entry : pendingURLs) {
+            if (entry->mType == entry->TypeFile) {
+                continue;
+            }
+
+            RefPtr<nsZipArchive> zip = entry->Archive();
+
+            auto item = zip->GetItem(entry->mPath.get());
+            if (!item) {
+                entry->mResultCode = NS_ERROR_FILE_NOT_FOUND;
+                continue;
+            }
+
+            size_t size = item->RealSize();
+
+            entry->mData.SetLength(size);
+            auto data = entry->mData.BeginWriting();
+
+            cursors.infallibleEmplaceBack(item, zip, reinterpret_cast<uint8_t*>(data),
+                                          size, true);
+        }
+
+        mReaderInitialized = true;
+        mal.NotifyAll();
+    }
+
+    // Loop over the entries, read the file's contents, store them in the
+    // entry's mData pointer, and notify any waiting threads to check for
+    // completion.
+    uint32_t i = 0;
+    for (auto entry : pendingURLs) {
+        // If there is any other error code, the entry has already failed at
+        // this point, so don't bother trying to read it again.
+        if (entry->mResultCode != NS_ERROR_NOT_INITIALIZED) {
+            continue;
+        }
+
+        nsresult rv = NS_OK;
+
+        if (entry->mType == entry->TypeFile) {
+            auto result = entry->Read();
+            if (result.isErr()) {
+                rv = result.unwrapErr();
+            }
+        } else {
+            auto& cursor = cursors[i++];
+
+            uint32_t len;
+            cursor.Copy(&len);
+            if (len != entry->mData.Length()) {
+                entry->mData.Truncate();
+                rv = NS_ERROR_FAILURE;
+            }
+        }
+
+        entry->mResultCode = rv;
+        mMonitor.NotifyAll();
+    }
+
+    // We're done reading pending entries, so clear the list.
+    pendingURLs.clear();
+
+    NS_DispatchToMainThread(
+        NewRunnableMethod("nsIThread::Shutdown",
+                          mReaderThread, &nsIThread::Shutdown));
+    mReaderThread = nullptr;
+}
+
+void
+URLPreloader::BeginBackgroundRead()
+{
+    if (!mReaderThread && !mReaderInitialized && sInitialized) {
+        nsCOMPtr<nsIRunnable> runnable =
+            NewRunnableMethod("URLPreloader::BackgroundReadFiles",
+                              this,
+                              &URLPreloader::BackgroundReadFiles);
+
+        Unused << NS_NewNamedThread(
+            "BGReadURLs", getter_AddRefs(mReaderThread), runnable);
+    }
+}
+
+
+Result<const nsCString, nsresult>
+URLPreloader::ReadInternal(const CacheKey& key, ReadType readType)
+{
+    if (mStartupFinished) {
+        URLEntry entry(key);
+
+        return entry.Read();
+    }
+
+    auto entry = mCachedURLs.LookupOrAdd(key, key);
+
+    entry->UpdateUsedTime();
+
+    return entry->ReadOrWait(readType);
+}
+
+Result<const nsCString, nsresult>
+URLPreloader::ReadURIInternal(nsIURI* uri, ReadType readType)
+{
+    CacheKey key;
+    MOZ_TRY_VAR(key, ResolveURI(uri));
+
+    return ReadInternal(key, readType);
+}
+
+/* static */ Result<const nsCString, nsresult>
+URLPreloader::Read(const CacheKey& key, ReadType readType)
+{
+    // If we're being called before the preloader has been initialized (i.e.,
+    // before the profile has been initialized), just fall back to a synchronous
+    // read. This happens when we're reading .ini and preference files that are
+    // needed to locate and initialize the profile.
+    if (!sInitialized) {
+        return URLEntry(key).Read();
+    }
+
+    return GetSingleton().ReadInternal(key, readType);
+}
+
+/* static */ Result<const nsCString, nsresult>
+URLPreloader::ReadURI(nsIURI* uri, ReadType readType)
+{
+    if (!sInitialized) {
+        return Err(NS_ERROR_NOT_INITIALIZED);
+    }
+
+    return GetSingleton().ReadURIInternal(uri, readType);
+}
+
+/* static */ Result<const nsCString, nsresult>
+URLPreloader::ReadFile(nsIFile* file, ReadType readType)
+{
+    return Read(CacheKey(file), readType);
+}
+
+/* static */ Result<const nsCString, nsresult>
+URLPreloader::ReadFile(const nsACString& path, ReadType readType)
+{
+    CacheKey key(CacheKey::TypeFile, path);
+    return Read(key, readType);
+}
+
+/* static */ Result<const nsCString, nsresult>
+URLPreloader::Read(FileLocation& location, ReadType readType)
+{
+    if (location.IsZip()) {
+        if (location.GetBaseZip()) {
+            nsCString path;
+            location.GetPath(path);
+            return ReadZip(location.GetBaseZip(), path);
+        }
+        return URLEntry::ReadLocation(location);
+    }
+
+    nsCOMPtr<nsIFile> file = location.GetBaseFile();
+    return ReadFile(file, readType);
+}
+
+/* static */ Result<const nsCString, nsresult>
+URLPreloader::ReadZip(nsZipArchive* zip, const nsACString& path, ReadType readType)
+{
+    // If the zip archive belongs to an Omnijar location, map it to a cache
+    // entry, and cache it as normal. Otherwise, simply read the entry
+    // synchronously, since other JAR archives are currently unsupported by the
+    // cache.
+    RefPtr<nsZipArchive> reader = Omnijar::GetReader(Omnijar::GRE);
+    if (zip == reader) {
+        CacheKey key(CacheKey::TypeGREJar, path);
+        return Read(key, readType);
+    }
+
+    reader = Omnijar::GetReader(Omnijar::APP);
+    if (zip == reader) {
+        CacheKey key(CacheKey::TypeAppJar, path);
+        return Read(key, readType);
+    }
+
+    // Not an Omnijar archive, so just read it directly.
+    FileLocation location(zip, PromiseFlatCString(path).BeginReading());
+    return URLEntry::ReadLocation(location);
+}
+
+Result<URLPreloader::CacheKey, nsresult>
+URLPreloader::ResolveURI(nsIURI* uri)
+{
+    nsCString spec;
+    nsCString scheme;
+    MOZ_TRY(uri->GetSpec(spec));
+    MOZ_TRY(uri->GetScheme(scheme));
+
+    nsCOMPtr<nsIURI> resolved;
+
+    // If the URI is a resource: or chrome: URI, first resolve it to the
+    // underlying URI that it wraps.
+    if (scheme.EqualsLiteral("resource")) {
+        MOZ_TRY(mResProto->ResolveURI(uri, spec));
+        MOZ_TRY(NS_NewURI(getter_AddRefs(resolved), spec));
+    } else if (scheme.EqualsLiteral("chrome")) {
+        MOZ_TRY(mChromeReg->ConvertChromeURL(uri, getter_AddRefs(resolved)));
+        MOZ_TRY(resolved->GetSpec(spec));
+    } else {
+        resolved = uri;
+    }
+    MOZ_TRY(resolved->GetScheme(scheme));
+
+    // Try the GRE and App Omnijar prefixes.
+    if (mGREPrefix.Length() && StartsWith(spec, mGREPrefix)) {
+        return CacheKey(CacheKey::TypeGREJar,
+                        Substring(spec, mGREPrefix.Length()));
+    }
+
+    if (mAppPrefix.Length() && StartsWith(spec, mAppPrefix)) {
+        return CacheKey(CacheKey::TypeAppJar,
+                        Substring(spec, mAppPrefix.Length()));
+    }
+
+    // Try for a file URI.
+    if (scheme.EqualsLiteral("file")) {
+        nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(resolved);
+        MOZ_ASSERT(fileURL);
+
+        nsCOMPtr<nsIFile> file;
+        MOZ_TRY(fileURL->GetFile(getter_AddRefs(file)));
+
+        nsCString path;
+        MOZ_TRY(file->GetNativePath(path));
+
+        return CacheKey(CacheKey::TypeFile, path);
+    }
+
+    // Not a file or Omnijar URI, so currently unsupported.
+    return Err(NS_ERROR_INVALID_ARG);
+}
+
+size_t
+URLPreloader::ShallowSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
+{
+    return (mallocSizeOf(this) +
+            mAppPrefix.SizeOfExcludingThisEvenIfShared(mallocSizeOf) +
+            mGREPrefix.SizeOfExcludingThisEvenIfShared(mallocSizeOf) +
+            mCachedURLs.ShallowSizeOfExcludingThis(mallocSizeOf));
+}
+
+Result<FileLocation, nsresult>
+URLPreloader::CacheKey::ToFileLocation()
+{
+    if (mType == TypeFile) {
+        nsCOMPtr<nsIFile> file;
+        MOZ_TRY(NS_NewNativeLocalFile(mPath, false, getter_AddRefs(file)));
+        return Move(FileLocation(file));
+    }
+
+    RefPtr<nsZipArchive> zip = Archive();
+    return Move(FileLocation(zip, mPath.get()));
+}
+
+Result<const nsCString, nsresult>
+URLPreloader::URLEntry::Read()
+{
+    FileLocation location;
+    MOZ_TRY_VAR(location, ToFileLocation());
+
+    MOZ_TRY_VAR(mData, ReadLocation(location));
+    return mData;
+}
+
+/* static */ Result<const nsCString, nsresult>
+URLPreloader::URLEntry::ReadLocation(FileLocation& location)
+{
+    FileLocation::Data data;
+    MOZ_TRY(location.GetData(data));
+
+    uint32_t size;
+    MOZ_TRY(data.GetSize(&size));
+
+    nsCString result;
+    result.SetLength(size);
+    MOZ_TRY(data.Copy(result.BeginWriting(), size));
+
+    return Move(result);
+}
+
+Result<const nsCString, nsresult>
+URLPreloader::URLEntry::ReadOrWait(ReadType readType)
+{
+    auto now = TimeStamp::Now();
+    LOG(Info, "Reading %s\n", mPath.get());
+    auto cleanup = MakeScopeExit([&] () {
+        LOG(Info, "Read in %fms\n", (TimeStamp::Now() - now).ToMilliseconds());
+    });
+
+    if (mResultCode == NS_ERROR_NOT_INITIALIZED) {
+        MonitorAutoLock mal(GetSingleton().mMonitor);
+
+        while (mResultCode == NS_ERROR_NOT_INITIALIZED) {
+            mal.Wait();
+        }
+    }
+
+    if (mResultCode == NS_OK && mData.IsVoid()) {
+        LOG(Info, "Reading synchronously...\n");
+        return Read();
+    }
+
+    if (NS_FAILED(mResultCode)) {
+        return Err(mResultCode);
+    }
+
+    nsCString res = mData;
+
+    if (readType == Forget) {
+        mData.SetIsVoid(true);
+    }
+    return res;
+}
+
+inline
+URLPreloader::CacheKey::CacheKey(InputBuffer& buffer)
+{
+    Code(buffer);
+}
+
+nsresult
+URLPreloader::Observe(nsISupports* subject, const char* topic, const char16_t* data)
+{
+    nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+    if (!strcmp(topic, DELAYED_STARTUP_TOPIC)) {
+        obs->RemoveObserver(this, DELAYED_STARTUP_TOPIC);
+        mStartupFinished = true;
+    }
+
+    return NS_OK;
+}
+
+
+NS_IMPL_ISUPPORTS(URLPreloader, nsIObserver, nsIMemoryReporter)
+
+#undef LOG
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/loader/URLPreloader.h
@@ -0,0 +1,325 @@
+/* -*-  Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef URLPreloader_h
+#define URLPreloader_h
+
+#include "mozilla/FileLocation.h"
+#include "mozilla/HashFunctions.h"
+#include "mozilla/LinkedList.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/Omnijar.h"
+#include "mozilla/Range.h"
+#include "mozilla/Vector.h"
+#include "mozilla/Result.h"
+#include "nsClassHashtable.h"
+#include "nsHashKeys.h"
+#include "nsIChromeRegistry.h"
+#include "nsIFile.h"
+#include "nsIURI.h"
+#include "nsIMemoryReporter.h"
+#include "nsIObserver.h"
+#include "nsIResProtocolHandler.h"
+#include "nsIThread.h"
+#include "nsReadableUtils.h"
+
+class nsZipArchive;
+
+namespace mozilla {
+namespace loader {
+    class InputBuffer;
+}
+
+using namespace mozilla::loader;
+
+class ScriptPreloader;
+
+/**
+ * A singleton class to manage loading local URLs during startup, recording
+ * them, and pre-loading them during early startup in the next session. URLs
+ * that are not already loaded (or already being pre-loaded) when required are
+ * read synchronously from disk, and (if startup is not already complete)
+ * added to the pre-load list for the next session.
+ */
+class URLPreloader final : public nsIObserver
+                         , public nsIMemoryReporter
+{
+    MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
+
+    URLPreloader();
+
+public:
+    NS_DECL_THREADSAFE_ISUPPORTS
+    NS_DECL_NSIOBSERVER
+    NS_DECL_NSIMEMORYREPORTER
+
+    static URLPreloader& GetSingleton();
+
+    // The type of read operation to perform.
+    enum ReadType
+    {
+        // Read the file and then immediately forget its data.
+        Forget,
+        // Read the file and retain its data for the next caller.
+        Retain,
+    };
+
+    // Helpers to read the contents of files or JAR archive entries with various
+    // representations. If the preloader has not yet been initialized, or the
+    // given location is not supported by the cache, the entries will be read
+    // synchronously, and not stored in the cache.
+    static Result<const nsCString, nsresult> Read(FileLocation& location, ReadType readType = Forget);
+
+    static Result<const nsCString, nsresult> ReadURI(nsIURI* uri, ReadType readType = Forget);
+
+    static Result<const nsCString, nsresult> ReadFile(nsIFile* file, ReadType readType = Forget);
+
+    static Result<const nsCString, nsresult> ReadFile(const nsACString& path, ReadType readType = Forget);
+
+    static Result<const nsCString, nsresult> ReadZip(nsZipArchive* archive,
+                                                     const nsACString& path,
+                                                     ReadType readType = Forget);
+
+private:
+    struct CacheKey;
+
+    Result<const nsCString, nsresult> ReadInternal(const CacheKey& key, ReadType readType);
+
+    Result<const nsCString, nsresult> ReadURIInternal(nsIURI* uri, ReadType readType);
+
+    Result<const nsCString, nsresult> ReadFileInternal(nsIFile* file, ReadType readType);
+
+    static Result<const nsCString, nsresult> Read(const CacheKey& key, ReadType readType);
+
+    static bool sInitialized;
+
+protected:
+    friend class ScriptPreloader;
+
+    virtual ~URLPreloader();
+
+    Result<Ok, nsresult> WriteCache();
+
+    // Clear leftover entries after the cache has been written.
+    void Cleanup();
+
+    // Begins reading files off-thread, and ensures that initialization has
+    // completed before leaving the current scope. The caller *must* ensure that
+    // no code on the main thread access Omnijar, either directly or indirectly,
+    // for the lifetime of this guard object.
+    struct MOZ_RAII AutoBeginReading final
+    {
+        AutoBeginReading()
+        {
+            GetSingleton().BeginBackgroundRead();
+        }
+
+        ~AutoBeginReading()
+        {
+            auto& reader = GetSingleton();
+
+            MonitorAutoLock mal(reader.mMonitor);
+
+            while (!reader.mReaderInitialized && reader.sInitialized) {
+                mal.Wait();
+            }
+        }
+    };
+
+private:
+    // Represents a key for an entry in the URI cache, based on its file or JAR
+    // location.
+    struct CacheKey
+    {
+        // The type of the entry. TypeAppJar and TypeGREJar entries are in the
+        // app-specific or toolkit Omnijar files, and are handled specially.
+        // TypeFile entries are plain files in the filesystem.
+        enum EntryType : uint8_t
+        {
+            TypeAppJar,
+            TypeGREJar,
+            TypeFile,
+        };
+
+        CacheKey() = default;
+        CacheKey(const CacheKey& other) = default;
+
+        CacheKey(EntryType type, const nsACString& path)
+            : mType(type), mPath(path)
+        {}
+
+        explicit CacheKey(nsIFile* file)
+          : mType(TypeFile)
+        {
+            MOZ_ALWAYS_SUCCEEDS(file->GetNativePath(mPath));
+        }
+
+        explicit inline CacheKey(InputBuffer& buffer);
+
+        // Encodes or decodes the cache key for storage in a session cache file.
+        template <typename Buffer>
+        void Code(Buffer& buffer)
+        {
+            buffer.codeUint8(*reinterpret_cast<uint8_t*>(&mType));
+            buffer.codeString(mPath);
+        }
+
+        uint32_t Hash() const
+        {
+            return HashGeneric(mType, HashString(mPath));
+        }
+
+        bool operator==(const CacheKey& other) const
+        {
+            return mType == other.mType && mPath == other.mPath;
+        }
+
+        // Returns the Omnijar type for this entry. This may *only* be called
+        // for Omnijar entries.
+        Omnijar::Type OmnijarType()
+        {
+            switch (mType) {
+            case TypeAppJar:
+                return Omnijar::APP;
+            case TypeGREJar:
+                return Omnijar::GRE;
+            default:
+                MOZ_CRASH("Unexpected entry type");
+                return Omnijar::GRE;
+            }
+        }
+
+        const char* TypeString()
+        {
+            switch (mType) {
+            case TypeAppJar: return "AppJar";
+            case TypeGREJar: return "GREJar";
+            case TypeFile: return "File";
+            }
+            MOZ_ASSERT_UNREACHABLE("no such type");
+            return "";
+        }
+
+        already_AddRefed<nsZipArchive> Archive()
+        {
+            return Omnijar::GetReader(OmnijarType());
+        }
+
+        Result<FileLocation, nsresult> ToFileLocation();
+
+        EntryType mType = TypeFile;
+
+        // The path of the entry. For Type*Jar entries, this is the path within
+        // the Omnijar archive. For TypeFile entries, this is the full path to
+        // the file.
+        nsCString mPath{};
+    };
+
+    // Represents an entry in the URI cache.
+    struct URLEntry final : public CacheKey
+                          , public LinkedListElement<URLEntry>
+    {
+        MOZ_IMPLICIT URLEntry(const CacheKey& key)
+            : CacheKey(key)
+            , mData(NullCString())
+        {}
+
+        explicit URLEntry(nsIFile* file)
+          : CacheKey(file)
+        {}
+
+        // For use with nsTArray::Sort.
+        //
+        // Sorts entries by the time they were initially read during this
+        // session.
+        struct Comparator final
+        {
+            bool Equals(const URLEntry* a, const URLEntry* b) const
+            {
+              return a->mReadTime == b->mReadTime;
+            }
+
+            bool LessThan(const URLEntry* a, const URLEntry* b) const
+            {
+                return a->mReadTime < b->mReadTime;
+            }
+        };
+
+        // Sets the first-used time of this file to the earlier of its current
+        // first-use time or the given timestamp.
+        void UpdateUsedTime(const TimeStamp& time = TimeStamp::Now())
+        {
+          if (!mReadTime || time < mReadTime) {
+            mReadTime = time;
+          }
+        }
+
+        Result<const nsCString, nsresult> Read();
+        static Result<const nsCString, nsresult> ReadLocation(FileLocation& location);
+
+        size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
+        {
+            return (mallocSizeOf(this) +
+                    mPath.SizeOfExcludingThisEvenIfShared(mallocSizeOf) +
+                    mData.SizeOfExcludingThisEvenIfShared(mallocSizeOf));
+        }
+
+        // Reads the contents of the file referenced by this entry, or wait for
+        // an off-thread read operation to finish if it is currently pending,
+        // and return the file's contents.
+        Result<const nsCString, nsresult> ReadOrWait(ReadType readType);
+
+        nsCString mData;
+
+        TimeStamp mReadTime{};
+
+        nsresult mResultCode = NS_OK;
+    };
+
+    // Resolves the given URI to a CacheKey, if the URI is cacheable.
+    Result<CacheKey, nsresult> ResolveURI(nsIURI* uri);
+
+    Result<Ok, nsresult> InitInternal();
+
+    // Returns a file pointer to the (possibly nonexistent) cache file with the
+    // given suffix.
+    Result<nsCOMPtr<nsIFile>, nsresult> GetCacheFile(const nsAString& suffix);
+    // Finds the correct cache file to use for this session.
+    Result<nsCOMPtr<nsIFile>, nsresult> FindCacheFile();
+
+    Result<Ok, nsresult> ReadCache(LinkedList<URLEntry>& pendingURLs);
+
+    void BackgroundReadFiles();
+    void BeginBackgroundRead();
+
+    using HashType = nsClassHashtable<nsGenericHashKey<CacheKey>, URLEntry>;
+
+    size_t ShallowSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
+
+
+    bool mStartupFinished = false;
+    bool mReaderInitialized = false;
+
+    // The prefix URLs for files in the GRE and App omni jar archives.
+    nsCString mGREPrefix;
+    nsCString mAppPrefix;
+
+    nsCOMPtr<nsIResProtocolHandler> mResProto;
+    nsCOMPtr<nsIChromeRegistry> mChromeReg;
+    nsCOMPtr<nsIFile> mProfD;
+
+    nsCOMPtr<nsIThread> mReaderThread;
+
+    // A map of URL entries which have were either read this session, or read
+    // from the last session's cache file.
+    HashType mCachedURLs;
+
+    Monitor mMonitor{"[URLPreloader::mMutex]"};
+};
+
+} // namespace mozilla
+
+#endif // URLPreloader_h
--- a/js/xpconnect/loader/moz.build
+++ b/js/xpconnect/loader/moz.build
@@ -6,30 +6,32 @@
 
 UNIFIED_SOURCES += [
     'AutoMemMap.cpp',
     'ChromeScriptLoader.cpp',
     'mozJSLoaderUtils.cpp',
     'mozJSSubScriptLoader.cpp',
     'ScriptCacheActors.cpp',
     'ScriptPreloader.cpp',
+    'URLPreloader.cpp',
 ]
 
 # mozJSComponentLoader.cpp cannot be built in unified mode because it uses
 # windows.h
 SOURCES += [
     'mozJSComponentLoader.cpp'
 ]
 
 IPDL_SOURCES += [
     'PScriptCache.ipdl',
 ]
 
 EXPORTS.mozilla += [
     'ScriptPreloader.h',
+    'URLPreloader.h',
 ]
 
 EXPORTS.mozilla.dom += [
     'PrecompiledScript.h',
 ]
 
 EXPORTS.mozilla.loader += [
     'AutoMemMap.h',
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -16,16 +16,18 @@
 #include "nsCycleCollector.h"
 #include "jsfriendapi.h"
 #include "js/StructuredClone.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "mozilla/Preferences.h"
 #include "nsJSEnvironment.h"
 #include "mozilla/TimeStamp.h"
+#include "mozilla/ResultExtensions.h"
+#include "mozilla/URLPreloader.h"
 #include "mozilla/XPTInterfaceInfoManager.h"
 #include "mozilla/dom/DOMException.h"
 #include "mozilla/dom/DOMExceptionBinding.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/StructuredCloneTags.h"
 #include "mozilla/dom/WindowBinding.h"
 #include "nsZipArchive.h"
 #include "nsIDOMFileList.h"
@@ -3375,16 +3377,25 @@ nsXPCComponents_Utils::AllowCPOWsInAddon
         return NS_ERROR_FAILURE;
     if (!XPCWrappedNativeScope::AllowCPOWsInAddon(cx, addonId, allow))
         return NS_ERROR_FAILURE;
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
+nsXPCComponents_Utils::ReadFile(nsIFile* aFile, nsACString& aResult)
+{
+    NS_ENSURE_TRUE(aFile, NS_ERROR_INVALID_ARG);
+
+    MOZ_TRY_VAR(aResult, URLPreloader::ReadFile(aFile));
+    return NS_OK;
+}
+
+NS_IMETHODIMP
 nsXPCComponents_Utils::Now(double* aRetval)
 {
     TimeStamp start = TimeStamp::ProcessCreation();
     *aRetval = (TimeStamp::Now() - start).ToMilliseconds();
     return NS_OK;
 }
 
 /***************************************************************************/
--- a/js/xpconnect/wrappers/WrapperFactory.cpp
+++ b/js/xpconnect/wrappers/WrapperFactory.cpp
@@ -560,19 +560,21 @@ WrapperFactory::Rewrap(JSContext* cx, Ha
         wrapper = SelectWrapper(securityWrapper, xrayType, waiveXrays, obj);
 
         // If we want to apply add-on interposition in the target compartment,
         // then we try to "upgrade" the wrapper to an interposing one.
         if (targetCompartmentPrivate->scope->HasInterposition())
             wrapper = SelectAddonWrapper(cx, obj, wrapper);
     }
 
-    if (!targetSubsumesOrigin) {
+    if (!targetSubsumesOrigin &&
+        !originCompartmentPrivate->forcePermissiveCOWs) {
         // Do a belt-and-suspenders check against exposing eval()/Function() to
-        // non-subsuming content.
+        // non-subsuming content.  But don't worry about doing it in the
+        // SpecialPowers case.
         if (JSFunction* fun = JS_GetObjectFunction(obj)) {
             if (JS_IsBuiltinEvalFunction(fun) || JS_IsBuiltinFunctionConstructor(fun)) {
                 NS_WARNING("Trying to expose eval or Function to non-subsuming content!");
                 wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>::singleton;
             }
         }
     }
 
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -4288,21 +4288,19 @@ SetFlagsOnSubtree(nsIContent *aNode, uin
                  "The node should not have any XBL children");
   }
 #endif
 
   // Set the flag on the node itself
   aNode->SetFlags(aFlagsToSet);
 
   // Set the flag on all of its children recursively
-  uint32_t count;
-  nsIContent * const *children = aNode->GetChildArray(&count);
-
-  for (uint32_t index = 0; index < count; ++index) {
-    SetFlagsOnSubtree(children[index], aFlagsToSet);
+  for (nsIContent* child = aNode->GetFirstChild(); child;
+       child = child->GetNextSibling()) {
+    SetFlagsOnSubtree(child, aFlagsToSet);
   }
 }
 
 /**
  * This function takes a tree of nsIAnonymousContentCreator::ContentInfo
  * objects where the nsIContent nodes have just been created, and appends the
  * nsIContent children in the tree to their parent. The leaf nsIContent objects
  * are appended first to minimize the number of notifications that are sent
--- a/layout/generic/nsPlaceholderFrame.cpp
+++ b/layout/generic/nsPlaceholderFrame.cpp
@@ -204,35 +204,20 @@ nsPlaceholderFrame::GetParentStyleContex
   }
 
   return GetLayoutParentStyleForOutOfFlow(aProviderFrame);
 }
 
 nsStyleContext*
 nsPlaceholderFrame::GetLayoutParentStyleForOutOfFlow(nsIFrame** aProviderFrame) const
 {
-  nsIFrame* parentFrame = GetParent();
-  // Placeholder of backdrop frame is a child of the corresponding top
-  // layer frame, and its style context inherits from that frame. In
-  // case of table, the top layer frame is the table wrapper frame.
-  // However, it will be skipped in CorrectStyleParentFrame below, so
-  // we need to handle it specially here.
-  if ((GetStateBits() & PLACEHOLDER_FOR_TOPLAYER) &&
-      parentFrame->IsTableWrapperFrame()) {
-    MOZ_ASSERT(mOutOfFlowFrame->IsBackdropFrame(),
-               "Only placeholder of backdrop frame can be put inside "
-               "a table wrapper frame");
-    *aProviderFrame = parentFrame;
-    return parentFrame->StyleContext();
-  }
-
   // Lie about our pseudo so we can step out of all anon boxes and
   // pseudo-elements.  The other option would be to reimplement the
   // {ib} split gunk here.
-  *aProviderFrame = CorrectStyleParentFrame(parentFrame,
+  *aProviderFrame = CorrectStyleParentFrame(GetParent(),
                                             nsGkAtoms::placeholderFrame);
   return *aProviderFrame ? (*aProviderFrame)->StyleContext() : nullptr;
 }
 
 
 #ifdef DEBUG
 static void
 PaintDebugPlaceholder(nsIFrame* aFrame, DrawTarget* aDrawTarget,
--- a/layout/style/Loader.cpp
+++ b/layout/style/Loader.cpp
@@ -11,16 +11,18 @@
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/LoadInfo.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/SystemGroup.h"
+#include "mozilla/ResultExtensions.h"
+#include "mozilla/URLPreloader.h"
 #include "nsIRunnable.h"
 #include "nsIUnicharStreamLoader.h"
 #include "nsSyncLoadService.h"
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsIDOMNode.h"
@@ -39,16 +41,17 @@
 #include "nsIStyleSheetLinkingElement.h"
 #include "nsICSSLoaderObserver.h"
 #include "nsCSSParser.h"
 #include "mozilla/css/ImportRule.h"
 #include "nsThreadUtils.h"
 #include "nsGkAtoms.h"
 #include "nsIThreadInternal.h"
 #include "nsINetworkPredictor.h"
+#include "nsStringStream.h"
 #include "mozilla/dom/MediaList.h"
 #include "mozilla/dom/ShadowRoot.h"
 #include "mozilla/dom/URL.h"
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/ServoBindings.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/ConsoleReportCollector.h"
@@ -1351,21 +1354,34 @@ Loader::LoadSheet(SheetLoadData* aLoadDa
                                                 securityFlags,
                                                 contentPolicyType);
     }
     else {
       // either we are loading something inside a document, in which case
       // we should always have a requestingNode, or we are loading something
       // outside a document, in which case the loadingPrincipal and the
       // triggeringPrincipal should always be the systemPrincipal.
-      rv = NS_NewChannel(getter_AddRefs(channel),
-                         aLoadData->mURI,
-                         nsContentUtils::GetSystemPrincipal(),
-                         securityFlags,
-                         contentPolicyType);
+      auto result = URLPreloader::ReadURI(aLoadData->mURI);
+      if (result.isOk()) {
+        nsCOMPtr<nsIInputStream> stream;
+        MOZ_TRY(NS_NewCStringInputStream(getter_AddRefs(stream), result.unwrap()));
+
+        rv = NS_NewInputStreamChannel(getter_AddRefs(channel),
+                                      aLoadData->mURI,
+                                      stream,
+                                      nsContentUtils::GetSystemPrincipal(),
+                                      securityFlags,
+                                      contentPolicyType);
+      } else {
+        rv = NS_NewChannel(getter_AddRefs(channel),
+                           aLoadData->mURI,
+                           nsContentUtils::GetSystemPrincipal(),
+                           securityFlags,
+                           contentPolicyType);
+      }
     }
     if (NS_FAILED(rv)) {
       LOG_ERROR(("  Failed to create channel"));
       SheetComplete(aLoadData, rv);
       return rv;
     }
 
     nsCOMPtr<nsIInputStream> stream;
--- a/layout/tables/nsTableColGroupFrame.cpp
+++ b/layout/tables/nsTableColGroupFrame.cpp
@@ -12,34 +12,27 @@
 #include "nsGkAtoms.h"
 #include "nsCOMPtr.h"
 #include "nsCSSRendering.h"
 #include "nsIPresShell.h"
 #include "mozilla/GeckoStyleContext.h"
 
 using namespace mozilla;
 
-#define COL_GROUP_TYPE_BITS          (NS_FRAME_STATE_BIT(30) | \
-                                      NS_FRAME_STATE_BIT(31))
-#define COL_GROUP_TYPE_OFFSET        30
+#define COLGROUP_SYNTHETIC_BIT NS_FRAME_STATE_BIT(30)
 
-nsTableColGroupType
-nsTableColGroupFrame::GetColType() const
+bool
+nsTableColGroupFrame::IsSynthetic() const
 {
-  return (nsTableColGroupType)((mState & COL_GROUP_TYPE_BITS) >> COL_GROUP_TYPE_OFFSET);
+  return HasAnyStateBits(COLGROUP_SYNTHETIC_BIT);
 }
 
-void nsTableColGroupFrame::SetColType(nsTableColGroupType aType)
+void nsTableColGroupFrame::SetIsSynthetic()
 {
-  NS_ASSERTION(GetColType() == eColGroupContent,
-               "should only call nsTableColGroupFrame::SetColType with aType "
-               "!= eColGroupContent once");
-  uint32_t type = aType - eColGroupContent;
-  RemoveStateBits(COL_GROUP_TYPE_BITS);
-  AddStateBits(nsFrameState(type << COL_GROUP_TYPE_OFFSET));
+  AddStateBits(COLGROUP_SYNTHETIC_BIT);
 }
 
 void nsTableColGroupFrame::ResetColIndices(nsIFrame*       aFirstColGroup,
                                            int32_t         aFirstColIndex,
                                            nsIFrame*       aStartColFrame)
 {
   nsTableColGroupFrame* colGroupFrame = (nsTableColGroupFrame*)aFirstColGroup;
   int32_t colIndex = aFirstColIndex;
@@ -109,33 +102,26 @@ nsTableColGroupFrame::AddColsToTable(int
 }
 
 
 nsTableColGroupFrame*
 nsTableColGroupFrame::GetLastRealColGroup(nsTableFrame* aTableFrame)
 {
   nsFrameList colGroups = aTableFrame->GetColGroups();
 
-  nsIFrame* nextToLastColGroup = nullptr;
-  nsFrameList::FrameLinkEnumerator link(colGroups);
-  for ( ; !link.AtEnd(); link.Next()) {
-    nextToLastColGroup = link.PrevFrame();
+  auto lastColGroup = static_cast<nsTableColGroupFrame*>(colGroups.LastChild());
+  if (!lastColGroup) {
+    return nullptr;
   }
 
-  if (!link.PrevFrame()) {
-    return nullptr; // there are no col group frames
+  if (!lastColGroup->IsSynthetic()) {
+    return lastColGroup;
   }
 
-  nsTableColGroupType lastColGroupType =
-    static_cast<nsTableColGroupFrame*>(link.PrevFrame())->GetColType();
-  if (eColGroupAnonymousCell == lastColGroupType) {
-    return static_cast<nsTableColGroupFrame*>(nextToLastColGroup);
-  }
-
-  return static_cast<nsTableColGroupFrame*>(link.PrevFrame());
+  return static_cast<nsTableColGroupFrame*>(lastColGroup->GetPrevSibling());
 }
 
 // don't set mColCount here, it is done in AddColsToTable
 void
 nsTableColGroupFrame::SetInitialChildList(ChildListID     aListID,
                                           nsFrameList&    aChildList)
 {
   MOZ_ASSERT(mFrames.IsEmpty(),
@@ -185,19 +171,18 @@ nsTableColGroupFrame::AppendFrames(Child
     // has content columns in it.
     nextCol = col->GetNextCol();
     RemoveFrame(kPrincipalList, col);
     col = nextCol;
   }
 
   // Our next colframe should be an eColContent.  We've removed all the
   // eColAnonymousColGroup colframes, eColAnonymousCol colframes always follow
-  // eColContent ones, and eColAnonymousCell colframes only appear in an
-  // eColGroupAnonymousCell colgroup, which never gets AppendFrames() called on
-  // it.
+  // eColContent ones, and eColAnonymousCell colframes only appear in a
+  // synthetic colgroup, which never gets AppendFrames() called on it.
   MOZ_ASSERT(!col || col->GetColType() == eColContent,
              "What's going on with our columns?");
 
   const nsFrameList::Slice& newFrames =
     mFrames.AppendFrames(this, aFrameList);
   InsertColsReflow(GetStartColumnIndex() + mColCount, newFrames);
 }
 
@@ -226,19 +211,18 @@ nsTableColGroupFrame::InsertFrames(Child
       aPrevFrame = nullptr;
     }
     RemoveFrame(kPrincipalList, col);
     col = nextCol;
   }
 
   // Our next colframe should be an eColContent.  We've removed all the
   // eColAnonymousColGroup colframes, eColAnonymousCol colframes always follow
-  // eColContent ones, and eColAnonymousCell colframes only appear in an
-  // eColGroupAnonymousCell colgroup, which never gets InsertFrames() called on
-  // it.
+  // eColContent ones, and eColAnonymousCell colframes only appear in a
+  // synthetic colgroup, which never gets InsertFrames() called on it.
   MOZ_ASSERT(!col || col->GetColType() == eColContent,
              "What's going on with our columns?");
 
   NS_ASSERTION(!aPrevFrame || aPrevFrame == aPrevFrame->LastContinuation(),
                "Prev frame should be last in continuation chain");
   NS_ASSERTION(!aPrevFrame || !GetNextColumn(aPrevFrame) ||
                GetNextColumn(aPrevFrame)->GetColType() != eColAnonymousCol,
                "Shouldn't be inserting before a spanned colframe");
@@ -333,18 +317,17 @@ nsTableColGroupFrame::RemoveFrame(ChildL
     }
 
     int32_t colIndex = colFrame->GetColIndex();
     // The RemoveChild call handles calling FrameNeedsReflow on us.
     RemoveChild(*colFrame, true);
 
     nsTableFrame* tableFrame = GetTableFrame();
     tableFrame->RemoveCol(this, colIndex, true, true);
-    if (mFrames.IsEmpty() && contentRemoval &&
-        GetColType() == eColGroupContent) {
+    if (mFrames.IsEmpty() && contentRemoval && !IsSynthetic()) {
       tableFrame->AppendAnonymousColFrames(this, GetSpan(),
                                            eColAnonymousColGroup, true);
     }
   }
   else {
     mFrames.DestroyFrame(aOldFrame);
   }
 }
@@ -510,30 +493,20 @@ void nsTableColGroupFrame::Dump(int32_t 
 {
   char* indent = new char[aIndent + 1];
   if (!indent) return;
   for (int32_t i = 0; i < aIndent + 1; i++) {
     indent[i] = ' ';
   }
   indent[aIndent] = 0;
 
-  printf("%s**START COLGROUP DUMP**\n%s startcolIndex=%d  colcount=%d span=%d coltype=",
-    indent, indent, GetStartColumnIndex(),  GetColCount(), GetSpan());
-  nsTableColGroupType colType = GetColType();
-  switch (colType) {
-  case eColGroupContent:
-    printf(" content ");
-    break;
-  case eColGroupAnonymousCol:
-    printf(" anonymous-column  ");
-    break;
-  case eColGroupAnonymousCell:
-    printf(" anonymous-cell ");
-    break;
-  }
+  printf("%s**START COLGROUP DUMP**\n%s startcolIndex=%d  colcount=%d span=%d isSynthetic=%s",
+         indent, indent, GetStartColumnIndex(),  GetColCount(), GetSpan(),
+         IsSynthetic() ? "true" : "false");
+
   // verify the colindices
   int32_t j = GetStartColumnIndex();
   nsTableColFrame* col = GetFirstColumn();
   while (col) {
     NS_ASSERTION(j == col->GetColIndex(), "wrong colindex on col frame");
     col = col->GetNextCol();
     j++;
   }
--- a/layout/tables/nsTableColGroupFrame.h
+++ b/layout/tables/nsTableColGroupFrame.h
@@ -49,29 +49,30 @@ public:
                "Col group should always be in a first-in-flow table frame");
     return static_cast<nsTableFrame*>(parent);
   }
 
   virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                 const nsDisplayListSet& aLists) override;
 
   /** A colgroup can be caused by three things:
-    * 1)	An element with table-column-group display
-    * 2)	An element with a table-column display without a
-	   *    table-column-group parent
-    * 3)	Cells that are not in a column (and hence get an anonymous
-	   *    column and colgroup).
-    * @return colgroup type
+    * 1) An element with table-column-group display
+    * 2) An element with a table-column display without a
+    *    table-column-group parent
+    * 3) Cells that are not in a column (and hence get an anonymous
+    *    column and colgroup).
+    *
+    * In practice, we don't need to differentiate between cases (1) and (2),
+    * because they both correspond to table-column-group boxes in the spec and
+    * hence have observably identical behavior.  Case three is flagged as a
+    * synthetic colgroup, because it may need to have different behavior in some
+    * cases.
     */
-  nsTableColGroupType GetColType() const;
-
-  /** Set the colgroup type based on the creation cause
-    * @param aType - the reason why this colgroup is needed
-    */
-  void SetColType(nsTableColGroupType aType);
+  bool IsSynthetic() const;
+  void SetIsSynthetic();
 
   /** Real in this context are colgroups that come from an element
     * with table-column-group display or wrap around columns that
     * come from an element with table-column display. Colgroups
     * that are the result of wrapping cells in an anonymous
     * column and colgroup are not considered real here.
     * @param aTableFrame - the table parent of the colgroups
     * @return the last real colgroup
@@ -219,17 +220,16 @@ protected:
   BCPixelSize mBEndContBorderWidth;
 };
 
 inline nsTableColGroupFrame::nsTableColGroupFrame(nsStyleContext* aContext)
   : nsContainerFrame(aContext, kClassID)
   , mColCount(0)
   , mStartColIndex(0)
 {
-  SetColType(eColGroupContent);
 }
 
 inline int32_t nsTableColGroupFrame::GetStartColumnIndex()
 {
   return mStartColIndex;
 }
 
 inline void nsTableColGroupFrame::SetStartColumnIndex (int32_t aIndex)
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -599,19 +599,20 @@ nsTableFrame::InsertCol(nsTableColFrame&
       bool removedFromCache = false;
       if (eColAnonymousCell != insertedColType) {
         nsTableColFrame* lastCol = mColFrames.ElementAt(numCacheCols - 1);
         if (lastCol) {
           nsTableColType lastColType = lastCol->GetColType();
           if (eColAnonymousCell == lastColType) {
             // remove the col from the cache
             mColFrames.RemoveElementAt(numCacheCols - 1);
-            // remove the col from the eColGroupAnonymousCell col group
+            // remove the col from the synthetic col group
             nsTableColGroupFrame* lastColGroup = (nsTableColGroupFrame *)mColGroups.LastChild();
             if (lastColGroup) {
+              MOZ_ASSERT(lastColGroup->IsSynthetic());
               lastColGroup->RemoveChild(*lastCol, false);
 
               // remove the col group if it is empty
               if (lastColGroup->GetColCount() <= 0) {
                 mColGroups.DestroyFrame((nsIFrame*)lastColGroup);
               }
             }
             removedFromCache = true;
@@ -673,48 +674,47 @@ nsTableFrame::RemoveCol(nsTableColGroupF
   * Only the first-in-flow has a legit cell map.
   */
 nsTableCellMap*
 nsTableFrame::GetCellMap() const
 {
   return static_cast<nsTableFrame*>(FirstInFlow())->mCellMap;
 }
 
-// XXX this needs to be moved to nsCSSFrameConstructor
 nsTableColGroupFrame*
-nsTableFrame::CreateAnonymousColGroupFrame(nsTableColGroupType aColGroupType)
+nsTableFrame::CreateSyntheticColGroupFrame()
 {
   nsIContent* colGroupContent = GetContent();
   nsPresContext* presContext = PresContext();
   nsIPresShell *shell = presContext->PresShell();
 
   RefPtr<nsStyleContext> colGroupStyle;
   colGroupStyle = shell->StyleSet()->
     ResolveNonInheritingAnonymousBoxStyle(nsCSSAnonBoxes::tableColGroup);
   // Create a col group frame
-  nsIFrame* newFrame = NS_NewTableColGroupFrame(shell, colGroupStyle);
-  ((nsTableColGroupFrame *)newFrame)->SetColType(aColGroupType);
+  nsTableColGroupFrame* newFrame =
+    NS_NewTableColGroupFrame(shell, colGroupStyle);
+  newFrame->SetIsSynthetic();
   newFrame->Init(colGroupContent, this, nullptr);
-  return (nsTableColGroupFrame *)newFrame;
+  return newFrame;
 }
 
 void
 nsTableFrame::AppendAnonymousColFrames(int32_t aNumColsToAdd)
 {
   MOZ_ASSERT(aNumColsToAdd > 0, "We should be adding _something_.");
   // get the last col group frame
   nsTableColGroupFrame* colGroupFrame =
     static_cast<nsTableColGroupFrame*>(mColGroups.LastChild());
 
-  if (!colGroupFrame ||
-      (colGroupFrame->GetColType() != eColGroupAnonymousCell)) {
+  if (!colGroupFrame || !colGroupFrame->IsSynthetic()) {
     int32_t colIndex = (colGroupFrame) ?
                         colGroupFrame->GetStartColumnIndex() +
                         colGroupFrame->GetColCount() : 0;
-    colGroupFrame = CreateAnonymousColGroupFrame(eColGroupAnonymousCell);
+    colGroupFrame = CreateSyntheticColGroupFrame();
     if (!colGroupFrame) {
       return;
     }
     // add the new frame to the child list
     mColGroups.AppendFrame(this, colGroupFrame);
     colGroupFrame->SetStartColumnIndex(colIndex);
   }
   AppendAnonymousColFrames(colGroupFrame, aNumColsToAdd, eColAnonymousCell,
--- a/layout/tables/nsTableFrame.h
+++ b/layout/tables/nsTableFrame.h
@@ -100,22 +100,16 @@ private:
   nsDisplayTableItem*   mOldCurrentItem;
 #ifdef DEBUG
   nsDisplayTableItem*   mPushedItem;
 #endif
 };
 
 /* ============================================================================ */
 
-enum nsTableColGroupType {
-  eColGroupContent            = 0, // there is real col group content associated
-  eColGroupAnonymousCol       = 1, // the result of a col
-  eColGroupAnonymousCell      = 2  // the result of a cell alone
-};
-
 enum nsTableColType {
   eColContent            = 0, // there is real col content associated
   eColAnonymousCol       = 1, // the result of a span on a col
   eColAnonymousColGroup  = 2, // the result of a span on a col group
   eColAnonymousCell      = 3  // the result of a cell alone
 };
 
 /**
@@ -499,23 +493,22 @@ public:
   /** Insert a col frame reference into the colframe cache and adapt the cellmap
     * @param aColFrame    - the column frame
     * @param aColIndex    - index where the column should be inserted into the
     *                       colframe cache
     */
   void InsertCol(nsTableColFrame& aColFrame,
                  int32_t          aColIndex);
 
-  nsTableColGroupFrame* CreateAnonymousColGroupFrame(nsTableColGroupType aType);
+  nsTableColGroupFrame* CreateSyntheticColGroupFrame();
 
   int32_t DestroyAnonymousColFrames(int32_t aNumFrames);
 
   // Append aNumColsToAdd anonymous col frames of type eColAnonymousCell to our
-  // last eColGroupAnonymousCell colgroup.  If we have no such colgroup, then
-  // create one.
+  // last synthetic colgroup.  If we have no such colgroup, then create one.
   void AppendAnonymousColFrames(int32_t aNumColsToAdd);
 
   // Append aNumColsToAdd anonymous col frames of type aColType to
   // aColGroupFrame.  If aAddToTable is true, also call AddColsToTable on the
   // new cols.
   void AppendAnonymousColFrames(nsTableColGroupFrame* aColGroupFrame,
                                 int32_t               aNumColsToAdd,
                                 nsTableColType        aColType,
--- a/mobile/android/components/extensions/ext-utils.js
+++ b/mobile/android/components/extensions/ext-utils.js
@@ -477,16 +477,20 @@ class Tab extends TabBase {
   get audible() {
     return this.nativeTab.playingAudio;
   }
 
   get browser() {
     return this.nativeTab.browser;
   }
 
+  get discarded() {
+    return this.browser.getAttribute("pending") === "true";
+  }
+
   get cookieStoreId() {
     return getCookieStoreIdForTab(this, this.nativeTab);
   }
 
   get height() {
     return this.browser.clientHeight;
   }
 
--- a/mobile/android/components/extensions/schemas/tabs.json
+++ b/mobile/android/components/extensions/schemas/tabs.json
@@ -66,16 +66,17 @@
           "pinned": {"type": "boolean", "description": "Whether the tab is pinned."},
           "lastAccessed": {"type": "integer", "optional": true, "description": "The last time the tab was accessed as the number of milliseconds since epoch."},
           "audible": {"type": "boolean", "optional": true, "description": "Whether the tab has produced sound over the past couple of seconds (but it might not be heard if also muted). Equivalent to whether the speaker audio indicator is showing."},
           "mutedInfo": {"$ref": "MutedInfo", "optional": true, "description": "Current tab muted state and the reason for the last state change."},
           "url": {"type": "string", "optional": true, "permissions": ["tabs"], "description": "The URL the tab is displaying. This property is only present if the extension's manifest includes the <code>\"tabs\"</code> permission."},
           "title": {"type": "string", "optional": true, "permissions": ["tabs"], "description": "The title of the tab. This property is only present if the extension's manifest includes the <code>\"tabs\"</code> permission."},
           "favIconUrl": {"type": "string", "optional": true, "permissions": ["tabs"], "description": "The URL of the tab's favicon. This property is only present if the extension's manifest includes the <code>\"tabs\"</code> permission. It may also be an empty string if the tab is loading."},
           "status": {"type": "string", "optional": true, "description": "Either <em>loading</em> or <em>complete</em>."},
+          "discarded": {"type": "boolean", "optional": true, "description": "True while the tab is not loaded with content."},
           "incognito": {"type": "boolean", "description": "Whether the tab is in an incognito window."},
           "width": {"type": "integer", "optional": true, "description": "The width of the tab in pixels."},
           "height": {"type": "integer", "optional": true, "description": "The height of the tab in pixels."},
           "sessionId": {"type": "string", "optional": true, "description": "The session ID used to uniquely identify a Tab obtained from the $(ref:sessions) API."},
           "cookieStoreId": {"type": "string", "optional": true, "description": "The CookieStoreId used for the tab."}
         }
       },
       {
@@ -489,16 +490,21 @@
                 "optional": true,
                 "description": "Whether the tabs are in the last focused window."
               },
               "status": {
                 "$ref": "TabStatus",
                 "optional": true,
                 "description": "Whether the tabs have completed loading."
               },
+              "discarded": {
+                "type": "boolean",
+                "optional": true,
+                "description": "True while the tabs are not loaded with content."
+              },
               "title": {
                 "type": "string",
                 "optional": true,
                 "description": "Match page titles against a pattern."
               },
               "url": {
                 "choices": [
                   {"type": "string"},
@@ -1051,16 +1057,21 @@
             "name": "changeInfo",
             "description": "Lists the changes to the state of the tab that was updated.",
             "properties": {
               "status": {
                 "type": "string",
                 "optional": true,
                 "description": "The status of the tab. Can be either <em>loading</em> or <em>complete</em>."
               },
+              "discarded": {
+                "type": "boolean",
+                "optional": true,
+                "description": "True while the tab is not loaded with content."
+              },
               "url": {
                 "type": "string",
                 "optional": true,
                 "description": "The tab's URL if it has changed."
               },
               "pinned": {
                 "type": "boolean",
                 "optional": true,
--- a/modules/libpref/Preferences.cpp
+++ b/modules/libpref/Preferences.cpp
@@ -5,19 +5,22 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/PContent.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/HashFunctions.h"
+#include "mozilla/ResultExtensions.h"
+#include "mozilla/ScopeExit.h"
 #include "mozilla/ServoStyleSet.h"
 #include "mozilla/SyncRunnable.h"
 #include "mozilla/Telemetry.h"
+#include "mozilla/URLPreloader.h"
 #include "mozilla/UniquePtrExtensions.h"
 
 #include "nsXULAppAPI.h"
 
 #include "mozilla/Preferences.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsDataHashtable.h"
 #include "nsDirectoryServiceDefs.h"
@@ -1260,56 +1263,30 @@ Preferences::WritePrefFile(nsIFile* aFil
   // as AllowOffMainThreadSave() returns a consistent value for the
   // lifetime of the parent process.
   PrefSaveData prefsData = pref_savePrefs(gHashTable);
   return PreferencesWriter::Write(aFile, prefsData);
 }
 
 static nsresult openPrefFile(nsIFile* aFile)
 {
-  nsCOMPtr<nsIInputStream> inStr;
-
-  nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(inStr), aFile);
-  if (NS_FAILED(rv))
-    return rv;
-
-  int64_t fileSize64;
-  rv = aFile->GetFileSize(&fileSize64);
-  if (NS_FAILED(rv))
-    return rv;
-  NS_ENSURE_TRUE(fileSize64 <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
-
-  uint32_t fileSize = (uint32_t)fileSize64;
-  auto fileBuffer = MakeUniqueFallible<char[]>(fileSize);
-  if (fileBuffer == nullptr)
-    return NS_ERROR_OUT_OF_MEMORY;
-
   PrefParseState ps;
   PREF_InitParseState(&ps, PREF_ReaderCallback, ReportToConsole, nullptr);
+  auto cleanup = MakeScopeExit([&] () {
+    PREF_FinalizeParseState(&ps);
+  });
 
-  // Read is not guaranteed to return a buf the size of fileSize,
-  // but usually will.
-  nsresult rv2 = NS_OK;
-  uint32_t offset = 0;
-  for (;;) {
-    uint32_t amtRead = 0;
-    rv = inStr->Read(fileBuffer.get(), fileSize, &amtRead);
-    if (NS_FAILED(rv) || amtRead == 0)
-      break;
-    if (!PREF_ParseBuf(&ps, fileBuffer.get(), amtRead))
-      rv2 = NS_ERROR_FILE_CORRUPTED;
-    offset += amtRead;
-    if (offset == fileSize) {
-      break;
-    }
+  nsCString data;
+  MOZ_TRY_VAR(data, URLPreloader::ReadFile(aFile));
+  if (!PREF_ParseBuf(&ps, data.get(), data.Length())) {
+    return NS_ERROR_FILE_CORRUPTED;
   }
 
-  PREF_FinalizeParseState(&ps);
+  return NS_OK;
 
-  return NS_FAILED(rv) ? rv : rv2;
 }
 
 /*
  * some stuff that gets called from Pref_Init()
  */
 
 static int
 pref_CompareFileNames(nsIFile* aFile1, nsIFile* aFile2, void* /*unused*/)
@@ -1456,22 +1433,22 @@ static nsresult pref_LoadPrefsInDirList(
     else
       pref_LoadPrefsInDir(path, nullptr, 0);
   }
   return NS_OK;
 }
 
 static nsresult pref_ReadPrefFromJar(nsZipArchive* jarReader, const char *name)
 {
-  nsZipItemPtr<char> manifest(jarReader, name, true);
-  NS_ENSURE_TRUE(manifest.Buffer(), NS_ERROR_NOT_AVAILABLE);
+  nsCString manifest;
+  MOZ_TRY_VAR(manifest, URLPreloader::ReadZip(jarReader, nsDependentCString(name)));
 
   PrefParseState ps;
   PREF_InitParseState(&ps, PREF_ReaderCallback, ReportToConsole, nullptr);
-  PREF_ParseBuf(&ps, manifest, manifest.Length());
+  PREF_ParseBuf(&ps, manifest.get(), manifest.Length());
   PREF_FinalizeParseState(&ps);
 
   return NS_OK;
 }
 
 //----------------------------------------------------------------------------------------
 // Initialize default preference JavaScript buffers from
 // appropriate TEXT resources
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -2299,16 +2299,28 @@ HttpChannelChild::AsyncOpen(nsIStreamLis
              mLoadInfo->GetSecurityMode() == 0 ||
              mLoadInfo->GetInitialSecurityCheckDone() ||
              (mLoadInfo->GetSecurityMode() == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL &&
               nsContentUtils::IsSystemPrincipal(mLoadInfo->LoadingPrincipal())),
              "security flags in loadInfo but asyncOpen2() not called");
 
   LOG(("HttpChannelChild::AsyncOpen [this=%p uri=%s]\n", this, mSpec.get()));
 
+  if (LOG4_ENABLED()) {
+    JSContext* cx = nsContentUtils::GetCurrentJSContext();
+    if (cx) {
+      nsAutoCString fileNameString;
+      uint32_t line = 0, col = 0;
+      if (nsJSUtils::GetCallingLocation(cx, fileNameString, &line, &col)) {
+        LOG(("HttpChannelChild %p source script=%s:%u:%u",
+             this, fileNameString.get(), line, col));
+      }
+    }
+  }
+
 #ifdef DEBUG
   AssertPrivateBrowsingId();
 #endif
 
   if (mCanceled)
     return mStatus;
 
   NS_ENSURE_TRUE(gNeckoChild != nullptr, NS_ERROR_FAILURE);
--- a/toolkit/components/extensions/ext-tabs-base.js
+++ b/toolkit/components/extensions/ext-tabs-base.js
@@ -486,16 +486,17 @@ class TabBase {
     let result = {
       id: this.id,
       index: this.index,
       windowId: this.windowId,
       highlighted: this.selected,
       active: this.selected,
       pinned: this.pinned,
       status: this.status,
+      discarded: this.discarded,
       incognito: this.incognito,
       width: this.width,
       height: this.height,
       lastAccessed: this.lastAccessed,
       audible: this.audible,
       mutedInfo: this.mutedInfo,
     };
 
--- a/toolkit/components/passwordmgr/LoginManagerContent.jsm
+++ b/toolkit/components/passwordmgr/LoginManagerContent.jsm
@@ -32,16 +32,20 @@ XPCOMUtils.defineLazyServiceGetter(this,
                                    "@mozilla.org/network/util;1",
                                    "nsINetUtil");
 
 XPCOMUtils.defineLazyGetter(this, "log", () => {
   let logger = LoginHelper.createLogger("LoginManagerContent");
   return logger.log.bind(logger);
 });
 
+Services.cpmm.addMessageListener("clearRecipeCache", () => {
+  LoginRecipesContent._clearRecipeCache();
+});
+
 // These mirror signon.* prefs.
 var gEnabled, gAutofillForms, gStoreWhenAutocompleteOff;
 var gLastRightClickTimeStamp = Number.NEGATIVE_INFINITY;
 
 var observer = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                           Ci.nsIFormSubmitObserver,
                                           Ci.nsIWebProgressListener,
@@ -555,16 +559,19 @@ var LoginManagerContent = {
       userTriggered: true,
     });
   },
 
   loginsFound({ form, loginsFound, recipes }) {
     let doc = form.ownerDocument;
     let autofillForm = gAutofillForms && !PrivateBrowsingUtils.isContentWindowPrivate(doc.defaultView);
 
+    let formOrigin = LoginUtils._getPasswordOrigin(doc.documentURI);
+    LoginRecipesContent.cacheRecipes(formOrigin, doc.defaultView, recipes);
+
     this._fillForm(form, loginsFound, recipes, {autofillForm});
   },
 
   /**
    * Focus event handler for username fields to decide whether to show autocomplete.
    * @param {FocusEvent} event
    */
   _onUsernameFocus(event) {
@@ -626,20 +633,18 @@ var LoginManagerContent = {
     // fillForm() to try filling in a login without a username
     // to filter on (bug 471906).
     if (!acInputField.value)
       return;
 
     log("onUsernameInput from", event.type);
 
     let doc = acForm.ownerDocument;
-    let messageManager = messageManagerFromWindow(doc.defaultView);
-    let recipes = messageManager.sendSyncMessage("RemoteLogins:findRecipes", {
-      formOrigin: LoginUtils._getPasswordOrigin(doc.documentURI),
-    })[0];
+    let formOrigin = LoginUtils._getPasswordOrigin(doc.documentURI);
+    let recipes = LoginRecipesContent.getRecipes(formOrigin, doc.defaultView);
 
     // Make sure the username field fillForm will use is the
     // same field as the autocomplete was activated on.
     var [usernameField, passwordField, ignored] =
         this._getFormFields(acForm, false, recipes);
     if (usernameField == acInputField && passwordField) {
       this._getLoginDataFromParent(acForm, { showMasterPassword: false })
           .then(({ form, loginsFound, recipes }) => {
@@ -923,19 +928,17 @@ var LoginManagerContent = {
     if (!hostname) {
       log("(form submission ignored -- invalid hostname)");
       return;
     }
 
     let formSubmitURL = LoginUtils._getActionOrigin(form);
     let messageManager = messageManagerFromWindow(win);
 
-    let recipes = messageManager.sendSyncMessage("RemoteLogins:findRecipes", {
-      formOrigin: hostname,
-    })[0];
+    let recipes = LoginRecipesContent.getRecipes(hostname, win);
 
     // Get the appropriate fields from the form.
     var [usernameField, newPasswordField, oldPasswordField] =
           this._getFormFields(form, true, recipes);
 
     // Need at least 1 valid password field to do anything.
     if (newPasswordField == null)
       return;
@@ -1340,20 +1343,18 @@ var LoginManagerContent = {
     if (!(aField instanceof Ci.nsIDOMHTMLInputElement) ||
         (aField.type != "password" && !LoginHelper.isUsernameFieldType(aField)) ||
         !aField.ownerDocument) {
       return null;
     }
     let form = LoginFormFactory.createFromField(aField);
 
     let doc = aField.ownerDocument;
-    let messageManager = messageManagerFromWindow(doc.defaultView);
-    let recipes = messageManager.sendSyncMessage("RemoteLogins:findRecipes", {
-      formOrigin: LoginUtils._getPasswordOrigin(doc.documentURI),
-    })[0];
+    let formOrigin = LoginUtils._getPasswordOrigin(doc.documentURI);
+    let recipes = LoginRecipesContent.getRecipes(formOrigin, doc.defaultView);
 
     let [usernameField, newPasswordField] =
           this._getFormFields(form, false, recipes);
 
     // If we are not verifying a password field, we want
     // to use aField as the username field.
     if (aField.type != "password") {
       usernameField = aField;
--- a/toolkit/components/passwordmgr/LoginRecipes.jsm
+++ b/toolkit/components/passwordmgr/LoginRecipes.jsm
@@ -108,16 +108,17 @@ LoginRecipesParent.prototype = {
             if (!Components.isSuccessCode(result)) {
               throw new Error("Error fetching recipe file:" + result);
             }
             let count = stream.available();
             let data = NetUtil.readInputStreamToString(stream, count, { charset: "UTF-8" });
             resolve(JSON.parse(data));
           });
         }).then(recipes => {
+          Services.ppmm.broadcastAsyncMessage("clearRecipeCache");
           return this.load(recipes);
         }).then(resolve => {
           return this;
         });
       } catch (e) {
         throw new Error("Error reading recipe file:" + e);
       }
     } else {
@@ -184,16 +185,72 @@ LoginRecipesParent.prototype = {
     }
 
     return hostRecipes;
   },
 };
 
 
 var LoginRecipesContent = {
+  _recipeCache: new WeakMap(),
+
+  _clearRecipeCache() {
+    this._recipeCache = new WeakMap();
+  },
+
+  /**
+   * Locally caches recipes for a given host.
+   *
+   * @param {String} aHost (e.g. example.com:8080 [non-default port] or sub.example.com)
+   * @param {Object} win - the window of the host
+   * @param {Set} recipes - recipes that apply to the host
+   */
+  cacheRecipes(aHost, win, recipes) {
+    let recipeMap = this._recipeCache.get(win);
+
+    if (!recipeMap) {
+      recipeMap = new Map();
+      this._recipeCache.set(win, recipeMap);
+    }
+
+    recipeMap.set(aHost, recipes);
+  },
+
+  /**
+   * Tries to fetch recipes for a given host, using a local cache if possible.
+   * Otherwise, the recipes are cached for later use.
+   *
+   * @param {String} aHost (e.g. example.com:8080 [non-default port] or sub.example.com)
+   * @param {Object} win - the window of the host
+   * @return {Set} of recipes that apply to the host
+   */
+  getRecipes(aHost, win) {
+    let recipes;
+    let recipeMap = this._recipeCache.get(win);
+
+    if (recipeMap) {
+      recipes = recipeMap.get(aHost);
+
+      if (recipes) {
+        return recipes;
+      }
+    }
+
+    let mm = win.QueryInterface(Ci.nsIInterfaceRequestor)
+                .getInterface(Ci.nsIWebNavigation)
+                .QueryInterface(Ci.nsIDocShell)
+                .QueryInterface(Ci.nsIInterfaceRequestor)
+                .getInterface(Ci.nsIContentFrameMessageManager);
+
+    recipes = mm.sendSyncMessage("RemoteLogins:findRecipes", { formOrigin: aHost })[0];
+    this.cacheRecipes(aHost, win, recipes);
+
+    return recipes;
+  },
+
   /**
    * @param {Set} aRecipes - Possible recipes that could apply to the form
    * @param {FormLike} aForm - We use a form instead of just a URL so we can later apply
    * tests to the page contents.
    * @return {Set} a subset of recipes that apply to the form with the order preserved
    */
   _filterRecipesForForm(aRecipes, aForm) {
     let formDocURL = aForm.ownerDocument.location;
--- a/toolkit/components/passwordmgr/test/mochitest/mochitest.ini
+++ b/toolkit/components/passwordmgr/test/mochitest/mochitest.ini
@@ -47,16 +47,17 @@ skip-if = toolkit == 'android' # autocom
 [test_form_action_javascript.html]
 [test_formless_autofill.html]
 [test_formless_submit.html]
 [test_formless_submit_navigation.html]
 [test_formless_submit_navigation_negative.html]
 [test_input_events.html]
 [test_input_events_for_identical_values.html]
 [test_maxlength.html]
+[test_onsubmit_value_change.html]
 [test_passwords_in_type_password.html]
 [test_prompt.html]
 skip-if = os == "linux" || toolkit == 'android' # Tests desktop prompts
 [test_prompt_http.html]
 skip-if = os == "linux" || toolkit == 'android' # Tests desktop prompts
 [test_prompt_noWindow.html]
 skip-if = e10s || toolkit == 'android' # Tests desktop prompts. e10s: bug 1217876
 [test_prompt_promptAuth.html]
--- a/toolkit/components/passwordmgr/test/mochitest/test_basic_form_autocomplete.html
+++ b/toolkit/components/passwordmgr/test/mochitest/test_basic_form_autocomplete.html
@@ -157,29 +157,29 @@ var setupScript = runInParent(function s
 
   <!-- test autocomplete dropdown -->
   <form id="form9" action="http://autocomplete5" onsubmit="return false;">
     <input  type="text"       name="uname">
     <input  type="password"   name="pword">
     <button type="submit">Submit</button>
   </form>
 
+  <!-- tests <form>-less autocomplete -->
+  <div id="form11">
+     <input  type="text"       name="uname" id="uname">
+     <input  type="password"   name="pword" id="pword">
+     <button type="submit">Submit</button>
+  </div>
+
   <!-- test for onUsernameInput recipe testing -->
-  <form id="form11" action="http://autocomplete7" onsubmit="return false;">
+  <form id="form12" action="http://autocomplete7" onsubmit="return false;">
     <input  type="text"   name="1">
     <input  type="text"   name="2">
     <button type="submit">Submit</button>
   </form>
-
-  <!-- tests <form>-less autocomplete -->
-  <div id="form12">
-     <input  type="text"       name="uname" id="uname">
-     <input  type="password"   name="pword" id="pword">
-     <button type="submit">Submit</button>
-   </div>
 </div>
 
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 /** Test for Login Manager: multiple login autocomplete. **/
 
 var uname = $_(1, "uname");
@@ -766,26 +766,72 @@ add_task(async function test_form9_autoc
   // check that empty results are cached - bug 496466
   promise0 = notifyMenuChanged(0);
   sendChar("z");
   await promise0;
   popupState = await getPopupState();
   is(popupState.open, false, "Check popup stays closed due to cached empty result");
 });
 
-add_task(async function test_form11_recipes() {
+add_task(async function test_form11_formless() {
+  // Test form-less autocomplete
+  uname = $_(11, "uname");
+  pword = $_(11, "pword");
+  restoreForm();
+  checkACForm("", "");
+  let shownPromise = promiseACShown();
+  doKey("down"); // open
+  await shownPromise;
+
+  // Trigger autocomplete
+  doKey("down");
+  checkACForm("", ""); // value shouldn't update
+  let processedPromise = promiseFormsProcessed();
+  doKey("return"); // not "enter"!
+  await processedPromise;
+  checkACForm("testuser", "testpass");
+});
+
+add_task(async function test_form11_open_on_trusted_focus() {
+  uname = $_(11, "uname");
+  pword = $_(11, "pword");
+  uname.value = "";
+  pword.value = "";
+
+  // Move focus to the password field so we can test the first click on the
+  // username field.
+  pword.focus();
+  checkACForm("", "");
+  const firePrivEventPromise = new Promise((resolve) => {
+    uname.addEventListener("click", (e) => {
+      ok(e.isTrusted, "Ensure event is trusted");
+      resolve();
+    });
+  });
+  const shownPromise = promiseACShown();
+  synthesizeMouseAtCenter(uname, {});
+  await firePrivEventPromise;
+  await shownPromise;
+  doKey("down");
+  const processedPromise = promiseFormsProcessed();
+  doKey("return"); // not "enter"!
+  await processedPromise;
+  checkACForm("testuser", "testpass");
+});
+
+add_task(async function test_form12_recipes() {
   await loadRecipes({
     siteRecipes: [{
       "hosts": ["mochi.test:8888"],
       "usernameSelector": "input[name='1']",
       "passwordSelector": "input[name='2']"
     }],
   });
-  uname = $_(11, "1");
-  pword = $_(11, "2");
+  uname = $_(12, "1");
+  pword = $_(12, "2");
 
   // First test DOMAutocomplete
   // Switch the password field to type=password so _fillForm marks the username
   // field for autocomplete.
   pword.type = "password";
   await promiseFormsProcessed();
   restoreForm();
   checkACForm("", "");
@@ -804,58 +850,12 @@ add_task(async function test_form11_reci
   checkACForm("", "");
   uname.value = "testuser10";
   checkACForm("testuser10", "");
   doKey("tab");
   await promiseFormsProcessed();
   checkACForm("testuser10", "testpass10");
   await resetRecipes();
 });
-
-add_task(async function test_form12_formless() {
-  // Test form-less autocomplete
-  uname = $_(12, "uname");
-  pword = $_(12, "pword");
-  restoreForm();
-  checkACForm("", "");
-  let shownPromise = promiseACShown();
-  doKey("down"); // open
-  await shownPromise;
-
-  // Trigger autocomplete
-  doKey("down");
-  checkACForm("", ""); // value shouldn't update
-  let processedPromise = promiseFormsProcessed();
-  doKey("return"); // not "enter"!
-  await processedPromise;
-  checkACForm("testuser", "testpass");
-});
-
-add_task(async function test_form12_open_on_trusted_focus() {
-  uname = $_(12, "uname");
-  pword = $_(12, "pword");
-  uname.value = "";
-  pword.value = "";
-
-  // Move focus to the password field so we can test the first click on the
-  // username field.
-  pword.focus();
-  checkACForm("", "");
-  const firePrivEventPromise = new Promise((resolve) => {
-    uname.addEventListener("click", (e) => {
-      ok(e.isTrusted, "Ensure event is trusted");
-      resolve();
-    });
-  });
-  const shownPromise = promiseACShown();
-  synthesizeMouseAtCenter(uname, {});
-  await firePrivEventPromise;
-  await shownPromise;
-  doKey("down");
-  const processedPromise = promiseFormsProcessed();
-  doKey("return"); // not "enter"!
-  await processedPromise;
-  checkACForm("testuser", "testpass");
-});
 </script>
 </pre>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/toolkit/components/passwordmgr/test/mochitest/test_onsubmit_value_change.html
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test input value change right after onsubmit event</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+  <script type="text/javascript" src="pwmgr_common.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+Login Manager test: input value change right after onsubmit event
+
+<script>
+  let chromeScript = runChecksAfterCommonInit();
+
+  function getSubmitMessage() {
+    info("getSubmitMessage");
+    return new Promise((resolve, reject) => {
+      chromeScript.addMessageListener("formSubmissionProcessed", function processed(...args) {
+        info("got formSubmissionProcessed");
+        chromeScript.removeMessageListener("formSubmissionProcessed", processed);
+        resolve(...args);
+      });
+    });
+  }
+</script>
+<p id="display"></p>
+
+<div id="content" style="display: none">
+
+  <form id="form1" action="formTest.js" onsubmit="return false;">
+    <input  type="text"     name="uname" id="ufield">
+    <input  type="password" name="pword" id="pfield">
+    <button type="submit" id="submitBtn">Submit</button>
+  </form>
+
+</div>
+
+<pre id="test"></pre>
+<script>
+  /** Test for Login Manager: input value change right after onsubmit event **/
+  add_task(async function checkFormValues() {
+    document.getElementById("ufield").value = "testuser";
+    document.getElementById("pfield").value = "testpass";
+    is($_(1, "uname").value, "testuser", "Checking for filled username");
+    is($_(1, "pword").value, "testpass", "Checking for filled password");
+
+    document.getElementById("form1").addEventListener("submit", () => {
+      document.getElementById("ufield").value = "newuser";
+      document.getElementById("pfield").value = "newpass";
+    }, true);
+
+    document.getElementById("form1").addEventListener("submit", (e) => e.preventDefault());
+
+    let processedPromise = getSubmitMessage();
+
+    let button = document.getElementById("submitBtn");
+    button.click();
+
+    let submittedResult = await processedPromise;
+    is(submittedResult.usernameField.value, "testuser", "Should have registered \"testuser\" for username");
+    is(submittedResult.newPasswordField.value, "testpass", "Should have registered \"testpass\" for username");
+  });
+</script>
+</body>
+</html>
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -8045,20 +8045,23 @@
   "SUBJECT_PRINCIPAL_ACCESSED_WITHOUT_SCRIPT_ON_STACK": {
     "record_in_processes": ["main", "content"],
     "expires_in_version": "46",
     "alert_emails": ["bholley@mozilla.com"],
     "kind": "flag",
     "description": "Whether the subject principal was accessed without script on the stack during the current session"
   },
   "TOUCH_ENABLED_DEVICE": {
-    "record_in_processes": ["main", "content"],
-    "expires_in_version": "never",
-    "kind": "boolean",
-    "description": "The device supports touch input",
+    "record_in_processes": ["main"],
+    "expires_in_version": "never",
+    "kind": "boolean",
+    "releaseChannelCollection": "opt-out",
+    "bug_numbers": [795307, 1390269],
+    "alert_emails": ["jimm@mozilla.com"],
+    "description": "Boolean indicating if a touch input device is detected.",
     "cpp_guard": "XP_WIN"
   },
   "COMPONENTS_SHIM_ACCESSED_BY_CONTENT": {
     "record_in_processes": ["main", "content"],
     "expires_in_version": "never",
     "kind": "flag",
     "description": "Whether content ever accesed the Components shim in this session"
   },
--- a/toolkit/components/telemetry/histogram-whitelists.json
+++ b/toolkit/components/telemetry/histogram-whitelists.json
@@ -544,17 +544,16 @@
     "SYSTEM_FONT_FALLBACK_SCRIPT",
     "TAP_TO_LOAD_ENABLED",
     "TAP_TO_LOAD_IMAGE_SIZE",
     "THUNDERBIRD_CONVERSATIONS_TIME_TO_2ND_GLODA_QUERY_MS",
     "THUNDERBIRD_GLODA_SIZE_MB",
     "THUNDERBIRD_INDEXING_RATE_MSG_PER_S",
     "TLS_ERROR_REPORT_UI",
     "TOP_LEVEL_CONTENT_DOCUMENTS_DESTROYED",
-    "TOUCH_ENABLED_DEVICE",
     "TRANSLATED_CHARACTERS",
     "TRANSLATED_PAGES",
     "TRANSLATED_PAGES_BY_LANGUAGE",
     "TRANSLATION_OPPORTUNITIES",
     "TRANSLATION_OPPORTUNITIES_BY_LANGUAGE",
     "VIDEO_CANPLAYTYPE_H264_CONSTRAINT_SET_FLAG",
     "VIDEO_CANPLAYTYPE_H264_LEVEL",
     "VIDEO_CANPLAYTYPE_H264_PROFILE",
@@ -1347,17 +1346,16 @@
     "THUNDERBIRD_CONVERSATIONS_TIME_TO_2ND_GLODA_QUERY_MS",
     "THUNDERBIRD_GLODA_SIZE_MB",
     "THUNDERBIRD_INDEXING_RATE_MSG_PER_S",
     "TLS_ERROR_REPORT_UI",
     "TOP_LEVEL_CONTENT_DOCUMENTS_DESTROYED",
     "TOTAL_CONTENT_PAGE_LOAD_TIME",
     "TOTAL_COUNT_HIGH_ERRORS",
     "TOTAL_COUNT_LOW_ERRORS",
-    "TOUCH_ENABLED_DEVICE",
     "TRANSACTION_WAIT_TIME_HTTP",
     "TRANSACTION_WAIT_TIME_SPDY",
     "TRANSLATED_CHARACTERS",
     "TRANSLATED_PAGES",
     "TRANSLATED_PAGES_BY_LANGUAGE",
     "TRANSLATION_OPPORTUNITIES",
     "TRANSLATION_OPPORTUNITIES_BY_LANGUAGE",
     "UPDATE_CANNOT_STAGE_EXTERNAL",
--- a/toolkit/components/xulstore/XULStore.js
+++ b/toolkit/components/xulstore/XULStore.js
@@ -80,31 +80,22 @@ XULStore.prototype = {
   log(message) {
     if (!debugMode)
       return;
     dump("XULStore: " + message + "\n");
     Services.console.logStringMessage("XULStore: " + message);
   },
 
   readFile() {
-    const MODE_RDONLY = 0x01;
-    const FILE_PERMS  = 0o600;
-
-    let stream = Cc["@mozilla.org/network/file-input-stream;1"].
-                 createInstance(Ci.nsIFileInputStream);
-    let json = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON);
     try {
-      stream.init(this._storeFile, MODE_RDONLY, FILE_PERMS, 0);
-      this._data = json.decodeFromStream(stream, stream.available());
+      this._data = JSON.parse(Cu.readFile(this._storeFile));
     } catch (e) {
       this.log("Error reading JSON: " + e);
       // This exception could mean that the file didn't exist.
       // We'll just ignore the error and start with a blank slate.
-    } finally {
-      stream.close();
     }
   },
 
   async writeFile() {
     if (!this._needsSaving)
       return;
 
     this._needsSaving = false;
--- a/toolkit/mozapps/extensions/AddonManagerStartup.cpp
+++ b/toolkit/mozapps/extensions/AddonManagerStartup.cpp
@@ -14,16 +14,17 @@
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/EndianUtils.h"
 #include "mozilla/Compression.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ResultExtensions.h"
 #include "mozilla/ScopeExit.h"
 #include "mozilla/Services.h"
+#include "mozilla/URLPreloader.h"
 #include "mozilla/Unused.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/ipc/StructuredCloneData.h"
 
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsAppRunner.h"
 #include "nsContentUtils.h"
 #include "nsChromeRegistry.h"
@@ -99,38 +100,16 @@ CloneAndAppend(nsIFile* aFile, const cha
 
 static bool
 IsNormalFile(nsIFile* file)
 {
   bool result;
   return NS_SUCCEEDED(file->IsFile(&result)) && result;
 }
 
-static Result<nsCString, nsresult>
-ReadFile(nsIFile* file)
-{
-  nsCString result;
-
-  AutoFDClose fd;
-  MOZ_TRY(file->OpenNSPRFileDesc(PR_RDONLY, 0, &fd.rwget()));
-
-  auto size = PR_Seek64(fd, 0, PR_SEEK_END);
-  PR_Seek64(fd, 0, PR_SEEK_SET);
-
-  result.SetLength(size);
-
-  auto len = PR_Read(fd, result.BeginWriting(), size);
-
-  if (size != len) {
-    return Err(NS_ERROR_FAILURE);
-  }
-
-  return result;
-}
-
 static const char STRUCTURED_CLONE_MAGIC[] = "mozJSSCLz40v001";
 
 template <typename T>
 static Result<nsCString, nsresult>
 DecodeLZ4(const nsACString& lz4, const T& magicNumber)
 {
   constexpr auto HEADER_SIZE = sizeof(magicNumber) + 4;
 
@@ -184,29 +163,24 @@ EncodeLZ4(const nsACString& data, const 
 }
 
 static_assert(sizeof STRUCTURED_CLONE_MAGIC % 8 == 0,
               "Magic number should be an array of uint64_t");
 
 /**
  * Reads the contents of a LZ4-compressed file, as stored by the OS.File
  * module, and returns the decompressed contents on success.
- *
- * A nonexistent or empty file is treated as success. A corrupt or non-LZ4
- * file is treated as failure.
  */
 static Result<nsCString, nsresult>
 ReadFileLZ4(nsIFile* file)
 {
   static const char MAGIC_NUMBER[] = "mozLz40";
 
-  nsCString result;
-
   nsCString lz4;
-  MOZ_TRY_VAR(lz4, ReadFile(file));
+  MOZ_TRY_VAR(lz4, URLPreloader::ReadFile(file));
 
   if (lz4.IsEmpty()) {
     return lz4;
   }
 
   return DecodeLZ4(lz4, MAGIC_NUMBER);
 }
 
@@ -581,17 +555,22 @@ AddonManagerStartup::AddInstallLocation(
 nsresult
 AddonManagerStartup::ReadStartupData(JSContext* cx, JS::MutableHandleValue locations)
 {
   locations.set(JS::UndefinedValue());
 
   nsCOMPtr<nsIFile> file = CloneAndAppend(ProfileDir(), "addonStartup.json.lz4");
 
   nsCString data;
-  MOZ_TRY_VAR(data, ReadFileLZ4(file));
+  auto res = ReadFileLZ4(file);
+  if (res.isOk()) {
+    data = res.unwrap();
+  } else if (res.unwrapErr() != NS_ERROR_FILE_NOT_FOUND) {
+    return res.unwrapErr();
+  }
 
   if (data.IsEmpty() || !ParseJSON(cx, data, locations)) {
     return NS_OK;
   }
 
   if (!locations.isObject()) {
     return NS_ERROR_UNEXPECTED;
   }
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -976,19 +976,26 @@ nsXULAppInfo::GetAccessibleHandlerUsed(b
 #endif
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXULAppInfo::GetAccessibilityInstantiator(nsAString &aInstantiator)
 {
 #if defined(ACCESSIBILITY) && defined(XP_WIN)
-  if (!a11y::GetInstantiator(aInstantiator)) {
+  if (!GetAccService()) {
     aInstantiator = NS_LITERAL_STRING("");
-  }
+    return NS_OK;
+  }
+  nsAutoString oopClientInfo, ipClientInfo;
+  a11y::Compatibility::GetHumanReadableConsumersStr(ipClientInfo);
+  aInstantiator.Append(ipClientInfo);
+  aInstantiator.AppendLiteral("|");
+  a11y::GetInstantiator(oopClientInfo);
+  aInstantiator.Append(oopClientInfo);
 #else
   aInstantiator = NS_LITERAL_STRING("");
 #endif
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXULAppInfo::GetIs64Bit(bool* aResult)
@@ -1002,17 +1009,18 @@ nsXULAppInfo::GetIs64Bit(bool* aResult)
 }
 
 NS_IMETHODIMP
 nsXULAppInfo::EnsureContentProcess()
 {
   if (!XRE_IsParentProcess())
     return NS_ERROR_NOT_AVAILABLE;
 
-  RefPtr<ContentParent> unused = ContentParent::GetNewOrUsedBrowserProcess();
+  RefPtr<ContentParent> unused = ContentParent::GetNewOrUsedBrowserProcess(
+    NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE));
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXULAppInfo::InvalidateCachesOnRestart()
 {
   nsCOMPtr<nsIFile> file;
   nsresult rv = NS_GetSpecialDirectory(NS_APP_PROFILE_DIR_STARTUP,
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -896,17 +896,23 @@ XRE_ShutdownChildProcess()
 #endif // XP_MACOSX
 }
 
 namespace {
 ContentParent* gContentParent; //long-lived, manually refcounted
 TestShellParent* GetOrCreateTestShellParent()
 {
     if (!gContentParent) {
-        RefPtr<ContentParent> parent = ContentParent::GetNewOrUsedBrowserProcess();
+        // Use a "web" child process by default.  File a bug if you don't like
+        // this and you're sure you wouldn't be better off writing a "browser"
+        // chrome mochitest where you can have multiple types of content
+        // processes.
+        RefPtr<ContentParent> parent =
+            ContentParent::GetNewOrUsedBrowserProcess(
+                NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE));
         parent.forget(&gContentParent);
     } else if (!gContentParent->IsAlive()) {
         return nullptr;
     }
     TestShellParent* tsp = gContentParent->GetTestShellSingleton();
     if (!tsp) {
         tsp = gContentParent->CreateTestShell();
     }
--- a/toolkit/xre/test/win/TestDllInterceptor.cpp
+++ b/toolkit/xre/test/win/TestDllInterceptor.cpp
@@ -427,16 +427,24 @@ bool TestTlsAlloc(void* aFunc)
 
 bool TestTlsFree(void* aFunc)
 {
   auto patchedTlsFree =
     reinterpret_cast<decltype(&TlsFree)>(aFunc);
   return sTlsIndex != 0 && patchedTlsFree(sTlsIndex);
 }
 
+bool TestPrintDlgW(void* aFunc)
+{
+  auto patchedPrintDlgW =
+    reinterpret_cast<decltype(&PrintDlgW)>(aFunc);
+  patchedPrintDlgW(0);
+  return true;
+}
+
 int main()
 {
   payload initial = { 0x12345678, 0xfc4e9d31, 0x87654321 };
   payload p0, p1;
   ZeroMemory(&p0, sizeof(p0));
   ZeroMemory(&p1, sizeof(p1));
 
   p0 = rotatePayload(initial);
@@ -526,16 +534,17 @@ int main()
       TestHook(TestImmNotifyIME, "imm32.dll", "ImmNotifyIME") &&
       TestHook(TestGetSaveFileNameW, "comdlg32.dll", "GetSaveFileNameW") &&
       TestHook(TestGetOpenFileNameW, "comdlg32.dll", "GetOpenFileNameW") &&
 #ifdef _M_X64
       TestHook(TestGetKeyState, "user32.dll", "GetKeyState") &&    // see Bug 1316415
       TestHook(TestLdrUnloadDll, "ntdll.dll", "LdrUnloadDll") &&
       MaybeTestHook(IsWin8OrLater(), TestLdrResolveDelayLoadedAPI, "ntdll.dll", "LdrResolveDelayLoadedAPI") &&
       MaybeTestHook(!IsWin8OrLater(), TestRtlInstallFunctionTableCallback, "kernel32.dll", "RtlInstallFunctionTableCallback") &&
+      TestHook(TestPrintDlgW, "comdlg32.dll", "PrintDlgW") &&
 #endif
       MaybeTestHook(ShouldTestTipTsf(), TestProcessCaretEvents, "tiptsf.dll", "ProcessCaretEvents") &&
 #ifdef _M_IX86
       TestHook(TestSendMessageTimeoutW, "user32.dll", "SendMessageTimeoutW") &&
 #endif
       TestHook(TestSetCursorPos, "user32.dll", "SetCursorPos") &&
       TestHook(TestTlsAlloc, "kernel32.dll", "TlsAlloc") &&
       TestHook(TestTlsFree, "kernel32.dll", "TlsFree") &&
--- a/xpcom/base/nsINIParser.cpp
+++ b/xpcom/base/nsINIParser.cpp
@@ -5,30 +5,26 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // Moz headers (alphabetical)
 #include "nsCRTGlue.h"
 #include "nsError.h"
 #include "nsIFile.h"
 #include "nsINIParser.h"
 #include "mozilla/FileUtils.h" // AutoFILE
+#include "mozilla/ResultExtensions.h"
+#include "mozilla/URLPreloader.h"
 
 // System headers (alphabetical)
 #include <stdio.h>
 #include <stdlib.h>
 #ifdef XP_WIN
 #include <windows.h>
 #endif
 
-#if defined(XP_WIN)
-#define READ_BINARYMODE L"rb"
-#else
-#define READ_BINARYMODE "r"
-#endif
-
 using namespace mozilla;
 
 #ifdef XP_WIN
 inline FILE*
 TS_tfopen(const char* aPath, const wchar_t* aMode)
 {
   wchar_t wPath[MAX_PATH];
   MultiByteToWideChar(CP_UTF8, 0, aPath, -1, wPath, MAX_PATH);
@@ -60,134 +56,60 @@ public:
   void operator=(FILE* aFp) { fp_ = aFp; }
 private:
   FILE* fp_;
 };
 
 nsresult
 nsINIParser::Init(nsIFile* aFile)
 {
-  /* open the file. Don't use OpenANSIFileDesc, because you mustn't
-     pass FILE* across shared library boundaries, which may be using
-     different CRTs */
-
-  AutoFILE fd;
-
-#ifdef XP_WIN
-  nsAutoString path;
-  nsresult rv = aFile->GetPath(path);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
+  nsCString result;
+  MOZ_TRY_VAR(result, URLPreloader::ReadFile(aFile));
 
-  fd = _wfopen(path.get(), READ_BINARYMODE);
-#else
-  nsAutoCString path;
-  aFile->GetNativePath(path);
-
-  fd = fopen(path.get(), READ_BINARYMODE);
-#endif
-
-  if (!fd) {
-    return NS_ERROR_FAILURE;
-  }
-
-  return InitFromFILE(fd);
+  return InitFromString(result);
 }
 
 nsresult
 nsINIParser::Init(const char* aPath)
 {
-  /* open the file */
-  AutoFILE fd(TS_tfopen(aPath, READ_BINARYMODE));
-  if (!fd) {
-    return NS_ERROR_FAILURE;
-  }
+  nsCString result;
+  MOZ_TRY_VAR(result, URLPreloader::ReadFile(nsDependentCString(aPath)));
 
-  return InitFromFILE(fd);
+  return InitFromString(result);
 }
 
 static const char kNL[] = "\r\n";
 static const char kEquals[] = "=";
 static const char kWhitespace[] = " \t";
 static const char kRBracket[] = "]";
 
 nsresult
-nsINIParser::InitFromFILE(FILE* aFd)
+nsINIParser::InitFromString(const nsCString& aStr)
 {
-  /* get file size */
-  if (fseek(aFd, 0, SEEK_END) != 0) {
-    return NS_ERROR_FAILURE;
-  }
-
-  long flen = ftell(aFd);
-  /* zero-sized file, or an error */
-  if (flen <= 0) {
-    return NS_ERROR_FAILURE;
-  }
-
-  /* malloc an internal buf the size of the file */
-  mFileContents = MakeUnique<char[]>(flen + 2);
-  if (!mFileContents) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
+  char* buffer;
 
-  /* read the file in one swoop */
-  if (fseek(aFd, 0, SEEK_SET) != 0) {
-    return NS_BASE_STREAM_OSERROR;
-  }
-
-  int rd = fread(mFileContents.get(), sizeof(char), flen, aFd);
-  if (rd != flen) {
-    return NS_BASE_STREAM_OSERROR;
-  }
-
-  // We write a UTF16 null so that the file is easier to convert to UTF8
-  mFileContents[flen] = mFileContents[flen + 1] = '\0';
-
-  char* buffer = &mFileContents[0];
-
-  if (flen >= 3 &&
-      mFileContents[0] == '\xEF' &&
-      mFileContents[1] == '\xBB' &&
-      mFileContents[2] == '\xBF') {
+  if (StringHead(aStr, 3) == "\xEF\xBB\xBF") {
     // Someone set us up the Utf-8 BOM
     // This case is easy, since we assume that BOM-less
     // files are Utf-8 anyway.  Just skip the BOM and process as usual.
-    buffer = &mFileContents[3];
-  }
+    mFileContents.Append(aStr);
+    buffer = mFileContents.BeginWriting() + 3;
+  } else {
+    if (StringHead(aStr, 2) == "\xFF\xFE") {
+      // Someone set us up the Utf-16LE BOM
+      nsDependentSubstring str(reinterpret_cast<const char16_t*>(aStr.get()),
+                               aStr.Length() / 2);
 
-#ifdef XP_WIN
-  if (flen >= 2 &&
-      mFileContents[0] == '\xFF' &&
-      mFileContents[1] == '\xFE') {
-    // Someone set us up the Utf-16LE BOM
-    buffer = &mFileContents[2];
-    // Get the size required for our Utf8 buffer
-    flen = WideCharToMultiByte(CP_UTF8,
-                               0,
-                               reinterpret_cast<LPWSTR>(buffer),
-                               -1,
-                               nullptr,
-                               0,
-                               nullptr,
-                               nullptr);
-    if (flen == 0) {
-      return NS_ERROR_FAILURE;
+      AppendUTF16toUTF8(Substring(str, 1), mFileContents);
+    } else {
+      mFileContents.Append(aStr);
     }
 
-    UniquePtr<char[]> utf8Buffer(new char[flen]);
-    if (WideCharToMultiByte(CP_UTF8, 0, reinterpret_cast<LPWSTR>(buffer), -1,
-                            utf8Buffer.get(), flen, nullptr, nullptr) == 0) {
-      return NS_ERROR_FAILURE;
-    }
-    mFileContents = Move(utf8Buffer);
-    buffer = mFileContents.get();
+    buffer = mFileContents.BeginWriting();
   }
-#endif
 
   char* currSection = nullptr;
 
   // outer loop tokenizes into lines
   while (char* token = NS_strtok(kNL, &buffer)) {
     if (token[0] == '#' || token[0] == ';') { // it's a comment
       continue;
     }
--- a/xpcom/base/nsINIParser.h
+++ b/xpcom/base/nsINIParser.h
@@ -105,14 +105,14 @@ private:
     }
 
     const char* key;
     const char* value;
     mozilla::UniquePtr<INIValue> next;
   };
 
   nsClassHashtable<nsDepCharHashKey, INIValue> mSections;
-  mozilla::UniquePtr<char[]> mFileContents;
+  nsCString mFileContents;
 
-  nsresult InitFromFILE(FILE* aFd);
+  nsresult InitFromString(const nsCString& aStr);
 };
 
 #endif /* nsINIParser_h__ */
--- a/xpcom/base/nsMemoryInfoDumper.cpp
+++ b/xpcom/base/nsMemoryInfoDumper.cpp
@@ -33,17 +33,19 @@
 #else
 #include <unistd.h>
 #endif
 
 #ifdef XP_UNIX
 #define MOZ_SUPPORTS_FIFO 1
 #endif
 
-#if defined(XP_LINUX) || defined(__FreeBSD__)
+// Some Android devices seem to send RT signals to Firefox so we want to avoid
+// consuming those as they're not user triggered.
+#if !defined(ANDROID) && (defined(XP_LINUX) || defined(__FreeBSD__))
 #define MOZ_SUPPORTS_RT_SIGNALS 1
 #endif
 
 #if defined(MOZ_SUPPORTS_RT_SIGNALS)
 #include <fcntl.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #endif
--- a/xpcom/build/FileLocation.cpp
+++ b/xpcom/build/FileLocation.cpp
@@ -19,16 +19,36 @@ FileLocation::FileLocation(nsIFile* aFil
   Init(aFile);
 }
 
 FileLocation::FileLocation(nsIFile* aFile, const char* aPath)
 {
   Init(aFile, aPath);
 }
 
+FileLocation::FileLocation(nsZipArchive* aZip, const char* aPath)
+{
+  Init(aZip, aPath);
+}
+
+FileLocation::FileLocation(const FileLocation& aOther)
+  : mBaseFile(aOther.mBaseFile)
+  , mBaseZip(aOther.mBaseZip)
+  , mPath(aOther.mPath)
+{
+}
+
+FileLocation::FileLocation(FileLocation&& aOther)
+  : mBaseFile(Move(aOther.mBaseFile))
+  , mBaseZip(Move(aOther.mBaseZip))
+  , mPath(Move(aOther.mPath))
+{
+  aOther.mPath.Truncate();
+}
+
 FileLocation::FileLocation(const FileLocation& aFile, const char* aPath)
 {
   if (aFile.IsZip()) {
     if (aFile.mBaseFile) {
       Init(aFile.mBaseFile, aFile.mPath.get());
     }
     else {
       Init(aFile.mBaseZip, aFile.mPath.get());
--- a/xpcom/build/FileLocation.h
+++ b/xpcom/build/FileLocation.h
@@ -30,33 +30,38 @@ public:
    * As such, it stores a path within an archive, as well as the archive
    * path itself, or the complete file path alone when on a filesystem.
    * When the archive is in an archive, an nsZipArchive is stored instead
    * of a file path.
    */
   FileLocation();
   ~FileLocation();
 
+  FileLocation(const FileLocation& aOther);
+  FileLocation(FileLocation&& aOther);
+
+  FileLocation& operator=(const FileLocation&) = default;
+
   /**
    * Constructor for plain files
    */
   explicit FileLocation(nsIFile* aFile);
 
   /**
    * Constructors for path within an archive. The archive can be given either
    * as nsIFile or nsZipArchive.
    */
   FileLocation(nsIFile* aZip, const char* aPath);
 
   FileLocation(nsZipArchive* aZip, const char* aPath);
 
   /**
    * Creates a new file location relative to another one.
    */
-  FileLocation(const FileLocation& aFile, const char* aPath = nullptr);
+  FileLocation(const FileLocation& aFile, const char* aPath);
 
   /**
    * Initialization functions corresponding to constructors
    */
   void Init(nsIFile* aFile);
 
   void Init(nsIFile* aZip, const char* aPath);
 
@@ -70,16 +75,18 @@ public:
   /**
    * Returns the base file of the location, where base file is defined as:
    * - The file itself when the location is on a filesystem
    * - The archive file when the location is in an archive
    * - The outer archive file when the location is in an archive in an archive
    */
   already_AddRefed<nsIFile> GetBaseFile();
 
+  nsZipArchive* GetBaseZip() { return mBaseZip; }
+
   /**
    * Returns whether the "base file" (see GetBaseFile) is an archive
    */
   bool IsZip() const { return !mPath.IsEmpty(); }
 
   /**
    * Returns the path within the archive, when within an archive
    */
--- a/xpcom/components/nsComponentManager.cpp
+++ b/xpcom/components/nsComponentManager.cpp
@@ -44,16 +44,17 @@
 
 #include "mozilla/GenericFactory.h"
 #include "nsSupportsPrimitives.h"
 #include "nsArray.h"
 #include "nsIMutableArray.h"
 #include "nsArrayEnumerator.h"
 #include "nsStringEnumerator.h"
 #include "mozilla/FileUtils.h"
+#include "mozilla/URLPreloader.h"
 #include "mozilla/UniquePtr.h"
 #include "nsDataHashtable.h"
 
 #include <new>     // for placement new
 
 #include "mozilla/Omnijar.h"
 
 #include "mozilla/Logging.h"
@@ -533,30 +534,22 @@ CutExtension(nsCString& aPath)
 
 static void
 DoRegisterManifest(NSLocationType aType,
                    FileLocation& aFile,
                    bool aChromeOnly,
                    bool aXPTOnly)
 {
   MOZ_ASSERT(!aXPTOnly || !nsComponentManagerImpl::gComponentManager);
-  uint32_t len;
-  FileLocation::Data data;
-  UniquePtr<char[]> buf;
-  nsresult rv = aFile.GetData(data);
-  if (NS_SUCCEEDED(rv)) {
-    rv = data.GetSize(&len);
-  }
-  if (NS_SUCCEEDED(rv)) {
-    buf = MakeUnique<char[]>(len + 1);
-    rv = data.Copy(buf.get(), len);
-  }
-  if (NS_SUCCEEDED(rv)) {
-    buf[len] = '\0';
-    ParseManifest(aType, aFile, buf.get(), aChromeOnly, aXPTOnly);
+
+  auto result = URLPreloader::Read(aFile);
+  if (result.isOk()) {
+    nsCString buf(result.unwrap());
+
+    ParseManifest(aType, aFile, buf.BeginWriting(), aChromeOnly, aXPTOnly);
   } else if (NS_BOOTSTRAPPED_LOCATION != aType) {
     nsCString uri;
     aFile.GetURIString(uri);
     LogMessage("Could not read chrome manifest '%s'.", uri.get());
   }
 }
 
 void