merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 03 Nov 2015 12:00:30 +0100
changeset 270906 bb4d614a0b09bcb9738c151dccfcd9b3857a6a7c
parent 270838 46dc2b5e7f34e2ed71500fda8256a6588e7b3e6d (current diff)
parent 270905 1a41401462ca2647f2690853688709b334bb63d8 (diff)
child 270907 aeabfab3ea4d7681e961786ecf1363253c07dc46
child 270923 09173d8e669469c5c72cfd35327bfdf9587265ee
child 270953 c10c657afbe1fb64a9ed2e62a399c52c4038c186
child 271004 574472951d246118ba1fc4da4b2380b99ef7ed8e
push id29626
push usercbook@mozilla.com
push dateTue, 03 Nov 2015 11:00:46 +0000
treeherdermozilla-central@bb4d614a0b09 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone45.0a1
first release with
nightly linux32
bb4d614a0b09 / 45.0a1 / 20151103030248 / files
nightly linux64
bb4d614a0b09 / 45.0a1 / 20151103030248 / files
nightly mac
bb4d614a0b09 / 45.0a1 / 20151103030248 / files
nightly win32
bb4d614a0b09 / 45.0a1 / 20151103030248 / files
nightly win64
bb4d614a0b09 / 45.0a1 / 20151103030248 / 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 a=merge
browser/components/newtab/RemoteDirectoryLinksProvider.jsm
browser/components/newtab/tests/xpcshell/test_RemoteDirectoryLinksProvider.js
browser/components/nsBrowserGlue.js
--- a/accessible/base/AccIterator.cpp
+++ b/accessible/base/AccIterator.cpp
@@ -331,74 +331,16 @@ IDRefsIterator::Next()
       return acc;
     }
   }
   return nullptr;
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
-// ARIAOwnedByIterator
-////////////////////////////////////////////////////////////////////////////////
-
-ARIAOwnedByIterator::ARIAOwnedByIterator(const Accessible* aDependent) :
-  RelatedAccIterator(aDependent->Document(), aDependent->GetContent(),
-                     nsGkAtoms::aria_owns), mDependent(aDependent)
-{
-}
-
-Accessible*
-ARIAOwnedByIterator::Next()
-{
-  Accessible* owner = RelatedAccIterator::Next();
-  Accessible* cur = owner;
-  while (cur) {
-    if (cur == mDependent)
-      return Next(); // owner cannot be a child of dependent.
-
-    if (cur->IsDoc())
-      break; // don't cross document boundaries
-
-    cur = cur->Parent();
-  }
-
-  return owner;
-}
-
-
-////////////////////////////////////////////////////////////////////////////////
-// ARIAOwnsIterator
-////////////////////////////////////////////////////////////////////////////////
-
-ARIAOwnsIterator::ARIAOwnsIterator(const Accessible* aOwner) :
-  mIter(aOwner->Document(), aOwner->GetContent(), nsGkAtoms::aria_owns),
-  mOwner(aOwner)
-{
-}
-
-Accessible*
-ARIAOwnsIterator::Next()
-{
-  Accessible* child = mIter.Next();
-  const Accessible* cur = mOwner;
-  while (cur) {
-    if (cur == child)
-      return Next(); // cannot own its own parent
-
-    if (cur->IsDoc())
-      break; // don't cross document boundaries
-
-    cur = cur->Parent();
-  }
-
-  return child;
-}
-
-
-////////////////////////////////////////////////////////////////////////////////
 // SingleAccIterator
 ////////////////////////////////////////////////////////////////////////////////
 
 Accessible*
 SingleAccIterator::Next()
 {
   RefPtr<Accessible> nextAcc;
   mAcc.swap(nextAcc);
--- a/accessible/base/AccIterator.h
+++ b/accessible/base/AccIterator.h
@@ -244,57 +244,16 @@ private:
   nsString mIDs;
   nsIContent* mContent;
   DocAccessible* mDoc;
   nsAString::index_type mCurrIdx;
 };
 
 
 /**
- * Iterates over related accessible referred by aria-owns.
- */
-class ARIAOwnedByIterator final : public RelatedAccIterator
-{
-public:
-  explicit ARIAOwnedByIterator(const Accessible* aDependent);
-  virtual ~ARIAOwnedByIterator() { }
-
-  virtual Accessible* Next() override;
-
-private:
-  ARIAOwnedByIterator() = delete;
-  ARIAOwnedByIterator(const ARIAOwnedByIterator&) = delete;
-  ARIAOwnedByIterator& operator = (const ARIAOwnedByIterator&) = delete;
-
-  const Accessible* mDependent;
-};
-
-
-/**
- * Iterates over related accessible referred by aria-owns.
- */
-class ARIAOwnsIterator final : public AccIterable
-{
-public:
-  explicit ARIAOwnsIterator(const Accessible* aOwner);
-  virtual ~ARIAOwnsIterator() { }
-
-  virtual Accessible* Next() override;
-
-private:
-  ARIAOwnsIterator() = delete;
-  ARIAOwnsIterator(const ARIAOwnsIterator&) = delete;
-  ARIAOwnsIterator& operator = (const ARIAOwnsIterator&) = delete;
-
-  IDRefsIterator mIter;
-  const Accessible* mOwner;
-};
-
-
-/**
  * Iterator that points to a single accessible returning it on the first call
  * to Next().
  */
 class SingleAccIterator : public AccIterable
 {
 public:
   explicit SingleAccIterator(Accessible* aTarget): mAcc(aTarget) { }
   virtual ~SingleAccIterator() { }
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -1598,18 +1598,17 @@ Accessible::RelationByType(RelationType 
       if (mContent->IsXULElement(nsGkAtoms::description))
         rel.AppendIter(new IDRefsIterator(mDoc, mContent,
                                           nsGkAtoms::control));
 
       return rel;
     }
 
     case RelationType::NODE_CHILD_OF: {
-      Relation rel(new ARIAOwnedByIterator(this));
-
+      Relation rel;
       // This is an ARIA tree or treegrid that doesn't use owns, so we need to
       // get the parent the hard way.
       if (mRoleMapEntry && (mRoleMapEntry->role == roles::OUTLINEITEM ||
                             mRoleMapEntry->role == roles::LISTITEM ||
                             mRoleMapEntry->role == roles::ROW)) {
         rel.AppendTarget(GetGroupInfo()->ConceptualParent());
       }
 
@@ -1628,31 +1627,29 @@ Accessible::RelationByType(RelationType 
             rel.AppendTarget(Parent());
         }
       }
 
       return rel;
     }
 
     case RelationType::NODE_PARENT_OF: {
-      Relation rel(new ARIAOwnsIterator(this));
-
       // ARIA tree or treegrid can do the hierarchy by @aria-level, ARIA trees
       // also can be organized by groups.
       if (mRoleMapEntry &&
           (mRoleMapEntry->role == roles::OUTLINEITEM ||
            mRoleMapEntry->role == roles::LISTITEM ||
            mRoleMapEntry->role == roles::ROW ||
            mRoleMapEntry->role == roles::OUTLINE ||
            mRoleMapEntry->role == roles::LIST ||
            mRoleMapEntry->role == roles::TREE_TABLE)) {
-        rel.AppendIter(new ItemIterator(this));
+        return Relation(new ItemIterator(this));
       }
 
-      return rel;
+      return Relation();
     }
 
     case RelationType::CONTROLLED_BY:
       return Relation(new RelatedAccIterator(Document(), mContent,
                                              nsGkAtoms::aria_controls));
 
     case RelationType::CONTROLLER_FOR: {
       Relation rel(new IDRefsIterator(mDoc, mContent,
--- a/accessible/jsat/AccessFu.jsm
+++ b/accessible/jsat/AccessFu.jsm
@@ -517,19 +517,20 @@ var Output = {
   },
 
   start: function start() {
     Cu.import('resource://gre/modules/Geometry.jsm');
   },
 
   stop: function stop() {
     if (this.highlightBox) {
-      let doc = Utils.win.document;
-      (doc.body || doc.documentElement).documentElement.removeChild(
-        this.highlightBox.get());
+      let highlightBox = this.highlightBox.get();
+      if (highlightBox) {
+        highlightBox.remove();
+      }
       delete this.highlightBox;
     }
   },
 
   B2G: function B2G(aDetails) {
     Utils.dispatchChromeEvent('accessibility-output', aDetails);
   },
 
--- a/accessible/jsat/Presentation.jsm
+++ b/accessible/jsat/Presentation.jsm
@@ -693,17 +693,19 @@ this.Presentation = { // jshint ignore:l
     this.displayedAccessibles = new WeakMap();
     return this.displayedAccessibles;
   },
 
   pivotChanged: function Presentation_pivotChanged(
     aPosition, aOldPosition, aReason, aStartOffset, aEndOffset, aIsUserInput) {
     let context = new PivotContext(
       aPosition, aOldPosition, aStartOffset, aEndOffset);
-    this.displayedAccessibles.set(context.accessible.document.window, context);
+    if (context.accessible) {
+      this.displayedAccessibles.set(context.accessible.document.window, context);
+    }
 
     return this.presenters.map(p => p.pivotChanged(context, aReason, aIsUserInput));
   },
 
   actionInvoked: function Presentation_actionInvoked(aObject, aActionName) {
     return this.presenters.map(p => p.actionInvoked(aObject, aActionName));
   },
 
--- a/accessible/tests/mochitest/relations/test_general.html
+++ b/accessible/tests/mochitest/relations/test_general.html
@@ -69,38 +69,27 @@
       testRelation("descr2", RELATION_DESCRIPTION_FOR, "checkbox5");
       testRelation("descr3", RELATION_DESCRIPTION_FOR, "checkbox5");
       testRelation("checkbox5", RELATION_DESCRIBED_BY, ["descr2", "descr3"]);
 
       // aria_owns, multiple relations
       testRelation("treeitem1", RELATION_NODE_CHILD_OF, "tree");
       testRelation("treeitem2", RELATION_NODE_CHILD_OF, "tree");
 
-      // aria-owns, bad relations
-      testRelation("ariaowns_container", RELATION_NODE_CHILD_OF, null);
-      testRelation("ariaowns_self", RELATION_NODE_CHILD_OF, null);
-      testRelation("ariaowns_uncle", RELATION_NODE_CHILD_OF, "ariaowns_self");
-
-      testRelation("ariaowns_container", RELATION_NODE_PARENT_OF, null);
-      testRelation("ariaowns_self", RELATION_NODE_PARENT_OF, "ariaowns_uncle");
-      testRelation("ariaowns_uncle", RELATION_NODE_PARENT_OF, null);
-
       // 'node child of' relation for outlineitem role
       testRelation("treeitem3", RELATION_NODE_CHILD_OF, "tree");
       testRelation("treeitem4", RELATION_NODE_CHILD_OF, "tree");
       testRelation("treeitem5", RELATION_NODE_CHILD_OF, "treeitem4");
       testRelation("treeitem6", RELATION_NODE_CHILD_OF, "tree");
       testRelation("treeitem7", RELATION_NODE_CHILD_OF, "treeitem6");
       testRelation("tree2_ti1", RELATION_NODE_CHILD_OF, "tree2");
       testRelation("tree2_ti1a", RELATION_NODE_CHILD_OF, "tree2_ti1");
       testRelation("tree2_ti1b", RELATION_NODE_CHILD_OF, "tree2_ti1");
 
       // 'node child of' relation for row role in grid.
-      // Relation for row associated using aria-owns should exist.
-      testRelation("simplegrid-ownrow", RELATION_NODE_CHILD_OF, "simplegrid");
       // Relation for row associated using aria-level should exist.
       testRelation("simplegrid-row3", RELATION_NODE_CHILD_OF,
                    "simplegrid-row2");
       // Relations for hierarchical children elements shouldn't exist.
       testAbsentRelation("simplegrid-row1", RELATION_NODE_CHILD_OF,
                          "simplegrid");
       testAbsentRelation("simplegrid-row2", RELATION_NODE_CHILD_OF,
                          "simplegrid");
@@ -135,18 +124,16 @@
       testRelation("tree2_ti1", RELATION_NODE_PARENT_OF,
                    ["tree2_ti1a", "tree2_ti1b"]); // group role
 
       testRelation("treegridrow2", RELATION_NODE_PARENT_OF, "treegridrow3");
       testRelation("treegrid", RELATION_NODE_PARENT_OF,
                    ["treegridrow1", "treegridrow2"]);
 
       // 'node parent of' relation on ARIA grid.
-      // Should only have relation to child added through aria-owns.
-      testRelation("simplegrid", RELATION_NODE_PARENT_OF, "simplegrid-ownrow");
       // 'node parent of' relation on ARIA grid's row.
       // Should only have relation to child through aria-level.
       testRelation("simplegrid-row2", RELATION_NODE_PARENT_OF,
                    "simplegrid-row3");
 
       // 'node parent of' relation on ARIA list structured by groups
       testRelation("list", RELATION_NODE_PARENT_OF,
                    "listitem1");
@@ -314,37 +301,30 @@
     <div role="treeitem" id="treeitem4" aria-level="1">Green</div>
     <div role="treeitem" id="treeitem5" aria-level="2">Light green</div>
     <div role="treeitem" id="treeitem6" aria-level="1">Green2</div>
     <div role="group">
       <div role="treeitem" id="treeitem7">Super light green</div>
     </div>
   </div>
 
-  <div id="ariaowns_container">
-    <div id="ariaowns_self"
-         aria-owns="aria_ownscontainer ariaowns_self ariaowns_uncle"></div>
-  </div>
-  <div id="ariaowns_uncle"></div>
-
-  <div aria-owns="simplegrid-ownrow" role="grid" id="simplegrid">
+  <div role="grid" id="simplegrid">
     <div role="row" id="simplegrid-row1" aria-level="1">
       <div role="gridcell">cell 1,1</div>
       <div role="gridcell">cell 1,2</div>
     </div>
     <div role="row" id="simplegrid-row2" aria-level="1">
       <div role="gridcell">cell 2,1</div>
       <div role="gridcell">cell 2,2</div>
     </div>
     <div role="row" id="simplegrid-row3" aria-level="2">
       <div role="gridcell">cell 3,1</div>
       <div role="gridcell">cell 3,2</div>
     </div>
   </div>
-  <div role="row" id="simplegrid-ownrow"></div>
 
   <ul role="tree" id="tree2">
     <li role="treeitem" id="tree2_ti1">Item 1
       <ul role="group">
         <li role="treeitem" id="tree2_ti1a">Item 1A</li>
         <li role="treeitem" id="tree2_ti1b">Item 1B</li>
       </ul>
     </li>
--- a/accessible/tests/mochitest/relations/test_update.html
+++ b/accessible/tests/mochitest/relations/test_update.html
@@ -133,20 +133,16 @@
       testRelated("aria-labelledby",
                   RELATION_LABELLED_BY, RELATION_LABEL_FOR,
                   "host", "host", "dependent1", "dependent2");
 
       testRelated("aria-describedby",
                   RELATION_DESCRIBED_BY, RELATION_DESCRIPTION_FOR,
                   "host", "host", "dependent1", "dependent2");
 
-      testRelated("aria-owns",
-                  null, RELATION_NODE_CHILD_OF,
-                  "host", "host", "dependent1", "dependent2");
-
       testRelated("aria-controls",
                   RELATION_CONTROLLER_FOR, RELATION_CONTROLLED_BY,
                   "host", "host", "dependent1", "dependent2");
 
       testRelated("aria-flowto",
                   RELATION_FLOWS_TO, RELATION_FLOWS_FROM,
                   "host", "host", "dependent1", "dependent2");
 
@@ -164,21 +160,16 @@
 
       gQueue.push(new insertRelated("aria-describedby", "dependent5", true,
                                     RELATION_DESCRIBED_BY,
                                     RELATION_DESCRIPTION_FOR));
       gQueue.push(new insertRelated("aria-describedby", "dependent6", false,
                                     RELATION_DESCRIBED_BY,
                                     RELATION_DESCRIPTION_FOR));
 
-      gQueue.push(new insertRelated("aria-owns", "dependent7", true,
-                                    null, RELATION_NODE_CHILD_OF));
-      gQueue.push(new insertRelated("aria-owns", "dependent8", false,
-                                    null, RELATION_NODE_CHILD_OF));
-
       gQueue.push(new insertRelated("aria-controls", "dependent9", true,
                                     RELATION_CONTROLLER_FOR,
                                     RELATION_CONTROLLED_BY));
       gQueue.push(new insertRelated("aria-controls", "dependent10", false,
                                     RELATION_CONTROLLER_FOR,
                                     RELATION_CONTROLLED_BY));
 
       gQueue.push(new insertRelated("aria-flowto", "dependent11", true,
--- a/accessible/tests/mochitest/tree/test_aria_owns.html
+++ b/accessible/tests/mochitest/tree/test_aria_owns.html
@@ -83,16 +83,46 @@
       tree =
         { SECTION: [ // t6_1
           { RADIOBUTTON: [ ] },
           { CHECKBUTTON: [ ] }, // t6_3, rearranged by aria-owns
           { PUSHBUTTON: [ ] }, // t6_2, rearranged by aria-owns
         ] };
       testAccessibleTree("t6_1", tree);
 
+      tree =
+        { SECTION: [ // ariaowns_container
+          { SECTION: [ // ariaowns_self
+            { SECTION: [ // ariaowns_uncle
+            ] }
+          ] }
+        ] };
+      testAccessibleTree("ariaowns_container", tree);
+
+      tree =
+        { TABLE: [
+          { ROW: [
+            { GRID_CELL: [
+              { TEXT_LEAF: [] }
+            ] },
+            { GRID_CELL: [
+              { TEXT_LEAF: [] }
+            ] }
+          ] },
+          { ROW: [
+            { GRID_CELL: [
+              { TEXT_LEAF: [] }
+            ] },
+            { GRID_CELL: [
+              { TEXT_LEAF: [] }
+            ] }
+          ] }
+        ] };
+      testAccessibleTree("grid", tree);
+
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
 
   </script>
 </head>
@@ -130,11 +160,28 @@
 
   <!-- rearrange children -->
   <div id="t6_1" aria-owns="t6_3 t6_2">
     <div id="t6_2" role="button"></div>
     <div id="t6_3" role="checkbox"></div>
     <div role="radio"></div>
   </div>
 
+  <div id="ariaowns_container">
+    <div id="ariaowns_self"
+         aria-owns="aria_ownscontainer ariaowns_self ariaowns_uncle"></div>
+  </div>
+  <div id="ariaowns_uncle"></div>
+
+  <!-- grid -->
+  <div aria-owns="grid-row2" role="grid" id="grid">
+    <div role="row">
+      <div role="gridcell">cell 1,1</div>
+      <div role="gridcell">cell 1,2</div>
+    </div>
+  </div>
+  <div role="row" id="grid-row2">
+    <div role="gridcell">cell 2,1</div>
+    <div role="gridcell">cell 2,2</div>
+  </div>
 </body>
 
 </html>
--- a/browser/components/newtab/RemoteAboutNewTab.jsm
+++ b/browser/components/newtab/RemoteAboutNewTab.jsm
@@ -1,14 +1,14 @@
 /* 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/. */
 
 /* globals Services, XPCOMUtils, RemotePages, RemoteNewTabLocation, RemoteNewTabUtils, Task  */
-/* globals BackgroundPageThumbs, PageThumbs, RemoteDirectoryLinksProvider */
+/* globals BackgroundPageThumbs, PageThumbs, DirectoryLinksProvider */
 /* exported RemoteAboutNewTab */
 
 "use strict";
 
 let Ci = Components.interfaces;
 let Cu = Components.utils;
 const XHTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
 
@@ -22,18 +22,18 @@ Cu.importGlobalProperties(["URL"]);
 XPCOMUtils.defineLazyModuleGetter(this, "RemotePages",
   "resource://gre/modules/RemotePageManager.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "RemoteNewTabUtils",
   "resource:///modules/RemoteNewTabUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "BackgroundPageThumbs",
   "resource://gre/modules/BackgroundPageThumbs.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PageThumbs",
   "resource://gre/modules/PageThumbs.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "RemoteDirectoryLinksProvider",
-  "resource:///modules/RemoteDirectoryLinksProvider.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "DirectoryLinksProvider",
+  "resource:///modules/DirectoryLinksProvider.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "RemoteNewTabLocation",
   "resource:///modules/RemoteNewTabLocation.jsm");
 
 let RemoteAboutNewTab = {
 
   pageListener: null,
 
   /**
@@ -167,17 +167,17 @@ let RemoteAboutNewTab = {
 
   /**
    * Get the set of enhanced links (if any) from the Directory Links Provider.
    */
   getEnhancedLinks: function() {
     let enhancedLinks = [];
     for (let link of RemoteNewTabUtils.links.getLinks()) {
       if (link) {
-        enhancedLinks.push(RemoteDirectoryLinksProvider.getEnhancedLink(link));
+        enhancedLinks.push(DirectoryLinksProvider.getEnhancedLink(link));
       }
     }
     return enhancedLinks;
   },
 
   /**
    * Listens for a preference change or session purge for all pages and sends
    * a message to update the pages that are open. If a session purge occured,
deleted file mode 100644
--- a/browser/components/newtab/RemoteDirectoryLinksProvider.jsm
+++ /dev/null
@@ -1,1171 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-this.EXPORTED_SYMBOLS = ["RemoteDirectoryLinksProvider"];
-
-const Ci = Components.interfaces;
-const Cc = Components.classes;
-const Cu = Components.utils;
-const ParserUtils =  Cc["@mozilla.org/parserutils;1"].getService(Ci.nsIParserUtils);
-
-Cu.importGlobalProperties(["XMLHttpRequest"]);
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/Task.jsm");
-Cu.import("resource://gre/modules/Timer.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
-  "resource://gre/modules/NetUtil.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "RemoteNewTabUtils",
-  "resource:///modules/RemoteNewTabUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "OS",
-  "resource://gre/modules/osfile.jsm")
-XPCOMUtils.defineLazyModuleGetter(this, "Promise",
-  "resource://gre/modules/Promise.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
-  "resource://gre/modules/UpdateUtils.jsm");
-XPCOMUtils.defineLazyServiceGetter(this, "eTLD",
-  "@mozilla.org/network/effective-tld-service;1",
-  "nsIEffectiveTLDService");
-XPCOMUtils.defineLazyGetter(this, "gTextDecoder", () => {
-  return new TextDecoder();
-});
-XPCOMUtils.defineLazyGetter(this, "gCryptoHash", function () {
-  return Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash);
-});
-XPCOMUtils.defineLazyGetter(this, "gUnicodeConverter", function () {
-  let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
-                    .createInstance(Ci.nsIScriptableUnicodeConverter);
-  converter.charset = 'utf8';
-  return converter;
-});
-
-
-// The filename where directory links are stored locally
-const DIRECTORY_LINKS_FILE = "directoryLinks.json";
-const DIRECTORY_LINKS_TYPE = "application/json";
-
-// The preference that tells whether to match the OS locale
-const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS";
-
-// The preference that tells what locale the user selected
-const PREF_SELECTED_LOCALE = "general.useragent.locale";
-
-// The preference that tells where to obtain directory links
-const PREF_DIRECTORY_SOURCE = "browser.newtabpage.directory.source";
-
-// The preference that tells where to send click/view pings
-const PREF_DIRECTORY_PING = "browser.newtabpage.directory.ping";
-
-// The preference that tells if newtab is enhanced
-const PREF_NEWTAB_ENHANCED = "browser.newtabpage.enhanced";
-
-// Only allow link urls that are http(s)
-const ALLOWED_LINK_SCHEMES = new Set(["http", "https"]);
-
-// Only allow link image urls that are https or data
-const ALLOWED_IMAGE_SCHEMES = new Set(["https", "data"]);
-
-// Only allow urls to Mozilla's CDN or empty (for data URIs)
-const ALLOWED_URL_BASE = new Set(["mozilla.net", ""]);
-
-// The frecency of a directory link
-const DIRECTORY_FRECENCY = 1000;
-
-// The frecency of a suggested link
-const SUGGESTED_FRECENCY = Infinity;
-
-// The filename where frequency cap data stored locally
-const FREQUENCY_CAP_FILE = "frequencyCap.json";
-
-// Default settings for daily and total frequency caps
-const DEFAULT_DAILY_FREQUENCY_CAP = 3;
-const DEFAULT_TOTAL_FREQUENCY_CAP = 10;
-
-// Default timeDelta to prune unused frequency cap objects
-// currently set to 10 days in milliseconds
-const DEFAULT_PRUNE_TIME_DELTA = 10*24*60*60*1000;
-
-// The min number of visible (not blocked) history tiles to have before showing suggested tiles
-const MIN_VISIBLE_HISTORY_TILES = 8;
-
-// The max number of visible (not blocked) history tiles to test for inadjacency
-const MAX_VISIBLE_HISTORY_TILES = 15;
-
-// Divide frecency by this amount for pings
-const PING_SCORE_DIVISOR = 10000;
-
-// Allowed ping actions remotely stored as columns: case-insensitive [a-z0-9_]
-const PING_ACTIONS = ["block", "click", "pin", "sponsored", "sponsored_link", "unpin", "view"];
-
-// Location of inadjacent sites json
-const INADJACENCY_SOURCE = "chrome://browser/content/newtab/newTab.inadjacent.json";
-
-/**
- * Singleton that serves as the provider of directory links.
- * Directory links are a hard-coded set of links shown if a user's link
- * inventory is empty.
- */
-let RemoteDirectoryLinksProvider = {
-
-  __linksURL: null,
-
-  _observers: new Set(),
-
-  // links download deferred, resolved upon download completion
-  _downloadDeferred: null,
-
-  // download default interval is 24 hours in milliseconds
-  _downloadIntervalMS: 86400000,
-
-  /**
-   * A mapping from eTLD+1 to an enhanced link objects
-   */
-  _enhancedLinks: new Map(),
-
-  /**
-   * A mapping from site to a list of suggested link objects
-   */
-  _suggestedLinks: new Map(),
-
-  /**
-   * Frequency Cap object - maintains daily and total tile counts, and frequency cap settings
-   */
-  _frequencyCaps: {},
-
-  /**
-   * A set of top sites that we can provide suggested links for
-   */
-  _topSitesWithSuggestedLinks: new Set(),
-
-  /**
-   * lookup Set of inadjacent domains
-   */
-  _inadjacentSites: new Set(),
-
-  /**
-   * This flag is set if there is a suggested tile configured to avoid
-   * inadjacent sites in new tab
-   */
-  _avoidInadjacentSites: false,
-
-  /**
-   * This flag is set if _avoidInadjacentSites is true and there is
-   * an inadjacent site in the new tab
-   */
-  _newTabHasInadjacentSite: false,
-
-  get _observedPrefs() {
-    return Object.freeze({
-      enhanced: PREF_NEWTAB_ENHANCED,
-      linksURL: PREF_DIRECTORY_SOURCE,
-      matchOSLocale: PREF_MATCH_OS_LOCALE,
-      prefSelectedLocale: PREF_SELECTED_LOCALE,
-    });
-  },
-
-  get _linksURL() {
-    if (!this.__linksURL) {
-      try {
-        this.__linksURL = Services.prefs.getCharPref(this._observedPrefs["linksURL"]);
-        this.__linksURLModified = Services.prefs.prefHasUserValue(this._observedPrefs["linksURL"]);
-      }
-      catch (e) {
-        Cu.reportError("Error fetching directory links url from prefs: " + e);
-      }
-    }
-    return this.__linksURL;
-  },
-
-  /**
-   * Gets the currently selected locale for display.
-   * @return  the selected locale or "en-US" if none is selected
-   */
-  get locale() {
-    let matchOS;
-    try {
-      matchOS = Services.prefs.getBoolPref(PREF_MATCH_OS_LOCALE);
-    }
-    catch (e) {}
-
-    if (matchOS) {
-      return Services.locale.getLocaleComponentForUserAgent();
-    }
-
-    try {
-      let locale = Services.prefs.getComplexValue(PREF_SELECTED_LOCALE,
-                                                  Ci.nsIPrefLocalizedString);
-      if (locale) {
-        return locale.data;
-      }
-    }
-    catch (e) {}
-
-    try {
-      return Services.prefs.getCharPref(PREF_SELECTED_LOCALE);
-    }
-    catch (e) {}
-
-    return "en-US";
-  },
-
-  /**
-   * Set appropriate default ping behavior controlled by enhanced pref
-   */
-  _setDefaultEnhanced: function RemoteDirectoryLinksProvider_setDefaultEnhanced() {
-    if (!Services.prefs.prefHasUserValue(PREF_NEWTAB_ENHANCED)) {
-      let enhanced = Services.prefs.getBoolPref(PREF_NEWTAB_ENHANCED);
-      try {
-        // Default to not enhanced if DNT is set to tell websites to not track
-        if (Services.prefs.getBoolPref("privacy.donottrackheader.enabled")) {
-          enhanced = false;
-        }
-      }
-      catch(ex) {}
-      Services.prefs.setBoolPref(PREF_NEWTAB_ENHANCED, enhanced);
-    }
-  },
-
-  observe: function RemoteDirectoryLinksProvider_observe(aSubject, aTopic, aData) {
-    if (aTopic == "nsPref:changed") {
-      switch (aData) {
-        // Re-set the default in case the user clears the pref
-        case this._observedPrefs.enhanced:
-          this._setDefaultEnhanced();
-          break;
-
-        case this._observedPrefs.linksURL:
-          delete this.__linksURL;
-          // fallthrough
-
-        // Force directory download on changes to fetch related prefs
-        case this._observedPrefs.matchOSLocale:
-        case this._observedPrefs.prefSelectedLocale:
-          this._fetchAndCacheLinksIfNecessary(true);
-          break;
-      }
-    }
-  },
-
-  _addPrefsObserver: function RemoteDirectoryLinksProvider_addObserver() {
-    for (let pref in this._observedPrefs) {
-      let prefName = this._observedPrefs[pref];
-      Services.prefs.addObserver(prefName, this, false);
-    }
-  },
-
-  _removePrefsObserver: function RemoteDirectoryLinksProvider_removeObserver() {
-    for (let pref in this._observedPrefs) {
-      let prefName = this._observedPrefs[pref];
-      Services.prefs.removeObserver(prefName, this);
-    }
-  },
-
-  _cacheSuggestedLinks: function(link) {
-    // Don't cache links that don't have the expected 'frecent_sites'
-    if (!link.frecent_sites) {
-      return;
-    }
-
-    for (let suggestedSite of link.frecent_sites) {
-      let suggestedMap = this._suggestedLinks.get(suggestedSite) || new Map();
-      suggestedMap.set(link.url, link);
-      this._setupStartEndTime(link);
-      this._suggestedLinks.set(suggestedSite, suggestedMap);
-    }
-  },
-
-  _fetchAndCacheLinks: function RemoteDirectoryLinksProvider_fetchAndCacheLinks(uri) {
-    // Replace with the same display locale used for selecting links data
-    uri = uri.replace("%LOCALE%", this.locale);
-    uri = uri.replace("%CHANNEL%", UpdateUtils.UpdateChannel);
-
-    return this._downloadJsonData(uri).then(json => {
-      return OS.File.writeAtomic(this._directoryFilePath, json, {tmpPath: this._directoryFilePath + ".tmp"});
-    });
-  },
-
-  /**
-   * Downloads a links with json content
-   * @param download uri
-   * @return promise resolved to json string, "{}" returned if status != 200
-   */
-  _downloadJsonData: function RemoteDirectoryLinksProvider__downloadJsonData(uri) {
-    let deferred = Promise.defer();
-    let xmlHttp = this._newXHR();
-
-    xmlHttp.onload = function(aResponse) {
-      let json = this.responseText;
-      if (this.status && this.status != 200) {
-        json = "{}";
-      }
-      deferred.resolve(json);
-    };
-
-    xmlHttp.onerror = function(e) {
-      deferred.reject("Fetching " + uri + " results in error code: " + e.target.status);
-    };
-
-    try {
-      xmlHttp.open("GET", uri);
-      // Override the type so XHR doesn't complain about not well-formed XML
-      xmlHttp.overrideMimeType(DIRECTORY_LINKS_TYPE);
-      // Set the appropriate request type for servers that require correct types
-      xmlHttp.setRequestHeader("Content-Type", DIRECTORY_LINKS_TYPE);
-      xmlHttp.send();
-    } catch (e) {
-      deferred.reject("Error fetching " + uri);
-      Cu.reportError(e);
-    }
-    return deferred.promise;
-  },
-
-  /**
-   * Downloads directory links if needed
-   * @return promise resolved immediately if no download needed, or upon completion
-   */
-  _fetchAndCacheLinksIfNecessary: function RemoteDirectoryLinksProvider_fetchAndCacheLinksIfNecessary(forceDownload=false) {
-    if (this._downloadDeferred) {
-      // fetching links already - just return the promise
-      return this._downloadDeferred.promise;
-    }
-
-    if (forceDownload || this._needsDownload) {
-      this._downloadDeferred = Promise.defer();
-      this._fetchAndCacheLinks(this._linksURL).then(() => {
-        // the new file was successfully downloaded and cached, so update a timestamp
-        this._lastDownloadMS = Date.now();
-        this._downloadDeferred.resolve();
-        this._downloadDeferred = null;
-        this._callObservers("onManyLinksChanged")
-      },
-      error => {
-        this._downloadDeferred.resolve();
-        this._downloadDeferred = null;
-        this._callObservers("onDownloadFail");
-      });
-      return this._downloadDeferred.promise;
-    }
-
-    // download is not needed
-    return Promise.resolve();
-  },
-
-  /**
-   * @return true if download is needed, false otherwise
-   */
-  get _needsDownload () {
-    // fail if last download occured less then 24 hours ago
-    if ((Date.now() - this._lastDownloadMS) > this._downloadIntervalMS) {
-      return true;
-    }
-    return false;
-  },
-
-  /**
-   * Create a new XMLHttpRequest that is anonymous, i.e., doesn't send cookies
-   */
-  _newXHR() {
-    return new XMLHttpRequest({mozAnon: true});
-  },
-
-  /**
-   * Reads directory links file and parses its content
-   * @return a promise resolved to an object with keys 'directory' and 'suggested',
-   *         each containing a valid list of links,
-   *         or {'directory': [], 'suggested': []} if read or parse fails.
-   */
-  _readDirectoryLinksFile: function RemoteDirectoryLinksProvider_readDirectoryLinksFile() {
-    let emptyOutput = {directory: [], suggested: [], enhanced: []};
-    return OS.File.read(this._directoryFilePath).then(binaryData => {
-      let output;
-      try {
-        let json = gTextDecoder.decode(binaryData);
-        let linksObj = JSON.parse(json);
-        output = {directory: linksObj.directory || [],
-                  suggested: linksObj.suggested || [],
-                  enhanced:  linksObj.enhanced  || []};
-      }
-      catch (e) {
-        Cu.reportError(e);
-      }
-      return output || emptyOutput;
-    },
-    error => {
-      Cu.reportError(error);
-      return emptyOutput;
-    });
-  },
-
-  /**
-   * Translates link.time_limits to UTC miliseconds and sets
-   * link.startTime and link.endTime properties in link object
-   */
-  _setupStartEndTime: function RemoteDirectoryLinksProvider_setupStartEndTime(link) {
-    // set start/end limits. Use ISO_8601 format: '2014-01-10T20:20:20.600Z'
-    // (details here http://en.wikipedia.org/wiki/ISO_8601)
-    // Note that if timezone is missing, FX will interpret as local time
-    // meaning that the server can sepecify any time, but if the capmaign
-    // needs to start at same time across multiple timezones, the server
-    // omits timezone indicator
-    if (!link.time_limits) {
-      return;
-    }
-
-    let parsedTime;
-    if (link.time_limits.start) {
-      parsedTime = Date.parse(link.time_limits.start);
-      if (parsedTime && !isNaN(parsedTime)) {
-        link.startTime = parsedTime;
-      }
-    }
-    if (link.time_limits.end) {
-      parsedTime = Date.parse(link.time_limits.end);
-      if (parsedTime && !isNaN(parsedTime)) {
-        link.endTime = parsedTime;
-      }
-    }
-  },
-
-  /*
-   * Handles campaign timeout
-   */
-  _onCampaignTimeout: function RemoteDirectoryLinksProvider_onCampaignTimeout() {
-    // _campaignTimeoutID is invalid here, so just set it to null
-    this._campaignTimeoutID = null;
-    this._updateSuggestedTile();
-  },
-
-  /*
-   * Clears capmpaign timeout
-   */
-  _clearCampaignTimeout: function RemoteDirectoryLinksProvider_clearCampaignTimeout() {
-    if (this._campaignTimeoutID) {
-      clearTimeout(this._campaignTimeoutID);
-      this._campaignTimeoutID = null;
-    }
-  },
-
-  /**
-   * Setup capmpaign timeout to recompute suggested tiles upon
-   * reaching soonest start or end time for the campaign
-   * @param timeout in milliseconds
-   */
-  _setupCampaignTimeCheck: function RemoteDirectoryLinksProvider_setupCampaignTimeCheck(timeout) {
-    // sanity check
-    if (!timeout || timeout <= 0) {
-      return;
-    }
-    this._clearCampaignTimeout();
-    // setup next timeout
-    this._campaignTimeoutID = setTimeout(this._onCampaignTimeout.bind(this), timeout);
-  },
-
-  /**
-   * Test link for campaign time limits: checks if link falls within start/end time
-   * and returns an object containing a use flag and the timeoutDate milliseconds
-   * when the link has to be re-checked for campaign start-ready or end-reach
-   * @param link
-   * @return object {use: true or false, timeoutDate: milliseconds or null}
-   */
-  _testLinkForCampaignTimeLimits: function RemoteDirectoryLinksProvider_testLinkForCampaignTimeLimits(link) {
-    let currentTime = Date.now();
-    // test for start time first
-    if (link.startTime && link.startTime > currentTime) {
-      // not yet ready for start
-      return {use: false, timeoutDate: link.startTime};
-    }
-    // otherwise check for end time
-    if (link.endTime) {
-      // passed end time
-      if (link.endTime <= currentTime) {
-        return {use: false};
-      }
-      // otherwise link is still ok, but we need to set timeoutDate
-      return {use: true, timeoutDate: link.endTime};
-    }
-    // if we are here, the link is ok and no timeoutDate needed
-    return {use: true};
-  },
-
-  /**
-   * Get the enhanced link object for a link (whether history or directory)
-   */
-  getEnhancedLink: function RemoteDirectoryLinksProvider_getEnhancedLink(link) {
-    // Use the provided link if it's already enhanced
-    return link.enhancedImageURI && link ? link :
-           this._enhancedLinks.get(RemoteNewTabUtils.extractSite(link.url));
-  },
-
-  /**
-   * Check if a url's scheme is in a Set of allowed schemes and if the base
-   * domain is allowed.
-   * @param url to check
-   * @param allowed Set of allowed schemes
-   * @param checkBase boolean to check the base domain
-   */
-  isURLAllowed(url, allowed, checkBase) {
-    // Assume no url is an allowed url
-    if (!url) {
-      return true;
-    }
-
-    let scheme = "", base = "";
-    try {
-      // A malformed url will not be allowed
-      let uri = Services.io.newURI(url, null, null);
-      scheme = uri.scheme;
-
-      // URIs without base domains will be allowed
-      base = Services.eTLD.getBaseDomain(uri);
-    }
-    catch(ex) {}
-    // Require a scheme match and the base only if desired
-    return allowed.has(scheme) && (!checkBase || ALLOWED_URL_BASE.has(base));
-  },
-
-  _escapeChars(text) {
-    let charMap = {
-      '&': '&amp;',
-      '<': '&lt;',
-      '>': '&gt;',
-      '"': '&quot;',
-      "'": '&#039;'
-    };
-
-    return text.replace(/[&<>"']/g, (character) => charMap[character]);
-  },
-
-  /**
-   * Gets the current set of directory links.
-   * @param aCallback The function that the array of links is passed to.
-   */
-  getLinks: function RemoteDirectoryLinksProvider_getLinks(aCallback) {
-    this._readDirectoryLinksFile().then(rawLinks => {
-      // Reset the cache of suggested tiles and enhanced images for this new set of links
-      this._enhancedLinks.clear();
-      this._suggestedLinks.clear();
-      this._clearCampaignTimeout();
-      this._avoidInadjacentSites = false;
-
-      // Only check base domain for images when using the default pref
-      let checkBase = !this.__linksURLModified;
-      let validityFilter = function(link) {
-        // Make sure the link url is allowed and images too if they exist
-        return this.isURLAllowed(link.url, ALLOWED_LINK_SCHEMES, false) &&
-               this.isURLAllowed(link.imageURI, ALLOWED_IMAGE_SCHEMES, checkBase) &&
-               this.isURLAllowed(link.enhancedImageURI, ALLOWED_IMAGE_SCHEMES, checkBase);
-      }.bind(this);
-
-      rawLinks.suggested.filter(validityFilter).forEach((link, position) => {
-        // Suggested sites must have an adgroup name.
-        if (!link.adgroup_name) {
-          return;
-        }
-
-        let sanitizeFlags = ParserUtils.SanitizerCidEmbedsOnly |
-          ParserUtils.SanitizerDropForms |
-          ParserUtils.SanitizerDropNonCSSPresentation;
-
-        link.explanation = this._escapeChars(link.explanation ? ParserUtils.convertToPlainText(link.explanation, sanitizeFlags, 0) : "");
-        link.targetedName = this._escapeChars(ParserUtils.convertToPlainText(link.adgroup_name, sanitizeFlags, 0));
-        link.lastVisitDate = rawLinks.suggested.length - position;
-        // check if link wants to avoid inadjacent sites
-        if (link.check_inadjacency) {
-          this._avoidInadjacentSites = true;
-        }
-
-        // We cache suggested tiles here but do not push any of them in the links list yet.
-        // The decision for which suggested tile to include will be made separately.
-        this._cacheSuggestedLinks(link);
-        this._updateFrequencyCapSettings(link);
-      });
-
-      rawLinks.enhanced.filter(validityFilter).forEach((link, position) => {
-        link.lastVisitDate = rawLinks.enhanced.length - position;
-
-        // Stash the enhanced image for the site
-        if (link.enhancedImageURI) {
-          this._enhancedLinks.set(RemoteNewTabUtils.extractSite(link.url), link);
-        }
-      });
-
-      let links = rawLinks.directory.filter(validityFilter).map((link, position) => {
-        link.lastVisitDate = rawLinks.directory.length - position;
-        link.frecency = DIRECTORY_FRECENCY;
-        return link;
-      });
-
-      // Allow for one link suggestion on top of the default directory links
-      this.maxNumLinks = links.length + 1;
-
-      // prune frequency caps of outdated urls
-      this._pruneFrequencyCapUrls();
-      // write frequency caps object to disk asynchronously
-      this._writeFrequencyCapFile();
-
-      return links;
-    }).catch(ex => {
-      Cu.reportError(ex);
-      return [];
-    }).then(links => {
-      aCallback(links);
-      this._populatePlacesLinks();
-    });
-  },
-
-  init: function RemoteDirectoryLinksProvider_init() {
-    this._setDefaultEnhanced();
-    this._addPrefsObserver();
-    // setup directory file path and last download timestamp
-    this._directoryFilePath = OS.Path.join(OS.Constants.Path.localProfileDir, DIRECTORY_LINKS_FILE);
-    this._lastDownloadMS = 0;
-
-    // setup frequency cap file path
-    this._frequencyCapFilePath = OS.Path.join(OS.Constants.Path.localProfileDir, FREQUENCY_CAP_FILE);
-    // setup inadjacent sites URL
-    this._inadjacentSitesUrl = INADJACENCY_SOURCE;
-
-    RemoteNewTabUtils.placesProvider.addObserver(this);
-    RemoteNewTabUtils.links.addObserver(this);
-
-    return Task.spawn(function() {
-      // get the last modified time of the links file if it exists
-      let doesFileExists = yield OS.File.exists(this._directoryFilePath);
-      if (doesFileExists) {
-        let fileInfo = yield OS.File.stat(this._directoryFilePath);
-        this._lastDownloadMS = Date.parse(fileInfo.lastModificationDate);
-      }
-      // read frequency cap file
-      yield this._readFrequencyCapFile();
-      // fetch directory on startup without force
-      yield this._fetchAndCacheLinksIfNecessary();
-      // fecth inadjacent sites on startup
-      yield this._loadInadjacentSites();
-    }.bind(this));
-  },
-
-  _handleManyLinksChanged: function() {
-    this._topSitesWithSuggestedLinks.clear();
-    this._suggestedLinks.forEach((suggestedLinks, site) => {
-      if (RemoteNewTabUtils.isTopPlacesSite(site)) {
-        this._topSitesWithSuggestedLinks.add(site);
-      }
-    });
-    this._updateSuggestedTile();
-  },
-
-  /**
-   * Updates _topSitesWithSuggestedLinks based on the link that was changed.
-   *
-   * @return true if _topSitesWithSuggestedLinks was modified, false otherwise.
-   */
-  _handleLinkChanged: function(aLink) {
-    let changedLinkSite = RemoteNewTabUtils.extractSite(aLink.url);
-    let linkStored = this._topSitesWithSuggestedLinks.has(changedLinkSite);
-
-    if (!RemoteNewTabUtils.isTopPlacesSite(changedLinkSite) && linkStored) {
-      this._topSitesWithSuggestedLinks.delete(changedLinkSite);
-      return true;
-    }
-
-    if (this._suggestedLinks.has(changedLinkSite) &&
-        RemoteNewTabUtils.isTopPlacesSite(changedLinkSite) && !linkStored) {
-      this._topSitesWithSuggestedLinks.add(changedLinkSite);
-      return true;
-    }
-
-    // always run _updateSuggestedTile if aLink is inadjacent
-    // and there are tiles configured to avoid it
-    if (this._avoidInadjacentSites && this._isInadjacentLink(aLink)) {
-      return true;
-    }
-
-    return false;
-  },
-
-  _populatePlacesLinks: function () {
-    RemoteNewTabUtils.links.populateProviderCache(RemoteNewTabUtils.placesProvider, () => {
-      this._handleManyLinksChanged();
-    });
-  },
-
-  onDeleteURI: function(aProvider, aLink) {
-    let {url} = aLink;
-    // remove clicked flag for that url and
-    // call observer upon disk write completion
-    this._removeTileClick(url).then(() => {
-      this._callObservers("onDeleteURI", url);
-    });
-  },
-
-  onClearHistory: function() {
-    // remove all clicked flags and call observers upon file write
-    this._removeAllTileClicks().then(() => {
-      this._callObservers("onClearHistory");
-    });
-  },
-
-  onLinkChanged: function (aProvider, aLink) {
-    // Make sure RemoteNewTabUtils.links handles the notification first.
-    setTimeout(() => {
-      if (this._handleLinkChanged(aLink) || this._shouldUpdateSuggestedTile()) {
-        this._updateSuggestedTile();
-      }
-    }, 0);
-  },
-
-  onManyLinksChanged: function () {
-    // Make sure RemoteNewTabUtils.links handles the notification first.
-    setTimeout(() => {
-      this._handleManyLinksChanged();
-    }, 0);
-  },
-
-  _getCurrentTopSiteCount: function() {
-    let visibleTopSiteCount = 0;
-    let newTabLinks = RemoteNewTabUtils.links.getLinks();
-    for (let link of newTabLinks.slice(0, MIN_VISIBLE_HISTORY_TILES)) {
-      // compute visibleTopSiteCount for suggested tiles
-      if (link && (link.type == "history" || link.type == "enhanced")) {
-        visibleTopSiteCount++;
-      }
-    }
-    // since newTabLinks are available, set _newTabHasInadjacentSite here
-    // note that _shouldUpdateSuggestedTile is called by _updateSuggestedTile
-    this._newTabHasInadjacentSite = this._avoidInadjacentSites && this._checkForInadjacentSites(newTabLinks);
-
-    return visibleTopSiteCount;
-  },
-
-  _shouldUpdateSuggestedTile: function() {
-    let sortedLinks = RemoteNewTabUtils.getProviderLinks(this);
-
-    let mostFrecentLink = {};
-    if (sortedLinks && sortedLinks.length) {
-      mostFrecentLink = sortedLinks[0]
-    }
-
-    let currTopSiteCount = this._getCurrentTopSiteCount();
-    if ((!mostFrecentLink.targetedSite && currTopSiteCount >= MIN_VISIBLE_HISTORY_TILES) ||
-        (mostFrecentLink.targetedSite && currTopSiteCount < MIN_VISIBLE_HISTORY_TILES)) {
-      // If mostFrecentLink has a targetedSite then mostFrecentLink is a suggested link.
-      // If we have enough history links (8+) to show a suggested tile and we are not
-      // already showing one, then we should update (to *attempt* to add a suggested tile).
-      // OR if we don't have enough history to show a suggested tile (<8) and we are
-      // currently showing one, we should update (to remove it).
-      return true;
-    }
-
-    return false;
-  },
-
-  /**
-   * Chooses and returns a suggested tile based on a user's top sites
-   * that we have an available suggested tile for.
-   *
-   * @return the chosen suggested tile, or undefined if there isn't one
-   */
-  _updateSuggestedTile: function() {
-    let sortedLinks = RemoteNewTabUtils.getProviderLinks(this);
-
-    if (!sortedLinks) {
-      // If RemoteNewTabUtils.links.resetCache() is called before getting here,
-      // sortedLinks may be undefined.
-      return;
-    }
-
-    // Delete the current suggested tile, if one exists.
-    let initialLength = sortedLinks.length;
-    if (initialLength) {
-      let mostFrecentLink = sortedLinks[0];
-      if (mostFrecentLink.targetedSite) {
-        this._callObservers("onLinkChanged", {
-          url: mostFrecentLink.url,
-          frecency: SUGGESTED_FRECENCY,
-          lastVisitDate: mostFrecentLink.lastVisitDate,
-          type: mostFrecentLink.type,
-        }, 0, true);
-      }
-    }
-
-    if (this._topSitesWithSuggestedLinks.size == 0 || !this._shouldUpdateSuggestedTile()) {
-      // There are no potential suggested links we can show or not
-      // enough history for a suggested tile.
-      return;
-    }
-
-    // Create a flat list of all possible links we can show as suggested.
-    // Note that many top sites may map to the same suggested links, but we only
-    // want to count each suggested link once (based on url), thus possibleLinks is a map
-    // from url to suggestedLink. Thus, each link has an equal chance of being chosen at
-    // random from flattenedLinks if it appears only once.
-    let nextTimeout;
-    let possibleLinks = new Map();
-    let targetedSites = new Map();
-    this._topSitesWithSuggestedLinks.forEach(topSiteWithSuggestedLink => {
-      let suggestedLinksMap = this._suggestedLinks.get(topSiteWithSuggestedLink);
-      suggestedLinksMap.forEach((suggestedLink, url) => {
-        // Skip this link if we've shown it too many times already
-        if (!this._testFrequencyCapLimits(url)) {
-          return;
-        }
-
-        // as we iterate suggestedLinks, check for campaign start/end
-        // time limits, and set nextTimeout to the closest timestamp
-        let {use, timeoutDate} = this._testLinkForCampaignTimeLimits(suggestedLink);
-        // update nextTimeout is necessary
-        if (timeoutDate && (!nextTimeout || nextTimeout > timeoutDate)) {
-          nextTimeout = timeoutDate;
-        }
-        // Skip link if it falls outside campaign time limits
-        if (!use) {
-          return;
-        }
-
-        // Skip link if it avoids inadjacent sites and newtab has one
-        if (suggestedLink.check_inadjacency && this._newTabHasInadjacentSite) {
-          return;
-        }
-
-        possibleLinks.set(url, suggestedLink);
-
-        // Keep a map of URL to targeted sites. We later use this to show the user
-        // what site they visited to trigger this suggestion.
-        if (!targetedSites.get(url)) {
-          targetedSites.set(url, []);
-        }
-        targetedSites.get(url).push(topSiteWithSuggestedLink);
-      })
-    });
-
-    // setup timeout check for starting or ending campaigns
-    if (nextTimeout) {
-      this._setupCampaignTimeCheck(nextTimeout - Date.now());
-    }
-
-    // We might have run out of possible links to show
-    let numLinks = possibleLinks.size;
-    if (numLinks == 0) {
-      return;
-    }
-
-    let flattenedLinks = [...possibleLinks.values()];
-
-    // Choose our suggested link at random
-    let suggestedIndex = Math.floor(Math.random() * numLinks);
-    let chosenSuggestedLink = flattenedLinks[suggestedIndex];
-
-    // Add the suggested link to the front with some extra values
-    this._callObservers("onLinkChanged", Object.assign({
-      frecency: SUGGESTED_FRECENCY,
-
-      // Choose the first site a user has visited as the target. In the future,
-      // this should be the site with the highest frecency. However, we currently
-      // store frecency by URL not by site.
-      targetedSite: targetedSites.get(chosenSuggestedLink.url).length ?
-        targetedSites.get(chosenSuggestedLink.url)[0] : null
-    }, chosenSuggestedLink));
-    return chosenSuggestedLink;
-   },
-
-  /**
-   * Loads inadjacent sites
-   * @return a promise resolved when lookup Set for sites is built
-   */
-  _loadInadjacentSites: function RemoteDirectoryLinksProvider_loadInadjacentSites() {
-    return this._downloadJsonData(this._inadjacentSitesUrl).then(jsonString => {
-      let jsonObject = {};
-      try {
-        jsonObject = JSON.parse(jsonString);
-      }
-      catch (e) {
-        Cu.reportError(e);
-      }
-
-      this._inadjacentSites = new Set(jsonObject.domains);
-    });
-  },
-
-  /**
-   * Genegrates hash suitable for looking up inadjacent site
-   * @param value to hsh
-   * @return hased value, base64-ed
-   */
-  _generateHash: function RemoteDirectoryLinksProvider_generateHash(value) {
-    let byteArr = gUnicodeConverter.convertToByteArray(value);
-    gCryptoHash.init(gCryptoHash.MD5);
-    gCryptoHash.update(byteArr, byteArr.length);
-    return gCryptoHash.finish(true);
-  },
-
-  /**
-   * Checks if link belongs to inadjacent domain
-   * @param link to check
-   * @return true for inadjacent domains, false otherwise
-   */
-  _isInadjacentLink: function RemoteDirectoryLinksProvider_isInadjacentLink(link) {
-    let baseDomain = link.baseDomain || RemoteNewTabUtils.extractSite(link.url || "");
-    if (!baseDomain) {
-        return false;
-    }
-    // check if hashed domain is inadjacent
-    return this._inadjacentSites.has(this._generateHash(baseDomain));
-  },
-
-  /**
-   * Checks if new tab has inadjacent site
-   * @param new tab links (or nothing, in which case RemoteNewTabUtils.links.getLinks() is called
-   * @return true if new tab shows has inadjacent site
-   */
-  _checkForInadjacentSites: function RemoteDirectoryLinksProvider_checkForInadjacentSites(newTabLink) {
-    let links = newTabLink || RemoteNewTabUtils.links.getLinks();
-    for (let link of links.slice(0, MAX_VISIBLE_HISTORY_TILES)) {
-      // check links against inadjacent list - specifically include ALL link types
-      if (this._isInadjacentLink(link)) {
-        return true;
-      }
-    }
-    return false;
-  },
-
-  /**
-   * Reads json file, parses its content, and returns resulting object
-   * @param json file path
-   * @param json object to return in case file read or parse fails
-   * @return a promise resolved to a valid object or undefined upon error
-   */
-  _readJsonFile: Task.async(function* (filePath, nullObject) {
-    let jsonObj;
-    try {
-      let binaryData = yield OS.File.read(filePath);
-      let json = gTextDecoder.decode(binaryData);
-      jsonObj = JSON.parse(json);
-    }
-    catch (e) {}
-    return jsonObj || nullObject;
-  }),
-
-  /**
-   * Loads frequency cap object from file and parses its content
-   * @return a promise resolved upon load completion
-   *         on error or non-exstent file _frequencyCaps is set to empty object
-   */
-  _readFrequencyCapFile: Task.async(function* () {
-    // set _frequencyCaps object to file's content or empty object
-    this._frequencyCaps = yield this._readJsonFile(this._frequencyCapFilePath, {});
-  }),
-
-  /**
-   * Saves frequency cap object to file
-   * @return a promise resolved upon file i/o completion
-   */
-  _writeFrequencyCapFile: function RemoteDirectoryLinksProvider_writeFrequencyCapFile() {
-    let json = JSON.stringify(this._frequencyCaps || {});
-    return OS.File.writeAtomic(this._frequencyCapFilePath, json, {tmpPath: this._frequencyCapFilePath + ".tmp"});
-  },
-
-  /**
-   * Clears frequency cap object and writes empty json to file
-   * @return a promise resolved upon file i/o completion
-   */
-  _clearFrequencyCap: function RemoteDirectoryLinksProvider_clearFrequencyCap() {
-    this._frequencyCaps = {};
-    return this._writeFrequencyCapFile();
-  },
-
-  /**
-   * updates frequency cap configuration for a link
-   */
-  _updateFrequencyCapSettings: function RemoteDirectoryLinksProvider_updateFrequencyCapSettings(link) {
-    let capsObject = this._frequencyCaps[link.url];
-    if (!capsObject) {
-      // create an object with empty counts
-      capsObject = {
-        dailyViews: 0,
-        totalViews: 0,
-        lastShownDate: 0,
-      };
-      this._frequencyCaps[link.url] = capsObject;
-    }
-    // set last updated timestamp
-    capsObject.lastUpdated = Date.now();
-    // check for link configuration
-    if (link.frequency_caps) {
-      capsObject.dailyCap = link.frequency_caps.daily || DEFAULT_DAILY_FREQUENCY_CAP;
-      capsObject.totalCap = link.frequency_caps.total || DEFAULT_TOTAL_FREQUENCY_CAP;
-    }
-    else {
-      // fallback to defaults
-      capsObject.dailyCap = DEFAULT_DAILY_FREQUENCY_CAP;
-      capsObject.totalCap = DEFAULT_TOTAL_FREQUENCY_CAP;
-    }
-  },
-
-  /**
-   * Prunes frequency cap objects for outdated links
-   * @param timeDetla milliseconds
-   *        all cap objects with lastUpdated less than (now() - timeDelta)
-   *        will be removed. This is done to remove frequency cap objects
-   *        for unused tile urls
-   */
-  _pruneFrequencyCapUrls: function RemoteDirectoryLinksProvider_pruneFrequencyCapUrls(timeDelta = DEFAULT_PRUNE_TIME_DELTA) {
-    let timeThreshold = Date.now() - timeDelta;
-    Object.keys(this._frequencyCaps).forEach(url => {
-      if (this._frequencyCaps[url].lastUpdated <= timeThreshold) {
-        delete this._frequencyCaps[url];
-      }
-    });
-  },
-
-  /**
-   * Checks if supplied timestamp happened today
-   * @param timestamp in milliseconds
-   * @return true if the timestamp was made today, false otherwise
-   */
-  _wasToday: function RemoteDirectoryLinksProvider_wasToday(timestamp) {
-    let showOn = new Date(timestamp);
-    let today = new Date();
-    // call timestamps identical if both day and month are same
-    return showOn.getDate() == today.getDate() &&
-           showOn.getMonth() == today.getMonth() &&
-           showOn.getYear() == today.getYear();
-  },
-
-  /**
-   * adds some number of views for a url
-   * @param url String url of the suggested link
-   */
-  _addFrequencyCapView: function RemoteDirectoryLinksProvider_addFrequencyCapView(url) {
-    let capObject = this._frequencyCaps[url];
-    // sanity check
-    if (!capObject) {
-      return;
-    }
-
-    // if the day is new: reset the daily counter and lastShownDate
-    if (!this._wasToday(capObject.lastShownDate)) {
-      capObject.dailyViews = 0;
-      // update lastShownDate
-      capObject.lastShownDate = Date.now();
-    }
-
-    // bump both daily and total counters
-    capObject.totalViews++;
-    capObject.dailyViews++;
-
-    // if any of the caps is reached - update suggested tiles
-    if (capObject.totalViews >= capObject.totalCap ||
-        capObject.dailyViews >= capObject.dailyCap) {
-      this._updateSuggestedTile();
-    }
-  },
-
-  /**
-   * Sets clicked flag for link url
-   * @param url String url of the suggested link
-   */
-  _setFrequencyCapClick(url) {
-    let capObject = this._frequencyCaps[url];
-    // sanity check
-    if (!capObject) {
-      return;
-    }
-    capObject.clicked = true;
-    // and update suggested tiles, since current tile became invalid
-    this._updateSuggestedTile();
-  },
-
-  /**
-   * Tests frequency cap limits for link url
-   * @param url String url of the suggested link
-   * @return true if link is viewable, false otherwise
-   */
-  _testFrequencyCapLimits: function RemoteDirectoryLinksProvider_testFrequencyCapLimits(url) {
-    let capObject = this._frequencyCaps[url];
-    // sanity check: if url is missing - do not show this tile
-    if (!capObject) {
-      return false;
-    }
-
-    // check for clicked set or total views reached
-    if (capObject.clicked || capObject.totalViews >= capObject.totalCap) {
-      return false;
-    }
-
-    // otherwise check if link is over daily views limit
-    if (this._wasToday(capObject.lastShownDate) &&
-        capObject.dailyViews >= capObject.dailyCap) {
-      return false;
-    }
-
-    // we passed all cap tests: return true
-    return true;
-  },
-
-  /**
-   * Removes clicked flag from frequency cap entry for tile landing url
-   * @param url String url of the suggested link
-   * @return promise resolved upon disk write completion
-   */
-  _removeTileClick: function RemoteDirectoryLinksProvider_removeTileClick(url = "") {
-    // remove trailing slash, to accomodate Places sending site urls ending with '/'
-    let noTrailingSlashUrl = url.replace(/\/$/,"");
-    let capObject = this._frequencyCaps[url] || this._frequencyCaps[noTrailingSlashUrl];
-    // return resolved promise if capObject is not found
-    if (!capObject) {
-      return Promise.resolve();
-    }
-    // otherwise remove clicked flag
-    delete capObject.clicked;
-    return this._writeFrequencyCapFile();
-  },
-
-  /**
-   * Removes all clicked flags from frequency cap object
-   * @return promise resolved upon disk write completion
-   */
-  _removeAllTileClicks: function RemoteDirectoryLinksProvider_removeAllTileClicks() {
-    Object.keys(this._frequencyCaps).forEach(url => {
-      delete this._frequencyCaps[url].clicked;
-    });
-    return this._writeFrequencyCapFile();
-  },
-
-  /**
-   * Return the object to its pre-init state
-   */
-  reset: function RemoteDirectoryLinksProvider_reset() {
-    delete this.__linksURL;
-    this._removePrefsObserver();
-    this._removeObservers();
-  },
-
-  addObserver: function RemoteDirectoryLinksProvider_addObserver(aObserver) {
-    this._observers.add(aObserver);
-  },
-
-  removeObserver: function RemoteDirectoryLinksProvider_removeObserver(aObserver) {
-    this._observers.delete(aObserver);
-  },
-
-  _callObservers(methodName, ...args) {
-    for (let obs of this._observers) {
-      if (typeof(obs[methodName]) == "function") {
-        try {
-          obs[methodName](this, ...args);
-        } catch (err) {
-          Cu.reportError(err);
-        }
-      }
-    }
-  },
-
-  _removeObservers: function() {
-    this._observers.clear();
-  }
-};
--- a/browser/components/newtab/moz.build
+++ b/browser/components/newtab/moz.build
@@ -9,12 +9,11 @@ BROWSER_CHROME_MANIFESTS += ['tests/brow
 XPCSHELL_TESTS_MANIFESTS += [
     'tests/xpcshell/xpcshell.ini',
 ]
 
 EXTRA_JS_MODULES += [
     'NewTabURL.jsm',
     'PlacesProvider.jsm',
     'RemoteAboutNewTab.jsm',
-    'RemoteDirectoryLinksProvider.jsm',
     'RemoteNewTabLocation.jsm',
     'RemoteNewTabUtils.jsm',
 ]
deleted file mode 100644
--- a/browser/components/newtab/tests/xpcshell/test_RemoteDirectoryLinksProvider.js
+++ /dev/null
@@ -1,1326 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-"use strict";
-
-/**
- * This file tests the RemoteDirectoryLinksProvider singleton in the RemoteDirectoryLinksProvider.jsm module.
- */
-
-const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu, Constructor: CC } = Components;
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource:///modules/RemoteDirectoryLinksProvider.jsm");
-Cu.import("resource://gre/modules/Promise.jsm");
-Cu.import("resource://gre/modules/Http.jsm");
-Cu.import("resource://testing-common/httpd.js");
-Cu.import("resource://gre/modules/osfile.jsm");
-Cu.import("resource://gre/modules/Task.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/PlacesUtils.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
-  "resource://gre/modules/NetUtil.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "RemoteNewTabUtils",
-  "resource:///modules/RemoteNewTabUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils",
-  "resource://testing-common/PlacesTestUtils.jsm");
-
-do_get_profile();
-
-const DIRECTORY_LINKS_FILE = "directoryLinks.json";
-const DIRECTORY_FRECENCY = 1000;
-const SUGGESTED_FRECENCY = Infinity;
-const kURLData = {"directory": [{"url":"http://example.com","title":"LocalSource"}]};
-const kTestURL = 'data:application/json,' + JSON.stringify(kURLData);
-
-// RemoteDirectoryLinksProvider preferences
-const kLocalePref = RemoteDirectoryLinksProvider._observedPrefs.prefSelectedLocale;
-const kSourceUrlPref = RemoteDirectoryLinksProvider._observedPrefs.linksURL;
-const kPingUrlPref = "browser.newtabpage.directory.ping";
-const kNewtabEnhancedPref = "browser.newtabpage.enhanced";
-
-// httpd settings
-var server;
-const kDefaultServerPort = 9000;
-const kBaseUrl = "http://localhost:" + kDefaultServerPort;
-const kExamplePath = "/exampleTest/";
-const kFailPath = "/fail/";
-const kPingPath = "/ping/";
-const kExampleURL = kBaseUrl + kExamplePath;
-const kFailURL = kBaseUrl + kFailPath;
-const kPingUrl = kBaseUrl + kPingPath;
-
-// app/profile/firefox.js are not avaialble in xpcshell: hence, preset them
-Services.prefs.setCharPref(kLocalePref, "en-US");
-Services.prefs.setCharPref(kSourceUrlPref, kTestURL);
-Services.prefs.setCharPref(kPingUrlPref, kPingUrl);
-Services.prefs.setBoolPref(kNewtabEnhancedPref, true);
-
-const kHttpHandlerData = {};
-kHttpHandlerData[kExamplePath] = {"directory": [{"url":"http://example.com","title":"RemoteSource"}]};
-
-const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
-                              "nsIBinaryInputStream",
-                              "setInputStream");
-
-let gLastRequestPath;
-
-let suggestedTile1 = {
-  url: "http://turbotax.com",
-  type: "affiliate",
-  lastVisitDate: 4,
-  adgroup_name: "Adgroup1",
-  frecent_sites: [
-    "taxact.com",
-    "hrblock.com",
-    "1040.com",
-    "taxslayer.com"
-  ]
-};
-let suggestedTile2 = {
-  url: "http://irs.gov",
-  type: "affiliate",
-  lastVisitDate: 3,
-  adgroup_name: "Adgroup2",
-  frecent_sites: [
-    "taxact.com",
-    "hrblock.com",
-    "freetaxusa.com",
-    "taxslayer.com"
-  ]
-};
-let suggestedTile3 = {
-  url: "http://hrblock.com",
-  type: "affiliate",
-  lastVisitDate: 2,
-  adgroup_name: "Adgroup3",
-  frecent_sites: [
-    "taxact.com",
-    "freetaxusa.com",
-    "1040.com",
-    "taxslayer.com"
-  ]
-};
-let suggestedTile4 = {
-  url: "http://sponsoredtile.com",
-  type: "sponsored",
-  lastVisitDate: 1,
-  adgroup_name: "Adgroup4",
-  frecent_sites: [
-    "sponsoredtarget.com"
-  ]
-}
-let suggestedTile5 = {
-  url: "http://eviltile.com",
-  type: "affiliate",
-  lastVisitDate: 5,
-  explanation: "This is an evil tile <form><button formaction='javascript:alert(1)''>X</button></form> muhahaha",
-  adgroup_name: "WE ARE EVIL <link rel='import' href='test.svg'/>",
-  frecent_sites: [
-    "eviltarget.com"
-  ]
-}
-let someOtherSite = {url: "http://someothersite.com", title: "Not_A_Suggested_Site"};
-
-function getHttpHandler(path) {
-  let code = 200;
-  let body = JSON.stringify(kHttpHandlerData[path]);
-  if (path == kFailPath) {
-    code = 204;
-  }
-  return function(aRequest, aResponse) {
-    gLastRequestPath = aRequest.path;
-    aResponse.setStatusLine(null, code);
-    aResponse.setHeader("Content-Type", "application/json");
-    aResponse.write(body);
-  };
-}
-
-function isIdentical(actual, expected) {
-  if (expected == null) {
-    do_check_eq(actual, expected);
-  }
-  else if (typeof expected == "object") {
-    // Make sure all the keys match up
-    do_check_eq(Object.keys(actual).sort() + "", Object.keys(expected).sort());
-
-    // Recursively check each value individually
-    Object.keys(expected).forEach(key => {
-      isIdentical(actual[key], expected[key]);
-    });
-  }
-  else {
-    do_check_eq(actual, expected);
-  }
-}
-
-function fetchData() {
-  let deferred = Promise.defer();
-
-  RemoteDirectoryLinksProvider.getLinks(linkData => {
-    deferred.resolve(linkData);
-  });
-  return deferred.promise;
-}
-
-function readJsonFile(jsonFile = DIRECTORY_LINKS_FILE) {
-  let decoder = new TextDecoder();
-  let directoryLinksFilePath = OS.Path.join(OS.Constants.Path.localProfileDir, jsonFile);
-  return OS.File.read(directoryLinksFilePath).then(array => {
-    let json = decoder.decode(array);
-    return JSON.parse(json);
-  }, () => { return "" });
-}
-
-function cleanJsonFile(jsonFile = DIRECTORY_LINKS_FILE) {
-  let directoryLinksFilePath = OS.Path.join(OS.Constants.Path.localProfileDir, jsonFile);
-  return OS.File.remove(directoryLinksFilePath);
-}
-
-function LinksChangeObserver() {
-  this.deferred = Promise.defer();
-  this.onManyLinksChanged = () => this.deferred.resolve();
-  this.onDownloadFail = this.onManyLinksChanged;
-}
-
-function promiseDirectoryDownloadOnPrefChange(pref, newValue) {
-  let oldValue = Services.prefs.getCharPref(pref);
-  if (oldValue != newValue) {
-    // if the preference value is already equal to newValue
-    // the pref service will not call our observer and we
-    // deadlock. Hence only setup observer if values differ
-    let observer = new LinksChangeObserver();
-    RemoteDirectoryLinksProvider.addObserver(observer);
-    Services.prefs.setCharPref(pref, newValue);
-    return observer.deferred.promise.then(() => {
-      RemoteDirectoryLinksProvider.removeObserver(observer);
-    });
-  }
-  return Promise.resolve();
-}
-
-function promiseSetupRemoteDirectoryLinksProvider(options = {}) {
-  return Task.spawn(function() {
-    let linksURL = options.linksURL || kTestURL;
-    yield RemoteDirectoryLinksProvider.init();
-    yield promiseDirectoryDownloadOnPrefChange(kLocalePref, options.locale || "en-US");
-    yield promiseDirectoryDownloadOnPrefChange(kSourceUrlPref, linksURL);
-    do_check_eq(RemoteDirectoryLinksProvider._linksURL, linksURL);
-    RemoteDirectoryLinksProvider._lastDownloadMS = options.lastDownloadMS || 0;
-  });
-}
-
-function promiseCleanRemoteDirectoryLinksProvider() {
-  return Task.spawn(function() {
-    yield promiseDirectoryDownloadOnPrefChange(kLocalePref, "en-US");
-    yield promiseDirectoryDownloadOnPrefChange(kSourceUrlPref, kTestURL);
-    yield RemoteDirectoryLinksProvider._clearFrequencyCap();
-    yield RemoteDirectoryLinksProvider._loadInadjacentSites();
-    RemoteDirectoryLinksProvider._lastDownloadMS  = 0;
-    RemoteDirectoryLinksProvider.reset();
-  });
-}
-
-function run_test() {
-  // Set up a mock HTTP server to serve a directory page
-  server = new HttpServer();
-  server.registerPrefixHandler(kExamplePath, getHttpHandler(kExamplePath));
-  server.registerPrefixHandler(kFailPath, getHttpHandler(kFailPath));
-  server.start(kDefaultServerPort);
-  RemoteNewTabUtils.init();
-
-  run_next_test();
-
-  // Teardown.
-  do_register_cleanup(function() {
-    server.stop(function() { });
-    RemoteDirectoryLinksProvider.reset();
-    Services.prefs.clearUserPref(kLocalePref);
-    Services.prefs.clearUserPref(kSourceUrlPref);
-    Services.prefs.clearUserPref(kPingUrlPref);
-    Services.prefs.clearUserPref(kNewtabEnhancedPref);
-  });
-}
-
-
-function setTimeout(fun, timeout) {
-  let timer = Components.classes["@mozilla.org/timer;1"]
-                        .createInstance(Components.interfaces.nsITimer);
-  var event = {
-    notify: function (timer) {
-      fun();
-    }
-  };
-  timer.initWithCallback(event, timeout,
-                         Components.interfaces.nsITimer.TYPE_ONE_SHOT);
-  return timer;
-}
-
-add_task(function test_shouldUpdateSuggestedTile() {
-  let suggestedLink = {
-    targetedSite: "somesite.com"
-  };
-
-  // RemoteDirectoryLinksProvider has no suggested tile and no top sites => no need to update
-  do_check_eq(RemoteDirectoryLinksProvider._getCurrentTopSiteCount(), 0);
-  isIdentical(RemoteNewTabUtils.getProviderLinks(), []);
-  do_check_eq(RemoteDirectoryLinksProvider._shouldUpdateSuggestedTile(), false);
-
-  // RemoteDirectoryLinksProvider has a suggested tile and no top sites => need to update
-  let origGetProviderLinks = RemoteNewTabUtils.getProviderLinks;
-  RemoteNewTabUtils.getProviderLinks = (provider) => [suggestedLink];
-
-  do_check_eq(RemoteDirectoryLinksProvider._getCurrentTopSiteCount(), 0);
-  isIdentical(RemoteNewTabUtils.getProviderLinks(), [suggestedLink]);
-  do_check_eq(RemoteDirectoryLinksProvider._shouldUpdateSuggestedTile(), true);
-
-  // RemoteDirectoryLinksProvider has a suggested tile and 8 top sites => no need to update
-  let origCurrentTopSiteCount = RemoteDirectoryLinksProvider._getCurrentTopSiteCount;
-  RemoteDirectoryLinksProvider._getCurrentTopSiteCount = () => 8;
-
-  do_check_eq(RemoteDirectoryLinksProvider._getCurrentTopSiteCount(), 8);
-  isIdentical(RemoteNewTabUtils.getProviderLinks(), [suggestedLink]);
-  do_check_eq(RemoteDirectoryLinksProvider._shouldUpdateSuggestedTile(), false);
-
-  // RemoteDirectoryLinksProvider has no suggested tile and 8 top sites => need to update
-  RemoteNewTabUtils.getProviderLinks = origGetProviderLinks;
-  do_check_eq(RemoteDirectoryLinksProvider._getCurrentTopSiteCount(), 8);
-  isIdentical(RemoteNewTabUtils.getProviderLinks(), []);
-  do_check_eq(RemoteDirectoryLinksProvider._shouldUpdateSuggestedTile(), true);
-
-  // Cleanup
-  RemoteDirectoryLinksProvider._getCurrentTopSiteCount = origCurrentTopSiteCount;
-});
-
-add_task(function test_updateSuggestedTile() {
-  let topSites = ["site0.com", "1040.com", "site2.com", "hrblock.com", "site4.com", "freetaxusa.com", "site6.com"];
-
-  // Initial setup
-  let data = {"suggested": [suggestedTile1, suggestedTile2, suggestedTile3], "directory": [someOtherSite]};
-  let dataURI = 'data:application/json,' + JSON.stringify(data);
-
-  let testObserver = new TestFirstRun();
-  RemoteDirectoryLinksProvider.addObserver(testObserver);
-
-  yield promiseSetupRemoteDirectoryLinksProvider({linksURL: dataURI});
-  let links = yield fetchData();
-
-  let origIsTopPlacesSite = RemoteNewTabUtils.isTopPlacesSite;
-  RemoteNewTabUtils.isTopPlacesSite = function(site) {
-    return topSites.indexOf(site) >= 0;
-  }
-
-  let origGetProviderLinks = RemoteNewTabUtils.getProviderLinks;
-  RemoteNewTabUtils.getProviderLinks = function(provider) {
-    return links;
-  }
-
-  let origCurrentTopSiteCount = RemoteDirectoryLinksProvider._getCurrentTopSiteCount;
-  RemoteDirectoryLinksProvider._getCurrentTopSiteCount = () => 8;
-
-  do_check_eq(RemoteDirectoryLinksProvider._updateSuggestedTile(), undefined);
-
-  function TestFirstRun() {
-    this.promise = new Promise(resolve => {
-      this.onLinkChanged = (directoryLinksProvider, link) => {
-        links.unshift(link);
-        let possibleLinks = [suggestedTile1.url, suggestedTile2.url, suggestedTile3.url];
-
-        isIdentical([...RemoteDirectoryLinksProvider._topSitesWithSuggestedLinks], ["hrblock.com", "1040.com", "freetaxusa.com"]);
-        do_check_true(possibleLinks.indexOf(link.url) > -1);
-        do_check_eq(link.frecency, SUGGESTED_FRECENCY);
-        do_check_eq(link.type, "affiliate");
-        resolve();
-      };
-    });
-  }
-
-  function TestChangingSuggestedTile() {
-    this.count = 0;
-    this.promise = new Promise(resolve => {
-      this.onLinkChanged = (directoryLinksProvider, link) => {
-        this.count++;
-        let possibleLinks = [suggestedTile1.url, suggestedTile2.url, suggestedTile3.url];
-
-        do_check_true(possibleLinks.indexOf(link.url) > -1);
-        do_check_eq(link.type, "affiliate");
-        do_check_true(this.count <= 2);
-
-        if (this.count == 1) {
-          // The removed suggested link is the one we added initially.
-          do_check_eq(link.url, links.shift().url);
-          do_check_eq(link.frecency, SUGGESTED_FRECENCY);
-        } else {
-          links.unshift(link);
-          do_check_eq(link.frecency, SUGGESTED_FRECENCY);
-        }
-        isIdentical([...RemoteDirectoryLinksProvider._topSitesWithSuggestedLinks], ["hrblock.com", "freetaxusa.com"]);
-        resolve();
-      }
-    });
-  }
-
-  function TestRemovingSuggestedTile() {
-    this.count = 0;
-    this.promise = new Promise(resolve => {
-      this.onLinkChanged = (directoryLinksProvider, link) => {
-        this.count++;
-
-        do_check_eq(link.type, "affiliate");
-        do_check_eq(this.count, 1);
-        do_check_eq(link.frecency, SUGGESTED_FRECENCY);
-        do_check_eq(link.url, links.shift().url);
-        isIdentical([...RemoteDirectoryLinksProvider._topSitesWithSuggestedLinks], []);
-        resolve();
-      }
-    });
-  }
-
-  // Test first call to '_updateSuggestedTile()', called when fetching directory links.
-  yield testObserver.promise;
-  RemoteDirectoryLinksProvider.removeObserver(testObserver);
-
-  // Removing a top site that doesn't have a suggested link should
-  // not change the current suggested tile.
-  let removedTopsite = topSites.shift();
-  do_check_eq(removedTopsite, "site0.com");
-  do_check_false(RemoteNewTabUtils.isTopPlacesSite(removedTopsite));
-  let updateSuggestedTile = RemoteDirectoryLinksProvider._handleLinkChanged({
-    url: "http://" + removedTopsite,
-    type: "history",
-  });
-  do_check_false(updateSuggestedTile);
-
-  // Removing a top site that has a suggested link should
-  // remove any current suggested tile and add a new one.
-  testObserver = new TestChangingSuggestedTile();
-  RemoteDirectoryLinksProvider.addObserver(testObserver);
-  removedTopsite = topSites.shift();
-  do_check_eq(removedTopsite, "1040.com");
-  do_check_false(RemoteNewTabUtils.isTopPlacesSite(removedTopsite));
-  RemoteDirectoryLinksProvider.onLinkChanged(RemoteDirectoryLinksProvider, {
-    url: "http://" + removedTopsite,
-    type: "history",
-  });
-  yield testObserver.promise;
-  do_check_eq(testObserver.count, 2);
-  RemoteDirectoryLinksProvider.removeObserver(testObserver);
-
-  // Removing all top sites with suggested links should remove
-  // the current suggested link and not replace it.
-  topSites = [];
-  testObserver = new TestRemovingSuggestedTile();
-  RemoteDirectoryLinksProvider.addObserver(testObserver);
-  RemoteDirectoryLinksProvider.onManyLinksChanged();
-  yield testObserver.promise;
-
-  // Cleanup
-  yield promiseCleanRemoteDirectoryLinksProvider();
-  RemoteNewTabUtils.isTopPlacesSite = origIsTopPlacesSite;
-  RemoteNewTabUtils.getProviderLinks = origGetProviderLinks;
-  RemoteDirectoryLinksProvider._getCurrentTopSiteCount = origCurrentTopSiteCount;
-});
-
-add_task(function test_suggestedLinksMap() {
-  let data = {"suggested": [suggestedTile1, suggestedTile2, suggestedTile3, suggestedTile4], "directory": [someOtherSite]};
-  let dataURI = 'data:application/json,' + JSON.stringify(data);
-
-  yield promiseSetupRemoteDirectoryLinksProvider({linksURL: dataURI});
-  let links = yield fetchData();
-
-  // Ensure the suggested tiles were not considered directory tiles.
-  do_check_eq(links.length, 1);
-  let expected_data = [{url: "http://someothersite.com", title: "Not_A_Suggested_Site", frecency: DIRECTORY_FRECENCY, lastVisitDate: 1}];
-  isIdentical(links, expected_data);
-
-  // Check for correctly saved suggested tiles data.
-  expected_data = {
-    "taxact.com": [suggestedTile1, suggestedTile2, suggestedTile3],
-    "hrblock.com": [suggestedTile1, suggestedTile2],
-    "1040.com": [suggestedTile1, suggestedTile3],
-    "taxslayer.com": [suggestedTile1, suggestedTile2, suggestedTile3],
-    "freetaxusa.com": [suggestedTile2, suggestedTile3],
-    "sponsoredtarget.com": [suggestedTile4],
-  };
-
-  let suggestedSites = [...RemoteDirectoryLinksProvider._suggestedLinks.keys()];
-  do_check_eq(suggestedSites.indexOf("sponsoredtarget.com"), 5);
-  do_check_eq(suggestedSites.length, Object.keys(expected_data).length);
-
-  RemoteDirectoryLinksProvider._suggestedLinks.forEach((suggestedLinks, site) => {
-    let suggestedLinksItr = suggestedLinks.values();
-    for (let link of expected_data[site]) {
-      let linkCopy = JSON.parse(JSON.stringify(link));
-      linkCopy.targetedName = link.adgroup_name;
-      linkCopy.explanation = "";
-      isIdentical(suggestedLinksItr.next().value, linkCopy);
-    }
-  })
-
-  yield promiseCleanRemoteDirectoryLinksProvider();
-});
-
-add_task(function test_topSitesWithSuggestedLinks() {
-  let topSites = ["site0.com", "1040.com", "site2.com", "hrblock.com", "site4.com", "freetaxusa.com", "site6.com"];
-  let origIsTopPlacesSite = RemoteNewTabUtils.isTopPlacesSite;
-  RemoteNewTabUtils.isTopPlacesSite = function(site) {
-    return topSites.indexOf(site) >= 0;
-  }
-
-  // Mock out getProviderLinks() so we don't have to populate cache in RemoteNewTabUtils
-  let origGetProviderLinks = RemoteNewTabUtils.getProviderLinks;
-  RemoteNewTabUtils.getProviderLinks = function(provider) {
-    return [];
-  }
-
-  // We start off with no top sites with suggested links.
-  do_check_eq(RemoteDirectoryLinksProvider._topSitesWithSuggestedLinks.size, 0);
-
-  let data = {"suggested": [suggestedTile1, suggestedTile2, suggestedTile3], "directory": [someOtherSite]};
-  let dataURI = 'data:application/json,' + JSON.stringify(data);
-
-  yield promiseSetupRemoteDirectoryLinksProvider({linksURL: dataURI});
-  let links = yield fetchData();
-
-  // Check we've populated suggested links as expected.
-  do_check_eq(RemoteDirectoryLinksProvider._suggestedLinks.size, 5);
-
-  // When many sites change, we update _topSitesWithSuggestedLinks as expected.
-  let expectedTopSitesWithSuggestedLinks = ["hrblock.com", "1040.com", "freetaxusa.com"];
-  RemoteDirectoryLinksProvider._handleManyLinksChanged();
-  isIdentical([...RemoteDirectoryLinksProvider._topSitesWithSuggestedLinks], expectedTopSitesWithSuggestedLinks);
-
-  // Removing site6.com as a topsite has no impact on _topSitesWithSuggestedLinks.
-  let popped = topSites.pop();
-  RemoteDirectoryLinksProvider._handleLinkChanged({url: "http://" + popped});
-  isIdentical([...RemoteDirectoryLinksProvider._topSitesWithSuggestedLinks], expectedTopSitesWithSuggestedLinks);
-
-  // Removing freetaxusa.com as a topsite will remove it from _topSitesWithSuggestedLinks.
-  popped = topSites.pop();
-  expectedTopSitesWithSuggestedLinks.pop();
-  RemoteDirectoryLinksProvider._handleLinkChanged({url: "http://" + popped});
-  isIdentical([...RemoteDirectoryLinksProvider._topSitesWithSuggestedLinks], expectedTopSitesWithSuggestedLinks);
-
-  // Re-adding freetaxusa.com as a topsite will add it to _topSitesWithSuggestedLinks.
-  topSites.push(popped);
-  expectedTopSitesWithSuggestedLinks.push(popped);
-  RemoteDirectoryLinksProvider._handleLinkChanged({url: "http://" + popped});
-  isIdentical([...RemoteDirectoryLinksProvider._topSitesWithSuggestedLinks], expectedTopSitesWithSuggestedLinks);
-
-  // Cleanup.
-  RemoteNewTabUtils.isTopPlacesSite = origIsTopPlacesSite;
-  RemoteNewTabUtils.getProviderLinks = origGetProviderLinks;
-});
-
-add_task(function test_fetchAndCacheLinks_local() {
-  yield RemoteDirectoryLinksProvider.init();
-  yield cleanJsonFile();
-  // Trigger cache of data or chrome uri files in profD
-  yield RemoteDirectoryLinksProvider._fetchAndCacheLinks(kTestURL);
-  let data = yield readJsonFile();
-  isIdentical(data, kURLData);
-});
-
-add_task(function test_fetchAndCacheLinks_remote() {
-  yield RemoteDirectoryLinksProvider.init();
-  yield cleanJsonFile();
-  // this must trigger directory links json download and save it to cache file
-  yield promiseDirectoryDownloadOnPrefChange(kSourceUrlPref, kExampleURL + "%LOCALE%");
-  do_check_eq(gLastRequestPath, kExamplePath + "en-US");
-  let data = yield readJsonFile();
-  isIdentical(data, kHttpHandlerData[kExamplePath]);
-});
-
-add_task(function test_fetchAndCacheLinks_malformedURI() {
-  yield RemoteDirectoryLinksProvider.init();
-  yield cleanJsonFile();
-  let someJunk = "some junk";
-  try {
-    yield RemoteDirectoryLinksProvider._fetchAndCacheLinks(someJunk);
-    do_throw("Malformed URIs should fail")
-  } catch (e) {
-    do_check_eq(e, "Error fetching " + someJunk)
-  }
-
-  // File should be empty.
-  let data = yield readJsonFile();
-  isIdentical(data, "");
-});
-
-add_task(function test_fetchAndCacheLinks_unknownHost() {
-  yield RemoteDirectoryLinksProvider.init();
-  yield cleanJsonFile();
-  let nonExistentServer = "http://localhost:56789/";
-  try {
-    yield RemoteDirectoryLinksProvider._fetchAndCacheLinks(nonExistentServer);
-    do_throw("BAD URIs should fail");
-  } catch (e) {
-    do_check_true(e.startsWith("Fetching " + nonExistentServer + " results in error code: "))
-  }
-
-  // File should be empty.
-  let data = yield readJsonFile();
-  isIdentical(data, "");
-});
-
-add_task(function test_fetchAndCacheLinks_non200Status() {
-  yield RemoteDirectoryLinksProvider.init();
-  yield cleanJsonFile();
-  yield promiseDirectoryDownloadOnPrefChange(kSourceUrlPref, kFailURL);
-  do_check_eq(gLastRequestPath, kFailPath);
-  let data = yield readJsonFile();
-  isIdentical(data, {});
-});
-
-// To test onManyLinksChanged observer, trigger a fetch
-add_task(function test_RemoteDirectoryLinksProvider__linkObservers() {
-  yield RemoteDirectoryLinksProvider.init();
-
-  let testObserver = new LinksChangeObserver();
-  RemoteDirectoryLinksProvider.addObserver(testObserver);
-  do_check_eq(RemoteDirectoryLinksProvider._observers.size, 1);
-  RemoteDirectoryLinksProvider._fetchAndCacheLinksIfNecessary(true);
-
-  yield testObserver.deferred.promise;
-  RemoteDirectoryLinksProvider._removeObservers();
-  do_check_eq(RemoteDirectoryLinksProvider._observers.size, 0);
-
-  yield promiseCleanRemoteDirectoryLinksProvider();
-});
-
-add_task(function test_RemoteDirectoryLinksProvider__prefObserver_url() {
-  yield promiseSetupRemoteDirectoryLinksProvider({linksURL: kTestURL});
-
-  let links = yield fetchData();
-  do_check_eq(links.length, 1);
-  let expectedData =  [{url: "http://example.com", title: "LocalSource", frecency: DIRECTORY_FRECENCY, lastVisitDate: 1}];
-  isIdentical(links, expectedData);
-
-  // tests these 2 things:
-  // 1. _linksURL is properly set after the pref change
-  // 2. invalid source url is correctly handled
-  let exampleUrl = 'http://localhost:56789/bad';
-  yield promiseDirectoryDownloadOnPrefChange(kSourceUrlPref, exampleUrl);
-  do_check_eq(RemoteDirectoryLinksProvider._linksURL, exampleUrl);
-
-  // since the download fail, the directory file must remain the same
-  let newLinks = yield fetchData();
-  isIdentical(newLinks, expectedData);
-
-  // now remove the file, and re-download
-  yield cleanJsonFile();
-  yield promiseDirectoryDownloadOnPrefChange(kSourceUrlPref, exampleUrl + " ");
-  // we now should see empty links
-  newLinks = yield fetchData();
-  isIdentical(newLinks, []);
-
-  yield promiseCleanRemoteDirectoryLinksProvider();
-});
-
-add_task(function test_RemoteDirectoryLinksProvider_getLinks_noDirectoryData() {
-  let data = {
-    "directory": [],
-  };
-  let dataURI = 'data:application/json,' + JSON.stringify(data);
-  yield promiseSetupRemoteDirectoryLinksProvider({linksURL: dataURI});
-
-  let links = yield fetchData();
-  do_check_eq(links.length, 0);
-  yield promiseCleanRemoteDirectoryLinksProvider();
-});
-
-add_task(function test_RemoteDirectoryLinksProvider_getLinks_badData() {
-  let data = {
-    "en-US": {
-      "en-US": [{url: "http://example.com", title: "US"}],
-    },
-  };
-  let dataURI = 'data:application/json,' + JSON.stringify(data);
-  yield promiseSetupRemoteDirectoryLinksProvider({linksURL: dataURI});
-
-  // Make sure we get nothing for incorrectly formatted data
-  let links = yield fetchData();
-  do_check_eq(links.length, 0);
-  yield promiseCleanRemoteDirectoryLinksProvider();
-});
-
-add_task(function test_RemoteDirectoryLinksProvider_needsDownload() {
-  // test timestamping
-  RemoteDirectoryLinksProvider._lastDownloadMS = 0;
-  do_check_true(RemoteDirectoryLinksProvider._needsDownload);
-  RemoteDirectoryLinksProvider._lastDownloadMS = Date.now();
-  do_check_false(RemoteDirectoryLinksProvider._needsDownload);
-  RemoteDirectoryLinksProvider._lastDownloadMS = Date.now() - (60*60*24 + 1)*1000;
-  do_check_true(RemoteDirectoryLinksProvider._needsDownload);
-  RemoteDirectoryLinksProvider._lastDownloadMS = 0;
-});
-
-add_task(function test_RemoteDirectoryLinksProvider_fetchAndCacheLinksIfNecessary() {
-  yield RemoteDirectoryLinksProvider.init();
-  yield cleanJsonFile();
-  // explicitly change source url to cause the download during setup
-  yield promiseSetupRemoteDirectoryLinksProvider({linksURL: kTestURL+" "});
-  yield RemoteDirectoryLinksProvider._fetchAndCacheLinksIfNecessary();
-
-  // inspect lastDownloadMS timestamp which should be 5 seconds less then now()
-  let lastDownloadMS = RemoteDirectoryLinksProvider._lastDownloadMS;
-  do_check_true((Date.now() - lastDownloadMS) < 5000);
-
-  // we should have fetched a new file during setup
-  let data = yield readJsonFile();
-  isIdentical(data, kURLData);
-
-  // attempt to download again - the timestamp should not change
-  yield RemoteDirectoryLinksProvider._fetchAndCacheLinksIfNecessary();
-  do_check_eq(RemoteDirectoryLinksProvider._lastDownloadMS, lastDownloadMS);
-
-  // clean the file and force the download
-  yield cleanJsonFile();
-  yield RemoteDirectoryLinksProvider._fetchAndCacheLinksIfNecessary(true);
-  data = yield readJsonFile();
-  isIdentical(data, kURLData);
-
-  // make sure that failed download does not corrupt the file, nor changes lastDownloadMS
-  lastDownloadMS = RemoteDirectoryLinksProvider._lastDownloadMS;
-  yield promiseDirectoryDownloadOnPrefChange(kSourceUrlPref, "http://");
-  yield RemoteDirectoryLinksProvider._fetchAndCacheLinksIfNecessary(true);
-  data = yield readJsonFile();
-  isIdentical(data, kURLData);
-  do_check_eq(RemoteDirectoryLinksProvider._lastDownloadMS, lastDownloadMS);
-
-  // _fetchAndCacheLinksIfNecessary must return same promise if download is in progress
-  let downloadPromise = RemoteDirectoryLinksProvider._fetchAndCacheLinksIfNecessary(true);
-  let anotherPromise = RemoteDirectoryLinksProvider._fetchAndCacheLinksIfNecessary(true);
-  do_check_true(downloadPromise === anotherPromise);
-  yield downloadPromise;
-
-  yield promiseCleanRemoteDirectoryLinksProvider();
-});
-
-add_task(function test_RemoteDirectoryLinksProvider_fetchDirectoryOnPrefChange() {
-  yield RemoteDirectoryLinksProvider.init();
-
-  let testObserver = new LinksChangeObserver();
-  RemoteDirectoryLinksProvider.addObserver(testObserver);
-
-  yield cleanJsonFile();
-  // ensure that provider does not think it needs to download
-  do_check_false(RemoteDirectoryLinksProvider._needsDownload);
-
-  // change the source URL, which should force directory download
-  yield promiseDirectoryDownloadOnPrefChange(kSourceUrlPref, kExampleURL);
-  // then wait for testObserver to fire and test that json is downloaded
-  yield testObserver.deferred.promise;
-  do_check_eq(gLastRequestPath, kExamplePath);
-  let data = yield readJsonFile();
-  isIdentical(data, kHttpHandlerData[kExamplePath]);
-
-  yield promiseCleanRemoteDirectoryLinksProvider();
-});
-
-add_task(function test_RemoteDirectoryLinksProvider_fetchDirectoryOnInit() {
-  // ensure preferences are set to defaults
-  yield promiseSetupRemoteDirectoryLinksProvider();
-  // now clean to provider, so we can init it again
-  yield promiseCleanRemoteDirectoryLinksProvider();
-
-  yield cleanJsonFile();
-  yield RemoteDirectoryLinksProvider.init();
-  let data = yield readJsonFile();
-  isIdentical(data, kURLData);
-
-  yield promiseCleanRemoteDirectoryLinksProvider();
-});
-
-add_task(function test_RemoteDirectoryLinksProvider_getLinksFromCorruptedFile() {
-  yield promiseSetupRemoteDirectoryLinksProvider();
-
-  // write bogus json to a file and attempt to fetch from it
-  let directoryLinksFilePath = OS.Path.join(OS.Constants.Path.profileDir, DIRECTORY_LINKS_FILE);
-  yield OS.File.writeAtomic(directoryLinksFilePath, '{"en-US":');
-  let data = yield fetchData();
-  isIdentical(data, []);
-
-  yield promiseCleanRemoteDirectoryLinksProvider();
-});
-
-add_task(function test_RemoteDirectoryLinksProvider_getAllowedLinks() {
-  let data = {"directory": [
-    {url: "ftp://example.com"},
-    {url: "http://example.net"},
-    {url: "javascript:5"},
-    {url: "https://example.com"},
-    {url: "httpJUNKjavascript:42"},
-    {url: "data:text/plain,hi"},
-    {url: "http/bork:eh"},
-  ]};
-  let dataURI = 'data:application/json,' + JSON.stringify(data);
-  yield promiseSetupRemoteDirectoryLinksProvider({linksURL: dataURI});
-
-  let links = yield fetchData();
-  do_check_eq(links.length, 2);
-
-  // The only remaining url should be http and https
-  do_check_eq(links[0].url, data["directory"][1].url);
-  do_check_eq(links[1].url, data["directory"][3].url);
-});
-
-add_task(function test_RemoteDirectoryLinksProvider_getAllowedImages() {
-  let data = {"directory": [
-    {url: "http://example.com", imageURI: "ftp://example.com"},
-    {url: "http://example.com", imageURI: "http://example.net"},
-    {url: "http://example.com", imageURI: "javascript:5"},
-    {url: "http://example.com", imageURI: "https://example.com"},
-    {url: "http://example.com", imageURI: "httpJUNKjavascript:42"},
-    {url: "http://example.com", imageURI: "data:text/plain,hi"},
-    {url: "http://example.com", imageURI: "http/bork:eh"},
-  ]};
-  let dataURI = 'data:application/json,' + JSON.stringify(data);
-  yield promiseSetupRemoteDirectoryLinksProvider({linksURL: dataURI});
-
-  let links = yield fetchData();
-  do_check_eq(links.length, 2);
-
-  // The only remaining images should be https and data
-  do_check_eq(links[0].imageURI, data["directory"][3].imageURI);
-  do_check_eq(links[1].imageURI, data["directory"][5].imageURI);
-});
-
-add_task(function test_RemoteDirectoryLinksProvider_getAllowedImages_base() {
-  let data = {"directory": [
-    {url: "http://example1.com", imageURI: "https://example.com"},
-    {url: "http://example2.com", imageURI: "https://tiles.cdn.mozilla.net"},
-    {url: "http://example3.com", imageURI: "https://tiles2.cdn.mozilla.net"},
-    {url: "http://example4.com", enhancedImageURI: "https://mozilla.net"},
-    {url: "http://example5.com", imageURI: "data:text/plain,hi"},
-  ]};
-  let dataURI = 'data:application/json,' + JSON.stringify(data);
-  yield promiseSetupRemoteDirectoryLinksProvider({linksURL: dataURI});
-
-  // Pretend we're using the default pref to trigger base matching
-  RemoteDirectoryLinksProvider.__linksURLModified = false;
-
-  let links = yield fetchData();
-  do_check_eq(links.length, 4);
-
-  // The only remaining images should be https with mozilla.net or data URI
-  do_check_eq(links[0].url, data["directory"][1].url);
-  do_check_eq(links[1].url, data["directory"][2].url);
-  do_check_eq(links[2].url, data["directory"][3].url);
-  do_check_eq(links[3].url, data["directory"][4].url);
-});
-
-add_task(function test_RemoteDirectoryLinksProvider_getAllowedEnhancedImages() {
-  let data = {"directory": [
-    {url: "http://example.com", enhancedImageURI: "ftp://example.com"},
-    {url: "http://example.com", enhancedImageURI: "http://example.net"},
-    {url: "http://example.com", enhancedImageURI: "javascript:5"},
-    {url: "http://example.com", enhancedImageURI: "https://example.com"},
-    {url: "http://example.com", enhancedImageURI: "httpJUNKjavascript:42"},
-    {url: "http://example.com", enhancedImageURI: "data:text/plain,hi"},
-    {url: "http://example.com", enhancedImageURI: "http/bork:eh"},
-  ]};
-  let dataURI = 'data:application/json,' + JSON.stringify(data);
-  yield promiseSetupRemoteDirectoryLinksProvider({linksURL: dataURI});
-
-  let links = yield fetchData();
-  do_check_eq(links.length, 2);
-
-  // The only remaining enhancedImages should be http and https and data
-  do_check_eq(links[0].enhancedImageURI, data["directory"][3].enhancedImageURI);
-  do_check_eq(links[1].enhancedImageURI, data["directory"][5].enhancedImageURI);
-});
-
-add_task(function test_RemoteDirectoryLinksProvider_getEnhancedLink() {
-  let data = {"enhanced": [
-    {url: "http://example.net", enhancedImageURI: "data:,net1"},
-    {url: "http://example.com", enhancedImageURI: "data:,com1"},
-    {url: "http://example.com", enhancedImageURI: "data:,com2"},
-  ]};
-  let dataURI = 'data:application/json,' + JSON.stringify(data);
-  yield promiseSetupRemoteDirectoryLinksProvider({linksURL: dataURI});
-
-  let links = yield fetchData();
-  do_check_eq(links.length, 0); // There are no directory links.
-
-  function checkEnhanced(url, image) {
-    let enhanced = RemoteDirectoryLinksProvider.getEnhancedLink({url: url});
-    do_check_eq(enhanced && enhanced.enhancedImageURI, image);
-  }
-
-  // Get the expected image for the same site
-  checkEnhanced("http://example.net/", "data:,net1");
-  checkEnhanced("http://example.net/path", "data:,net1");
-  checkEnhanced("https://www.example.net/", "data:,net1");
-  checkEnhanced("https://www3.example.net/", "data:,net1");
-
-  // Get the image of the last entry
-  checkEnhanced("http://example.com", "data:,com2");
-
-  // Get the inline enhanced image
-  let inline = RemoteDirectoryLinksProvider.getEnhancedLink({
-    url: "http://example.com/echo",
-    enhancedImageURI: "data:,echo",
-  });
-  do_check_eq(inline.enhancedImageURI, "data:,echo");
-  do_check_eq(inline.url, "http://example.com/echo");
-
-  // Undefined for not enhanced
-  checkEnhanced("http://sub.example.net/", undefined);
-  checkEnhanced("http://example.org", undefined);
-  checkEnhanced("http://localhost", undefined);
-  checkEnhanced("http://127.0.0.1", undefined);
-
-  // Make sure old data is not cached
-  data = {"enhanced": [
-    {url: "http://example.com", enhancedImageURI: "data:,fresh"},
-  ]};
-  dataURI = 'data:application/json,' + JSON.stringify(data);
-  yield promiseSetupRemoteDirectoryLinksProvider({linksURL: dataURI});
-  links = yield fetchData();
-  do_check_eq(links.length, 0); // There are no directory links.
-  checkEnhanced("http://example.net", undefined);
-  checkEnhanced("http://example.com", "data:,fresh");
-});
-
-add_task(function test_RemoteDirectoryLinksProvider_setDefaultEnhanced() {
-  function checkDefault(expected) {
-    Services.prefs.clearUserPref(kNewtabEnhancedPref);
-    do_check_eq(Services.prefs.getBoolPref(kNewtabEnhancedPref), expected);
-  }
-
-  // Use the default donottrack prefs (enabled = false)
-  Services.prefs.clearUserPref("privacy.donottrackheader.enabled");
-  checkDefault(true);
-
-  // Turn on DNT - no track
-  Services.prefs.setBoolPref("privacy.donottrackheader.enabled", true);
-  checkDefault(false);
-
-  // Turn off DNT header
-  Services.prefs.clearUserPref("privacy.donottrackheader.enabled");
-  checkDefault(true);
-
-  // Clean up
-  Services.prefs.clearUserPref("privacy.donottrackheader.value");
-});
-
-add_task(function test_timeSensetiveSuggestedTiles() {
-  // make tile json with start and end dates
-  let testStartTime = Date.now();
-  // start date is now + 1 seconds
-  let startDate = new Date(testStartTime + 1000);
-  // end date is now + 3 seconds
-  let endDate = new Date(testStartTime + 3000);
-  let suggestedTile = Object.assign({
-    time_limits: {
-      start: startDate.toISOString(),
-      end: endDate.toISOString(),
-    }
-  }, suggestedTile1);
-
-  // Initial setup
-  let topSites = ["site0.com", "1040.com", "site2.com", "hrblock.com", "site4.com", "freetaxusa.com", "site6.com"];
-  let data = {"suggested": [suggestedTile], "directory": [someOtherSite]};
-  let dataURI = 'data:application/json,' + JSON.stringify(data);
-
-  let testObserver = new TestTimingRun();
-  RemoteDirectoryLinksProvider.addObserver(testObserver);
-
-  yield promiseSetupRemoteDirectoryLinksProvider({linksURL: dataURI});
-  let links = yield fetchData();
-
-  let origIsTopPlacesSite = RemoteNewTabUtils.isTopPlacesSite;
-  RemoteNewTabUtils.isTopPlacesSite = function(site) {
-    return topSites.indexOf(site) >= 0;
-  }
-
-  let origGetProviderLinks = RemoteNewTabUtils.getProviderLinks;
-  RemoteNewTabUtils.getProviderLinks = function(provider) {
-    return links;
-  }
-
-  let origCurrentTopSiteCount = RemoteDirectoryLinksProvider._getCurrentTopSiteCount;
-  RemoteDirectoryLinksProvider._getCurrentTopSiteCount = () => 8;
-
-  do_check_eq(RemoteDirectoryLinksProvider._updateSuggestedTile(), undefined);
-
-  // this tester will fire twice: when start limit is reached and when tile link
-  // is removed upon end of the campaign, in which case deleteFlag will be set
-  function TestTimingRun() {
-    this.promise = new Promise(resolve => {
-      this.onLinkChanged = (directoryLinksProvider, link, ignoreFlag, deleteFlag) => {
-        // if we are not deleting, add link to links, so we can catch it's removal
-        if (!deleteFlag) {
-          links.unshift(link);
-        }
-
-        isIdentical([...RemoteDirectoryLinksProvider._topSitesWithSuggestedLinks], ["hrblock.com", "1040.com"]);
-        do_check_eq(link.frecency, SUGGESTED_FRECENCY);
-        do_check_eq(link.type, "affiliate");
-        do_check_eq(link.url, suggestedTile.url);
-        let timeDelta = Date.now() - testStartTime;
-        if (!deleteFlag) {
-          // this is start timeout corresponding to campaign start
-          // a seconds must pass and targetedSite must be set
-          do_print("TESTING START timeDelta: " + timeDelta);
-          do_check_true(timeDelta >= 1000 / 2); // check for at least half time
-          do_check_eq(link.targetedSite, "hrblock.com");
-          do_check_true(RemoteDirectoryLinksProvider._campaignTimeoutID);
-        }
-        else {
-          // this is the campaign end timeout, so 3 seconds must pass
-          // and timeout should be cleared
-          do_print("TESTING END timeDelta: " + timeDelta);
-          do_check_true(timeDelta >= 3000 / 2); // check for at least half time
-          do_check_false(link.targetedSite);
-          do_check_false(RemoteDirectoryLinksProvider._campaignTimeoutID);
-          resolve();
-        }
-      };
-    });
-  }
-
-  // _updateSuggestedTile() is called when fetching directory links.
-  yield testObserver.promise;
-  RemoteDirectoryLinksProvider.removeObserver(testObserver);
-
-  // shoudl suggest nothing
-  do_check_eq(RemoteDirectoryLinksProvider._updateSuggestedTile(), undefined);
-
-  // set links back to contain directory tile only
-  links.shift();
-
-  // drop the end time - we should pick up the tile
-  suggestedTile.time_limits.end = null;
-  data = {"suggested": [suggestedTile], "directory": [someOtherSite]};
-  dataURI = 'data:application/json,' + JSON.stringify(data);
-
-  // redownload json and getLinks to force time recomputation
-  yield promiseDirectoryDownloadOnPrefChange(kSourceUrlPref, dataURI);
-
-  // ensure that there's a link returned by _updateSuggestedTile and no timeout
-  let deferred = Promise.defer();
-  RemoteDirectoryLinksProvider.getLinks(() => {
-    let link = RemoteDirectoryLinksProvider._updateSuggestedTile();
-    // we should have a suggested tile and no timeout
-    do_check_eq(link.type, "affiliate");
-    do_check_eq(link.url, suggestedTile.url);
-    do_check_false(RemoteDirectoryLinksProvider._campaignTimeoutID);
-    deferred.resolve();
-  });
-  yield deferred.promise;
-
-  // repeat the test for end time only
-  suggestedTile.time_limits.start = null;
-  suggestedTile.time_limits.end = (new Date(Date.now() + 3000)).toISOString();
-
-  data = {"suggested": [suggestedTile], "directory": [someOtherSite]};
-  dataURI = 'data:application/json,' + JSON.stringify(data);
-
-  // redownload json and call getLinks() to force time recomputation
-  yield promiseDirectoryDownloadOnPrefChange(kSourceUrlPref, dataURI);
-
-  // ensure that there's a link returned by _updateSuggestedTile and timeout set
-  deferred = Promise.defer();
-  RemoteDirectoryLinksProvider.getLinks(() => {
-    let link = RemoteDirectoryLinksProvider._updateSuggestedTile();
-    // we should have a suggested tile and timeout set
-    do_check_eq(link.type, "affiliate");
-    do_check_eq(link.url, suggestedTile.url);
-    do_check_true(RemoteDirectoryLinksProvider._campaignTimeoutID);
-    RemoteDirectoryLinksProvider._clearCampaignTimeout();
-    deferred.resolve();
-  });
-  yield deferred.promise;
-
-  // Cleanup
-  yield promiseCleanRemoteDirectoryLinksProvider();
-  RemoteNewTabUtils.isTopPlacesSite = origIsTopPlacesSite;
-  RemoteNewTabUtils.getProviderLinks = origGetProviderLinks;
-  RemoteDirectoryLinksProvider._getCurrentTopSiteCount = origCurrentTopSiteCount;
-});
-
-add_task(function test_setupStartEndTime() {
-  let currentTime = Date.now();
-  let dt = new Date(currentTime);
-  let link = {
-    time_limits: {
-      start: dt.toISOString()
-    }
-  };
-
-  // test ISO translation
-  RemoteDirectoryLinksProvider._setupStartEndTime(link);
-  do_check_eq(link.startTime, currentTime);
-
-  // test localtime translation
-  let shiftedDate = new Date(currentTime - dt.getTimezoneOffset()*60*1000);
-  link.time_limits.start = shiftedDate.toISOString().replace(/Z$/, "");
-
-  RemoteDirectoryLinksProvider._setupStartEndTime(link);
-  do_check_eq(link.startTime, currentTime);
-
-  // throw some garbage into date string
-  delete link.startTime;
-  link.time_limits.start = "no date"
-  RemoteDirectoryLinksProvider._setupStartEndTime(link);
-  do_check_false(link.startTime);
-
-  link.time_limits.start = "2015-99999-01T00:00:00"
-  RemoteDirectoryLinksProvider._setupStartEndTime(link);
-  do_check_false(link.startTime);
-
-  link.time_limits.start = "20150501T00:00:00"
-  RemoteDirectoryLinksProvider._setupStartEndTime(link);
-  do_check_false(link.startTime);
-});
-
-add_task(function test_RemoteDirectoryLinksProvider_frequencyCapSetup() {
-  yield promiseSetupRemoteDirectoryLinksProvider();
-  yield RemoteDirectoryLinksProvider.init();
-
-  yield promiseCleanRemoteDirectoryLinksProvider();
-  yield RemoteDirectoryLinksProvider._readFrequencyCapFile();
-  isIdentical(RemoteDirectoryLinksProvider._frequencyCaps, {});
-
-  // setup few links
-  RemoteDirectoryLinksProvider._updateFrequencyCapSettings({
-      url: "1",
-  });
-  RemoteDirectoryLinksProvider._updateFrequencyCapSettings({
-      url: "2",
-      frequency_caps: {daily: 1, total: 2}
-  });
-  RemoteDirectoryLinksProvider._updateFrequencyCapSettings({
-      url: "3",
-      frequency_caps: {total: 2}
-  });
-  RemoteDirectoryLinksProvider._updateFrequencyCapSettings({
-      url: "4",
-      frequency_caps: {daily: 1}
-  });
-  let freqCapsObject = RemoteDirectoryLinksProvider._frequencyCaps;
-  let capObject = freqCapsObject["1"];
-  let defaultDaily = capObject.dailyCap;
-  let defaultTotal = capObject.totalCap;
-  // check if we have defaults set
-  do_check_true(capObject.dailyCap > 0);
-  do_check_true(capObject.totalCap > 0);
-  // check if defaults are properly handled
-  do_check_eq(freqCapsObject["2"].dailyCap, 1);
-  do_check_eq(freqCapsObject["2"].totalCap, 2);
-  do_check_eq(freqCapsObject["3"].dailyCap, defaultDaily);
-  do_check_eq(freqCapsObject["3"].totalCap, 2);
-  do_check_eq(freqCapsObject["4"].dailyCap, 1);
-  do_check_eq(freqCapsObject["4"].totalCap, defaultTotal);
-
-  // write object to file
-  yield RemoteDirectoryLinksProvider._writeFrequencyCapFile();
-  // empty out freqCapsObject and read file back
-  RemoteDirectoryLinksProvider._frequencyCaps = {};
-  yield RemoteDirectoryLinksProvider._readFrequencyCapFile();
-  // re-ran tests - they should all pass
-  do_check_eq(freqCapsObject["2"].dailyCap, 1);
-  do_check_eq(freqCapsObject["2"].totalCap, 2);
-  do_check_eq(freqCapsObject["3"].dailyCap, defaultDaily);
-  do_check_eq(freqCapsObject["3"].totalCap, 2);
-  do_check_eq(freqCapsObject["4"].dailyCap, 1);
-  do_check_eq(freqCapsObject["4"].totalCap, defaultTotal);
-
-  // wait a second and prune frequency caps
-  yield new Promise(resolve => {
-    setTimeout(resolve, 1100);
-  });
-
-  // update one link and create another
-  RemoteDirectoryLinksProvider._updateFrequencyCapSettings({
-      url: "3",
-      frequency_caps: {daily: 1, total: 2}
-  });
-  RemoteDirectoryLinksProvider._updateFrequencyCapSettings({
-      url: "7",
-      frequency_caps: {daily: 1, total: 2}
-  });
-  // now prune the ones that have been in the object longer than 1 second
-  RemoteDirectoryLinksProvider._pruneFrequencyCapUrls(1000);
-  // make sure all keys but "3" and "7" are deleted
-  Object.keys(RemoteDirectoryLinksProvider._frequencyCaps).forEach(key => {
-    do_check_true(key == "3" || key == "7");
-  });
-
-  yield promiseCleanRemoteDirectoryLinksProvider();
-});
-
-add_task(function test_RemoteDirectoryLinksProvider_getFrequencyCapLogic() {
-  yield promiseSetupRemoteDirectoryLinksProvider();
-  yield RemoteDirectoryLinksProvider.init();
-
-  // setup suggested links
-  RemoteDirectoryLinksProvider._updateFrequencyCapSettings({
-    url: "1",
-    frequency_caps: {daily: 2, total: 4}
-  });
-
-  do_check_true(RemoteDirectoryLinksProvider._testFrequencyCapLimits("1"));
-  // exhaust daily views
-  RemoteDirectoryLinksProvider._addFrequencyCapView("1")
-  do_check_true(RemoteDirectoryLinksProvider._testFrequencyCapLimits("1"));
-  RemoteDirectoryLinksProvider._addFrequencyCapView("1")
-  do_check_false(RemoteDirectoryLinksProvider._testFrequencyCapLimits("1"));
-
-  // now step into the furture
-  let _wasTodayOrig = RemoteDirectoryLinksProvider._wasToday;
-  RemoteDirectoryLinksProvider._wasToday = function () {return false;}
-  // exhaust total views
-  RemoteDirectoryLinksProvider._addFrequencyCapView("1")
-  do_check_true(RemoteDirectoryLinksProvider._testFrequencyCapLimits("1"));
-  RemoteDirectoryLinksProvider._addFrequencyCapView("1")
-  // reached totalViews 4, should return false
-  do_check_false(RemoteDirectoryLinksProvider._testFrequencyCapLimits("1"));
-
-  // add more views by updating configuration
-  RemoteDirectoryLinksProvider._updateFrequencyCapSettings({
-    url: "1",
-    frequency_caps: {daily: 5, total: 10}
-  });
-  // should be true, since we have more total views
-  do_check_true(RemoteDirectoryLinksProvider._testFrequencyCapLimits("1"));
-
-  // set click flag
-  RemoteDirectoryLinksProvider._setFrequencyCapClick("1");
-  // always false after click
-  do_check_false(RemoteDirectoryLinksProvider._testFrequencyCapLimits("1"));
-
-  // use unknown urls and ensure nothing breaks
-  RemoteDirectoryLinksProvider._addFrequencyCapView("nosuch.url");
-  RemoteDirectoryLinksProvider._setFrequencyCapClick("nosuch.url");
-  // testing unknown url should always return false
-  do_check_false(RemoteDirectoryLinksProvider._testFrequencyCapLimits("nosuch.url"));
-
-  // reset _wasToday back to original function
-  RemoteDirectoryLinksProvider._wasToday = _wasTodayOrig;
-  yield promiseCleanRemoteDirectoryLinksProvider();
-});
-
-add_task(function test_RemoteDirectoryLinksProvider_ClickRemoval() {
-  yield promiseSetupRemoteDirectoryLinksProvider();
-  yield RemoteDirectoryLinksProvider.init();
-  let landingUrl = "http://foo.com";
-
-  // setup suggested links
-  RemoteDirectoryLinksProvider._updateFrequencyCapSettings({
-    url: landingUrl,
-    frequency_caps: {daily: 2, total: 4}
-  });
-
-  // add views
-  RemoteDirectoryLinksProvider._addFrequencyCapView(landingUrl)
-  RemoteDirectoryLinksProvider._addFrequencyCapView(landingUrl)
-  // make a click
-  RemoteDirectoryLinksProvider._setFrequencyCapClick(landingUrl);
-
-  // views must be 2 and click must be set
-  do_check_eq(RemoteDirectoryLinksProvider._frequencyCaps[landingUrl].totalViews, 2);
-  do_check_true(RemoteDirectoryLinksProvider._frequencyCaps[landingUrl].clicked);
-
-  // now insert a visit into places
-  yield new Promise(resolve => {
-    PlacesUtils.asyncHistory.updatePlaces(
-      {
-        uri: NetUtil.newURI(landingUrl),
-        title: "HELLO",
-        visits: [{
-          visitDate: Date.now()*1000,
-          transitionType: Ci.nsINavHistoryService.TRANSITION_LINK
-        }]
-      },
-      {
-        handleError: function () {do_check_true(false);},
-        handleResult: function () {},
-        handleCompletion: function () {resolve();}
-      }
-    );
-  });
-
-  function UrlDeletionTester() {
-    this.promise = new Promise(resolve => {
-      this.onDeleteURI = (directoryLinksProvider, link) => {
-        resolve();
-      };
-      this.onClearHistory = (directoryLinksProvider) => {
-        resolve();
-      };
-    });
-  };
-
-  let testObserver = new UrlDeletionTester();
-  RemoteDirectoryLinksProvider.addObserver(testObserver);
-
-  PlacesUtils.bhistory.removePage(NetUtil.newURI(landingUrl));
-  yield testObserver.promise;
-  RemoteDirectoryLinksProvider.removeObserver(testObserver);
-  // views must be 2 and click should not exist
-  do_check_eq(RemoteDirectoryLinksProvider._frequencyCaps[landingUrl].totalViews, 2);
-  do_check_false(RemoteDirectoryLinksProvider._frequencyCaps[landingUrl].hasOwnProperty("clicked"));
-
-  // verify that disk written data is kosher
-  let data = yield readJsonFile(RemoteDirectoryLinksProvider._frequencyCapFilePath);
-  do_check_eq(data[landingUrl].totalViews, 2);
-  do_check_false(data[landingUrl].hasOwnProperty("clicked"));
-
-  // now test clear history
-  RemoteDirectoryLinksProvider._updateFrequencyCapSettings({
-    url: landingUrl,
-    frequency_caps: {daily: 2, total: 4}
-  });
-  RemoteDirectoryLinksProvider._updateFrequencyCapSettings({
-    url: "http://bar.com",
-    frequency_caps: {daily: 2, total: 4}
-  });
-
-  RemoteDirectoryLinksProvider._setFrequencyCapClick(landingUrl);
-  RemoteDirectoryLinksProvider._setFrequencyCapClick("http://bar.com");
-  // both tiles must have clicked
-  do_check_true(RemoteDirectoryLinksProvider._frequencyCaps[landingUrl].clicked);
-  do_check_true(RemoteDirectoryLinksProvider._frequencyCaps["http://bar.com"].clicked);
-
-  testObserver = new UrlDeletionTester();
-  RemoteDirectoryLinksProvider.addObserver(testObserver);
-  yield PlacesTestUtils.clearHistory();
-
-  yield testObserver.promise;
-  RemoteDirectoryLinksProvider.removeObserver(testObserver);
-  // no clicks should remain in the cap object
-  do_check_false(RemoteDirectoryLinksProvider._frequencyCaps[landingUrl].hasOwnProperty("clicked"));
-  do_check_false(RemoteDirectoryLinksProvider._frequencyCaps["http://bar.com"].hasOwnProperty("clicked"));
-
-  // verify that disk written data is kosher
-  data = yield readJsonFile(RemoteDirectoryLinksProvider._frequencyCapFilePath);
-  do_check_false(data[landingUrl].hasOwnProperty("clicked"));
-  do_check_false(data["http://bar.com"].hasOwnProperty("clicked"));
-
-  yield promiseCleanRemoteDirectoryLinksProvider();
-});
-
-add_task(function test_RemoteDirectoryLinksProvider_anonymous() {
-  do_check_true(RemoteDirectoryLinksProvider._newXHR().mozAnon);
-});
-
-add_task(function test_sanitizeExplanation() {
-  // Note: this is a basic test to ensure we applied sanitization to the link explanation.
-  // Full testing for appropriate sanitization is done in parser/xml/test/unit/test_sanitizer.js.
-  let data = {"suggested": [suggestedTile5]};
-  let dataURI = 'data:application/json,' + encodeURIComponent(JSON.stringify(data));
-
-  yield promiseSetupRemoteDirectoryLinksProvider({linksURL: dataURI});
-  let links = yield fetchData();
-
-  let suggestedSites = [...RemoteDirectoryLinksProvider._suggestedLinks.keys()];
-  do_check_eq(suggestedSites.indexOf("eviltarget.com"), 0);
-  do_check_eq(suggestedSites.length, 1);
-
-  let suggestedLink = [...RemoteDirectoryLinksProvider._suggestedLinks.get(suggestedSites[0]).values()][0];
-  do_check_eq(suggestedLink.explanation, "This is an evil tile X muhahaha");
-  do_check_eq(suggestedLink.targetedName, "WE ARE EVIL ");
-});
--- a/browser/components/newtab/tests/xpcshell/xpcshell.ini
+++ b/browser/components/newtab/tests/xpcshell/xpcshell.ini
@@ -2,11 +2,10 @@
 head =
 tail =
 firefox-appdir = browser
 skip-if = toolkit == 'android' || toolkit == 'gonk'
 
 [test_AboutNewTabService.js]
 [test_NewTabURL.js]
 [test_PlacesProvider.js]
-[test_RemoteDirectoryLinksProvider.js]
 [test_RemoteNewTabLocation.js]
 [test_RemoteNewTabUtils.js]
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -24,19 +24,16 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource:///modules/DirectoryLinksProvider.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "NewTabUtils",
                                   "resource://gre/modules/NewTabUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "RemoteAboutNewTab",
                                   "resource:///modules/RemoteAboutNewTab.jsm");
 
-XPCOMUtils.defineLazyModuleGetter(this, "RemoteDirectoryLinksProvider",
-                                  "resource:///modules/RemoteDirectoryLinksProvider.jsm");
-
 XPCOMUtils.defineLazyModuleGetter(this, "RemoteNewTabUtils",
                                   "resource:///modules/RemoteNewTabUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "UITour",
                                   "resource:///modules/UITour.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
                                   "resource://gre/modules/AddonManager.jsm");
@@ -838,26 +835,25 @@ BrowserGlue.prototype = {
 #ifdef NIGHTLY_BUILD
     if (Services.prefs.getBoolPref("dom.identity.enabled")) {
       SignInToWebsiteUX.init();
     }
 #endif
     webrtcUI.init();
     AboutHome.init();
 
-    RemoteDirectoryLinksProvider.init();
-    RemoteNewTabUtils.init();
-    RemoteNewTabUtils.links.addProvider(RemoteDirectoryLinksProvider);
-    RemoteAboutNewTab.init();
-
     DirectoryLinksProvider.init();
     NewTabUtils.init();
     NewTabUtils.links.addProvider(DirectoryLinksProvider);
     AboutNewTab.init();
 
+    RemoteNewTabUtils.init();
+    RemoteNewTabUtils.links.addProvider(DirectoryLinksProvider);
+    RemoteAboutNewTab.init();
+
     SessionStore.init();
     BrowserUITelemetry.init();
     ContentSearch.init();
     FormValidationHandler.init();
 
     ContentClick.init();
     RemotePrompt.init();
     Feeds.init();
--- a/browser/config/tooltool-manifests/macosx64/releng.manifest
+++ b/browser/config/tooltool-manifests/macosx64/releng.manifest
@@ -12,15 +12,15 @@
 {
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "algorithm": "sha512",
 "filename": "sccache.tar.bz2",
 "unpack": true
 },
 {
-"size": 86474070,
-"digest": "85d528ba396d35e38280d01586d91c082a57846fb82c471579632995565caa54320b083581b04d96e526efa6cef53ea076a32c25f220f7f32f651a4dfdf30e31",
 "algorithm": "sha512",
-"filename": "rustc.tar.gz",
-"unpack": true
+"filename": "rustc.tar.bz2",
+"unpack": true,
+"digest": "28e0d27846cfa6fac5be2df4debb469b6f488d45357d21f2bec14c7c2b6abe5713965497961e7ac10120d3687751ccced95b9498cc54c529cbdfcd59d51a67ec",
+"size": 84116661
 }
 ]
--- a/browser/confvars.sh
+++ b/browser/confvars.sh
@@ -58,14 +58,15 @@ MOZ_WEBAPP_RUNTIME=1
 MOZ_MEDIA_NAVIGATOR=1
 MOZ_WEBGL_CONFORMANT=1
 # Enable navigator.mozPay
 MOZ_PAY=1
 # Enable activities. These are used for FxOS developers currently.
 MOZ_ACTIVITIES=1
 MOZ_JSDOWNLOADS=1
 MOZ_WEBM_ENCODER=1
+MOZ_RUST_MP4PARSE=1
 
 # Enable checking that add-ons are signed by the trusted root
 MOZ_ADDON_SIGNING=1
 
 # Include the DevTools client, not just the server (which is the default)
 MOZ_DEVTOOLS=all
--- a/browser/modules/DirectoryLinksProvider.jsm
+++ b/browser/modules/DirectoryLinksProvider.jsm
@@ -26,16 +26,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
   "resource://gre/modules/osfile.jsm")
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
   "resource://gre/modules/Promise.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
   "resource://gre/modules/UpdateUtils.jsm");
 XPCOMUtils.defineLazyServiceGetter(this, "eTLD",
   "@mozilla.org/network/effective-tld-service;1",
   "nsIEffectiveTLDService");
+XPCOMUtils.defineLazyModuleGetter(this, "RemoteNewTabUtils",
+  "resource:///modules/RemoteNewTabUtils.jsm");
 XPCOMUtils.defineLazyGetter(this, "gTextDecoder", () => {
   return new TextDecoder();
 });
 XPCOMUtils.defineLazyGetter(this, "gCryptoHash", function () {
   return Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash);
 });
 XPCOMUtils.defineLazyGetter(this, "gUnicodeConverter", function () {
   let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
@@ -752,16 +754,18 @@ var DirectoryLinksProvider = {
 
     // setup frequency cap file path
     this._frequencyCapFilePath = OS.Path.join(OS.Constants.Path.localProfileDir, FREQUENCY_CAP_FILE);
     // setup inadjacent sites URL
     this._inadjacentSitesUrl = INADJACENCY_SOURCE;
 
     NewTabUtils.placesProvider.addObserver(this);
     NewTabUtils.links.addObserver(this);
+    RemoteNewTabUtils.placesProvider.addObserver(this);
+    RemoteNewTabUtils.links.addObserver(this);
 
     return Task.spawn(function() {
       // get the last modified time of the links file if it exists
       let doesFileExists = yield OS.File.exists(this._directoryFilePath);
       if (doesFileExists) {
         let fileInfo = yield OS.File.stat(this._directoryFilePath);
         this._lastDownloadMS = Date.parse(fileInfo.lastModificationDate);
       }
--- a/caps/nsIPrincipal.idl
+++ b/caps/nsIPrincipal.idl
@@ -55,18 +55,20 @@ interface nsIPrincipal : nsISerializable
     /**
      * The codebase URI to which this principal pertains.  This is
      * generally the document URI.
      */
     readonly attribute nsIURI URI;
 
     /**
      * The domain URI to which this principal pertains.
-     * This is congruent with HTMLDocument.domain, and may be null.
+     * This is null unless script successfully sets document.domain to our URI
+     * or a superdomain of our URI.
      * Setting this has no effect on the URI.
+     * See https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy#Changing_origin
      */
     [noscript] attribute nsIURI domain;
 
     /**
      * Returns whether the other principal is equal to or weaker than this
      * principal. Principals are equal if they are the same object or they
      * have the same origin.
      *
--- a/configure.in
+++ b/configure.in
@@ -4025,16 +4025,23 @@ fi
 if test -n "$MOZ_MULET"; then
     AC_DEFINE(MOZ_MULET)
 fi
 
 if test -n "$MOZ_B2GDROID"; then
     AC_DEFINE(MOZ_B2GDROID)
 fi
 
+# Propagate feature switches for code written in rust from confvars.sh
+if test -n "$MOZ_RUST"; then
+    if test -n "$MOZ_RUST_MP4PARSE"; then
+        AC_DEFINE(MOZ_RUST_MP4PARSE)
+    fi
+fi
+
 AC_SUBST(MOZ_BUILD_APP)
 AC_SUBST(MOZ_PHOENIX)
 AC_SUBST(MOZ_XULRUNNER)
 AC_SUBST(MOZ_B2G)
 AC_SUBST(MOZ_MULET)
 AC_SUBST(MOZ_B2G_VERSION)
 AC_SUBST(MOZ_B2GDROID)
 
@@ -7042,28 +7049,20 @@ elif test "$GNU_CC"; then
         MOZ_C_SUPPORTS_WARNING(-W, no-error=coverage-mismatch, ac_c_has_noerror_coverage_mismatch)
         MOZ_CXX_SUPPORTS_WARNING(-W, no-error=coverage-mismatch, ac_cxx_has_noerror_coverage_mismatch)
         MOZ_C_SUPPORTS_WARNING(-W, no-error=free-nonheap-object, ac_c_has_noerror_free_nonheap_object)
         MOZ_CXX_SUPPORTS_WARNING(-W, no-error=free-nonheap-object, ac_cxx_has_noerror_free_nonheap_object)
     fi
 fi
 
 dnl ========================================================
-dnl = Disable runtime logging checks
-dnl ========================================================
-MOZ_ARG_DISABLE_BOOL(logging,
-[  --disable-logging       Disable logging facilities],
-    NS_DISABLE_LOGGING=1,
-    NS_DISABLE_LOGGING= )
-if test "$NS_DISABLE_LOGGING"; then
-    AC_DEFINE(NS_DISABLE_LOGGING)
-else
-    AC_DEFINE(MOZ_LOGGING)
-    AC_DEFINE(FORCE_PR_LOG)
-fi
+dnl = Enable runtime logging
+dnl ========================================================
+AC_DEFINE(MOZ_LOGGING)
+AC_DEFINE(FORCE_PR_LOG)
 
 dnl ========================================================
 dnl = This will enable logging of addref, release, ctor, dtor.
 dnl ========================================================
 _ENABLE_LOGREFCNT=42
 MOZ_ARG_ENABLE_BOOL(logrefcnt,
 [  --enable-logrefcnt      Enable logging of refcounts (default=debug) ],
     _ENABLE_LOGREFCNT=1,
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -18,32 +18,29 @@ class nsIPresShell;
  */
 
 [ptr] native nsPresContext(nsPresContext);
 [ptr] native nsIPresShell(nsIPresShell);
 
 interface nsIURI;
 interface nsIChannel;
 interface nsIContentViewer;
-interface nsIURIContentListener;
 interface nsIDOMEventTarget;
 interface nsIDocShellLoadInfo;
 interface nsIEditor;
-interface nsIWebNavigation;
 interface nsISimpleEnumerator;
 interface nsIInputStream;
 interface nsIRequest;
 interface nsISHEntry;
 interface nsILayoutHistoryState;
 interface nsISecureBrowserUI;
 interface nsIScriptGlobalObject;
 interface nsIDOMStorage;
 interface nsIPrincipal;
 interface nsIWebBrowserPrint;
-interface nsIVariant;
 interface nsIPrivacyTransitionObserver;
 interface nsIReflowObserver;
 interface nsIScrollObserver;
 interface nsITabParent;
 
 typedef unsigned long nsLoadFlags;
 
 [scriptable, builtinclass, uuid(539bce70-8261-462f-bbd0-40ee90b7d636)]
--- a/dom/base/ConsoleReportCollector.cpp
+++ b/dom/base/ConsoleReportCollector.cpp
@@ -74,13 +74,34 @@ ConsoleReportCollector::FlushConsoleRepo
                                     aDocument, report.mPropertiesFile,
                                     report.mMessageName.get(),
                                     params.get(),
                                     paramsLength, uri, EmptyString(),
                                     report.mLineNumber, report.mColumnNumber);
   }
 }
 
+void
+ConsoleReportCollector::FlushConsoleReports(nsIConsoleReportCollector* aCollector)
+{
+  MOZ_ASSERT(aCollector);
+
+  nsTArray<PendingReport> reports;
+
+  {
+    MutexAutoLock lock(mMutex);
+    mPendingReports.SwapElements(reports);
+  }
+
+  for (uint32_t i = 0; i < reports.Length(); ++i) {
+    PendingReport& report = reports[i];
+    aCollector->AddConsoleReport(report.mErrorFlags, report.mCategory,
+                                 report.mPropertiesFile, report.mSourceFileURI,
+                                 report.mLineNumber, report.mColumnNumber,
+                                 report.mMessageName, report.mStringParams);
+  }
+}
+
 ConsoleReportCollector::~ConsoleReportCollector()
 {
 }
 
 } // namespace mozilla
--- a/dom/base/ConsoleReportCollector.h
+++ b/dom/base/ConsoleReportCollector.h
@@ -24,16 +24,19 @@ public:
                    const nsACString& aSourceFileURI,
                    uint32_t aLineNumber, uint32_t aColumnNumber,
                    const nsACString& aMessageName,
                    const nsTArray<nsString>& aStringParams) override;
 
   void
   FlushConsoleReports(nsIDocument* aDocument) override;
 
+  void
+  FlushConsoleReports(nsIConsoleReportCollector* aCollector) override;
+
 private:
   ~ConsoleReportCollector();
 
   struct PendingReport
   {
     PendingReport(uint32_t aErrorFlags, const nsACString& aCategory,
                nsContentUtils::PropertiesFile aPropertiesFile,
                const nsACString& aSourceFileURI, uint32_t aLineNumber,
--- a/dom/base/nsDOMDataChannel.cpp
+++ b/dom/base/nsDOMDataChannel.cpp
@@ -4,36 +4,35 @@
  * 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 "nsDOMDataChannel.h"
 
 #include "base/basictypes.h"
 #include "mozilla/Logging.h"
 
-extern PRLogModuleInfo* GetDataChannelLog();
-#undef LOG
-#define LOG(args) MOZ_LOG(GetDataChannelLog(), mozilla::LogLevel::Debug, args)
-
-
 #include "nsDOMDataChannelDeclarations.h"
 #include "nsDOMDataChannel.h"
 #include "nsIDOMDataChannel.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/MessageEvent.h"
 #include "mozilla/dom/ScriptSettings.h"
 
 #include "nsError.h"
 #include "nsAutoPtr.h"
 #include "nsContentUtils.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIScriptObjectPrincipal.h"
 
 #include "DataChannel.h"
+#include "DataChannelLog.h"
+
+#undef LOG
+#define LOG(args) MOZ_LOG(mozilla::gDataChannelLog, mozilla::LogLevel::Debug, args)
 
 // Since we've moved the windows.h include down here, we have to explicitly
 // undef GetBinaryType, otherwise we'll get really odd conflicts
 #ifdef GetBinaryType
 #undef GetBinaryType
 #endif
 
 using namespace mozilla;
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -2467,23 +2467,20 @@ nsGlobalWindow::SetNewDocument(nsIDocume
 
   // Sometimes, WouldReuseInnerWindow() returns true even if there's no inner
   // window (see bug 776497). Be safe.
   bool reUseInnerWindow = (aForceReuseInnerWindow || wouldReuseInnerWindow) &&
                           GetCurrentInnerWindowInternal();
 
   nsresult rv = NS_OK;
 
-  // Set mDoc even if this is an outer window to avoid
+  // We set mDoc even though this is an outer window to avoid
   // having to *always* reach into the inner window to find the
   // document.
   mDoc = aDocument;
-  if (IsInnerWindow()) {
-    ClearDocumentDependentSlots(cx);
-  }
 
   // Take this opportunity to clear mSuspendedDoc. Our old inner window is now
   // responsible for unsuspending it.
   mSuspendedDoc = nullptr;
 
 #ifdef DEBUG
   mLastOpenedURI = aDocument->GetDocumentURI();
 #endif
--- a/dom/base/nsIConsoleReportCollector.h
+++ b/dom/base/nsIConsoleReportCollector.h
@@ -61,21 +61,29 @@ public:
                    Params... aParams)
   {
     nsTArray<nsString> params;
     mozilla::dom::StringArrayAppender::Append(params, sizeof...(Params), aParams...);
     AddConsoleReport(aErrorFlags, aCategory, aPropertiesFile, aSourceFileURI,
                      aLineNumber, aColumnNumber, aMessageName, params);
   }
 
-  // Flush all pending reports to the console.
+  // Flush all pending reports to the console.  Main thread only.
   //
   // aDocument      An optional document representing where to flush the
   //                reports.  If provided, then the corresponding window's
   //                web console will get the reports.  Otherwise the reports
   //                go to the browser console.
   virtual void
   FlushConsoleReports(nsIDocument* aDocument) = 0;
+
+  // Flush all pending reports to another collector.  May be called from any
+  // thread.
+  //
+  // aCollector     A required collector object that will effectively take
+  //                ownership of our currently console reports.
+  virtual void
+  FlushConsoleReports(nsIConsoleReportCollector* aCollector) = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIConsoleReportCollector, NS_NSICONSOLEREPORTCOLLECTOR_IID)
 
 #endif // nsIConsoleReportCollector_h
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -245,32 +245,38 @@ public:
    * @param aBoundTitleElement true if an HTML or SVG <title> element
    * has just been bound to the document.
    */
   virtual void NotifyPossibleTitleChange(bool aBoundTitleElement) = 0;
 
   /**
    * Return the URI for the document. May return null.
    *
-   * The value returned corresponds to the "document's current address" in
+   * The value returned corresponds to the "document's address" in
    * HTML5.  As such, it may change over the lifetime of the document, for
-   * instance as a result of a call to pushState() or replaceState().
+   * instance as a result of the user navigating to a fragment identifier on
+   * the page, or as a result to a call to pushState() or replaceState().
+   *
+   * https://html.spec.whatwg.org/multipage/dom.html#the-document%27s-address
    */
   nsIURI* GetDocumentURI() const
   {
     return mDocumentURI;
   }
 
   /**
    * Return the original URI of the document.  This is the same as the
-   * document's URI unless history.pushState() or replaceState() is invoked on
-   * the document.
+   * document's URI unless that has changed from its original value (for
+   * example, due to history.pushState() or replaceState() being invoked on the
+   * document).
    *
-   * This method corresponds to the "document's address" in HTML5 and, once
-   * set, doesn't change over the lifetime of the document.
+   * This method corresponds to the "creation URL" in HTML5 and, once set,
+   * doesn't change over the lifetime of the document.
+   *
+   * https://html.spec.whatwg.org/multipage/webappapis.html#creation-url
    */
   nsIURI* GetOriginalURI() const
   {
     return mOriginalURI;
   }
 
   /**
    * Set the URI for the document.  This also sets the document's original URI,
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -466,20 +466,28 @@ public:
    * @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;
 
   /**
-   * Return the "owner document" of this node.  Note that this is not the same
-   * as the DOM ownerDocument -- that's null for Document nodes, whereas for a
-   * nsIDocument GetOwnerDocument returns the document itself.  For nsIContent
-   * implementations the two are the same.
+   * Returns the "node document" of this node.
+   *
+   * https://dom.spec.whatwg.org/#concept-node-document
+   *
+   * Note that in the case that this node is a document node this method
+   * will return |this|.  That is different to the Node.ownerDocument DOM
+   * attribute (implemented by nsINode::GetOwnerDocument) which is specified to
+   * be null in that case:
+   *
+   * https://dom.spec.whatwg.org/#dom-node-ownerdocument
+   *
+   * For all other cases GetOwnerDoc and GetOwnerDocument behave identically.
    */
   nsIDocument *OwnerDoc() const
   {
     return mNodeInfo->GetDocument();
   }
 
   /**
    * Return the "owner document" of this node as an nsINode*.  Implemented
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -1393,16 +1393,19 @@ nsHTMLDocument::Open(JSContext* /* unuse
 }
 
 already_AddRefed<nsIDocument>
 nsHTMLDocument::Open(JSContext* cx,
                      const nsAString& aType,
                      const nsAString& aReplace,
                      ErrorResult& rv)
 {
+  // Implements the "When called with two arguments (or fewer)" steps here:
+  // https://html.spec.whatwg.org/multipage/webappapis.html#opening-the-input-stream
+
   NS_ASSERTION(nsContentUtils::CanCallerAccess(static_cast<nsIDOMHTMLDocument*>(this)),
                "XOW should have caught this!");
   if (!IsHTMLDocument() || mDisableDocWrite || !IsMasterDocument()) {
     // No calling document.open() on XHTML
     rv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
 
@@ -1533,17 +1536,17 @@ nsHTMLDocument::Open(JSContext* cx,
     // The Stop call may have cancelled the onload blocker request or prevented
     // it from getting added, so we need to make sure it gets added to the
     // document again otherwise the document could have a non-zero onload block
     // count without the onload blocker request being in the loadgroup.
     EnsureOnloadBlocker();
   }
 
   // The open occurred after the document finished loading.
-  // So we reset the document and create a new one.
+  // So we reset the document and then reinitialize it.
   nsCOMPtr<nsIChannel> channel;
   nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
   rv = NS_NewChannel(getter_AddRefs(channel),
                      uri,
                      callerDoc,
                      nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
                      nsIContentPolicy::TYPE_OTHER,
                      group);
@@ -1602,18 +1605,19 @@ nsHTMLDocument::Open(JSContext* cx,
     nsDocument* templateContentsOwner =
       static_cast<nsDocument*>(mTemplateContentsOwner.get());
 
     if (templateContentsOwner) {
       templateContentsOwner->mWillReparent = true;
     }
 #endif
 
-    // Should this pass true for aForceReuseInnerWindow?
-    rv = window->SetNewDocument(this, nullptr, false);
+    // Per spec, we pass false here so that a new Window is created.
+    rv = window->SetNewDocument(this, nullptr,
+                                /* aForceReuseInnerWindow */ false);
     if (rv.Failed()) {
       return nullptr;
     }
 
 #ifdef DEBUG
     if (templateContentsOwner) {
       templateContentsOwner->mWillReparent = willReparent;
     }
--- a/dom/media/AudioCompactor.h
+++ b/dom/media/AudioCompactor.h
@@ -36,33 +36,33 @@ public:
             uint32_t aFrames, uint32_t aChannels, CopyFunc aCopyFunc)
   {
     // If we are losing more than a reasonable amount to padding, try to chunk
     // the data.
     size_t maxSlop = AudioDataSize(aFrames, aChannels) / MAX_SLOP_DIVISOR;
 
     while (aFrames > 0) {
       uint32_t samples = GetChunkSamples(aFrames, aChannels, maxSlop);
-      nsAutoArrayPtr<AudioDataValue> buffer(new AudioDataValue[samples]);
+      auto buffer = MakeUnique<AudioDataValue[]>(samples);
 
       // Copy audio data to buffer using caller-provided functor.
-      uint32_t framesCopied = aCopyFunc(buffer, samples);
+      uint32_t framesCopied = aCopyFunc(buffer.get(), samples);
 
       NS_ASSERTION(framesCopied <= aFrames, "functor copied too many frames");
 
       CheckedInt64 duration = FramesToUsecs(framesCopied, aSampleRate);
       if (!duration.isValid()) {
         return false;
       }
 
       mQueue.Push(new AudioData(aOffset,
                                 aTime,
                                 duration.value(),
                                 framesCopied,
-                                buffer.forget(),
+                                Move(buffer),
                                 aChannels,
                                 aSampleRate));
 
       // Remove the frames we just pushed into the queue and loop if there is
       // more to be done.
       aTime += duration.value();
       aFrames -= framesCopied;
 
--- a/dom/media/MediaData.cpp
+++ b/dom/media/MediaData.cpp
@@ -41,40 +41,38 @@ AudioData::EnsureAudioBuffer()
       data[j*mFrames + i] = mAudioData[i*mChannels + j];
     }
   }
 }
 
 size_t
 AudioData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
-  size_t size = aMallocSizeOf(this) + aMallocSizeOf(mAudioData);
+  size_t size = aMallocSizeOf(this) + aMallocSizeOf(mAudioData.get());
   if (mAudioBuffer) {
     size += mAudioBuffer->SizeOfIncludingThis(aMallocSizeOf);
   }
   return size;
 }
 
 /* static */
 already_AddRefed<AudioData>
 AudioData::TransferAndUpdateTimestampAndDuration(AudioData* aOther,
                                                   int64_t aTimestamp,
                                                   int64_t aDuration)
 {
   NS_ENSURE_TRUE(aOther, nullptr);
   RefPtr<AudioData> v = new AudioData(aOther->mOffset,
-                                        aTimestamp,
-                                        aDuration,
-                                        aOther->mFrames,
-                                        aOther->mAudioData,
-                                        aOther->mChannels,
-                                        aOther->mRate);
+                                      aTimestamp,
+                                      aDuration,
+                                      aOther->mFrames,
+                                      Move(aOther->mAudioData),
+                                      aOther->mChannels,
+                                      aOther->mRate);
   v->mDiscontinuity = aOther->mDiscontinuity;
-  // Remove aOther's AudioData as it can't be shared across two targets.
-  aOther->mAudioData.forget();
 
   return v.forget();
 }
 
 static bool
 ValidatePlane(const VideoData::YCbCrBuffer::Plane& aPlane)
 {
   return aPlane.mWidth <= PlanarYCbCrImage::MAX_DIMENSION &&
--- a/dom/media/MediaData.h
+++ b/dom/media/MediaData.h
@@ -8,16 +8,17 @@
 
 #include "nsSize.h"
 #include "mozilla/gfx/Rect.h"
 #include "nsRect.h"
 #include "AudioSampleFormat.h"
 #include "nsIMemoryReporter.h"
 #include "SharedBuffer.h"
 #include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
 #include "nsTArray.h"
 
 namespace mozilla {
 
 namespace layers {
 class Image;
 class ImageContainer;
 } // namespace layers
@@ -118,23 +119,23 @@ protected:
 // Holds chunk a decoded audio frames.
 class AudioData : public MediaData {
 public:
 
   AudioData(int64_t aOffset,
             int64_t aTime,
             int64_t aDuration,
             uint32_t aFrames,
-            AudioDataValue* aData,
+            UniquePtr<AudioDataValue[]> aData,
             uint32_t aChannels,
             uint32_t aRate)
     : MediaData(sType, aOffset, aTime, aDuration, aFrames)
     , mChannels(aChannels)
     , mRate(aRate)
-    , mAudioData(aData) {}
+    , mAudioData(Move(aData)) {}
 
   static const Type sType = AUDIO_DATA;
   static const char* sTypeName;
 
   // Creates a new AudioData identical to aOther, but with a different
   // specified timestamp and duration. All data from aOther is copied
   // into the new AudioData but the audio data which is transferred.
   // After such call, the original aOther is unusable.
@@ -149,17 +150,17 @@ public:
   void EnsureAudioBuffer();
 
   const uint32_t mChannels;
   const uint32_t mRate;
   // At least one of mAudioBuffer/mAudioData must be non-null.
   // mChannels channels, each with mFrames frames
   RefPtr<SharedBuffer> mAudioBuffer;
   // mFrames frames, each with mChannels values
-  nsAutoArrayPtr<AudioDataValue> mAudioData;
+  UniquePtr<AudioDataValue[]> mAudioData;
 
 protected:
   ~AudioData() {}
 };
 
 namespace layers {
 class TextureClient;
 class PlanarYCbCrImage;
--- a/dom/media/MediaDecoderReader.h
+++ b/dom/media/MediaDecoderReader.h
@@ -243,17 +243,17 @@ public:
     OwnerThread()->Dispatch(
       r.forget(), AbstractThread::DontAssertDispatchSuccess);
   }
 
   void NotifyDataArrived(const media::Interval<int64_t>& aInfo)
   {
     MOZ_ASSERT(OnTaskQueue());
     NS_ENSURE_TRUE_VOID(!mShutdown);
-    NotifyDataArrivedInternal(aInfo.Length(), aInfo.mStart);
+    NotifyDataArrivedInternal();
     UpdateBuffered();
   }
 
   virtual MediaQueue<AudioData>& AudioQueue() { return mAudioQueue; }
   virtual MediaQueue<VideoData>& VideoQueue() { return mVideoQueue; }
 
   AbstractCanonical<media::TimeIntervals>* CanonicalBuffered()
   {
@@ -413,17 +413,17 @@ private:
   virtual nsresult ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
   {
     MOZ_CRASH();
   }
 
   // Recomputes mBuffered.
   virtual void UpdateBuffered();
 
-  virtual void NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset) {}
+  virtual void NotifyDataArrivedInternal() {}
 
   // Invokes NotifyDataArrived while throttling the calls to occur
   // at most every mThrottleDuration ms.
   void ThrottledNotifyDataArrived(const media::Interval<int64_t>& aInterval);
   void DoThrottledNotify();
 
   // Overrides of this function should decodes an unspecified amount of
   // audio data, enqueuing the audio data in mAudioQueue. Returns true
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -2419,16 +2419,19 @@ nsresult MediaDecoderStateMachine::RunSt
 
       if (mPlayState == MediaDecoder::PLAY_STATE_PLAYING &&
           !mSentPlaybackEndedEvent)
       {
         int64_t clockTime = std::max(AudioEndTime(), VideoEndTime());
         clockTime = std::max(int64_t(0), std::max(clockTime, Duration().ToMicroseconds()));
         UpdatePlaybackPosition(clockTime);
 
+        // Ensure readyState is updated before firing the 'ended' event.
+        UpdateNextFrameStatus();
+
         nsCOMPtr<nsIRunnable> event =
           NS_NewRunnableMethod(mDecoder, &MediaDecoder::PlaybackEnded);
         AbstractThread::MainThread()->Dispatch(event.forget());
 
         mSentPlaybackEndedEvent = true;
 
         // MediaSink::GetEndTime() must be called before stopping playback.
         StopMediaSink();
@@ -2648,31 +2651,31 @@ MediaDecoderStateMachine::DropAudioUpToS
   if (framesToPrune.value() > audio->mFrames) {
     // We've messed up somehow. Don't try to trim frames, the |frames|
     // variable below will overflow.
     DECODER_WARN("Can't prune more frames that we have!");
     return NS_ERROR_FAILURE;
   }
   uint32_t frames = audio->mFrames - static_cast<uint32_t>(framesToPrune.value());
   uint32_t channels = audio->mChannels;
-  nsAutoArrayPtr<AudioDataValue> audioData(new AudioDataValue[frames * channels]);
+  auto audioData = MakeUnique<AudioDataValue[]>(frames * channels);
   memcpy(audioData.get(),
          audio->mAudioData.get() + (framesToPrune.value() * channels),
          frames * channels * sizeof(AudioDataValue));
   CheckedInt64 duration = FramesToUsecs(frames, mInfo.mAudio.mRate);
   if (!duration.isValid()) {
     return NS_ERROR_FAILURE;
   }
   RefPtr<AudioData> data(new AudioData(audio->mOffset,
-                                         mCurrentSeek.mTarget.mTime,
-                                         duration.value(),
-                                         frames,
-                                         audioData.forget(),
-                                         channels,
-                                         audio->mRate));
+                                       mCurrentSeek.mTarget.mTime,
+                                       duration.value(),
+                                       frames,
+                                       Move(audioData),
+                                       channels,
+                                       audio->mRate));
   PushFront(data, MediaData::AUDIO_DATA);
 
   return NS_OK;
 }
 
 void MediaDecoderStateMachine::UpdateNextFrameStatus()
 {
   MOZ_ASSERT(OnTaskQueue());
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -1529,21 +1529,20 @@ void MediaFormatReader::ReleaseMediaReso
 
 bool
 MediaFormatReader::VideoIsHardwareAccelerated() const
 {
   return mVideo.mIsHardwareAccelerated;
 }
 
 void
-MediaFormatReader::NotifyDemuxer(uint32_t aLength, int64_t aOffset)
+MediaFormatReader::NotifyDemuxer()
 {
   MOZ_ASSERT(OnTaskQueue());
 
-  LOGV("aLength=%u, aOffset=%lld", aLength, aOffset);
   if (mShutdown || !mDemuxer ||
       (!mDemuxerInitDone && !mDemuxerInitRequest.Exists())) {
     return;
   }
 
   mDemuxer->NotifyDataArrived();
 
   if (!mInitDone) {
@@ -1555,22 +1554,20 @@ MediaFormatReader::NotifyDemuxer(uint32_
   }
   if (HasAudio()) {
     mAudio.mReceivedNewData = true;
     ScheduleUpdate(TrackType::kAudioTrack);
   }
 }
 
 void
-MediaFormatReader::NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset)
+MediaFormatReader::NotifyDataArrivedInternal()
 {
   MOZ_ASSERT(OnTaskQueue());
-  MOZ_ASSERT(aLength);
-
-  NotifyDemuxer(aLength, aOffset);
+  NotifyDemuxer();
 }
 
 bool
 MediaFormatReader::ForceZeroStartTime() const
 {
   return !mDemuxer->ShouldComputeStartTime();
 }
 
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -50,17 +50,17 @@ public:
   Seek(int64_t aTime, int64_t aUnused) override;
 
   bool IsMediaSeekable() override
   {
     return mSeekable;
   }
 
 protected:
-  void NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset) override;
+  void NotifyDataArrivedInternal() override;
 
 public:
   media::TimeIntervals GetBuffered() override;
 
   virtual bool ForceZeroStartTime() const override;
 
   // For Media Resource Management
   void ReleaseMediaResources() override;
@@ -106,17 +106,17 @@ private:
   bool HasAudio() { return mAudio.mTrackDemuxer; }
 
   bool IsWaitingOnCDMResource();
 
   bool InitDemuxer();
   // Notify the demuxer that new data has been received.
   // The next queued task calling GetBuffered() is guaranteed to have up to date
   // buffered ranges.
-  void NotifyDemuxer(uint32_t aLength, int64_t aOffset);
+  void NotifyDemuxer();
   void ReturnOutput(MediaData* aData, TrackType aTrack);
 
   bool EnsureDecodersCreated();
   bool EnsureDecoderInitialized(TrackType aTrack);
 
   // Enqueues a task to call Update(aTrack) on the decoder task queue.
   // Lock for corresponding track must be held.
   void ScheduleUpdate(TrackType aTrack);
--- a/dom/media/apple/AppleMP3Reader.cpp
+++ b/dom/media/apple/AppleMP3Reader.cpp
@@ -203,17 +203,17 @@ AppleMP3Reader::AudioSampleCallback(UInt
 
   // This API insists on having MP3 packets spoon-fed to it from a callback.
   // This structure exists only to pass our state and the result of the parser
   // on to the callback above.
   PassthroughUserData userData = { this, aNumPackets, aNumBytes, aData, aPackets, false };
 
   do {
     // Decompressed audio buffer
-    nsAutoArrayPtr<uint8_t> decoded(new uint8_t[decodedSize]);
+    auto decoded = MakeUnique<uint8_t[]>(decodedSize);
 
     AudioBufferList decBuffer;
     decBuffer.mNumberBuffers = 1;
     decBuffer.mBuffers[0].mNumberChannels = mAudioChannels;
     decBuffer.mBuffers[0].mDataByteSize = decodedSize;
     decBuffer.mBuffers[0].mData = decoded.get();
 
     // in: the max number of packets we can handle from the decoder.
@@ -241,19 +241,21 @@ AppleMP3Reader::AudioSampleCallback(UInt
     }
 
     int64_t time = FramesToUsecs(mCurrentAudioFrame, mAudioSampleRate).value();
     int64_t duration = FramesToUsecs(numFrames, mAudioSampleRate).value();
 
     LOGD("pushed audio at time %lfs; duration %lfs\n",
          (double)time / USECS_PER_S, (double)duration / USECS_PER_S);
 
+    auto samples = UniquePtr<AudioDataValue[]>(reinterpret_cast<AudioDataValue*>
+					       (decoded.release()));
     AudioData *audio = new AudioData(mResource.Tell(),
                                      time, duration, numFrames,
-                                     reinterpret_cast<AudioDataValue *>(decoded.forget()),
+                                     Move(samples),
                                      mAudioChannels, mAudioSampleRate);
     mAudioQueue.Push(audio);
 
     mCurrentAudioFrame += numFrames;
 
     if (rv == kNeedMoreData) {
       // No error; we just need more data.
       LOGD("FillComplexBuffer out of data\n");
@@ -495,17 +497,17 @@ AppleMP3Reader::Seek(int64_t aTime, int6
   mResource.Seek(nsISeekableStream::NS_SEEK_SET, byteOffset);
 
   ResetDecode();
 
   return SeekPromise::CreateAndResolve(aTime, __func__);
 }
 
 void
-AppleMP3Reader::NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset)
+AppleMP3Reader::NotifyDataArrivedInternal()
 {
   MOZ_ASSERT(OnTaskQueue());
   if (!mMP3FrameParser.NeedsData()) {
     return;
   }
 
   AutoPinned<MediaResource> resource(mResource.GetResource());
   nsTArray<MediaByteRange> byteRanges;
--- a/dom/media/apple/AppleMP3Reader.h
+++ b/dom/media/apple/AppleMP3Reader.h
@@ -39,18 +39,17 @@ public:
                            const void *aData,
                            AudioStreamPacketDescription *aPackets);
 
   void AudioMetadataCallback(AudioFileStreamID aFileStream,
                              AudioFileStreamPropertyID aPropertyID,
                              UInt32 *aFlags);
 
 protected:
-  virtual void NotifyDataArrivedInternal(uint32_t aLength,
-                                         int64_t aOffset) override;
+  virtual void NotifyDataArrivedInternal() override;
 public:
 
   virtual bool IsMediaSeekable() override;
 
 private:
   void SetupDecoder();
   nsresult Read(uint32_t *aNumBytes, char *aData);
 
--- a/dom/media/directshow/DirectShowReader.cpp
+++ b/dom/media/directshow/DirectShowReader.cpp
@@ -375,17 +375,17 @@ DirectShowReader::SeekInternal(int64_t a
 
   hr = mControl->Run();
   NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
 
   return NS_OK;
 }
 
 void
-DirectShowReader::NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset)
+DirectShowReader::NotifyDataArrivedInternal()
 {
   MOZ_ASSERT(OnTaskQueue());
   if (!mMP3FrameParser.NeedsData()) {
     return;
   }
 
   AutoPinned<MediaResource> resource(mDecoder->GetResource());
   nsTArray<MediaByteRange> byteRanges;
--- a/dom/media/directshow/DirectShowReader.h
+++ b/dom/media/directshow/DirectShowReader.h
@@ -49,18 +49,17 @@ public:
 
   nsresult ReadMetadata(MediaInfo* aInfo,
                         MetadataTags** aTags) override;
 
   RefPtr<SeekPromise>
   Seek(int64_t aTime, int64_t aEndTime) override;
 
 protected:
-  void NotifyDataArrivedInternal(uint32_t aLength,
-                                 int64_t aOffset) override;
+  void NotifyDataArrivedInternal() override;
 public:
 
   bool IsMediaSeekable() override;
 
 private:
 
   // Notifies the filter graph that playback is complete. aStatus is
   // the code to send to the filter graph. Always returns false, so
--- a/dom/media/gstreamer/GStreamerReader.cpp
+++ b/dom/media/gstreamer/GStreamerReader.cpp
@@ -1270,18 +1270,17 @@ GStreamerReader::AutoplugSortCb(GstEleme
    */
   return nullptr;
 }
 
 /**
  * If this is an MP3 stream, pass any new data we get to the MP3 frame parser
  * for duration estimation.
  */
-void GStreamerReader::NotifyDataArrivedInternal(uint32_t aLength,
-                                                int64_t aOffset)
+void GStreamerReader::NotifyDataArrivedInternal()
 {
   MOZ_ASSERT(OnTaskQueue());
   if (HasVideo()) {
     return;
   }
   if (!mMP3FrameParser.NeedsData()) {
     return;
   }
--- a/dom/media/gstreamer/GStreamerReader.h
+++ b/dom/media/gstreamer/GStreamerReader.h
@@ -48,18 +48,17 @@ public:
                                 int64_t aTimeThreshold) override;
   virtual nsresult ReadMetadata(MediaInfo* aInfo,
                                 MetadataTags** aTags) override;
   virtual RefPtr<SeekPromise>
   Seek(int64_t aTime, int64_t aEndTime) override;
   virtual media::TimeIntervals GetBuffered() override;
 
 protected:
-  virtual void NotifyDataArrivedInternal(uint32_t aLength,
-                                         int64_t aOffset) override;
+  virtual void NotifyDataArrivedInternal() override;
 public:
   layers::ImageContainer* GetImageContainer() { return mDecoder->GetImageContainer(); }
 
   virtual bool IsMediaSeekable() override;
 
 private:
   bool HasAudio() { return mInfo.HasAudio(); }
   bool HasVideo() { return mInfo.HasVideo(); }
--- a/dom/media/mediasink/DecodedAudioDataSink.cpp
+++ b/dom/media/mediasink/DecodedAudioDataSink.cpp
@@ -468,17 +468,17 @@ DecodedAudioDataSink::PlayFromAudioQueue
   AssertOnAudioThread();
   NS_ASSERTION(!mAudioStream->IsPaused(), "Don't play when paused");
   RefPtr<AudioData> audio =
     dont_AddRef(AudioQueue().PopFront().take()->As<AudioData>());
 
   SINK_LOG_V("playing %u frames of audio at time %lld",
              audio->mFrames, audio->mTime);
   if (audio->mRate == mInfo.mRate && audio->mChannels == mInfo.mChannels) {
-    mAudioStream->Write(audio->mAudioData, audio->mFrames);
+    mAudioStream->Write(audio->mAudioData.get(), audio->mFrames);
   } else {
     SINK_LOG_V("mismatched sample format mInfo=[%uHz/%u channels] audio=[%uHz/%u channels]",
                mInfo.mRate, mInfo.mChannels, audio->mRate, audio->mChannels);
     PlaySilence(audio->mFrames);
   }
 
   StartAudioStreamPlaybackIfNeeded();
 
--- a/dom/media/ogg/OggReader.cpp
+++ b/dom/media/ogg/OggReader.cpp
@@ -510,17 +510,17 @@ nsresult OggReader::DecodeVorbis(ogg_pac
   }
 
   VorbisPCMValue** pcm = 0;
   int32_t frames = 0;
   uint32_t channels = mVorbisState->mInfo.channels;
   ogg_int64_t endFrame = aPacket->granulepos;
   while ((frames = vorbis_synthesis_pcmout(&mVorbisState->mDsp, &pcm)) > 0) {
     mVorbisState->ValidateVorbisPacketSamples(aPacket, frames);
-    nsAutoArrayPtr<AudioDataValue> buffer(new AudioDataValue[frames * channels]);
+    auto buffer = MakeUnique<AudioDataValue[]>(frames * channels);
     for (uint32_t j = 0; j < channels; ++j) {
       VorbisPCMValue* channel = pcm[j];
       for (uint32_t i = 0; i < uint32_t(frames); ++i) {
         buffer[i*channels + j] = MOZ_CONVERT_VORBIS_SAMPLE(channel[i]);
       }
     }
 
     // No channel mapping for more than 8 channels.
@@ -529,17 +529,17 @@ nsresult OggReader::DecodeVorbis(ogg_pac
     }
 
     int64_t duration = mVorbisState->Time((int64_t)frames);
     int64_t startTime = mVorbisState->Time(endFrame - frames);
     mAudioQueue.Push(new AudioData(mResource.Tell(),
                                    startTime,
                                    duration,
                                    frames,
-                                   buffer.forget(),
+                                   Move(buffer),
                                    channels,
                                    mVorbisState->mInfo.rate));
 
     mDecodedAudioFrames += frames;
 
     endFrame -= frames;
     if (vorbis_synthesis_read(&mVorbisState->mDsp, frames) != 0) {
       return NS_ERROR_FAILURE;
@@ -559,27 +559,27 @@ nsresult OggReader::DecodeOpus(ogg_packe
   int32_t samples = opus_packet_get_samples_per_frame(aPacket->packet,
                                                       (opus_int32) mOpusState->mRate);
   int32_t frames = frames_number*samples;
 
   // A valid Opus packet must be between 2.5 and 120 ms long.
   if (frames < 120 || frames > 5760)
     return NS_ERROR_FAILURE;
   uint32_t channels = mOpusState->mChannels;
-  nsAutoArrayPtr<AudioDataValue> buffer(new AudioDataValue[frames * channels]);
+  auto buffer = MakeUnique<AudioDataValue[]>(frames * channels);
 
   // Decode to the appropriate sample type.
 #ifdef MOZ_SAMPLE_TYPE_FLOAT32
   int ret = opus_multistream_decode_float(mOpusState->mDecoder,
                                           aPacket->packet, aPacket->bytes,
-                                          buffer, frames, false);
+                                          buffer.get(), frames, false);
 #else
   int ret = opus_multistream_decode(mOpusState->mDecoder,
                                     aPacket->packet, aPacket->bytes,
-                                    buffer, frames, false);
+                                    buffer.get(), frames, false);
 #endif
   if (ret < 0)
     return NS_ERROR_FAILURE;
   NS_ASSERTION(ret == frames, "Opus decoded too few audio samples");
 
   int64_t endFrame = aPacket->granulepos;
   int64_t startFrame;
   // If this is the last packet, perform end trimming.
@@ -599,23 +599,23 @@ nsresult OggReader::DecodeOpus(ogg_packe
       // discard the whole packet
       mOpusState->mSkip -= frames;
       LOG(LogLevel::Debug, ("Opus decoder skipping %d frames"
                          " (whole packet)", frames));
       return NS_OK;
     }
     int32_t keepFrames = frames - skipFrames;
     int samples = keepFrames * channels;
-    nsAutoArrayPtr<AudioDataValue> trimBuffer(new AudioDataValue[samples]);
+    auto trimBuffer = MakeUnique<AudioDataValue[]>(samples);
     for (int i = 0; i < samples; i++)
       trimBuffer[i] = buffer[skipFrames*channels + i];
 
     startFrame = endFrame - keepFrames;
     frames = keepFrames;
-    buffer = trimBuffer;
+    buffer = Move(trimBuffer);
 
     mOpusState->mSkip -= skipFrames;
     LOG(LogLevel::Debug, ("Opus decoder skipping %d frames", skipFrames));
   }
   // Save this packet's granule position in case we need to perform end
   // trimming on the next packet.
   mOpusState->mPrevPacketGranulepos = endFrame;
 
@@ -646,17 +646,17 @@ nsresult OggReader::DecodeOpus(ogg_packe
 
   LOG(LogLevel::Debug, ("Opus decoder pushing %d frames", frames));
   int64_t startTime = mOpusState->Time(startFrame);
   int64_t endTime = mOpusState->Time(endFrame);
   mAudioQueue.Push(new AudioData(mResource.Tell(),
                                  startTime,
                                  endTime - startTime,
                                  frames,
-                                 buffer.forget(),
+                                 Move(buffer),
                                  channels,
                                  mOpusState->mRate));
 
   mDecodedAudioFrames += frames;
 
   return NS_OK;
 }
 
--- a/dom/media/omx/MediaCodecReader.cpp
+++ b/dom/media/omx/MediaCodecReader.cpp
@@ -512,18 +512,17 @@ MediaCodecReader::HasAudio()
 
 bool
 MediaCodecReader::HasVideo()
 {
   return mInfo.HasVideo();
 }
 
 void
-MediaCodecReader::NotifyDataArrivedInternal(uint32_t aLength,
-                                            int64_t aOffset)
+MediaCodecReader::NotifyDataArrivedInternal()
 {
   AutoPinned<MediaResource> resource(mDecoder->GetResource());
   nsTArray<MediaByteRange> byteRanges;
   nsresult rv = resource->GetCachedRanges(byteRanges);
 
   if (NS_FAILED(rv)) {
     return;
   }
--- a/dom/media/omx/MediaCodecReader.h
+++ b/dom/media/omx/MediaCodecReader.h
@@ -67,17 +67,17 @@ public:
   // This is different from ReleaseMediaResources() as Shutdown() is
   // irreversible, whereas ReleaseMediaResources() is reversible.
   virtual RefPtr<ShutdownPromise> Shutdown();
 
 protected:
   // Used to retrieve some special information that can only be retrieved after
   // all contents have been continuously parsed. (ex. total duration of some
   // variable-bit-rate MP3 files.)
-  virtual void NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset) override;
+  virtual void NotifyDataArrivedInternal() override;
 public:
 
   // Flush the TaskQueue, flush MediaCodec and raise the mDiscontinuity.
   virtual nsresult ResetDecode() override;
 
   // Disptach a DecodeVideoFrameTask to decode video data.
   virtual RefPtr<VideoDataPromise>
   RequestVideoData(bool aSkipToNextKeyframe,
--- a/dom/media/omx/MediaOmxReader.cpp
+++ b/dom/media/omx/MediaOmxReader.cpp
@@ -444,17 +444,17 @@ bool MediaOmxReader::DecodeVideoFrame(bo
     mVideoQueue.Push(v);
 
     break;
   }
 
   return true;
 }
 
-void MediaOmxReader::NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset)
+void MediaOmxReader::NotifyDataArrivedInternal()
 {
   MOZ_ASSERT(OnTaskQueue());
   RefPtr<AbstractMediaDecoder> decoder = SafeGetDecoder();
   if (!decoder) { // reader has shut down
     return;
   }
   if (HasVideo()) {
     return;
--- a/dom/media/omx/MediaOmxReader.h
+++ b/dom/media/omx/MediaOmxReader.h
@@ -66,17 +66,17 @@ protected:
 
   virtual void HandleResourceAllocated();
 
 public:
   MediaOmxReader(AbstractMediaDecoder* aDecoder);
   ~MediaOmxReader();
 
 protected:
-  virtual void NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset) override;
+  virtual void NotifyDataArrivedInternal() override;
 public:
 
   virtual nsresult ResetDecode()
   {
     mSeekRequest.DisconnectIfExists();
     mSeekPromise.RejectIfExists(NS_OK, __func__);
     return MediaDecoderReader::ResetDecode();
   }
--- a/dom/media/platforms/agnostic/BlankDecoderModule.cpp
+++ b/dom/media/platforms/agnostic/BlankDecoderModule.cpp
@@ -178,32 +178,32 @@ public:
     CheckedInt64 frames =
       UsecsToFrames(aDuration.ToMicroseconds()+1, mSampleRate);
     if (!frames.isValid() ||
         !mChannelCount ||
         !mSampleRate ||
         frames.value() > (UINT32_MAX / mChannelCount)) {
       return nullptr;
     }
-    AudioDataValue* samples = new AudioDataValue[frames.value() * mChannelCount];
+    auto samples = MakeUnique<AudioDataValue[]>(frames.value() * mChannelCount);
     // Fill the sound buffer with an A4 tone.
     static const float pi = 3.14159265f;
     static const float noteHz = 440.0f;
     for (int i = 0; i < frames.value(); i++) {
       float f = sin(2 * pi * noteHz * mFrameSum / mSampleRate);
       for (unsigned c = 0; c < mChannelCount; c++) {
         samples[i * mChannelCount + c] = AudioDataValue(f);
       }
       mFrameSum++;
     }
     return new AudioData(aOffsetInStream,
                          aDTS.ToMicroseconds(),
                          aDuration.ToMicroseconds(),
                          uint32_t(frames.value()),
-                         samples,
+                         Move(samples),
                          mChannelCount,
                          mSampleRate);
   }
 
 private:
   int64_t mFrameSum;
   uint32_t mChannelCount;
   uint32_t mSampleRate;
--- a/dom/media/platforms/agnostic/OpusDecoder.cpp
+++ b/dom/media/platforms/agnostic/OpusDecoder.cpp
@@ -163,27 +163,27 @@ OpusDataDecoder::DoDecode(MediaRawData* 
 
   // A valid Opus packet must be between 2.5 and 120 ms long (48kHz).
   int32_t frames = frames_number*samples;
   if (frames < 120 || frames > 5760) {
     OPUS_DEBUG("Invalid packet frames: %ld", frames);
     return -1;
   }
 
-  nsAutoArrayPtr<AudioDataValue> buffer(new AudioDataValue[frames * channels]);
+  auto buffer = MakeUnique<AudioDataValue[]>(frames * channels);
 
   // Decode to the appropriate sample type.
 #ifdef MOZ_SAMPLE_TYPE_FLOAT32
   int ret = opus_multistream_decode_float(mOpusDecoder,
                                           aSample->Data(), aSample->Size(),
-                                          buffer, frames, false);
+                                          buffer.get(), frames, false);
 #else
   int ret = opus_multistream_decode(mOpusDecoder,
                                     aSample->Data(), aSample->Size(),
-                                    buffer, frames, false);
+                                    buffer.get(), frames, false);
 #endif
   if (ret < 0) {
     return -1;
   }
   NS_ASSERTION(ret == frames, "Opus decoded too few audio samples");
   CheckedInt64 startTime = aSample->mTime;
 
   // Trim the initial frames while the decoder is settling.
@@ -259,17 +259,17 @@ OpusDataDecoder::DoDecode(MediaRawData* 
     NS_WARNING("OpusDataDecoder: Int overflow shifting tstamp by codec delay");
     return -1;
   };
 
   mCallback->Output(new AudioData(aSample->mOffset,
                                   time.value(),
                                   duration.value(),
                                   frames,
-                                  buffer.forget(),
+                                  Move(buffer),
                                   mOpusParser->mChannels,
                                   mOpusParser->mRate));
   mFrames += frames;
   return frames;
 }
 
 void
 OpusDataDecoder::DoDrain()
--- a/dom/media/platforms/agnostic/VorbisDecoder.cpp
+++ b/dom/media/platforms/agnostic/VorbisDecoder.cpp
@@ -179,17 +179,17 @@ VorbisDataDecoder::DoDecode(MediaRawData
                                     0,
                                     0,
                                     nullptr,
                                     mVorbisDsp.vi->channels,
                                     mVorbisDsp.vi->rate));
   }
   while (frames > 0) {
     uint32_t channels = mVorbisDsp.vi->channels;
-    nsAutoArrayPtr<AudioDataValue> buffer(new AudioDataValue[frames*channels]);
+    auto buffer = MakeUnique<AudioDataValue[]>(frames*channels);
     for (uint32_t j = 0; j < channels; ++j) {
       VorbisPCMValue* channel = pcm[j];
       for (uint32_t i = 0; i < uint32_t(frames); ++i) {
         buffer[i*channels + j] = MOZ_CONVERT_VORBIS_SAMPLE(channel[i]);
       }
     }
 
     CheckedInt64 duration = FramesToUsecs(frames, mVorbisDsp.vi->rate);
@@ -210,17 +210,17 @@ VorbisDataDecoder::DoDecode(MediaRawData
       return -1;
     };
 
     aTotalFrames += frames;
     mCallback->Output(new AudioData(aOffset,
                                     time.value(),
                                     duration.value(),
                                     frames,
-                                    buffer.forget(),
+                                    Move(buffer),
                                     mVorbisDsp.vi->channels,
                                     mVorbisDsp.vi->rate));
     mFrames += aTotalFrames;
     if (vorbis_synthesis_read(&mVorbisDsp, frames) != 0) {
       return -1;
     }
 
     frames = vorbis_synthesis_pcmout(&mVorbisDsp, &pcm);
--- a/dom/media/platforms/agnostic/gmp/GMPAudioDecoder.cpp
+++ b/dom/media/platforms/agnostic/gmp/GMPAudioDecoder.cpp
@@ -32,17 +32,17 @@ AudioCallbackAdapter::Decoded(const nsTA
   if (aRate == 0 || aChannels == 0) {
     NS_WARNING("Invalid rate or num channels returned on GMP audio samples");
     mCallback->Error();
     return;
   }
 
   size_t numFrames = aPCM.Length() / aChannels;
   MOZ_ASSERT((aPCM.Length() % aChannels) == 0);
-  nsAutoArrayPtr<AudioDataValue> audioData(new AudioDataValue[aPCM.Length()]);
+  auto audioData = MakeUnique<AudioDataValue[]>(aPCM.Length());
 
   for (size_t i = 0; i < aPCM.Length(); ++i) {
     audioData[i] = AudioSampleToFloat(aPCM[i]);
   }
 
   if (mMustRecaptureAudioPosition) {
     mAudioFrameSum = 0;
     auto timestamp = UsecsToFrames(aTimeStamp, aRate);
@@ -66,22 +66,22 @@ AudioCallbackAdapter::Decoded(const nsTA
   auto duration = FramesToUsecs(numFrames, aRate);
   if (!duration.isValid()) {
     NS_WARNING("Invalid duration on audio samples");
     mCallback->Error();
     return;
   }
 
   RefPtr<AudioData> audio(new AudioData(mLastStreamOffset,
-                                          timestamp.value(),
-                                          duration.value(),
-                                          numFrames,
-                                          audioData.forget(),
-                                          aChannels,
-                                          aRate));
+                                        timestamp.value(),
+                                        duration.value(),
+                                        numFrames,
+                                        Move(audioData),
+                                        aChannels,
+                                        aRate));
 
 #ifdef LOG_SAMPLE_DECODE
   LOG("Decoded audio sample! timestamp=%lld duration=%lld currentLength=%u",
       timestamp, duration, currentLength);
 #endif
 
   mCallback->Output(audio);
 }
--- a/dom/media/platforms/android/AndroidDecoderModule.cpp
+++ b/dom/media/platforms/android/AndroidDecoderModule.cpp
@@ -252,28 +252,28 @@ public:
 
 #ifdef MOZ_SAMPLE_TYPE_S16
     int32_t numSamples = size / 2;
 #else
 #error We only support 16-bit integer PCM
 #endif
 
     const int32_t numFrames = numSamples / numChannels;
-    AudioDataValue* audio = new AudioDataValue[numSamples];
+    auto audio = MakeUnique<AudioDataValue[]>(numSamples);
 
     uint8_t* bufferStart = static_cast<uint8_t*>(aBuffer) + offset;
-    PodCopy(audio, reinterpret_cast<AudioDataValue*>(bufferStart), numSamples);
+    PodCopy(audio.get(), reinterpret_cast<AudioDataValue*>(bufferStart), numSamples);
 
     int64_t presentationTimeUs;
     NS_ENSURE_SUCCESS(rv = aInfo->PresentationTimeUs(&presentationTimeUs), rv);
 
     RefPtr<AudioData> data = new AudioData(0, presentationTimeUs,
                                            aDuration.ToMicroseconds(),
                                            numFrames,
-                                           audio,
+                                           Move(audio),
                                            numChannels,
                                            sampleRate);
     INVOKE_CALLBACK(Output, data);
     return NS_OK;
   }
 };
 
 
--- a/dom/media/platforms/apple/AppleATDecoder.cpp
+++ b/dom/media/platforms/apple/AppleATDecoder.cpp
@@ -268,25 +268,25 @@ AppleATDecoder::DecodeSample(MediaRawDat
   }
 
 #ifdef LOG_SAMPLE_DECODE
   LOG("pushed audio at time %lfs; duration %lfs\n",
       (double)aSample->mTime / USECS_PER_S,
       duration.ToSeconds());
 #endif
 
-  nsAutoArrayPtr<AudioDataValue> data(new AudioDataValue[outputData.Length()]);
+  auto data = MakeUnique<AudioDataValue[]>(outputData.Length());
   PodCopy(data.get(), &outputData[0], outputData.Length());
   RefPtr<AudioData> audio = new AudioData(aSample->mOffset,
-                                            aSample->mTime,
-                                            duration.ToMicroseconds(),
-                                            numFrames,
-                                            data.forget(),
-                                            channels,
-                                            rate);
+                                          aSample->mTime,
+                                          duration.ToMicroseconds(),
+                                          numFrames,
+                                          Move(data),
+                                          channels,
+                                          rate);
   mCallback->Output(audio);
   return NS_OK;
 }
 
 nsresult
 AppleATDecoder::GetInputAudioDescription(AudioStreamBasicDescription& aDesc,
                                          const nsTArray<uint8_t>& aExtraData)
 {
--- a/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp
@@ -30,60 +30,59 @@ RefPtr<MediaDataDecoder::InitPromise>
 FFmpegAudioDecoder<LIBAV_VER>::Init()
 {
   nsresult rv = InitDecoder();
 
   return rv == NS_OK ? InitPromise::CreateAndResolve(TrackInfo::kAudioTrack, __func__)
                      : InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
 }
 
-static AudioDataValue*
+static UniquePtr<AudioDataValue[]>
 CopyAndPackAudio(AVFrame* aFrame, uint32_t aNumChannels, uint32_t aNumAFrames)
 {
   MOZ_ASSERT(aNumChannels <= MAX_CHANNELS);
 
-  nsAutoArrayPtr<AudioDataValue> audio(
-    new AudioDataValue[aNumChannels * aNumAFrames]);
+  auto audio = MakeUnique<AudioDataValue[]>(aNumChannels * aNumAFrames);
 
   if (aFrame->format == AV_SAMPLE_FMT_FLT) {
     // Audio data already packed. No need to do anything other than copy it
     // into a buffer we own.
-    memcpy(audio, aFrame->data[0],
+    memcpy(audio.get(), aFrame->data[0],
            aNumChannels * aNumAFrames * sizeof(AudioDataValue));
   } else if (aFrame->format == AV_SAMPLE_FMT_FLTP) {
     // Planar audio data. Pack it into something we can understand.
-    AudioDataValue* tmp = audio;
+    AudioDataValue* tmp = audio.get();
     AudioDataValue** data = reinterpret_cast<AudioDataValue**>(aFrame->data);
     for (uint32_t frame = 0; frame < aNumAFrames; frame++) {
       for (uint32_t channel = 0; channel < aNumChannels; channel++) {
         *tmp++ = data[channel][frame];
       }
     }
   } else if (aFrame->format == AV_SAMPLE_FMT_S16) {
     // Audio data already packed. Need to convert from S16 to 32 bits Float
-    AudioDataValue* tmp = audio;
+    AudioDataValue* tmp = audio.get();
     int16_t* data = reinterpret_cast<int16_t**>(aFrame->data)[0];
     for (uint32_t frame = 0; frame < aNumAFrames; frame++) {
       for (uint32_t channel = 0; channel < aNumChannels; channel++) {
         *tmp++ = AudioSampleToFloat(*data++);
       }
     }
   } else if (aFrame->format == AV_SAMPLE_FMT_S16P) {
     // Planar audio data. Convert it from S16 to 32 bits float
     // and pack it into something we can understand.
-    AudioDataValue* tmp = audio;
+    AudioDataValue* tmp = audio.get();
     int16_t** data = reinterpret_cast<int16_t**>(aFrame->data);
     for (uint32_t frame = 0; frame < aNumAFrames; frame++) {
       for (uint32_t channel = 0; channel < aNumChannels; channel++) {
         *tmp++ = AudioSampleToFloat(data[channel][frame]);
       }
     }
   }
 
-  return audio.forget();
+  return audio;
 }
 
 void
 FFmpegAudioDecoder<LIBAV_VER>::DecodePacket(MediaRawData* aSample)
 {
   MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
   AVPacket packet;
   av_init_packet(&packet);
@@ -110,34 +109,34 @@ FFmpegAudioDecoder<LIBAV_VER>::DecodePac
       mCallback->Error();
       return;
     }
 
     if (decoded) {
       uint32_t numChannels = mCodecContext->channels;
       uint32_t samplingRate = mCodecContext->sample_rate;
 
-      nsAutoArrayPtr<AudioDataValue> audio(
-        CopyAndPackAudio(mFrame, numChannels, mFrame->nb_samples));
+      UniquePtr<AudioDataValue[]> audio =
+        CopyAndPackAudio(mFrame, numChannels, mFrame->nb_samples);
 
       media::TimeUnit duration =
         FramesToTimeUnit(mFrame->nb_samples, samplingRate);
       if (!duration.IsValid()) {
         NS_WARNING("Invalid count of accumulated audio samples");
         mCallback->Error();
         return;
       }
 
       RefPtr<AudioData> data = new AudioData(samplePosition,
-                                               pts.ToMicroseconds(),
-                                               duration.ToMicroseconds(),
-                                               mFrame->nb_samples,
-                                               audio.forget(),
-                                               numChannels,
-                                               samplingRate);
+                                             pts.ToMicroseconds(),
+                                             duration.ToMicroseconds(),
+                                             mFrame->nb_samples,
+                                             Move(audio),
+                                             numChannels,
+                                             samplingRate);
       mCallback->Output(data);
       pts += duration;
       if (!pts.IsValid()) {
         NS_WARNING("Invalid count of accumulated audio samples");
         mCallback->Error();
         return;
       }
     }
--- a/dom/media/platforms/wmf/WMFAudioMFTManager.cpp
+++ b/dom/media/platforms/wmf/WMFAudioMFTManager.cpp
@@ -275,17 +275,17 @@ WMFAudioMFTManager::Output(int64_t aStre
   MOZ_ASSERT(numFrames >= 0);
   MOZ_ASSERT(numSamples >= 0);
   if (numFrames == 0) {
     // All data from this chunk stripped, loop back and try to output the next
     // frame, if possible.
     return S_OK;
   }
 
-  nsAutoArrayPtr<AudioDataValue> audioData(new AudioDataValue[numSamples]);
+  auto audioData = MakeUnique<AudioDataValue[]>(numSamples);
 
   int16_t* pcm = (int16_t*)data;
   for (int32_t i = 0; i < numSamples; ++i) {
     audioData[i] = AudioSampleToFloat(pcm[i]);
   }
 
   buffer->Unlock();
 
@@ -297,17 +297,17 @@ WMFAudioMFTManager::Output(int64_t aStre
 
   media::TimeUnit duration = FramesToTimeUnit(numFrames, mAudioRate);
   NS_ENSURE_TRUE(duration.IsValid(), E_FAIL);
 
   aOutData = new AudioData(aStreamOffset,
                            timestamp.ToMicroseconds(),
                            duration.ToMicroseconds(),
                            numFrames,
-                           audioData.forget(),
+                           Move(audioData),
                            mAudioChannels,
                            mAudioRate);
 
   #ifdef LOG_SAMPLE_DECODE
   LOG("Decoded audio sample! timestamp=%lld duration=%lld currentLength=%u",
       timestamp.ToMicroseconds(), duration.ToMicroseconds(), currentLength);
   #endif
 
--- a/dom/media/wave/WaveReader.cpp
+++ b/dom/media/wave/WaveReader.cpp
@@ -189,17 +189,17 @@ bool WaveReader::DecodeAudioData()
   static const int64_t BLOCK_SIZE = 4096;
   int64_t readSize = std::min(BLOCK_SIZE, remaining);
   int64_t frames = readSize / mFrameSize;
 
   static_assert(uint64_t(BLOCK_SIZE) < UINT_MAX /
                 sizeof(AudioDataValue) / MAX_CHANNELS,
                 "bufferSize calculation could overflow.");
   const size_t bufferSize = static_cast<size_t>(frames * mChannels);
-  nsAutoArrayPtr<AudioDataValue> sampleBuffer(new AudioDataValue[bufferSize]);
+  auto sampleBuffer = MakeUnique<AudioDataValue[]>(bufferSize);
 
   static_assert(uint64_t(BLOCK_SIZE) < UINT_MAX / sizeof(char),
                 "BLOCK_SIZE too large for enumerator.");
   nsAutoArrayPtr<char> dataBuffer(new char[static_cast<size_t>(readSize)]);
 
   if (!ReadAll(dataBuffer, readSize)) {
     return false;
   }
@@ -224,17 +224,17 @@ bool WaveReader::DecodeAudioData()
   NS_ASSERTION(posTime <= INT64_MAX / USECS_PER_S, "posTime overflow");
   NS_ASSERTION(readSizeTime <= INT64_MAX / USECS_PER_S, "readSizeTime overflow");
   NS_ASSERTION(frames < INT32_MAX, "frames overflow");
 
   mAudioQueue.Push(new AudioData(pos,
                                  static_cast<int64_t>(posTime * USECS_PER_S),
                                  static_cast<int64_t>(readSizeTime * USECS_PER_S),
                                  static_cast<int32_t>(frames),
-                                 sampleBuffer.forget(),
+                                 Move(sampleBuffer),
                                  mChannels,
                                  mSampleRate));
 
   return true;
 }
 
 bool WaveReader::DecodeVideoFrame(bool &aKeyframeSkip,
                                       int64_t aTimeThreshold)
--- a/dom/media/webm/AudioDecoder.cpp
+++ b/dom/media/webm/AudioDecoder.cpp
@@ -175,17 +175,17 @@ VorbisDecoder::Decode(const unsigned cha
   // data.
   if (frames == 0 && first_packet) {
     mReader->AudioQueue().Push(new AudioData(aOffset, aTstampUsecs, 0, 0, nullptr,
                                              mVorbisDsp.vi->channels,
                                              mVorbisDsp.vi->rate));
   }
   while (frames > 0) {
     uint32_t channels = mVorbisDsp.vi->channels;
-    nsAutoArrayPtr<AudioDataValue> buffer(new AudioDataValue[frames*channels]);
+    auto buffer = MakeUnique<AudioDataValue[]>(frames*channels);
     for (uint32_t j = 0; j < channels; ++j) {
       VorbisPCMValue* channel = pcm[j];
       for (uint32_t i = 0; i < uint32_t(frames); ++i) {
         buffer[i*channels + j] = MOZ_CONVERT_VORBIS_SAMPLE(channel[i]);
       }
     }
 
     CheckedInt64 duration = FramesToUsecs(frames, mVorbisDsp.vi->rate);
@@ -206,17 +206,17 @@ VorbisDecoder::Decode(const unsigned cha
       return false;
     };
 
     *aTotalFrames += frames;
     mReader->AudioQueue().Push(new AudioData(aOffset,
                                              time.value(),
                                              duration.value(),
                                              frames,
-                                             buffer.forget(),
+                                             Move(buffer),
                                              mVorbisDsp.vi->channels,
                                              mVorbisDsp.vi->rate));
     if (vorbis_synthesis_read(&mVorbisDsp, frames)) {
       return false;
     }
 
     frames = vorbis_synthesis_pcmout(&mVorbisDsp, &pcm);
   }
@@ -366,27 +366,27 @@ OpusDecoder::Decode(const unsigned char*
   int32_t samples =
     opus_packet_get_samples_per_frame(aData, opus_int32(mOpusParser->mRate));
 
   // A valid Opus packet must be between 2.5 and 120 ms long (48kHz).
   int32_t frames = frames_number*samples;
   if (frames < 120 || frames > 5760)
     return false;
 
-  nsAutoArrayPtr<AudioDataValue> buffer(new AudioDataValue[frames * channels]);
+  auto buffer = MakeUnique<AudioDataValue[]>(frames * channels);
 
   // Decode to the appropriate sample type.
 #ifdef MOZ_SAMPLE_TYPE_FLOAT32
   int ret = opus_multistream_decode_float(mOpusDecoder,
                                           aData, aLength,
-                                          buffer, frames, false);
+                                          buffer.get(), frames, false);
 #else
   int ret = opus_multistream_decode(mOpusDecoder,
                                     aData, aLength,
-                                    buffer, frames, false);
+                                    buffer.get(), frames, false);
 #endif
   if (ret < 0)
     return false;
   NS_ASSERTION(ret == frames, "Opus decoded too few audio samples");
   CheckedInt64 startTime = aTstampUsecs;
 
   // Trim the initial frames while the decoder is settling.
   if (mSkip > 0) {
@@ -458,15 +458,15 @@ OpusDecoder::Decode(const unsigned char*
   if (!time.isValid()) {
     NS_WARNING("Int overflow shifting tstamp by codec delay");
     return false;
   };
   mReader->AudioQueue().Push(new AudioData(aOffset,
                                            time.value(),
                                            duration.value(),
                                            frames,
-                                           buffer.forget(),
+                                           Move(buffer),
                                            mOpusParser->mChannels,
                                            mOpusParser->mRate));
   return true;
 }
 
 } // namespace mozilla
--- a/dom/media/webm/WebMReader.cpp
+++ b/dom/media/webm/WebMReader.cpp
@@ -782,32 +782,32 @@ media::TimeIntervals WebMReader::GetBuff
       buffered += media::TimeInterval(media::TimeUnit::FromSeconds(startTime),
                                       media::TimeUnit::FromSeconds(endTime));
     }
   }
 
   return buffered;
 }
 
-void WebMReader::NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset)
+void WebMReader::NotifyDataArrivedInternal()
 {
   MOZ_ASSERT(OnTaskQueue());
   AutoPinned<MediaResource> resource(mDecoder->GetResource());
   nsTArray<MediaByteRange> byteRanges;
   nsresult rv = resource->GetCachedRanges(byteRanges);
 
   if (NS_FAILED(rv)) {
     return;
   }
 
   for (auto& range : byteRanges) {
     RefPtr<MediaByteBuffer> bytes =
       resource->MediaReadAt(range.mStart, range.Length());
     NS_ENSURE_TRUE_VOID(bytes);
-    mBufferedState->NotifyDataArrived(bytes->Elements(), aLength, aOffset);
+    mBufferedState->NotifyDataArrived(bytes->Elements(), bytes->Length(), range.mStart);
   }
 }
 
 int WebMReader::GetVideoCodec()
 {
   return mVideoCodec;
 }
 
--- a/dom/media/webm/WebMReader.h
+++ b/dom/media/webm/WebMReader.h
@@ -113,17 +113,17 @@ public:
   nsIntRect GetPicture();
   nsIntSize GetInitialFrame();
   int64_t GetLastVideoFrameTime();
   void SetLastVideoFrameTime(int64_t aFrameTime);
   layers::LayersBackend GetLayersBackendType() { return mLayersBackendType; }
   uint64_t GetCodecDelay() { return mCodecDelay; }
 
 protected:
-  virtual void NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset) override;
+  virtual void NotifyDataArrivedInternal() override;
 
   // Decode a nestegg packet of audio data. Push the audio data on the
   // audio queue. Returns true when there's more audio to decode,
   // false if the audio is finished, end of file has been reached,
   // or an un-recoverable read error has occured. The reader's monitor
   // must be held during this call. The caller is responsible for freeing
   // aPacket.
   bool DecodeAudioPacket(NesteggPacketHolder* aHolder);
--- a/dom/network/UDPSocket.h
+++ b/dom/network/UDPSocket.h
@@ -16,17 +16,17 @@
 #include "nsIUDPSocketChild.h"
 #include "nsTArray.h"
 
 struct JSContext;
 
 //
 // set NSPR_LOG_MODULES=UDPSocket:5
 //
-extern PRLogModuleInfo *gUDPSocketLog;
+extern mozilla::LazyLogModule gUDPSocketLog;
 #define UDPSOCKET_LOG(args)     MOZ_LOG(gUDPSocketLog, mozilla::LogLevel::Debug, args)
 #define UDPSOCKET_LOG_ENABLED() MOZ_LOG_TEST(gUDPSocketLog, mozilla::LogLevel::Debug)
 
 namespace mozilla {
 namespace dom {
 
 struct UDPOptions;
 class StringOrBlobOrArrayBufferOrArrayBufferView;
--- a/dom/network/UDPSocketChild.cpp
+++ b/dom/network/UDPSocketChild.cpp
@@ -15,17 +15,17 @@
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "nsIIPCBackgroundChildCreateCallback.h"
 
 using mozilla::net::gNeckoChild;
 
 //
 // set NSPR_LOG_MODULES=UDPSocket:5
 //
-extern PRLogModuleInfo *gUDPSocketLog;
+extern mozilla::LazyLogModule gUDPSocketLog;
 #define UDPSOCKET_LOG(args)     MOZ_LOG(gUDPSocketLog, mozilla::LogLevel::Debug, args)
 #define UDPSOCKET_LOG_ENABLED() MOZ_LOG_TEST(gUDPSocketLog, mozilla::LogLevel::Debug)
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_ISUPPORTS(UDPSocketChildBase, nsIUDPSocketChild)
 
--- a/dom/network/UDPSocketParent.cpp
+++ b/dom/network/UDPSocketParent.cpp
@@ -20,17 +20,17 @@
 #include "mozilla/dom/TabParent.h"
 #include "nsIPermissionManager.h"
 #include "nsIScriptSecurityManager.h"
 #include "mozilla/ipc/PBackgroundParent.h"
 
 //
 // set NSPR_LOG_MODULES=UDPSocket:5
 //
-extern PRLogModuleInfo *gUDPSocketLog;
+extern mozilla::LazyLogModule gUDPSocketLog;
 #define UDPSOCKET_LOG(args)     MOZ_LOG(gUDPSocketLog, mozilla::LogLevel::Debug, args)
 #define UDPSOCKET_LOG_ENABLED() MOZ_LOG_TEST(gUDPSocketLog, mozilla::LogLevel::Debug)
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_ISUPPORTS(UDPSocketParent, nsIUDPSocketListener)
 
--- a/dom/plugins/base/nsPluginsDirDarwin.cpp
+++ b/dom/plugins/base/nsPluginsDirDarwin.cpp
@@ -18,16 +18,17 @@
 #include "prnetdb.h"
 #include "nsXPCOM.h"
 
 #include "nsPluginsDir.h"
 #include "nsNPAPIPlugin.h"
 #include "nsPluginsDirUtils.h"
 
 #include "nsILocalFileMac.h"
+#include "mozilla/UniquePtr.h"
 
 #include "nsCocoaFeatures.h"
 #if defined(MOZ_CRASHREPORTER)
 #include "nsExceptionHandler.h"
 #endif
 
 #include <string.h>
 #include <stdio.h>
@@ -37,17 +38,16 @@
 #include <Carbon/Carbon.h>
 #include <CoreServices/CoreServices.h>
 #include <mach-o/loader.h>
 #include <mach-o/fat.h>
 
 typedef NS_NPAPIPLUGIN_CALLBACK(const char *, NP_GETMIMEDESCRIPTION) ();
 typedef NS_NPAPIPLUGIN_CALLBACK(OSErr, BP_GETSUPPORTEDMIMETYPES) (BPSupportedMIMETypes *mimeInfo, UInt32 flags);
 
-
 /*
 ** Returns a CFBundleRef if the path refers to a Mac OS X bundle directory.
 ** The caller is responsible for calling CFRelease() to deallocate.
 */
 static CFBundleRef getPluginBundle(const char* path)
 {
   CFBundleRef bundle = nullptr;
   CFStringRef pathRef = ::CFStringCreateWithCString(nullptr, path,
@@ -233,26 +233,26 @@ static void ParsePlistPluginInfo(nsPlugi
     return;
   memset(info.fExtensionArray, 0, mimeDataArraySize);
   info.fMimeDescriptionArray = static_cast<char**>(moz_xmalloc(mimeDataArraySize));
   if (!info.fMimeDescriptionArray)
     return;
   memset(info.fMimeDescriptionArray, 0, mimeDataArraySize);
 
   // Allocate memory for mime dictionary keys and values
-  nsAutoArrayPtr<CFTypeRef> keys(new CFTypeRef[mimeDictKeyCount]);
+  mozilla::UniquePtr<CFTypeRef[]> keys(new CFTypeRef[mimeDictKeyCount]);
   if (!keys)
     return;
-  nsAutoArrayPtr<CFTypeRef> values(new CFTypeRef[mimeDictKeyCount]);
+  mozilla::UniquePtr<CFTypeRef[]> values(new CFTypeRef[mimeDictKeyCount]);
   if (!values)
     return;
   
   info.fVariantCount = 0;
 
-  ::CFDictionaryGetKeysAndValues(mimeDict, keys, values);
+  ::CFDictionaryGetKeysAndValues(mimeDict, keys.get(), values.get());
   for (int i = 0; i < mimeDictKeyCount; i++) {
     CFTypeRef mimeString = keys[i];
     if (!mimeString || ::CFGetTypeID(mimeString) != ::CFStringGetTypeID()) {
       continue;
     }
     CFTypeRef mimeDict = values[i];
     if (mimeDict && ::CFGetTypeID(mimeDict) == ::CFDictionaryGetTypeID()) {
       if (!MimeTypeEnabled(static_cast<CFDictionaryRef>(mimeDict))) {
--- a/dom/plugins/ipc/BrowserStreamParent.cpp
+++ b/dom/plugins/ipc/BrowserStreamParent.cpp
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 #include "BrowserStreamParent.h"
 #include "PluginAsyncSurrogate.h"
 #include "PluginInstanceParent.h"
 #include "nsNPAPIPlugin.h"
 
+#include "mozilla/UniquePtr.h"
 #include "mozilla/unused.h"
 
 // How much data are we willing to send across the wire
 // in one chunk?
 static const int32_t kSendDataChunk = 0xffff;
 
 namespace mozilla {
 namespace plugins {
@@ -104,25 +105,25 @@ BrowserStreamParent::AnswerNPN_RequestRe
   }
 
   if (!mStream)
     return false;
 
   if (ranges.Length() > INT32_MAX)
     return false;
 
-  nsAutoArrayPtr<NPByteRange> rp(new NPByteRange[ranges.Length()]);
+  UniquePtr<NPByteRange[]> rp(new NPByteRange[ranges.Length()]);
   for (uint32_t i = 0; i < ranges.Length(); ++i) {
     rp[i].offset = ranges[i].offset;
     rp[i].length = ranges[i].length;
     rp[i].next = &rp[i + 1];
   }
   rp[ranges.Length() - 1].next = nullptr;
 
-  *result = mNPP->mNPNIface->requestread(mStream, rp);
+  *result = mNPP->mNPNIface->requestread(mStream, rp.get());
   return true;
 }
 
 bool
 BrowserStreamParent::RecvNPN_DestroyStream(const NPReason& reason)
 {
   switch (mState) {
   case ALIVE:
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -28,16 +28,17 @@ using mozilla::gfx::SharedDIBSurface;
 #include "gfxSharedImageSurface.h"
 #include "gfxUtils.h"
 #include "gfxAlphaRecovery.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/ipc/MessageChannel.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/StaticPtr.h"
+#include "mozilla/UniquePtr.h"
 #include "ImageContainer.h"
 
 using namespace mozilla;
 using mozilla::ipc::ProcessChild;
 using namespace mozilla::plugins;
 using namespace mozilla::layers;
 using namespace mozilla::gfx;
 using namespace std;
@@ -223,30 +224,30 @@ PluginInstanceChild::~PluginInstanceChil
 NPError
 PluginInstanceChild::DoNPP_New()
 {
     // unpack the arguments into a C format
     int argc = mNames.Length();
     NS_ASSERTION(argc == (int) mValues.Length(),
                  "argn.length != argv.length");
 
-    nsAutoArrayPtr<char*> argn(new char*[1 + argc]);
-    nsAutoArrayPtr<char*> argv(new char*[1 + argc]);
+    UniquePtr<char*[]> argn(new char*[1 + argc]);
+    UniquePtr<char*[]> argv(new char*[1 + argc]);
     argn[argc] = 0;
     argv[argc] = 0;
 
     for (int i = 0; i < argc; ++i) {
         argn[i] = const_cast<char*>(NullableStringGet(mNames[i]));
         argv[i] = const_cast<char*>(NullableStringGet(mValues[i]));
     }
 
     NPP npp = GetNPP();
 
     NPError rv = mPluginIface->newp((char*)NullableStringGet(mMimeType), npp,
-                                    mMode, argc, argn, argv, 0);
+                                    mMode, argc, argn.get(), argv.get(), 0);
     if (NPERR_NO_ERROR != rv) {
         return rv;
     }
 
     Initialize();
 
 #if defined(XP_MACOSX) && defined(__i386__)
     // If an i386 Mac OS X plugin has selected the Carbon event model then
--- a/dom/plugins/ipc/PluginMessageUtils.h
+++ b/dom/plugins/ipc/PluginMessageUtils.h
@@ -7,16 +7,17 @@
 #ifndef DOM_PLUGINS_PLUGINMESSAGEUTILS_H
 #define DOM_PLUGINS_PLUGINMESSAGEUTILS_H
 
 #include "ipc/IPCMessageUtils.h"
 #include "base/message_loop.h"
 
 #include "mozilla/ipc/MessageChannel.h"
 #include "mozilla/ipc/CrossProcessMutex.h"
+#include "mozilla/UniquePtr.h"
 #include "gfxipc/ShadowLayerUtils.h"
 
 #include "npapi.h"
 #include "npruntime.h"
 #include "npfunctions.h"
 #include "nsAutoPtr.h"
 #include "nsString.h"
 #include "nsTArray.h"
@@ -415,20 +416,20 @@ struct ParamTraits<NPString>
     if (ReadParam(aMsg, aIter, &aResult->UTF8Length)) {
       int byteCount = aResult->UTF8Length * sizeof(NPUTF8);
       if (!byteCount) {
         aResult->UTF8Characters = "\0";
         return true;
       }
 
       const char* messageBuffer = nullptr;
-      nsAutoArrayPtr<char> newBuffer(new char[byteCount]);
+      mozilla::UniquePtr<char[]> newBuffer(new char[byteCount]);
       if (newBuffer && aMsg->ReadBytes(aIter, &messageBuffer, byteCount )) {
         memcpy((void*)messageBuffer, newBuffer.get(), byteCount);
-        aResult->UTF8Characters = newBuffer.forget();
+        aResult->UTF8Characters = newBuffer.release();
         return true;
       }
     }
     return false;
   }
 
   static void Log(const paramType& aParam, std::wstring* aLog)
   {
--- a/dom/workers/ServiceWorkerEvents.cpp
+++ b/dom/workers/ServiceWorkerEvents.cpp
@@ -108,20 +108,17 @@ namespace {
 
 void
 AsyncLog(nsIInterceptedChannel *aInterceptedChannel,
          const nsACString& aRespondWithScriptSpec,
          uint32_t aRespondWithLineNumber, uint32_t aRespondWithColumnNumber,
          const nsACString& aMessageName, const nsTArray<nsString>& aParams)
 {
   MOZ_ASSERT(aInterceptedChannel);
-  // Since the intercepted channel is kept alive and paused while handling
-  // the FetchEvent, we are guaranteed the reporter is stable on the worker
-  // thread.
-  nsIConsoleReportCollector* reporter =
+  nsCOMPtr<nsIConsoleReportCollector> reporter =
     aInterceptedChannel->GetConsoleReportCollector();
   if (reporter) {
     reporter->AddConsoleReport(nsIScriptError::errorFlag,
                                NS_LITERAL_CSTRING("Service Worker Interception"),
                                nsContentUtils::eDOM_PROPERTIES,
                                aRespondWithScriptSpec,
                                aRespondWithLineNumber,
                                aRespondWithColumnNumber,
@@ -391,18 +388,18 @@ ExtractErrorValues(JSContext* aCx, JS::H
       }
       aMessageOut.Assign(report->mErrorMsg);
     }
 
     // Next, try to unwrap the rejection value as a DOMException.
     else if(NS_SUCCEEDED(UNWRAP_OBJECT(DOMException, obj, domException))) {
 
       nsAutoString filename;
-      if (NS_SUCCEEDED(domException->GetFilename(filename)) &&
-          !filename.IsEmpty()) {
+      domException->GetFilename(filename);
+      if (!filename.IsEmpty()) {
         CopyUTF16toUTF8(filename, aSourceSpecOut);
         *aLineOut = domException->LineNumber();
         *aColumnOut = domException->ColumnNumber();
       }
 
       domException->GetName(aMessageOut);
       aMessageOut.AppendLiteral(": ");
 
@@ -414,16 +411,18 @@ ExtractErrorValues(JSContext* aCx, JS::H
 
   // If we could not unwrap a specific error type, then perform default safe
   // string conversions on primitives.  Objects will result in "[Object]"
   // unfortunately.
   if (aMessageOut.IsEmpty()) {
     nsAutoJSString jsString;
     if (jsString.init(aCx, aValue)) {
       aMessageOut = jsString;
+    } else {
+      JS_ClearPendingException(aCx);
     }
   }
 }
 
 } // anonymous namespace
 
 class MOZ_STACK_CLASS AutoCancel
 {
--- a/dom/xul/XULDocument.cpp
+++ b/dom/xul/XULDocument.cpp
@@ -3239,57 +3239,43 @@ XULDocument::LoadScript(nsXULPrototypeSc
             rv = ExecuteScript(aScriptProto);
 
             // Ignore return value from execution, and don't block
             *aBlock = false;
             return NS_OK;
         }
     }
 
-    // Allow security manager and content policies to veto the load. Note that
-    // at this point we already lost context information of the script.
-    rv = nsScriptLoader::ShouldLoadScript(
-                            this,
-                            static_cast<nsIDocument*>(this),
-                            aScriptProto->mSrcURI,
-                            NS_LITERAL_STRING("application/x-javascript"),
-                            false);
-    if (NS_FAILED(rv)) {
-      *aBlock = false;
-      return rv;
-    }
-
     // Release script objects from FastLoad since we decided against using them
     aScriptProto->UnlinkJSObjects();
 
     // Set the current script prototype so that OnStreamComplete can report
     // the right file if there are errors in the script.
     NS_ASSERTION(!mCurrentScriptProto,
                  "still loading a script when starting another load?");
     mCurrentScriptProto = aScriptProto;
 
-    if (aScriptProto->mSrcLoading) {
+    if (isChromeDoc && aScriptProto->mSrcLoading) {
         // Another XULDocument load has started, which is still in progress.
         // Remember to ResumeWalk this document when the load completes.
         mNextSrcLoadWaiter = aScriptProto->mSrcLoadWaiters;
         aScriptProto->mSrcLoadWaiters = this;
         NS_ADDREF_THIS();
     }
     else {
         nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
 
         // Note: the loader will keep itself alive while it's loading.
         nsCOMPtr<nsIStreamLoader> loader;
         rv = NS_NewStreamLoader(getter_AddRefs(loader),
                                 aScriptProto->mSrcURI,
                                 this, // aObserver
                                 this, // aRequestingContext
-                                nsILoadInfo::SEC_NORMAL,
-                                nsIContentPolicy::TYPE_OTHER,
-                                nullptr, // aContext
+                                nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS,
+                                nsIContentPolicy::TYPE_INTERNAL_SCRIPT,
                                 group);
 
         if (NS_FAILED(rv)) {
             mCurrentScriptProto = nullptr;
             return rv;
         }
 
         aScriptProto->mSrcLoading = true;
new file mode 100644
--- /dev/null
+++ b/gfx/tests/crashtests/691581-1.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html style="font-size: 0.03rem;">
+<body>
+    Test Text
+</body>
+</html>
--- a/gfx/tests/crashtests/crashtests.list
+++ b/gfx/tests/crashtests/crashtests.list
@@ -96,16 +96,17 @@ load 595042-1.html
 load 595727-1.html
 load 624198.xhtml
 load 633322-1.html
 load 633453-1.html
 load 662467-1.html
 load 665218.html
 load 675550-1.html
 load 686190-1.html
+load 691581-1.html
 load 693143-1.html
 load 696936-1.html
 load 699563-1.html
 load 710149-1.html
 load 768079-1.html
 load 783041-1.html
 load 783041-2.html
 load 783041-3.html
--- a/gfx/thebes/gfxBlur.cpp
+++ b/gfx/thebes/gfxBlur.cpp
@@ -947,17 +947,18 @@ gfxAlphaBoxBlur::GetInsetBlur(IntMargin&
     return nullptr;
   }
 
   DrawTarget* minDrawTarget = minGfxContext->GetDrawTarget();
   RefPtr<Path> maskPath = GetBoxShadowInsetPath(minDrawTarget, ToRect(outerRect),
                                                 ToRect(innerRect), aHasBorderRadius,
                                                 aInnerClipRadii);
 
-  minGfxContext->SetColor(aShadowColor);
+  Color black(0.f, 0.f, 0.f, 1.f);
+  minGfxContext->SetColor(black);
   minGfxContext->SetPath(maskPath);
   minGfxContext->Fill();
 
   IntPoint topLeft;
   RefPtr<SourceSurface> minMask = DoBlur(minDrawTarget, &topLeft);
   if (!minMask) {
     return nullptr;
   }
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -743,17 +743,17 @@ gfxFont::RunMetrics::CombineWith(const R
 
 gfxFont::gfxFont(gfxFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
                  AntialiasOption anAAOption, cairo_scaled_font_t *aScaledFont) :
     mScaledFont(aScaledFont),
     mFontEntry(aFontEntry), mIsValid(true),
     mApplySyntheticBold(false),
     mStyle(*aFontStyle),
     mAdjustedSize(0.0),
-    mFUnitsConvFactor(0.0f),
+    mFUnitsConvFactor(-1.0f), // negative to indicate "not yet initialized"
     mAntialiasOption(anAAOption)
 {
 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
     ++gFontCount;
 #endif
     mKerningSet = HasFeatureSet(HB_TAG('k','e','r','n'), mKerningEnabled);
 }
 
@@ -780,20 +780,20 @@ gfxFloat
 gfxFont::GetGlyphHAdvance(gfxContext *aCtx, uint16_t aGID)
 {
     if (!SetupCairoFont(aCtx)) {
         return 0;
     }
     if (ProvidesGlyphWidths()) {
         return GetGlyphWidth(*aCtx->GetDrawTarget(), aGID) / 65536.0;
     }
-    if (mFUnitsConvFactor == 0.0f) {
+    if (mFUnitsConvFactor < 0.0f) {
         GetMetrics(eHorizontal);
     }
-    NS_ASSERTION(mFUnitsConvFactor > 0.0f,
+    NS_ASSERTION(mFUnitsConvFactor >= 0.0f,
                  "missing font unit conversion factor");
     if (!mHarfBuzzShaper) {
         mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
     }
     gfxHarfBuzzShaper* shaper =
         static_cast<gfxHarfBuzzShaper*>(mHarfBuzzShaper.get());
     if (!shaper->Initialize()) {
         return 0;
@@ -3210,17 +3210,17 @@ gfxFont::InitMetricsFromSfntTables(Metri
     mIsValid = false; // font is NOT valid in case of early return
 
     const uint32_t kHheaTableTag = TRUETYPE_TAG('h','h','e','a');
     const uint32_t kPostTableTag = TRUETYPE_TAG('p','o','s','t');
     const uint32_t kOS_2TableTag = TRUETYPE_TAG('O','S','/','2');
 
     uint32_t len;
 
-    if (mFUnitsConvFactor == 0.0) {
+    if (mFUnitsConvFactor < 0.0) {
         // If the conversion factor from FUnits is not yet set,
         // get the unitsPerEm from the 'head' table via the font entry
         uint16_t unitsPerEm = GetFontEntry()->UnitsPerEm();
         if (unitsPerEm == gfxFontEntry::kInvalidUPEM) {
             return false;
         }
         mFUnitsConvFactor = GetAdjustedSize() / unitsPerEm;
     }
@@ -3446,28 +3446,28 @@ gfxFont::CreateVerticalMetrics()
     metrics->emDescent = metrics->emHeight - metrics->emAscent;
 
     metrics->maxAscent = metrics->emAscent;
     metrics->maxDescent = metrics->emDescent;
 
     const float UNINITIALIZED_LEADING = -10000.0f;
     metrics->externalLeading = UNINITIALIZED_LEADING;
 
-    if (mFUnitsConvFactor == 0.0) {
+    if (mFUnitsConvFactor < 0.0) {
         uint16_t upem = GetFontEntry()->UnitsPerEm();
         if (upem != gfxFontEntry::kInvalidUPEM) {
             mFUnitsConvFactor = GetAdjustedSize() / upem;
         }
     }
 
 #define SET_UNSIGNED(field,src) metrics->field = uint16_t(src) * mFUnitsConvFactor
 #define SET_SIGNED(field,src)   metrics->field = int16_t(src) * mFUnitsConvFactor
 
     gfxFontEntry::AutoTable os2Table(mFontEntry, kOS_2TableTag);
-    if (os2Table && mFUnitsConvFactor > 0.0) {
+    if (os2Table && mFUnitsConvFactor >= 0.0) {
         const OS2Table *os2 =
             reinterpret_cast<const OS2Table*>(hb_blob_get_data(os2Table, &len));
         // These fields should always be present in any valid OS/2 table
         if (len >= offsetof(OS2Table, sTypoLineGap) + sizeof(int16_t)) {
             SET_SIGNED(strikeoutSize, os2->yStrikeoutSize);
             // Use ascent+descent from the horizontal metrics as the default
             // advance (aveCharWidth) in vertical mode
             gfxFloat ascentDescent = gfxFloat(mFUnitsConvFactor) *
@@ -3483,33 +3483,33 @@ gfxFont::CreateVerticalMetrics()
             metrics->maxDescent = std::max(metrics->maxDescent, halfCharWidth);
         }
     }
 
     // If we didn't set aveCharWidth from OS/2, try to read 'hhea' metrics
     // and use the line height from its ascent/descent.
     if (!metrics->aveCharWidth) {
         gfxFontEntry::AutoTable hheaTable(mFontEntry, kHheaTableTag);
-        if (hheaTable && mFUnitsConvFactor > 0.0) {
+        if (hheaTable && mFUnitsConvFactor >= 0.0) {
             const MetricsHeader* hhea =
                 reinterpret_cast<const MetricsHeader*>
                     (hb_blob_get_data(hheaTable, &len));
             if (len >= sizeof(MetricsHeader)) {
                 SET_SIGNED(aveCharWidth, int16_t(hhea->ascender) -
                                          int16_t(hhea->descender));
                 metrics->maxAscent = metrics->aveCharWidth / 2;
                 metrics->maxDescent =
                     metrics->aveCharWidth - metrics->maxAscent;
             }
         }
     }
 
     // Read real vertical metrics if available.
     gfxFontEntry::AutoTable vheaTable(mFontEntry, kVheaTableTag);
-    if (vheaTable && mFUnitsConvFactor > 0.0) {
+    if (vheaTable && mFUnitsConvFactor >= 0.0) {
         const MetricsHeader* vhea =
             reinterpret_cast<const MetricsHeader*>
                 (hb_blob_get_data(vheaTable, &len));
         if (len >= sizeof(MetricsHeader)) {
             SET_UNSIGNED(maxAdvance, vhea->advanceWidthMax);
             // Redistribute space between ascent/descent because we want a
             // centered vertical baseline by default.
             gfxFloat halfExtent = 0.5 * gfxFloat(mFUnitsConvFactor) *
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -1385,17 +1385,17 @@ public:
     virtual gfxFloat GetAdjustedSize() const {
         return mAdjustedSize > 0.0
                  ? mAdjustedSize
                  : (mStyle.sizeAdjust == 0.0 ? 0.0 : mStyle.size);
     }
 
     float FUnitsToDevUnitsFactor() const {
         // check this was set up during font initialization
-        NS_ASSERTION(mFUnitsConvFactor > 0.0f, "mFUnitsConvFactor not valid");
+        NS_ASSERTION(mFUnitsConvFactor >= 0.0f, "mFUnitsConvFactor not valid");
         return mFUnitsConvFactor;
     }
 
     // check whether this is an sfnt we can potentially use with harfbuzz
     bool FontCanSupportHarfBuzz() {
         return mFontEntry->HasCmapTable();
     }
 
@@ -2027,17 +2027,20 @@ protected:
 
     nsExpirationState          mExpirationState;
     gfxFontStyle               mStyle;
     nsAutoTArray<gfxGlyphExtents*,1> mGlyphExtentsArray;
     nsAutoPtr<nsTHashtable<nsPtrHashKey<GlyphChangeObserver> > > mGlyphChangeObservers;
 
     gfxFloat                   mAdjustedSize;
 
-    float                      mFUnitsConvFactor; // conversion factor from font units to dev units
+    // Conversion factor from font units to dev units; note that this may be
+    // zero (in the degenerate case where mAdjustedSize has become zero).
+    // This is OK because we only multiply by this factor, never divide.
+    float                      mFUnitsConvFactor;
 
     // the AA setting requested for this font - may affect glyph bounds
     AntialiasOption            mAntialiasOption;
 
     // a copy of the font without antialiasing, if needed for separate
     // measurement by mathml code
     nsAutoPtr<gfxFont>         mNonAAFont;
 
--- a/gfx/thebes/gfxGDIFont.cpp
+++ b/gfx/thebes/gfxGDIFont.cpp
@@ -355,16 +355,18 @@ gfxGDIFont::Initialize()
         if (ret != GDI_ERROR && glyph != 0xFFFF) {
             GetTextExtentPoint32W(dc.GetDC(), L"0", 1, &size);
             mMetrics->zeroOrAveCharWidth = ROUND(size.cx);
         } else {
             mMetrics->zeroOrAveCharWidth = mMetrics->aveCharWidth;
         }
 
         SanitizeMetrics(mMetrics, GetFontEntry()->mIsBadUnderlineFont);
+    } else {
+        mFUnitsConvFactor = 0.0; // zero-sized font: all values scale to zero
     }
 
     if (IsSyntheticBold()) {
         mMetrics->aveCharWidth += GetSyntheticBoldOffset();
         mMetrics->maxAdvance += GetSyntheticBoldOffset();
     }
 
     mFontFace = cairo_win32_font_face_create_for_logfontw_hfont(&logFont,
--- a/js/src/jit/BacktrackingAllocator.cpp
+++ b/js/src/jit/BacktrackingAllocator.cpp
@@ -3004,17 +3004,17 @@ BacktrackingAllocator::splitAcrossCalls(
         LiveRange* callRange = LiveRange::get(*iter);
         if (bundle->rangeFor(callRange->from()) && bundle->rangeFor(callRange->from().previous())) {
             if (!callPositions.append(callRange->from()))
                 return false;
         }
     }
     MOZ_ASSERT(callPositions.length());
 
-#ifdef DEBUG
+#ifdef JS_JITSPEW
     JitSpewStart(JitSpew_RegAlloc, "  split across calls at ");
     for (size_t i = 0; i < callPositions.length(); ++i)
         JitSpewCont(JitSpew_RegAlloc, "%s%u", i != 0 ? ", " : "", callPositions[i].bits());
     JitSpewFin(JitSpew_RegAlloc);
 #endif
 
     return splitAt(bundle, callPositions);
 }
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -966,17 +966,19 @@ InitFromBailout(JSContext* cx, HandleScr
                 // For fun.apply({}, arguments) the reconstructStackDepth will
                 // have stackdepth 4, but it could be that we inlined the
                 // funapply. In that case exprStackSlots, will have the real
                 // arguments in the slots and not be 4.
                 MOZ_ASSERT(exprStackSlots == expectedDepth);
             }
         }
     }
+#endif
 
+#ifdef JS_JITSPEW
     JitSpew(JitSpew_BaselineBailouts, "      Resuming %s pc offset %d (op %s) (line %d) of %s:%" PRIuSIZE,
                 resumeAfter ? "after" : "at", (int) pcOff, js_CodeName[op],
                 PCToLineNumber(script, pc), script->filename(), script->lineno());
     JitSpew(JitSpew_BaselineBailouts, "      Bailout kind: %s",
             BailoutKindString(bailoutKind));
 #endif
 
     bool pushedNewTarget = op == JSOP_NEW;
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -2827,17 +2827,17 @@ InvalidateActivation(FreeOp* fop, const 
         activations->asJit()->setCheckRegs(false);
 #endif
 
     size_t frameno = 1;
 
     for (JitFrameIterator it(activations); !it.done(); ++it, ++frameno) {
         MOZ_ASSERT_IF(frameno == 1, it.isExitFrame() || it.type() == JitFrame_Bailout);
 
-#ifdef DEBUG
+#ifdef JS_JITSPEW
         switch (it.type()) {
           case JitFrame_Exit:
           case JitFrame_LazyLink:
             JitSpew(JitSpew_IonInvalidate, "#%d exit frame @ %p", frameno, it.fp());
             break;
           case JitFrame_BaselineJS:
           case JitFrame_IonJS:
           case JitFrame_Bailout:
@@ -2877,17 +2877,17 @@ InvalidateActivation(FreeOp* fop, const 
             break;
           case JitFrame_IonAccessorIC:
             JitSpew(JitSpew_IonInvalidate, "#%d ion IC getter/setter frame @ %p", frameno, it.fp());
             break;
           case JitFrame_Entry:
             JitSpew(JitSpew_IonInvalidate, "#%d entry frame @ %p", frameno, it.fp());
             break;
         }
-#endif
+#endif // JS_JITSPEW
 
         if (!it.isIonScripted())
             continue;
 
         bool calledFromLinkStub = false;
         JitCode* lazyLinkStub = fop->runtime()->jitRuntime()->lazyLinkStub();
         if (it.returnAddressToFp() >= lazyLinkStub->raw() &&
             it.returnAddressToFp() < lazyLinkStub->rawEnd())
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -184,22 +184,26 @@ IonBuilder::clearForBackEnd()
     gsn.purge();
     scopeCoordinateNameCache.purge();
 }
 
 bool
 IonBuilder::abort(const char* message, ...)
 {
     // Don't call PCToLineNumber in release builds.
-#ifdef DEBUG
+#ifdef JS_JITSPEW
     va_list ap;
     va_start(ap, message);
     abortFmt(message, ap);
     va_end(ap);
+# ifdef DEBUG
     JitSpew(JitSpew_IonAbort, "aborted @ %s:%d", script()->filename(), PCToLineNumber(script(), pc));
+# else
+    JitSpew(JitSpew_IonAbort, "aborted @ %s", script()->filename());
+# endif
 #endif
     trackActionableAbort(message);
     return false;
 }
 
 IonBuilder*
 IonBuilder::outermostBuilder()
 {
@@ -10577,17 +10581,17 @@ IonBuilder::annotateGetPropertyCache(MDe
             return false;
     }
 
     if (inlinePropTable->numEntries() == 0) {
         getPropCache->clearInlinePropertyTable();
         return true;
     }
 
-#ifdef DEBUG
+#ifdef JS_JITSPEW
     if (inlinePropTable->numEntries() > 0)
         JitSpew(JitSpew_Inlining, "Annotated GetPropertyCache with %d/%d inline cases",
                                     (int) inlinePropTable->numEntries(), (int) objCount);
 #endif
 
     // If we successfully annotated the GetPropertyCache and there are inline cases,
     // then keep a resume point of the state right before this instruction for use
     // later when we have to bail out to this point in the fallback case of a
--- a/js/src/jit/JitcodeMap.cpp
+++ b/js/src/jit/JitcodeMap.cpp
@@ -1267,17 +1267,17 @@ JitcodeRegionEntry::ExpectedRunLength(co
         curBytecodeOffset = nextBytecodeOffset;
     }
 
     return runLength;
 }
 
 struct JitcodeMapBufferWriteSpewer
 {
-#ifdef DEBUG
+#ifdef JS_JITSPEW
     CompactBufferWriter* writer;
     uint32_t startPos;
 
     static const uint32_t DumpMaxBytes = 50;
 
     explicit JitcodeMapBufferWriteSpewer(CompactBufferWriter& w)
       : writer(&w), startPos(writer->length())
     {}
@@ -1300,20 +1300,20 @@ struct JitcodeMapBufferWriteSpewer
         else
             buffer[bytes*3 - 1] = '\0';
 
         JitSpew(JitSpew_Profiling, "%s@%d[%d bytes] - %s", name, int(startPos), int(bytes), buffer);
 
         // Move to the end of the current buffer.
         startPos = writer->length();
     }
-#else // !DEBUG
+#else // !JS_JITSPEW
     explicit JitcodeMapBufferWriteSpewer(CompactBufferWriter& w) {}
     void spewAndAdvance(const char* name) {}
-#endif // DEBUG
+#endif // JS_JITSPEW
 };
 
 // Write a run, starting at the given NativeToBytecode entry, into the given buffer writer.
 /* static */ bool
 JitcodeRegionEntry::WriteRun(CompactBufferWriter& writer,
                              JSScript** scriptList, uint32_t scriptListSize,
                              uint32_t runLength, const CodeGeneratorShared::NativeToBytecode* entry)
 {
--- a/js/src/jit/LICM.cpp
+++ b/js/src/jit/LICM.cpp
@@ -22,17 +22,17 @@ LoopContainsPossibleCall(MIRGraph& graph
         MOZ_ASSERT(i != graph.rpoEnd(), "Reached end of graph searching for blocks in loop");
         MBasicBlock* block = *i;
         if (!block->isMarked())
             continue;
 
         for (auto insIter(block->begin()), insEnd(block->end()); insIter != insEnd; ++insIter) {
             MInstruction* ins = *insIter;
             if (ins->possiblyCalls()) {
-#ifdef DEBUG
+#ifdef JS_JITSPEW
                 JitSpew(JitSpew_LICM, "    Possile call found at %s%u", ins->opName(), ins->id());
 #endif
                 return true;
             }
         }
 
         if (block == backedge)
             break;
@@ -149,71 +149,71 @@ MoveDeferredOperands(MInstruction* ins, 
         MOZ_ASSERT(RequiresHoistedUse(op, hasCalls),
                    "Deferred loop-invariant operand is not cheap");
         MInstruction* opIns = op->toInstruction();
 
         // Recursively move the operands. Note that the recursion is bounded
         // because we require RequiresHoistedUse to be set at each level.
         MoveDeferredOperands(opIns, hoistPoint, hasCalls);
 
-#ifdef DEBUG
+#ifdef JS_JITSPEW
         JitSpew(JitSpew_LICM, "    Hoisting %s%u (now that a user will be hoisted)",
                 opIns->opName(), opIns->id());
 #endif
 
         opIns->block()->moveBefore(hoistPoint, opIns);
     }
 }
 
 static void
 VisitLoopBlock(MBasicBlock* block, MBasicBlock* header, MInstruction* hoistPoint, bool hasCalls)
 {
     for (auto insIter(block->begin()), insEnd(block->end()); insIter != insEnd; ) {
         MInstruction* ins = *insIter++;
 
         if (!IsHoistable(ins, header, hasCalls)) {
-#ifdef DEBUG
+#ifdef JS_JITSPEW
             if (IsHoistableIgnoringDependency(ins, hasCalls)) {
                 JitSpew(JitSpew_LICM, "    %s%u isn't hoistable due to dependency on %s%u",
                         ins->opName(), ins->id(),
                         ins->dependency()->opName(), ins->dependency()->id());
             }
 #endif
             continue;
         }
 
         // Don't hoist a cheap constant if it doesn't enable us to hoist one of
         // its uses. We want those instructions as close as possible to their
         // use, to minimize register pressure.
         if (RequiresHoistedUse(ins, hasCalls)) {
-#ifdef DEBUG
+#ifdef JS_JITSPEW
             JitSpew(JitSpew_LICM, "    %s%u will be hoisted only if its users are",
                     ins->opName(), ins->id());
 #endif
             continue;
         }
 
         // Hoist operands which were too cheap to hoist on their own.
         MoveDeferredOperands(ins, hoistPoint, hasCalls);
 
-#ifdef DEBUG
+#ifdef JS_JITSPEW
         JitSpew(JitSpew_LICM, "    Hoisting %s%u", ins->opName(), ins->id());
 #endif
 
         // Move the instruction to the hoistPoint.
         block->moveBefore(hoistPoint, ins);
     }
 }
 
 static void
 VisitLoop(MIRGraph& graph, MBasicBlock* header)
 {
     MInstruction* hoistPoint = header->loopPredecessor()->lastIns();
 
-#ifdef DEBUG
+#ifdef JS_JITSPEW
     JitSpew(JitSpew_LICM, "  Visiting loop with header block%u, hoisting to %s%u",
             header->id(), hoistPoint->opName(), hoistPoint->id());
 #endif
 
     MBasicBlock* backedge = header->backedge();
 
     // This indicates whether the loop contains calls or other things which
     // clobber most or all floating-point registers. In such loops,
--- a/js/src/jit/LIR.cpp
+++ b/js/src/jit/LIR.cpp
@@ -467,17 +467,17 @@ LNode::printOperands(GenericPrinter& out
 }
 
 void
 LInstruction::assignSnapshot(LSnapshot* snapshot)
 {
     MOZ_ASSERT(!snapshot_);
     snapshot_ = snapshot;
 
-#ifdef DEBUG
+#ifdef JS_JITSPEW
     if (JitSpewEnabled(JitSpew_IonSnapshots)) {
         JitSpewHeader(JitSpew_IonSnapshots);
         Fprinter& out = JitSpewPrinter();
         out.printf("Assigning snapshot %p to instruction %p (",
                    (void*)snapshot, (void*)this);
         printName(out);
         out.printf(")\n");
     }
--- a/js/src/jit/LoopUnroller.cpp
+++ b/js/src/jit/LoopUnroller.cpp
@@ -163,17 +163,17 @@ LoopUnroller::go(LoopIterationBound* bou
     for (size_t i = 0; i < ArrayLength(bodyBlocks); i++) {
         MBasicBlock* block = bodyBlocks[i];
         for (MInstructionIterator iter(block->begin()); iter != block->end(); iter++) {
             MInstruction* ins = *iter;
             if (ins->canClone())
                 continue;
             if (ins->isTest() || ins->isGoto() || ins->isInterruptCheck())
                 continue;
-#ifdef DEBUG
+#ifdef JS_JITSPEW
             JitSpew(JitSpew_Unrolling, "Aborting: can't clone instruction %s", ins->opName());
 #endif
             return;
         }
     }
 
     // Compute the linear inequality we will use for exiting the unrolled loop:
     //
--- a/js/src/jit/OptimizationTracking.cpp
+++ b/js/src/jit/OptimizationTracking.cpp
@@ -134,44 +134,44 @@ JS::TrackedTypeSiteString(TrackedTypeSit
         MOZ_CRASH("bad type site");
     }
 }
 
 void
 SpewTempOptimizationTypeInfoVector(const TempOptimizationTypeInfoVector* types,
                                    const char* indent = nullptr)
 {
-#ifdef DEBUG
+#ifdef JS_JITSPEW
     for (const OptimizationTypeInfo* t = types->begin(); t != types->end(); t++) {
         JitSpewStart(JitSpew_OptimizationTracking, "   %s%s of type %s, type set",
                      indent ? indent : "",
                      TrackedTypeSiteString(t->site()), StringFromMIRType(t->mirType()));
         for (uint32_t i = 0; i < t->types().length(); i++)
             JitSpewCont(JitSpew_OptimizationTracking, " %s", TypeSet::TypeString(t->types()[i]));
         JitSpewFin(JitSpew_OptimizationTracking);
     }
 #endif
 }
 
 void
 SpewTempOptimizationAttemptsVector(const TempOptimizationAttemptsVector* attempts,
                                    const char* indent = nullptr)
 {
-#ifdef DEBUG
+#ifdef JS_JITSPEW
     for (const OptimizationAttempt* a = attempts->begin(); a != attempts->end(); a++) {
         JitSpew(JitSpew_OptimizationTracking, "   %s%s: %s", indent ? indent : "",
                 TrackedStrategyString(a->strategy()), TrackedOutcomeString(a->outcome()));
     }
 #endif
 }
 
 void
 TrackedOptimizations::spew() const
 {
-#ifdef DEBUG
+#ifdef JS_JITSPEW
     SpewTempOptimizationTypeInfoVector(&types_);
     SpewTempOptimizationAttemptsVector(&attempts_);
 #endif
 }
 
 bool
 OptimizationTypeInfo::trackTypeSet(TemporaryTypeSet* typeSet)
 {
@@ -847,17 +847,17 @@ MaybeConstructorFromType(TypeSet::Type t
     if (!newScript && obj->maybeUnboxedLayout())
         newScript = obj->unboxedLayout().newScript();
     return newScript ? newScript->function() : nullptr;
 }
 
 static void
 SpewConstructor(TypeSet::Type ty, JSFunction* constructor)
 {
-#ifdef DEBUG
+#ifdef JS_JITSPEW
     if (!constructor->isInterpreted()) {
         JitSpew(JitSpew_OptimizationTracking, "   Unique type %s has native constructor",
                 TypeSet::TypeString(ty));
         return;
     }
 
     char buf[512];
     if (constructor->displayAtom())
@@ -878,17 +878,17 @@ SpewConstructor(TypeSet::Type ty, JSFunc
     JitSpew(JitSpew_OptimizationTracking, "   Unique type %s has constructor %s (%s:%" PRIuSIZE ")",
             TypeSet::TypeString(ty), buf, filename, lineno);
 #endif
 }
 
 static void
 SpewAllocationSite(TypeSet::Type ty, JSScript* script, uint32_t offset)
 {
-#ifdef DEBUG
+#ifdef JS_JITSPEW
     JitSpew(JitSpew_OptimizationTracking, "   Unique type %s has alloc site %s:%u",
             TypeSet::TypeString(ty), script->filename(),
             PCToLineNumber(script, script->offsetToPC(offset)));
 #endif
 }
 
 bool
 jit::WriteIonTrackedOptimizationsTable(JSContext* cx, CompactBufferWriter& writer,
@@ -898,17 +898,17 @@ jit::WriteIonTrackedOptimizationsTable(J
                                        uint32_t* numRegions,
                                        uint32_t* regionTableOffsetp,
                                        uint32_t* typesTableOffsetp,
                                        uint32_t* optimizationTableOffsetp,
                                        IonTrackedTypeVector* allTypes)
 {
     MOZ_ASSERT(unique.sorted());
 
-#ifdef DEBUG
+#ifdef JS_JITSPEW
     // Spew training data, which may be fed into a script to determine a good
     // encoding strategy.
     if (JitSpewEnabled(JitSpew_OptimizationTracking)) {
         JitSpewStart(JitSpew_OptimizationTracking, "=> Training data: ");
         for (const NativeToTrackedOptimizations* entry = start; entry != end; entry++) {
             JitSpewCont(JitSpew_OptimizationTracking, "%u,%u,%u ",
                         entry->startOffset.offset(), entry->endOffset.offset(),
                         unique.indexOf(entry->optimizations));
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -107,17 +107,17 @@ IsDominatedUse(MBasicBlock* block, MUse*
     }
 
     return block->dominates(n->block());
 }
 
 static inline void
 SpewRange(MDefinition* def)
 {
-#ifdef DEBUG
+#ifdef JS_JITSPEW
     if (JitSpewEnabled(JitSpew_Range) && def->type() != MIRType_None && def->range()) {
         JitSpewHeader(JitSpew_Range);
         Fprinter& out = JitSpewPrinter();
         def->printName(out);
         out.printf(" has range ");
         def->range()->dump(out);
     }
 #endif
--- a/js/src/jit/RegisterAllocator.cpp
+++ b/js/src/jit/RegisterAllocator.cpp
@@ -78,20 +78,21 @@ AllocationIntegrityState::record()
     return seen.init();
 }
 
 bool
 AllocationIntegrityState::check(bool populateSafepoints)
 {
     MOZ_ASSERT(!instructions.empty());
 
-#ifdef DEBUG
+#ifdef JS_JITSPEW
     if (JitSpewEnabled(JitSpew_RegAlloc))
         dump();
-
+#endif
+#ifdef DEBUG
     for (size_t blockIndex = 0; blockIndex < graph.numBlocks(); blockIndex++) {
         LBlock* block = graph.getBlock(blockIndex);
 
         // Check that all instruction inputs and outputs have been assigned an allocation.
         for (LInstructionIterator iter = block->begin(); iter != block->end(); iter++) {
             LInstruction* ins = *iter;
 
             for (LInstruction::InputIterator alloc(*ins); alloc.more(); alloc.next())
--- a/js/src/jit/Safepoints.cpp
+++ b/js/src/jit/Safepoints.cpp
@@ -106,17 +106,17 @@ SafepointWriter::writeGcRegs(LSafepoint*
     }
 
     // GC registers are a subset of the spilled registers.
     MOZ_ASSERT((valueRegs.bits() & ~spilledGpr.bits()) == 0);
     MOZ_ASSERT((gc.bits() & ~spilledGpr.bits()) == 0);
 
     WriteFloatRegisterMask(stream_, spilledFloat.bits());
 
-#ifdef DEBUG
+#ifdef JS_JITSPEW
     if (JitSpewEnabled(JitSpew_Safepoints)) {
         for (GeneralRegisterForwardIterator iter(spilledGpr); iter.more(); iter++) {
             const char* type = gc.has(*iter)
                                ? "gc"
                                : slots.has(*iter)
                                  ? "slots"
                                  : valueRegs.has(*iter)
                                    ? "value"
@@ -158,17 +158,17 @@ MapSlotsToBitset(BitSet& stackSet, BitSe
     WriteBitset(argumentSet, stream);
 }
 
 void
 SafepointWriter::writeGcSlots(LSafepoint* safepoint)
 {
     LSafepoint::SlotList& slots = safepoint->gcSlots();
 
-#ifdef DEBUG
+#ifdef JS_JITSPEW
     for (uint32_t i = 0; i < slots.length(); i++)
         JitSpew(JitSpew_Safepoints, "    gc slot: %d", slots[i]);
 #endif
 
     MapSlotsToBitset(frameSlots_, argumentSlots_, stream_, slots);
 }
 
 void
@@ -176,29 +176,29 @@ SafepointWriter::writeSlotsOrElementsSlo
 {
     LSafepoint::SlotList& slots = safepoint->slotsOrElementsSlots();
 
     stream_.writeUnsigned(slots.length());
 
     for (uint32_t i = 0; i < slots.length(); i++) {
         if (!slots[i].stack)
             MOZ_CRASH();
-#ifdef DEBUG
+#ifdef JS_JITSPEW
         JitSpew(JitSpew_Safepoints, "    slots/elements slot: %d", slots[i].slot);
 #endif
         stream_.writeUnsigned(slots[i].slot);
     }
 }
 
 void
 SafepointWriter::writeValueSlots(LSafepoint* safepoint)
 {
     LSafepoint::SlotList& slots = safepoint->valueSlots();
 
-#ifdef DEBUG
+#ifdef JS_JITSPEW
     for (uint32_t i = 0; i < slots.length(); i++)
         JitSpew(JitSpew_Safepoints, "    gc value: %d", slots[i]);
 #endif
 
     MapSlotsToBitset(frameSlots_, argumentSlots_, stream_, slots);
 }
 
 #if defined(DEBUG) && defined(JS_NUNBOX32)
@@ -284,17 +284,17 @@ CanEncodeInfoInHeader(const LAllocation&
     return *out < MAX_INFO_VALUE;
 }
 
 void
 SafepointWriter::writeNunboxParts(LSafepoint* safepoint)
 {
     LSafepoint::NunboxList& entries = safepoint->nunboxParts();
 
-# ifdef DEBUG
+# ifdef JS_JITSPEW
     if (JitSpewEnabled(JitSpew_Safepoints)) {
         for (uint32_t i = 0; i < entries.length(); i++) {
             SafepointNunboxEntry& entry = entries[i];
             if (entry.type.isUse() || entry.payload.isUse())
                 continue;
             JitSpewHeader(JitSpew_Safepoints);
             Fprinter& out = JitSpewPrinter();
             out.printf("    nunbox (type in ");
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -26,17 +26,17 @@
 #include "jit/MacroAssembler-inl.h"
 #include "vm/Interpreter-inl.h"
 
 using mozilla::BitwiseCast;
 
 namespace js {
 namespace jit {
 
-#ifdef DEBUG
+#ifdef JS_JITSPEW
 void
 FallbackICSpew(JSContext* cx, ICFallbackStub* stub, const char* fmt, ...)
 {
     if (JitSpewEnabled(JitSpew_BaselineICFallback)) {
         RootedScript script(cx, GetTopJitJSScript(cx));
         jsbytecode* pc = stub->icEntry()->pc(script);
 
         char fmtbuf[100];
@@ -76,17 +76,17 @@ TypeFallbackICSpew(JSContext* cx, ICType
                 script->lineno(),
                 script->pcToOffset(pc),
                 PCToLineNumber(script, pc),
                 script->getWarmUpCount(),
                 (int) stub->numOptimizedMonitorStubs(),
                 fmtbuf);
     }
 }
-#endif
+#endif // JS_JITSPEW
 
 ICFallbackStub*
 ICEntry::fallbackStub() const
 {
     return firstStub()->getChainFallback();
 }
 
 void
--- a/js/src/jit/SharedIC.h
+++ b/js/src/jit/SharedIC.h
@@ -192,17 +192,17 @@ namespace jit {
 class ICStub;
 class ICFallbackStub;
 
 #define FORWARD_DECLARE_STUBS(kindName) class IC##kindName;
     IC_BASELINE_STUB_KIND_LIST(FORWARD_DECLARE_STUBS)
     IC_SHARED_STUB_KIND_LIST(FORWARD_DECLARE_STUBS)
 #undef FORWARD_DECLARE_STUBS
 
-#ifdef DEBUG
+#ifdef JS_JITSPEW
 void FallbackICSpew(JSContext* cx, ICFallbackStub* stub, const char* fmt, ...);
 void TypeFallbackICSpew(JSContext* cx, ICTypeMonitor_Fallback* stub, const char* fmt, ...);
 #else
 #define FallbackICSpew(...)
 #define TypeFallbackICSpew(...)
 #endif
 
 //
--- a/js/src/jit/ValueNumbering.cpp
+++ b/js/src/jit/ValueNumbering.cpp
@@ -51,17 +51,17 @@ bool
 ValueNumberer::VisibleValues::ValueHasher::match(Key k, Lookup l)
 {
     // If one of the instructions depends on a store, and the other instruction
     // does not depend on the same store, the instructions are not congruent.
     if (k->dependency() != l->dependency())
         return false;
 
     bool congruent = k->congruentTo(l); // Ask the values themselves what they think.
-#ifdef DEBUG
+#ifdef JS_JITSPEW
     if (congruent != l->congruentTo(k)) {
        JitSpew(JitSpew_GVN, "      congruentTo relation is not symmetric between %s%u and %s%u!!",
                k->opName(), k->id(),
                l->opName(), l->id());
     }
 #endif
     return congruent;
 }
@@ -327,21 +327,22 @@ ValueNumberer::releaseOperands(MDefiniti
     }
     return true;
 }
 
 // Discard |def| and mine its operands for any subsequently dead defs.
 bool
 ValueNumberer::discardDef(MDefinition* def)
 {
-#ifdef DEBUG
+#ifdef JS_JITSPEW
     JitSpew(JitSpew_GVN, "      Discarding %s %s%u",
             def->block()->isMarked() ? "unreachable" : "dead",
             def->opName(), def->id());
-
+#endif
+#ifdef DEBUG
     MOZ_ASSERT(def != nextDef_, "Invalidating the MDefinition iterator");
     if (def->block()->isMarked()) {
         MOZ_ASSERT(!def->hasUses(), "Discarding def that still has uses");
     } else {
         MOZ_ASSERT(IsDiscardable(def), "Discarding non-discardable definition");
         MOZ_ASSERT(!values_.has(def), "Discarding a definition still in the set");
     }
 #endif
@@ -527,17 +528,17 @@ ValueNumberer::removePredecessorAndClean
                 origBackedgeForOSRFixup = block->backedge();
             } else {
                 // Deleting the entry into the loop makes the loop unreachable.
                 isUnreachableLoop = true;
                 JitSpew(JitSpew_GVN, "      "
                         "Loop with header block%u is no longer reachable",
                         block->id());
             }
-#ifdef DEBUG
+#ifdef JS_JITSPEW
         } else if (block->hasUniqueBackedge() && block->backedge() == pred) {
             JitSpew(JitSpew_GVN, "      Loop with header block%u is no longer a loop",
                     block->id());
 #endif
         }
     }
 
     // Actually remove the CFG edge.
@@ -643,17 +644,17 @@ ValueNumberer::leader(MDefinition* def)
             // dominator tree, so overwrite it.
             values_.overwrite(p, def);
         } else {
             // No match. Add a new entry.
             if (!values_.add(p, def))
                 return nullptr;
         }
 
-#ifdef DEBUG
+#ifdef JS_JITSPEW
         JitSpew(JitSpew_GVN, "      Recording %s%u", def->opName(), def->id());
 #endif
     }
 
     return def;
 }
 
 // Test whether |phi| is dominated by a congruent phi.
@@ -757,17 +758,17 @@ ValueNumberer::visitDefinition(MDefiniti
             return false;
 
         bool isNewInstruction = sim->block() == nullptr;
 
         // If |sim| doesn't belong to a block, insert it next to |def|.
         if (isNewInstruction)
             def->block()->insertAfter(def->toInstruction(), sim->toInstruction());
 
-#ifdef DEBUG
+#ifdef JS_JITSPEW
         JitSpew(JitSpew_GVN, "      Folded %s%u to %s%u",
                 def->opName(), def->id(), sim->opName(), sim->id());
 #endif
         MOZ_ASSERT(!sim->isDiscarded());
         ReplaceAllUsesWith(def, sim);
 
         // The node's foldsTo said |def| can be replaced by |rep|. If |def| is a
         // guard, then either |rep| is also a guard, or a guard isn't actually
@@ -799,17 +800,17 @@ ValueNumberer::visitDefinition(MDefiniti
         def->setDependency(dep);
 
     // Look for a dominating def which makes |def| redundant.
     MDefinition* rep = leader(def);
     if (rep != def) {
         if (rep == nullptr)
             return false;
         if (rep->updateForReplacement(def)) {
-#ifdef DEBUG
+#ifdef JS_JITSPEW
             JitSpew(JitSpew_GVN,
                     "      Replacing %s%u with %s%u",
                     def->opName(), def->id(), rep->opName(), rep->id());
 #endif
             ReplaceAllUsesWith(def, rep);
 
             // The node's congruentTo said |def| is congruent to |rep|, and it's
             // dominated by |rep|. If |def| is a guard, it's covered by |rep|,
@@ -843,17 +844,17 @@ ValueNumberer::visitControlInstruction(M
         return true;
 
     if (rep == nullptr)
         return false;
 
     MControlInstruction* newControl = rep->toControlInstruction();
     MOZ_ASSERT(!newControl->block(),
                "Control instruction replacement shouldn't already be in a block");
-#ifdef DEBUG
+#ifdef JS_JITSPEW
     JitSpew(JitSpew_GVN, "      Folded control instruction %s%u to %s%u",
             control->opName(), control->id(), newControl->opName(), graph_.getNumInstructionIds());
 #endif
 
     // If the simplification removes any CFG edges, update the CFG and remove
     // any blocks that become dead.
     size_t oldNumSuccs = control->numSuccessors();
     size_t newNumSuccs = newControl->numSuccessors();
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -273,29 +273,29 @@ CodeGeneratorShared::addNativeToBytecode
     JitSpew(JitSpew_Profiling, " => Push new entry.");
     dumpNativeToBytecodeEntry(nativeToBytecodeList_.length() - 1);
     return true;
 }
 
 void
 CodeGeneratorShared::dumpNativeToBytecodeEntries()
 {
-#ifdef DEBUG
+#ifdef JS_JITSPEW
     InlineScriptTree* topTree = gen->info().inlineScriptTree();
     JitSpewStart(JitSpew_Profiling, "Native To Bytecode Entries for %s:%d\n",
                  topTree->script()->filename(), topTree->script()->lineno());
     for (unsigned i = 0; i < nativeToBytecodeList_.length(); i++)
         dumpNativeToBytecodeEntry(i);
 #endif
 }
 
 void
 CodeGeneratorShared::dumpNativeToBytecodeEntry(uint32_t idx)
 {
-#ifdef DEBUG
+#ifdef JS_JITSPEW
     NativeToBytecode& ref = nativeToBytecodeList_[idx];
     InlineScriptTree* tree = ref.tree;
     JSScript* script = tree->script();
     uint32_t nativeOffset = ref.nativeOffset.offset();
     unsigned nativeDelta = 0;
     unsigned pcDelta = 0;
     if (idx + 1 < nativeToBytecodeList_.length()) {
         NativeToBytecode* nextRef = &ref + 1;
--- a/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h
+++ b/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h
@@ -447,18 +447,18 @@ struct AssemblerBufferWithConstantPools 
         // check.
         MOZ_ASSERT_IF(numPoolEntries, !canNotPlacePool_);
 
         if (this->oom() && !this->bail())
             return BufferOffset();
 
         insertNopFill();
 
-#ifdef DEBUG
-        if (numPoolEntries) {
+#ifdef JS_JITSPEW
+        if (numPoolEntries && JitSpewEnabled(JitSpew_Pools)) {
             JitSpew(JitSpew_Pools, "[%d] Inserting %d entries into pool", id, numPoolEntries);
             JitSpewStart(JitSpew_Pools, "[%d] data is: 0x", id);
             size_t length = numPoolEntries * sizeof(PoolAllocUnit);
             for (unsigned idx = 0; idx < length; idx++) {
                 JitSpewCont(JitSpew_Pools, "%02x", data[length - idx - 1]);
                 if (((idx & 3) == 3) && (idx + 1 != length))
                     JitSpewCont(JitSpew_Pools, "_");
             }
--- a/layout/base/ActiveLayerTracker.cpp
+++ b/layout/base/ActiveLayerTracker.cpp
@@ -1,16 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ActiveLayerTracker.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/gfx/Matrix.h"
+#include "mozilla/PodOperations.h"
 #include "nsExpirationTracker.h"
 #include "nsContainerFrame.h"
 #include "nsIContent.h"
 #include "nsRefreshDriver.h"
 #include "nsPIDOMWindow.h"
 #include "nsIDocument.h"
 #include "nsAnimationManager.h"
 #include "nsStyleTransformMatrix.h"
@@ -28,94 +29,112 @@ using namespace gfx;
  * When no changes of *any* kind are detected after 75-100ms we remove this
  * object. Because we only track all kinds of activity with a single
  * nsExpirationTracker, it's possible a frame might remain active somewhat
  * spuriously if different kinds of changes kept happening, but that almost
  * certainly doesn't matter.
  */
 class LayerActivity {
 public:
+  enum ActivityIndex {
+    ACTIVITY_OPACITY,
+    ACTIVITY_TRANSFORM,
+    ACTIVITY_LEFT,
+    ACTIVITY_TOP,
+    ACTIVITY_RIGHT,
+    ACTIVITY_BOTTOM,
+    ACTIVITY_MARGIN_LEFT,
+    ACTIVITY_MARGIN_TOP,
+    ACTIVITY_MARGIN_RIGHT,
+    ACTIVITY_MARGIN_BOTTOM,
+    ACTIVITY_BACKGROUND_POSITION,
+
+    ACTIVITY_SCALE,
+
+    // keep as last item
+    ACTIVITY_COUNT
+  };
+
   explicit LayerActivity(nsIFrame* aFrame)
     : mFrame(aFrame)
     , mContent(nullptr)
-    , mOpacityRestyleCount(0)
-    , mTransformRestyleCount(0)
-    , mScaleRestyleCount(0)
-    , mLeftRestyleCount(0)
-    , mTopRestyleCount(0)
-    , mRightRestyleCount(0)
-    , mBottomRestyleCount(0)
-    , mMarginLeftRestyleCount(0)
-    , mMarginTopRestyleCount(0)
-    , mMarginRightRestyleCount(0)
-    , mMarginBottomRestyleCount(0)
-    , mBackgroundPositionRestyleCount(0)
     , mContentActive(false)
-  {}
+  {
+    PodArrayZero(mRestyleCounts);
+  }
   ~LayerActivity();
   nsExpirationState* GetExpirationState() { return &mState; }
   uint8_t& RestyleCountForProperty(nsCSSProperty aProperty)
   {
+    return mRestyleCounts[GetActivityIndexForProperty(aProperty)];
+  }
+
+  static ActivityIndex GetActivityIndexForProperty(nsCSSProperty aProperty)
+  {
     switch (aProperty) {
-    case eCSSProperty_opacity: return mOpacityRestyleCount;
-    case eCSSProperty_transform: return mTransformRestyleCount;
-    case eCSSProperty_left: return mLeftRestyleCount;
-    case eCSSProperty_top: return mTopRestyleCount;
-    case eCSSProperty_right: return mRightRestyleCount;
-    case eCSSProperty_bottom: return mBottomRestyleCount;
-    case eCSSProperty_margin_left: return mMarginLeftRestyleCount;
-    case eCSSProperty_margin_top: return mMarginTopRestyleCount;
-    case eCSSProperty_margin_right: return mMarginRightRestyleCount;
-    case eCSSProperty_margin_bottom: return mMarginBottomRestyleCount;
-    case eCSSProperty_background_position: return mBackgroundPositionRestyleCount;
-    default: MOZ_ASSERT(false); return mOpacityRestyleCount;
+    case eCSSProperty_opacity: return ACTIVITY_OPACITY;
+    case eCSSProperty_transform: return ACTIVITY_TRANSFORM;
+    case eCSSProperty_left: return ACTIVITY_LEFT;
+    case eCSSProperty_top: return ACTIVITY_TOP;
+    case eCSSProperty_right: return ACTIVITY_RIGHT;
+    case eCSSProperty_bottom: return ACTIVITY_BOTTOM;
+    case eCSSProperty_margin_left: return ACTIVITY_MARGIN_LEFT;
+    case eCSSProperty_margin_top: return ACTIVITY_MARGIN_TOP;
+    case eCSSProperty_margin_right: return ACTIVITY_MARGIN_RIGHT;
+    case eCSSProperty_margin_bottom: return ACTIVITY_MARGIN_BOTTOM;
+    case eCSSProperty_background_position: return ACTIVITY_BACKGROUND_POSITION;
+    default: MOZ_ASSERT(false); return ACTIVITY_OPACITY;
     }
   }
 
   // While tracked, exactly one of mFrame or mContent is non-null, depending
   // on whether this property is stored on a frame or on a content node.
   // When this property is expired by the layer activity tracker, both mFrame
   // and mContent are nulled-out and the property is deleted.
   nsIFrame* mFrame;
   nsIContent* mContent;
 
   nsExpirationState mState;
 
   // Previous scale due to the CSS transform property.
   Maybe<gfxSize> mPreviousTransformScale;
 
+  // The scroll frame during for which we most recently received a call to
+  // NotifyAnimatedFromScrollHandler.
+  nsWeakFrame mAnimatingScrollHandlerFrame;
+  // The set of activities that were triggered during
+  // mAnimatingScrollHandlerFrame's scroll event handler.
+  EnumSet<ActivityIndex> mScrollHandlerInducedActivity;
+
   // Number of restyle operations detected
-  uint8_t mOpacityRestyleCount;
-  uint8_t mTransformRestyleCount;
-  uint8_t mScaleRestyleCount;
-  uint8_t mLeftRestyleCount;
-  uint8_t mTopRestyleCount;
-  uint8_t mRightRestyleCount;
-  uint8_t mBottomRestyleCount;
-  uint8_t mMarginLeftRestyleCount;
-  uint8_t mMarginTopRestyleCount;
-  uint8_t mMarginRightRestyleCount;
-  uint8_t mMarginBottomRestyleCount;
-  uint8_t mBackgroundPositionRestyleCount;
+  uint8_t mRestyleCounts[ACTIVITY_COUNT];
   bool mContentActive;
 };
 
 class LayerActivityTracker final : public nsExpirationTracker<LayerActivity,4> {
 public:
   // 75-100ms is a good timeout period. We use 4 generations of 25ms each.
   enum { GENERATION_MS = 100 };
   LayerActivityTracker()
     : nsExpirationTracker<LayerActivity,4>(GENERATION_MS,
                                            "LayerActivityTracker")
+    , mDestroying(false)
   {}
   ~LayerActivityTracker() {
+    mDestroying = true;
     AgeAllGenerations();
   }
 
   virtual void NotifyExpired(LayerActivity* aObject);
+
+public:
+  nsWeakFrame mCurrentScrollHandlerFrame;
+
+private:
+  bool mDestroying;
 };
 
 static LayerActivityTracker* gLayerActivityTracker = nullptr;
 
 LayerActivity::~LayerActivity()
 {
   if (mFrame || mContent) {
     NS_ASSERTION(gLayerActivityTracker, "Should still have a tracker");
@@ -124,16 +143,23 @@ LayerActivity::~LayerActivity()
 }
 
 // Frames with this property have NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY set
 NS_DECLARE_FRAME_PROPERTY(LayerActivityProperty, DeleteValue<LayerActivity>)
 
 void
 LayerActivityTracker::NotifyExpired(LayerActivity* aObject)
 {
+  if (!mDestroying && aObject->mAnimatingScrollHandlerFrame.IsAlive()) {
+    // Reset the restyle counts, but let the layer activity survive.
+    PodArrayZero(aObject->mRestyleCounts);
+    MarkUsed(aObject);
+    return;
+  }
+
   RemoveObject(aObject);
 
   nsIFrame* f = aObject->mFrame;
   nsIContent* c = aObject->mContent;
   aObject->mFrame = nullptr;
   aObject->mContent = nullptr;
 
   MOZ_ASSERT((f == nullptr) != (c == nullptr),
@@ -223,17 +249,17 @@ ActiveLayerTracker::TransferActivityToFr
 
 static void
 IncrementScaleRestyleCountIfNeeded(nsIFrame* aFrame, LayerActivity* aActivity)
 {
   const nsStyleDisplay* display = aFrame->StyleDisplay();
   if (!display->mSpecifiedTransform) {
     // The transform was removed.
     aActivity->mPreviousTransformScale = Nothing();
-    IncrementMutationCount(&aActivity->mScaleRestyleCount);
+    IncrementMutationCount(&aActivity->mRestyleCounts[LayerActivity::ACTIVITY_SCALE]);
     return;
   }
 
   // Compute the new scale due to the CSS transform property.
   nsPresContext* presContext = aFrame->PresContext();
   RuleNodeCacheConditions dummy;
   nsStyleTransformMatrix::TransformReferenceBox refBox(aFrame);
   Matrix4x4 transform =
@@ -241,27 +267,27 @@ IncrementScaleRestyleCountIfNeeded(nsIFr
                                            aFrame->StyleContext(),
                                            presContext,
                                            dummy, refBox,
                                            presContext->AppUnitsPerCSSPixel());
   Matrix transform2D;
   if (!transform.Is2D(&transform2D)) {
     // We don't attempt to handle 3D transforms; just assume the scale changed.
     aActivity->mPreviousTransformScale = Nothing();
-    IncrementMutationCount(&aActivity->mScaleRestyleCount);
+    IncrementMutationCount(&aActivity->mRestyleCounts[LayerActivity::ACTIVITY_SCALE]);
     return;
   }
 
   gfxSize scale = ThebesMatrix(transform2D).ScaleFactors(true);
   if (aActivity->mPreviousTransformScale == Some(scale)) {
     return;  // Nothing changed.
   }
 
   aActivity->mPreviousTransformScale = Some(scale);
-  IncrementMutationCount(&aActivity->mScaleRestyleCount);
+  IncrementMutationCount(&aActivity->mRestyleCounts[LayerActivity::ACTIVITY_SCALE]);
 }
 
 /* static */ void
 ActiveLayerTracker::NotifyRestyle(nsIFrame* aFrame, nsCSSProperty aProperty)
 {
   LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame);
   uint8_t& mutationCount = layerActivity->RestyleCountForProperty(aProperty);
   IncrementMutationCount(&mutationCount);
@@ -270,59 +296,107 @@ ActiveLayerTracker::NotifyRestyle(nsIFra
     IncrementScaleRestyleCountIfNeeded(aFrame, layerActivity);
   }
 }
 
 /* static */ void
 ActiveLayerTracker::NotifyOffsetRestyle(nsIFrame* aFrame)
 {
   LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame);
-  IncrementMutationCount(&layerActivity->mLeftRestyleCount);
-  IncrementMutationCount(&layerActivity->mTopRestyleCount);
-  IncrementMutationCount(&layerActivity->mRightRestyleCount);
-  IncrementMutationCount(&layerActivity->mBottomRestyleCount);
+  IncrementMutationCount(&layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_LEFT]);
+  IncrementMutationCount(&layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_TOP]);
+  IncrementMutationCount(&layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_RIGHT]);
+  IncrementMutationCount(&layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_BOTTOM]);
 }
 
 /* static */ void
 ActiveLayerTracker::NotifyAnimated(nsIFrame* aFrame, nsCSSProperty aProperty)
 {
   LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame);
   uint8_t& mutationCount = layerActivity->RestyleCountForProperty(aProperty);
   // We know this is animated, so just hack the mutation count.
   mutationCount = 0xFF;
 }
 
+/* static */ void
+ActiveLayerTracker::NotifyAnimatedFromScrollHandler(nsIFrame* aFrame, nsCSSProperty aProperty,
+                                                    nsIFrame* aScrollFrame)
+{
+  if (aFrame->PresContext() != aScrollFrame->PresContext()) {
+    // Don't allow cross-document dependencies.
+    return;
+  }
+  LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame);
+  LayerActivity::ActivityIndex activityIndex = LayerActivity::GetActivityIndexForProperty(aProperty);
+
+  if (layerActivity->mAnimatingScrollHandlerFrame.GetFrame() != aScrollFrame) {
+    // Discard any activity of a different scroll frame. We only track the
+    // most recent scroll handler induced activity.
+    layerActivity->mScrollHandlerInducedActivity.clear();
+    layerActivity->mAnimatingScrollHandlerFrame = aScrollFrame;
+  }
+
+  layerActivity->mScrollHandlerInducedActivity += activityIndex;
+}
+
 static bool
 IsPresContextInScriptAnimationCallback(nsPresContext* aPresContext)
 {
   if (aPresContext->RefreshDriver()->IsInRefresh()) {
     return true;
   }
   // Treat timeouts/setintervals as scripted animation callbacks for our
   // purposes.
   nsPIDOMWindow* win = aPresContext->Document()->GetInnerWindow();
   return win && win->IsRunningTimeout();
 }
 
 /* static */ void
 ActiveLayerTracker::NotifyInlineStyleRuleModified(nsIFrame* aFrame,
                                                   nsCSSProperty aProperty)
 {
-  if (!IsPresContextInScriptAnimationCallback(aFrame->PresContext())) {
-    return;
+  if (IsPresContextInScriptAnimationCallback(aFrame->PresContext())) {
+    NotifyAnimated(aFrame, aProperty);
   }
-  NotifyAnimated(aFrame, aProperty);
+  if (gLayerActivityTracker &&
+      gLayerActivityTracker->mCurrentScrollHandlerFrame.IsAlive()) {
+    NotifyAnimatedFromScrollHandler(aFrame, aProperty,
+      gLayerActivityTracker->mCurrentScrollHandlerFrame.GetFrame());
+  }
 }
 
 /* static */ bool
 ActiveLayerTracker::IsStyleMaybeAnimated(nsIFrame* aFrame, nsCSSProperty aProperty)
 {
   return IsStyleAnimated(nullptr, aFrame, aProperty);
 }
 
+static bool
+CheckScrollInducedActivity(LayerActivity* aLayerActivity,
+                           LayerActivity::ActivityIndex aActivityIndex,
+                           nsDisplayListBuilder* aBuilder)
+{
+  if (!aLayerActivity->mScrollHandlerInducedActivity.contains(aActivityIndex) ||
+      !aLayerActivity->mAnimatingScrollHandlerFrame.IsAlive()) {
+    return false;
+  }
+
+  nsIScrollableFrame* scrollFrame =
+    do_QueryFrame(aLayerActivity->mAnimatingScrollHandlerFrame.GetFrame());
+  if (scrollFrame && (!aBuilder || scrollFrame->IsScrollingActive(aBuilder))) {
+    return true;
+  }
+
+  // The scroll frame has been destroyed or has become inactive. Clear it from
+  // the layer activity so that it can expire.
+  aLayerActivity->mAnimatingScrollHandlerFrame = nullptr;
+  aLayerActivity->mScrollHandlerInducedActivity.clear();
+  return false;
+}
+
 /* static */ bool
 ActiveLayerTracker::IsStyleAnimated(nsDisplayListBuilder* aBuilder,
                                     nsIFrame* aFrame, nsCSSProperty aProperty)
 {
   // TODO: Add some abuse restrictions
   if ((aFrame->StyleDisplay()->mWillChangeBitField & NS_STYLE_WILL_CHANGE_TRANSFORM) &&
       aProperty == eCSSProperty_transform &&
       (!aBuilder || aBuilder->IsInWillChangeBudget(aFrame, aFrame->GetSize()))) {
@@ -331,39 +405,43 @@ ActiveLayerTracker::IsStyleAnimated(nsDi
   if ((aFrame->StyleDisplay()->mWillChangeBitField & NS_STYLE_WILL_CHANGE_OPACITY) &&
       aProperty == eCSSProperty_opacity &&
       (!aBuilder || aBuilder->IsInWillChangeBudget(aFrame, aFrame->GetSize()))) {
     return true;
   }
 
   LayerActivity* layerActivity = GetLayerActivity(aFrame);
   if (layerActivity) {
-    if (layerActivity->RestyleCountForProperty(aProperty) >= 2) {
+    LayerActivity::ActivityIndex activityIndex = LayerActivity::GetActivityIndexForProperty(aProperty);
+    if (layerActivity->mRestyleCounts[activityIndex] >= 2) {
+      return true;
+    }
+    if (CheckScrollInducedActivity(layerActivity, activityIndex, aBuilder)) {
       return true;
     }
   }
   if (aProperty == eCSSProperty_transform && aFrame->Combines3DTransformWithAncestors()) {
     return IsStyleAnimated(aBuilder, aFrame->GetParent(), aProperty);
   }
   return nsLayoutUtils::HasCurrentAnimationsForProperties(aFrame, &aProperty, 1);
 }
 
 /* static */ bool
 ActiveLayerTracker::IsOffsetOrMarginStyleAnimated(nsIFrame* aFrame)
 {
   LayerActivity* layerActivity = GetLayerActivity(aFrame);
   if (layerActivity) {
-    if (layerActivity->mLeftRestyleCount >= 2 ||
-        layerActivity->mTopRestyleCount >= 2 ||
-        layerActivity->mRightRestyleCount >= 2 ||
-        layerActivity->mBottomRestyleCount >= 2 ||
-        layerActivity->mMarginLeftRestyleCount >= 2 ||
-        layerActivity->mMarginTopRestyleCount >= 2 ||
-        layerActivity->mMarginRightRestyleCount >= 2 ||
-        layerActivity->mMarginBottomRestyleCount >= 2) {
+    if (layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_LEFT] >= 2 ||
+        layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_TOP] >= 2 ||
+        layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_RIGHT] >= 2 ||
+        layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_BOTTOM] >= 2 ||
+        layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_MARGIN_LEFT] >= 2 ||
+        layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_MARGIN_TOP] >= 2 ||
+        layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_MARGIN_RIGHT] >= 2 ||
+        layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_MARGIN_BOTTOM] >= 2) {
       return true;
     }
   }
   // We should also check for running CSS animations of these properties once
   // bug 1009693 is fixed. Until that happens, layerization isn't useful for
   // animations of these properties because we'll invalidate the layer contents
   // on every change anyway.
   // See bug 1151346 for a patch that adds a check for CSS animations.
@@ -404,17 +482,17 @@ ContainsAnimatedScale(AnimationCollectio
   return false;
 }
 
 /* static */ bool
 ActiveLayerTracker::IsScaleSubjectToAnimation(nsIFrame* aFrame)
 {
   // Check whether JavaScript is animating this frame's scale.
   LayerActivity* layerActivity = GetLayerActivity(aFrame);
-  if (layerActivity && layerActivity->mScaleRestyleCount >= 2) {
+  if (layerActivity && layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_SCALE] >= 2) {
     return true;
   }
 
   nsIContent* content = aFrame->GetContent();
   if (!content || !content->IsElement()) {
     return false;
   }
 
@@ -450,15 +528,24 @@ ActiveLayerTracker::NotifyContentChange(
 /* static */ bool
 ActiveLayerTracker::IsContentActive(nsIFrame* aFrame)
 {
   LayerActivity* layerActivity = GetLayerActivity(aFrame);
   return layerActivity && layerActivity->mContentActive;
 }
 
 /* static */ void
+ActiveLayerTracker::SetCurrentScrollHandlerFrame(nsIFrame* aFrame)
+{
+  if (!gLayerActivityTracker) {
+    gLayerActivityTracker = new LayerActivityTracker();
+  }
+  gLayerActivityTracker->mCurrentScrollHandlerFrame = aFrame;
+}
+
+/* static */ void
 ActiveLayerTracker::Shutdown()
 {
   delete gLayerActivityTracker;
   gLayerActivityTracker = nullptr;
 }
 
 } // namespace mozilla
--- a/layout/base/ActiveLayerTracker.h
+++ b/layout/base/ActiveLayerTracker.h
@@ -45,16 +45,22 @@ public:
    */
   static void NotifyOffsetRestyle(nsIFrame* aFrame);
   /**
    * Mark aFrame as being known to have an animation of aProperty.
    * Any such marking will time out after a short period.
    */
   static void NotifyAnimated(nsIFrame* aFrame, nsCSSProperty aProperty);
   /**
+   * Notify aFrame as being known to have an animation of aProperty through an
+   * inline style modification during aScrollFrame's scroll event handler.
+   */
+  static void NotifyAnimatedFromScrollHandler(nsIFrame* aFrame, nsCSSProperty aProperty,
+                                              nsIFrame* aScrollFrame);
+  /**
    * Notify that a property in the inline style rule of aFrame's element
    * has been modified.
    * This notification is incomplete --- not all modifications to inline
    * style will trigger this.
    */
   static void NotifyInlineStyleRuleModified(nsIFrame* aFrame, nsCSSProperty aProperty);
   /**
    * Return true if aFrame's aProperty style should be considered as being animated
@@ -102,13 +108,20 @@ public:
    * Mark aFrame's content as being active. This marking will time out after
    * a short period.
    */
   static void NotifyContentChange(nsIFrame* aFrame);
   /**
    * Return true if this frame's content is still marked as active.
    */
   static bool IsContentActive(nsIFrame* aFrame);
+
+  /**
+   * Called before and after a scroll event handler is executed, with the
+   * scrollframe or nullptr, respectively. This acts as a hint to treat
+   * inline style changes during the handler differently.
+   */
+  static void SetCurrentScrollHandlerFrame(nsIFrame* aFrame);
 };
 
 } // namespace mozilla
 
 #endif /* ACTIVELAYERTRACKER_H_ */
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -2136,16 +2136,35 @@ nsDisplayBackgroundImage::nsDisplayBackg
                                                    const nsStyleBackground* aBackgroundStyle)
   : nsDisplayImageContainer(aBuilder, aFrame)
   , mBackgroundStyle(aBackgroundStyle)
   , mLayer(aLayer)
 {
   MOZ_COUNT_CTOR(nsDisplayBackgroundImage);
 
   mBounds = GetBoundsInternal(aBuilder);
+  mDestArea = GetDestAreaInternal(aBuilder);
+}
+
+nsRect
+nsDisplayBackgroundImage::GetDestAreaInternal(nsDisplayListBuilder* aBuilder)
+{
+  if (!mBackgroundStyle) {
+    return nsRect();
+  }
+
+  nsPresContext* presContext = mFrame->PresContext();
+  uint32_t flags = aBuilder->GetBackgroundPaintFlags();
+  nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
+  const nsStyleBackground::Layer &layer = mBackgroundStyle->mLayers[mLayer];
+
+  nsBackgroundLayerState state =
+    nsCSSRendering::PrepareBackgroundLayer(presContext, mFrame, flags,
+                                           borderArea, borderArea, layer);
+  return state.mDestArea;
 }
 
 nsDisplayBackgroundImage::~nsDisplayBackgroundImage()
 {
 #ifdef NS_BUILD_REFCNT_LOGGING
   MOZ_COUNT_DTOR(nsDisplayBackgroundImage);
 #endif
 }
@@ -2429,17 +2448,17 @@ nsDisplayBackgroundImage::CanOptimizeToI
   if (!state.mDestArea.IsEqualEdges(state.mFillArea)) {
     return false;
   }
 
   // XXX Ignoring state.mAnchor. ImageLayer drawing snaps mDestArea edges to
   // layer pixel boundaries. This should be OK for now.
 
   int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
-  mDestRect =
+  mImageLayerDestRect =
     LayoutDeviceRect::FromAppUnits(state.mDestArea, appUnitsPerDevPixel);
 
   // Ok, we can turn this into a layer if needed.
   mImage = imageRenderer->GetImage();
   MOZ_ASSERT(mImage);
 
   return true;
 }
@@ -2522,17 +2541,17 @@ nsDisplayBackgroundImage::GetLayerState(
 
     MOZ_ASSERT(mImage);
     int32_t imageWidth;
     int32_t imageHeight;
     mImage->GetWidth(&imageWidth);
     mImage->GetHeight(&imageHeight);
     NS_ASSERTION(imageWidth != 0 && imageHeight != 0, "Invalid image size!");
 
-    const LayerRect destLayerRect = mDestRect * aParameters.Scale();
+    const LayerRect destLayerRect = mImageLayerDestRect * aParameters.Scale();
 
     // Calculate the scaling factor for the frame.
     const gfxSize scale = gfxSize(destLayerRect.width / imageWidth,
                                   destLayerRect.height / imageHeight);
 
     if ((scale.width != 1.0f || scale.height != 1.0f) &&
         (destLayerRect.width * destLayerRect.height >= 64 * 64)) {
       // Separate this image into a layer.
@@ -2584,20 +2603,20 @@ nsDisplayBackgroundImage::ConfigureLayer
   }
 
   // XXX(seth): Right now we ignore aParameters.Scale() and
   // aParameters.Offset(), because FrameLayerBuilder already applies
   // aParameters.Scale() via the layer's post-transform, and
   // aParameters.Offset() is always zero.
   MOZ_ASSERT(aParameters.Offset() == LayerIntPoint(0,0));
 
-  const LayoutDevicePoint p = mDestRect.TopLeft();
+  const LayoutDevicePoint p = mImageLayerDestRect.TopLeft();
   Matrix transform = Matrix::Translation(p.x, p.y);
-  transform.PreScale(mDestRect.width / imageWidth,
-                     mDestRect.height / imageHeight);
+  transform.PreScale(mImageLayerDestRect.width / imageWidth,
+                     mImageLayerDestRect.height / imageHeight);
   aLayer->SetBaseTransform(gfx::Matrix4x4::From2D(transform));
 }
 
 void
 nsDisplayBackgroundImage::HitTest(nsDisplayListBuilder* aBuilder,
                                   const nsRect& aRect,
                                   HitTestState* aState,
                                   nsTArray<nsIFrame*> *aOutFrames)
@@ -2786,16 +2805,23 @@ void nsDisplayBackgroundImage::ComputeIn
     // so invalidate everything (both old and new painting areas).
     aInvalidRegion->Or(bounds, geometry->mBounds);
 
     if (positioningArea.Size() != geometry->mPositioningArea.Size()) {
       NotifyRenderingChanged();
     }
     return;
   }
+  if (!mDestArea.IsEqualInterior(geometry->mDestArea)) {
+    // Dest area changed in a way that could cause everything to change,
+    // so invalidate everything (both old and new painting areas).
+    aInvalidRegion->Or(bounds, geometry->mBounds);
+    NotifyRenderingChanged();
+    return;
+  }
   if (aBuilder->ShouldSyncDecodeImages()) {
     const nsStyleImage& image = mBackgroundStyle->mLayers[mLayer].mImage;
     if (image.GetType() == eStyleImageType_Image &&
         geometry->ShouldInvalidateToSyncDecodeImages()) {
       aInvalidRegion->Or(*aInvalidRegion, bounds);
 
       NotifyRenderingChanged();
     }
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -2571,16 +2571,21 @@ public:
   /**
    * Return the background positioning area.
    * (GetBounds() returns the background painting area.)
    * Can be called only when mBackgroundStyle is non-null.
    */
   nsRect GetPositioningArea();
 
   /**
+   * Return the destination area of one instance of the image.
+   */
+  nsRect GetDestArea() const { return mDestArea; }
+
+  /**
    * Returns true if existing rendered pixels of this display item may need
    * to be redrawn if the positioning area size changes but its position does
    * not.
    * If false, only the changed painting area needs to be redrawn when the
    * positioning area size changes but its position does not.
    */
   bool RenderingMightDependOnPositioningAreaSizeChange();
 
@@ -2610,16 +2615,17 @@ protected:
   typedef class mozilla::layers::ImageLayer ImageLayer;
 
   bool TryOptimizeToImageLayer(LayerManager* aManager, nsDisplayListBuilder* aBuilder);
   bool IsSingleFixedPositionImage(nsDisplayListBuilder* aBuilder,
                                   const nsRect& aClipRect,
                                   gfxRect* aDestRect);
   bool IsNonEmptyFixedImage() const;
   nsRect GetBoundsInternal(nsDisplayListBuilder* aBuilder);
+  nsRect GetDestAreaInternal(nsDisplayListBuilder* aBuilder);
 
   void PaintInternal(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx,
                      const nsRect& aBounds, nsRect* aClipRect);
 
   // Determine whether we want to be separated into our own layer, independent
   // of whether this item can actually be layerized.
   enum ImageLayerization {
     WHENEVER_POSSIBLE,
@@ -2629,19 +2635,20 @@ protected:
   ImageLayerization ShouldCreateOwnLayer(nsDisplayListBuilder* aBuilder,
                                          LayerManager* aManager);
 
   // Cache the result of nsCSSRendering::FindBackground. Always null if
   // mIsThemed is true or if FindBackground returned false.
   const nsStyleBackground* mBackgroundStyle;
   nsCOMPtr<imgIContainer> mImage;
   RefPtr<ImageContainer> mImageContainer;
-  LayoutDeviceRect mDestRect;
+  LayoutDeviceRect mImageLayerDestRect;
   /* Bounds of this display item */
   nsRect mBounds;
+  nsRect mDestArea;
   uint32_t mLayer;
 };
 
 
 /**
  * A display item to paint the native theme background for a frame.
  */
 class nsDisplayThemedBackground : public nsDisplayItem {
--- a/layout/base/nsDisplayListInvalidation.cpp
+++ b/layout/base/nsDisplayListInvalidation.cpp
@@ -58,23 +58,25 @@ nsDisplayBorderGeometry::MoveBy(const ns
   mContentRect.MoveBy(aOffset);
 }
 
 nsDisplayBackgroundGeometry::nsDisplayBackgroundGeometry(nsDisplayBackgroundImage* aItem,
                                                          nsDisplayListBuilder* aBuilder)
   : nsDisplayItemGeometry(aItem, aBuilder)
   , nsImageGeometryMixin(aItem, aBuilder)
   , mPositioningArea(aItem->GetPositioningArea())
+  , mDestArea(aItem->GetDestArea())
 {}
 
 void
 nsDisplayBackgroundGeometry::MoveBy(const nsPoint& aOffset)
 {
   nsDisplayItemGeometry::MoveBy(aOffset);
   mPositioningArea.MoveBy(aOffset);
+  mDestArea.MoveBy(aOffset);
 }
 
 nsDisplayThemedBackgroundGeometry::nsDisplayThemedBackgroundGeometry(nsDisplayThemedBackground* aItem,
                                                                      nsDisplayListBuilder* aBuilder)
   : nsDisplayItemGeometry(aItem, aBuilder)
   , mPositioningArea(aItem->GetPositioningArea())
   , mWindowIsActive(aItem->IsWindowActive())
 {}
--- a/layout/base/nsDisplayListInvalidation.h
+++ b/layout/base/nsDisplayListInvalidation.h
@@ -189,16 +189,17 @@ class nsDisplayBackgroundGeometry
   , public nsImageGeometryMixin<nsDisplayBackgroundGeometry>
 {
 public:
   nsDisplayBackgroundGeometry(nsDisplayBackgroundImage* aItem, nsDisplayListBuilder* aBuilder);
 
   virtual void MoveBy(const nsPoint& aOffset) override;
 
   nsRect mPositioningArea;
+  nsRect mDestArea;
 };
 
 class nsDisplayThemedBackgroundGeometry : public nsDisplayItemGeometry
 {
 public:
   nsDisplayThemedBackgroundGeometry(nsDisplayThemedBackground* aItem, nsDisplayListBuilder* aBuilder);
 
   virtual void MoveBy(const nsPoint& aOffset) override;
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -356,16 +356,21 @@ private:
     virtual ~RefreshDriverVsyncObserver() {}
 
     void TickRefreshDriver(TimeStamp aVsyncTimestamp)
     {
       MOZ_ASSERT(NS_IsMainThread());
 
       if (XRE_IsParentProcess()) {
         MonitorAutoLock lock(mRefreshTickLock);
+        #ifndef ANDROID  /* bug 1142079 */
+          TimeDuration vsyncLatency = TimeStamp::Now() - aVsyncTimestamp;
+          Telemetry::Accumulate(Telemetry::FX_REFRESH_DRIVER_CHROME_FRAME_DELAY_MS,
+                                vsyncLatency.ToMilliseconds());
+        #endif
         aVsyncTimestamp = mRecentVsync;
         mProcessedVsync = true;
       }
       MOZ_ASSERT(aVsyncTimestamp <= TimeStamp::Now());
 
       // We might have a problem that we call ~VsyncRefreshDriverTimer() before
       // the scheduled TickRefreshDriver() runs. Check mVsyncRefreshDriverTimer
       // before use.
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -4201,16 +4201,17 @@ ScrollFrameHelper::ScrollEvent::Run()
   return NS_OK;
 }
 
 void
 ScrollFrameHelper::FireScrollEvent()
 {
   mScrollEvent.Forget();
 
+  ActiveLayerTracker::SetCurrentScrollHandlerFrame(mOuter);
   WidgetGUIEvent event(true, eScroll, nullptr);
   nsEventStatus status = nsEventStatus_eIgnore;
   nsIContent* content = mOuter->GetContent();
   nsPresContext* prescontext = mOuter->PresContext();
   // Fire viewport scroll events at the document (where they
   // will bubble to the window)
   if (mIsRoot) {
     nsIDocument* doc = content->GetCurrentDoc();
@@ -4218,16 +4219,17 @@ ScrollFrameHelper::FireScrollEvent()
       EventDispatcher::Dispatch(doc, prescontext, &event, nullptr,  &status);
     }
   } else {
     // scroll events fired at elements don't bubble (although scroll events
     // fired at documents do, to the window)
     event.mFlags.mBubbles = false;
     EventDispatcher::Dispatch(content, prescontext, &event, nullptr, &status);
   }
+  ActiveLayerTracker::SetCurrentScrollHandlerFrame(nullptr);
 }
 
 void
 ScrollFrameHelper::PostScrollEvent()
 {
   if (mScrollEvent.IsPending())
     return;
 
--- a/layout/reftests/image-rect/reftest.list
+++ b/layout/reftests/image-rect/reftest.list
@@ -1,14 +1,14 @@
 skip-if(B2G||Mulet) == background-common-usage-floating-point.html background-common-usage-ref.html # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop
 == background-common-usage-percent.html background-common-usage-ref.html
 == background-common-usage-pixel.html background-common-usage-ref.html
 == background-draw-nothing-empty-rect.html background-draw-nothing-ref.html
 == background-draw-nothing-invalid-syntax.html background-draw-nothing-ref.html
-asserts(0-4) == background-draw-nothing-malformed-images.html background-draw-nothing-ref.html # Bug 576419
+asserts(0-6) == background-draw-nothing-malformed-images.html background-draw-nothing-ref.html # Bug 576419
 == background-monster-rect.html background-monster-rect-ref.html
 == background-over-size-rect.html background-over-size-rect-ref.html
 == background-test-parser.html background-test-parser-ref.html
 fuzzy-if(Android||B2G,113,124) == background-with-other-properties.html background-with-other-properties-ref.html
 fuzzy-if(Android||B2G||Mulet,16,22) == background-zoom-1.html background-zoom-1-ref.html  # Bug 1128229 # Bug 1153574
 fuzzy-if(Mulet,2,11) == background-zoom-2.html background-zoom-2-ref.html # Bug 1153574
 == background-zoom-3.html background-zoom-3-ref.html
 == background-zoom-4.html background-zoom-4-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/invalidation/background-position-1-ref.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html lang="en">
+<meta charset="utf-8">
+<title>Changes to background-position should not cause things to repaint that don't intersect the background image.</title>
+
+<style>
+
+body {
+  margin: 0;
+}
+
+#background {
+  height: 512px;
+  background-image: url(image_rgrg-256x256.png);
+  background-repeat: no-repeat;
+  background-position: 300px 100px;
+}
+
+#not-intersecting-background {
+  box-sizing: border-box;
+  width: 200px;
+  height: 200px;
+  margin: 50px;
+  border: 1px solid lime;
+}
+
+</style>
+
+<div id="background">
+  <div id="not-intersecting-background"></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/invalidation/background-position-1.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html lang="en" class="reftest-wait">
+<meta charset="utf-8">
+<title>Changes to background-position should not cause things to repaint that don't intersect the background image.</title>
+
+<style>
+
+body {
+  margin: 0;
+}
+
+#background {
+  height: 512px;
+  background-image: url(image_rgrg-256x256.png);
+  background-repeat: no-repeat;
+  background-position: 300px 50px;
+}
+
+#not-intersecting-background {
+  box-sizing: border-box;
+  width: 200px;
+  height: 200px;
+  margin: 50px;
+  border: 1px solid lime;
+}
+
+</style>
+
+<div id="background">
+  <div id="not-intersecting-background" class="reftest-no-paint"></div>
+</div>
+
+<script>
+
+function doTest() {
+  document.querySelector("#background").style.backgroundPosition = "300px 100px";
+  document.documentElement.removeAttribute("class");
+}
+document.addEventListener("MozReftestInvalidate", doTest);
+
+</script>
--- a/layout/reftests/invalidation/reftest.list
+++ b/layout/reftests/invalidation/reftest.list
@@ -65,8 +65,9 @@ pref(layout.animated-image-layers.enable
 != layer-splitting-6.html about:blank
 != layer-splitting-7.html about:blank
 fuzzy-if(gtkWidget,2,4) fuzzy-if(asyncPan,2,3955) fuzzy-if(OSX,179,30) == image-scrolling-zoom-1.html image-scrolling-zoom-1-ref.html
 != image-scrolling-zoom-1-ref.html image-scrolling-zoom-1-notref.html
 != fast-scrolling.html about:blank
 != fractional-transform-1.html about:blank
 != fractional-transform-2.html about:blank
 != fractional-transform-3.html about:blank
+== background-position-1.html background-position-1-ref.html
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -2246,37 +2246,42 @@ nsStyleBackground::Destroy(nsPresContext
 
 nsChangeHint nsStyleBackground::CalcDifference(const nsStyleBackground& aOther) const
 {
   const nsStyleBackground* moreLayers =
     mImageCount > aOther.mImageCount ? this : &aOther;
   const nsStyleBackground* lessLayers =
     mImageCount > aOther.mImageCount ? &aOther : this;
 
-  bool hasVisualDifference = false;
+  nsChangeHint hint = nsChangeHint(0);
 
   NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, moreLayers) {
     if (i < lessLayers->mImageCount) {
-      if (moreLayers->mLayers[i] != lessLayers->mLayers[i]) {
-        if ((moreLayers->mLayers[i].mImage.GetType() == eStyleImageType_Element) ||
-            (lessLayers->mLayers[i].mImage.GetType() == eStyleImageType_Element))
-          return NS_CombineHint(nsChangeHint_UpdateEffects,
-                                nsChangeHint_RepaintFrame);
-        hasVisualDifference = true;
+      nsChangeHint layerDifference = moreLayers->mLayers[i].CalcDifference(lessLayers->mLayers[i]);
+      hint |= layerDifference;
+      if (layerDifference &&
+          ((moreLayers->mLayers[i].mImage.GetType() == eStyleImageType_Element) ||
+           (lessLayers->mLayers[i].mImage.GetType() == eStyleImageType_Element))) {
+        hint |= nsChangeHint_UpdateEffects | nsChangeHint_RepaintFrame;
       }
     } else {
-      if (moreLayers->mLayers[i].mImage.GetType() == eStyleImageType_Element)
-        return NS_CombineHint(nsChangeHint_UpdateEffects,
-                              nsChangeHint_RepaintFrame);
-      hasVisualDifference = true;
+      hint |= nsChangeHint_RepaintFrame;
+      if (moreLayers->mLayers[i].mImage.GetType() == eStyleImageType_Element) {
+        hint |= nsChangeHint_UpdateEffects | nsChangeHint_RepaintFrame;
+      }
     }
   }
 
-  if (hasVisualDifference || mBackgroundColor != aOther.mBackgroundColor)
-    return nsChangeHint_RepaintFrame;
+  if (mBackgroundColor != aOther.mBackgroundColor) {
+    hint |= nsChangeHint_RepaintFrame;
+  }
+
+  if (hint) {
+    return hint;
+  }
 
   if (mAttachmentCount != aOther.mAttachmentCount ||
       mClipCount != aOther.mClipCount ||
       mOriginCount != aOther.mOriginCount ||
       mRepeatCount != aOther.mRepeatCount ||
       mPositionCount != aOther.mPositionCount ||
       mSizeCount != aOther.mSizeCount) {
     return nsChangeHint_NeutralChange;
@@ -2477,16 +2482,35 @@ nsStyleBackground::Layer::operator==(con
          mOrigin == aOther.mOrigin &&
          mRepeat == aOther.mRepeat &&
          mBlendMode == aOther.mBlendMode &&
          mPosition == aOther.mPosition &&
          mSize == aOther.mSize &&
          mImage == aOther.mImage;
 }
 
+nsChangeHint
+nsStyleBackground::Layer::CalcDifference(const Layer& aOther) const
+{
+  nsChangeHint hint = nsChangeHint(0);
+  if (mAttachment != aOther.mAttachment ||
+      mClip != aOther.mClip ||
+      mOrigin != aOther.mOrigin ||
+      mRepeat != aOther.mRepeat ||
+      mBlendMode != aOther.mBlendMode ||
+      mSize != aOther.mSize ||
+      mImage != aOther.mImage) {
+    hint |= nsChangeHint_RepaintFrame;
+  }
+  if (mPosition != aOther.mPosition) {
+    hint |= nsChangeHint_SchedulePaint;
+  }
+  return hint;
+}
+
 // --------------------
 // nsStyleDisplay
 //
 void nsTimingFunction::AssignFromKeyword(int32_t aTimingFunctionType)
 {
   switch (aTimingFunctionType) {
     case NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START:
       mType = Type::StepStart;
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -391,19 +391,20 @@ struct nsStyleBackground {
   void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW {
     return aContext->PresShell()->
       AllocateByObjectID(mozilla::eArenaObjectID_nsStyleBackground, sz);
   }
   void Destroy(nsPresContext* aContext);
 
   nsChangeHint CalcDifference(const nsStyleBackground& aOther) const;
   static nsChangeHint MaxDifference() {
-    return NS_CombineHint(nsChangeHint_UpdateEffects,
-                          NS_CombineHint(nsChangeHint_RepaintFrame,
-                                         nsChangeHint_NeutralChange));
+    return nsChangeHint_UpdateEffects |
+           nsChangeHint_RepaintFrame |
+           nsChangeHint_SchedulePaint |
+           nsChangeHint_NeutralChange;
   }
   static nsChangeHint DifferenceAlwaysHandledForDescendants() {
     // CalcDifference never returns the reflow hints that are sometimes
     // handled for descendants at all.
     return nsChangeHint(0);
   }
 
   struct Position;
@@ -547,16 +548,19 @@ struct nsStyleBackground {
 
     // True if the rendering of this layer might change when the size
     // of the background positioning area changes.  This is true for any
     // non-solid-color background whose position or size depends on
     // the size of the positioning area.  It's also true for SVG images
     // whose root <svg> node has a viewBox.
     bool RenderingMightDependOnPositioningAreaSizeChange() const;
 
+    // Compute the change hint required by changes in just this layer.
+    nsChangeHint CalcDifference(const Layer& aOther) const;
+
     // An equality operator that compares the images using URL-equality
     // rather than pointer-equality.
     bool operator==(const Layer& aOther) const;
     bool operator!=(const Layer& aOther) const {
       return !(*this == aOther);
     }
   };
 
new file mode 100644
--- /dev/null
+++ b/layout/svg/crashtests/803562-1.svg
@@ -0,0 +1,18 @@
+<svg xmlns="http://www.w3.org/2000/svg">
+
+<foreignObject width="500" height="500" style="-moz-appearance: checkbox;">
+  <option xmlns="http://www.w3.org/1999/xhtml"></option>
+</foreignObject>
+
+<script>
+<![CDATA[
+
+window.addEventListener("load", function() {
+  document.getElementsByTagName("option")[0].appendChild(document.createTextNode("Option 1"));
+}, false);
+
+]]>
+</script>
+
+</svg>
+
--- a/layout/svg/crashtests/crashtests.list
+++ b/layout/svg/crashtests/crashtests.list
@@ -146,16 +146,17 @@ load 780764-1.svg
 load 780963-1.html
 load 782141-1.svg
 load 784061-1.svg
 load 788831-1.svg
 load 789390-1.html
 load 790072.svg
 load 791826-1.svg
 load 808318-1.svg
+load 803562-1.svg
 load 813420-1.svg
 load 841163-1.svg
 load 841812-1.svg
 load 842009-1.svg
 load 842630-1.svg
 load 842909-1.svg
 load 843072-1.svg
 load 843917-1.svg
--- a/layout/svg/svg.css
+++ b/layout/svg/svg.css
@@ -27,16 +27,17 @@ svg:not(:root), symbol, image, marker, p
    stroke-opacity: context-stroke-opacity;
    stroke-width: context-value;
    stroke-dasharray: context-value;
    stroke-dashoffset: context-value;
  }
 }
 
 foreignObject {
+  -moz-appearance: none ! important;
   margin: 0 ! important;
   padding: 0 ! important;
   border-width: 0 ! important;
   white-space: normal;
 }
 
 @media all and (-moz-is-resource-document) {
  foreignObject *|* {
--- a/media/libstagefright/binding/MP4Metadata.cpp
+++ b/media/libstagefright/binding/MP4Metadata.cpp
@@ -2,22 +2,24 @@
  * 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 "include/MPEG4Extractor.h"
 #include "media/stagefright/DataSource.h"
 #include "media/stagefright/MediaDefs.h"
 #include "media/stagefright/MediaSource.h"
 #include "media/stagefright/MetaData.h"
+#include "mozilla/Logging.h"
 #include "mozilla/Monitor.h"
 #include "mp4_demuxer/MoofParser.h"
 #include "mp4_demuxer/MP4Metadata.h"
 
 #include <limits>
 #include <stdint.h>
+#include <vector>
 
 using namespace stagefright;
 
 namespace mp4_demuxer
 {
 
 struct StageFrightPrivate
 {
@@ -101,19 +103,52 @@ MP4Metadata::MP4Metadata(Stream* aSource
 
   UpdateCrypto(metaData.get());
 }
 
 MP4Metadata::~MP4Metadata()
 {
 }
 
+#ifdef MOZ_RUST_MP4PARSE
+#include "mp4parse.h"
+
+// Helper to test the rust parser on a data source.
+static bool try_rust(RefPtr<Stream> aSource)
+{
+  static LazyLogModule sLog("MP4Metadata");
+  int64_t length;
+  if (!aSource->Length(&length) || length <= 0) {
+    MOZ_LOG(sLog, LogLevel::Warning, ("Couldn't get source length"));
+    return false;
+  }
+  MOZ_LOG(sLog, LogLevel::Debug,
+         ("Source length %d bytes\n", (long long int)length));
+  size_t bytes_read = 0;
+  auto buffer = std::vector<uint8_t>(length);
+  bool rv = aSource->ReadAt(0, buffer.data(), length, &bytes_read);
+  if (!rv || bytes_read != size_t(length)) {
+    MOZ_LOG(sLog, LogLevel::Warning, ("Error copying mp4 data"));
+    return false;
+  }
+  auto context = mp4parse_new();
+  int32_t tracks = mp4parse_read(context, buffer.data(), bytes_read);
+  mp4parse_free(context);
+  MOZ_LOG(sLog, LogLevel::Info, ("rust parser found %d tracks", int(tracks)));
+  return true;
+}
+#endif
+
 uint32_t
 MP4Metadata::GetNumberTracks(mozilla::TrackInfo::TrackType aType) const
 {
+#ifdef MOZ_RUST_MP4PARSE
+  // Try in rust first.
+  try_rust(mSource);
+#endif
   size_t tracks = mPrivate->mMetadataExtractor->countTracks();
   uint32_t total = 0;
   for (size_t i = 0; i < tracks; i++) {
     sp<MetaData> metaData = mPrivate->mMetadataExtractor->getTrackMetaData(i);
 
     const char* mimeType;
     if (metaData == nullptr || !metaData->findCString(kKeyMIMEType, &mimeType)) {
       continue;
--- a/media/libstagefright/binding/MP4Metadata.rs
+++ b/media/libstagefright/binding/MP4Metadata.rs
@@ -1,163 +1,208 @@
-// Module for parsing ISO Base Media Format aka video/mp4 streams.
+//! Module for parsing ISO Base Media Format aka video/mp4 streams.
 
 // 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 https://mozilla.org/MPL/2.0/.
 
+mod byteorder; // 'extern crate' upstream.
+use byteorder::ReadBytesExt;
+use std::error::Error as ErrorTrait; // For Err(e) => e.description().
+use std::io::{Read, BufRead, Take};
+use std::io::Cursor;
+use std::cmp;
 use std::fmt;
 
-/// Expose C api wrapper.
+// Expose C api wrapper.
 pub mod capi;
 // FIXME: We can 'pub use capi::*' in rustc 1.5 and later.
 pub use capi::{mp4parse_new, mp4parse_free, mp4parse_read};
 
+/// Describes parser failures.
+///
+/// This enum wraps athe standard `io::Error` type, unified with
+/// our own parser error states and those of crates we use.
+#[derive(Debug)]
+pub enum Error {
+    /// Parse error caused by corrupt or malformed data.
+    InvalidData,
+    /// Parse error caused by limited parser support rather than invalid data.
+    Unsupported,
+    /// Reflect `byteorder::Error::UnexpectedEOF` for short data.
+    UnexpectedEOF,
+    /// Propagate underlying errors from `std::io`.
+    Io(std::io::Error),
+}
+
+impl From<std::io::Error> for Error {
+    fn from(err: std::io::Error) -> Error { Error::Io(err) }
+}
+
+impl From<byteorder::Error> for Error {
+    fn from(err: byteorder::Error) -> Error {
+        match err {
+            byteorder::Error::UnexpectedEOF => Error::UnexpectedEOF,
+            byteorder::Error::Io(e) => Error::Io(e),
+        }
+    }
+}
+
+/// Result shorthand using our Error enum.
+pub type Result<T> = std::result::Result<T, Error>;
+
+/// Four-byte 'character code' describing the type of a piece of data.
 #[derive(Clone, Copy, Eq, PartialEq)]
-pub struct FourCC(pub u32);
+pub struct FourCC([u8; 4]);
 
 impl fmt::Debug for FourCC {
   fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-    write!(f, "'{}'", fourcc_to_string(*self))
+    write!(f, "'{}'", String::from_utf8_lossy(&self.0))
   }
 }
 
 /// Basic ISO box structure.
+///
+/// mp4 files are a sequence of possibly-nested 'box' structures.
+/// Each box begins with a header describing the length of the
+/// box's data and a four-byte 'character code' or `FourCC` which
+/// identifies the type of the box. Together these are enough to
+/// interpret the contents of that section of the file.
 #[derive(Debug)]
 pub struct BoxHeader {
-    /// Four character box type
+    /// Four character box type.
     pub name: FourCC,
-    /// Size of the box in bytes
+    /// Size of the box in bytes.
     pub size: u64,
     /// Offset to the start of the contained data (or header size).
     pub offset: u64,
 }
 
 /// File type box 'ftyp'.
 #[derive(Debug)]
-pub struct FileTypeBox {
+struct FileTypeBox {
     name: FourCC,
     size: u64,
     major_brand: FourCC,
     minor_version: u32,
     compatible_brands: Vec<FourCC>,
 }
 
 /// Movie header box 'mvhd'.
 #[derive(Debug)]
-pub struct MovieHeaderBox {
-    pub name: FourCC,
-    pub size: u64,
-    pub timescale: u32,
-    pub duration: u64,
+struct MovieHeaderBox {
+    name: FourCC,
+    size: u64,
+    timescale: u32,
+    duration: u64,
     // Ignore other fields.
 }
 
 /// Track header box 'tkhd'
 #[derive(Debug)]
-pub struct TrackHeaderBox {
-    pub name: FourCC,
-    pub size: u64,
-    pub track_id: u32,
-    pub enabled: bool,
-    pub duration: u64,
-    pub width: u32,
-    pub height: u32,
+struct TrackHeaderBox {
+    name: FourCC,
+    size: u64,
+    track_id: u32,
+    enabled: bool,
+    duration: u64,
+    width: u32,
+    height: u32,
 }
 
 /// Edit list box 'elst'
 #[derive(Debug)]
-pub struct EditListBox {
+struct EditListBox {
     name: FourCC,
     size: u64,
     edits: Vec<Edit>,
 }
 
 #[derive(Debug)]
-pub struct Edit {
+struct Edit {
     segment_duration: u64,
     media_time: i64,
     media_rate_integer: i16,
     media_rate_fraction: i16,
 }
 
 /// Media header box 'mdhd'
 #[derive(Debug)]
-pub struct MediaHeaderBox {
+struct MediaHeaderBox {
     name: FourCC,
     size: u64,
     timescale: u32,
     duration: u64,
 }
 
 // Chunk offset box 'stco' or 'co64'
 #[derive(Debug)]
-pub struct ChunkOffsetBox {
+struct ChunkOffsetBox {
     name: FourCC,
     size: u64,
     offsets: Vec<u64>,
 }
 
 // Sync sample box 'stss'
 #[derive(Debug)]
-pub struct SyncSampleBox {
+struct SyncSampleBox {
     name: FourCC,
     size: u64,
     samples: Vec<u32>,
 }
 
 // Sample to chunk box 'stsc'
 #[derive(Debug)]
-pub struct SampleToChunkBox {
+struct SampleToChunkBox {
     name: FourCC,
     size: u64,
     samples: Vec<SampleToChunk>,
 }
 
 #[derive(Debug)]
-pub struct SampleToChunk {
+struct SampleToChunk {
     first_chunk: u32,
     samples_per_chunk: u32,
     sample_description_index: u32,
 }
 
 // Sample size box 'stsz'
 #[derive(Debug)]
-pub struct SampleSizeBox {
+struct SampleSizeBox {
     name: FourCC,
     size: u64,
     sample_size: u32,
     sample_sizes: Vec<u32>,
 }
 
 // Time to sample box 'stts'
 #[derive(Debug)]
-pub struct TimeToSampleBox {
+struct TimeToSampleBox {
     name: FourCC,
     size: u64,
     samples: Vec<Sample>,
 }
 
 #[derive(Debug)]
-pub struct Sample {
+struct Sample {
     sample_count: u32,
     sample_delta: u32,
 }
 
 // Handler reference box 'hdlr'
 #[derive(Debug)]
-pub struct HandlerBox {
+struct HandlerBox {
     name: FourCC,
     size: u64,
     handler_type: FourCC,
 }
 
 // Sample description box 'stsd'
 #[derive(Debug)]
-pub struct SampleDescriptionBox {
+struct SampleDescriptionBox {
     name: FourCC,
     size: u64,
     descriptions: Vec<SampleEntry>,
 }
 
 #[allow(dead_code)]
 #[derive(Debug)]
 enum SampleEntry {
@@ -173,24 +218,24 @@ enum SampleEntry {
         width: u16,
         height: u16,
         avcc: AVCDecoderConfigurationRecord,
     },
 }
 
 #[allow(dead_code)]
 #[derive(Debug)]
-pub struct AVCDecoderConfigurationRecord {
+struct AVCDecoderConfigurationRecord {
     data: Vec<u8>,
 }
 
 #[allow(non_camel_case_types)]
 #[allow(dead_code)]
 #[derive(Debug)]
-pub struct ES_Descriptor {
+struct ES_Descriptor {
     data: Vec<u8>,
 }
 
 /// Internal data structures.
 #[derive(Debug)]
 pub struct MediaContext {
     tracks: Vec<Track>,
 }
@@ -205,189 +250,199 @@ impl MediaContext {
 
 #[derive(Debug)]
 enum TrackType {
     Video,
     Audio
 }
 
 #[derive(Debug)]
-pub struct Track {
+struct Track {
     track_type: TrackType,
 }
 
-mod byteorder; // 'extern crate' upstream.
-use byteorder::{BigEndian, ReadBytesExt};
-use std::io::{Read, BufRead, Take};
-use std::io::Cursor;
-use std::cmp;
-
-/// Parse a box out of a data buffer.
-pub fn read_box_header<T: ReadBytesExt>(src: &mut T) -> byteorder::Result<BoxHeader> {
+/// Read and parse a box header.
+///
+/// Call this first to determine the type of a particular mp4 box
+/// and its length. Used internally for dispatching to specific
+/// parsers for the internal content, or to get the length to
+/// skip unknown or uninteresting boxes.
+pub fn read_box_header<T: ReadBytesExt>(src: &mut T) -> Result<BoxHeader> {
     let size32 = try!(be_u32(src));
-    let name = FourCC(try!(be_u32(src)));
+    let name = try!(be_fourcc(src));
     let size = match size32 {
-        0 => panic!("unknown box size not implemented"),
+        0 => return Err(Error::Unsupported),
         1 => {
             let size64 = try!(be_u64(src));
-            assert!(size64 >= 16);
+            if size64 < 16 {
+                return Err(Error::InvalidData);
+            }
             size64
         },
-        2 ... 7 => panic!("invalid box size"),
+        2 ... 7 => return Err(Error::InvalidData),
         _ => size32 as u64,
     };
     let offset = match size32 {
         1 => 4 + 4 + 8,
         _ => 4 + 4,
     };
     assert!(offset <= size);
     Ok(BoxHeader{
       name: name,
       size: size,
       offset: offset,
     })
 }
 
 /// Parse the extra header fields for a full box.
-fn read_fullbox_extra<T: ReadBytesExt>(src: &mut T) -> byteorder::Result<(u8, u32)> {
+fn read_fullbox_extra<T: ReadBytesExt>(src: &mut T) -> Result<(u8, u32)> {
     let version = try!(src.read_u8());
     let flags_a = try!(src.read_u8());
     let flags_b = try!(src.read_u8());
     let flags_c = try!(src.read_u8());
     Ok((version, (flags_a as u32) << 16 |
                  (flags_b as u32) <<  8 |
                  (flags_c as u32)))
 }
 
 /// Skip over the entire contents of a box.
-pub fn skip_box_content<T: BufRead> (src: &mut T, header: &BoxHeader) -> byteorder::Result<usize> {
+fn skip_box_content<T: BufRead> (src: &mut T, header: &BoxHeader) -> Result<usize> {
     skip(src, (header.size - header.offset) as usize)
 }
 
 /// Skip over the remaining contents of a box.
-pub fn skip_remaining_box_content<T: BufRead> (src: &mut T, header: &BoxHeader) -> byteorder::Result<()> {
+fn skip_remaining_box_content<T: BufRead> (src: &mut T, header: &BoxHeader) -> Result<()> {
     match skip(src, (header.size - header.offset) as usize) {
-        Ok(_) | Err(byteorder::Error::UnexpectedEOF) => Ok(()),
+        Ok(_) | Err(Error::UnexpectedEOF) => Ok(()),
         e @ _ => Err(e.err().unwrap())
     }
 }
 
 /// Helper to construct a Take over the contents of a box.
 fn limit<'a, T: Read>(f: &'a mut T, h: &BoxHeader) -> Take<&'a mut T> {
     f.take(h.size - h.offset)
 }
 
 /// Helper to construct a Cursor over the contents of a box.
-fn recurse<T: Read>(f: &mut T, h: &BoxHeader, context: &mut MediaContext) -> byteorder::Result<()> {
-    use std::error::Error;
+fn recurse<T: Read>(f: &mut T, h: &BoxHeader, context: &mut MediaContext) -> Result<()> {
     println!("{:?} -- recursing", h);
     // FIXME: I couldn't figure out how to do this without copying.
     // We use Seek on the Read we return in skip_box_content, but
     // that trait isn't implemented for a Take like our limit()
     // returns. Slurping the buffer and wrapping it in a Cursor
     // functions as a work around.
     let buf: Vec<u8> = f
         .bytes()
         .map(|u| u.unwrap())
         .collect();
     let mut content = Cursor::new(buf);
     loop {
         match read_box(&mut content, context) {
             Ok(_) => {},
-            Err(byteorder::Error::UnexpectedEOF) => {
+            Err(Error::UnexpectedEOF) => {
                 // byteorder returns EOF at the end of the buffer.
                 // This isn't an error for us, just an signal to
                 // stop recursion.
-                println!("Caught byteorder::Error::UnexpectedEOF");
+                println!("Caught Error::UnexpectedEOF");
                 break;
             },
-            Err(byteorder::Error::Io(e)) => {
+            Err(Error::InvalidData) => {
+                println!("Invalid data");
+                return Err(Error::InvalidData);
+            },
+            Err(Error::Unsupported) => {
+                println!("Unsupported BMFF construct");
+                return Err(Error::Unsupported);
+            },
+            Err(Error::Io(e)) => {
                 println!("I/O Error '{:?}' reading box: {:?}",
                          e.kind(), e.description());
-                return Err(byteorder::Error::Io(e));
+                return Err(Error::Io(e));
             },
         }
     }
     assert!(content.position() == h.size - h.offset);
     println!("{:?} -- end", h);
     Ok(())
 }
 
 /// Read the contents of a box, including sub boxes.
-/// Metadata is accumulated in the passed-through MediaContext struct.
-pub fn read_box<T: BufRead>(f: &mut T, context: &mut MediaContext) -> byteorder::Result<()> {
+///
+/// Metadata is accumulated in the passed-through MediaContext struct,
+/// which can be examined later.
+pub fn read_box<T: BufRead>(f: &mut T, context: &mut MediaContext) -> Result<()> {
     read_box_header(f).and_then(|h| {
         let mut content = limit(f, &h);
-        match &fourcc_to_string(h.name)[..] {
-            "ftyp" => {
+        match &h.name.0 {
+            b"ftyp" => {
                 let ftyp = try!(read_ftyp(&mut content, &h));
                 println!("{:?}", ftyp);
             },
-            "moov" => try!(recurse(&mut content, &h, context)),
-            "mvhd" => {
+            b"moov" => try!(recurse(&mut content, &h, context)),
+            b"mvhd" => {
                 let mvhd = try!(read_mvhd(&mut content, &h));
                 println!("  {:?}", mvhd);
             },
-            "trak" => try!(recurse(&mut content, &h, context)),
-            "tkhd" => {
+            b"trak" => try!(recurse(&mut content, &h, context)),
+            b"tkhd" => {
                 let tkhd = try!(read_tkhd(&mut content, &h));
                 println!("  {:?}", tkhd);
             },
-            "edts" => try!(recurse(&mut content, &h, context)),
-            "elst" => {
+            b"edts" => try!(recurse(&mut content, &h, context)),
+            b"elst" => {
                 let elst = try!(read_elst(&mut content, &h));
                 println!("  {:?}", elst);
             },
-            "mdia" => try!(recurse(&mut content, &h, context)),
-            "mdhd" => {
+            b"mdia" => try!(recurse(&mut content, &h, context)),
+            b"mdhd" => {
                 let mdhd = try!(read_mdhd(&mut content, &h));
                 println!("  {:?}", mdhd);
             },
-            "minf" => try!(recurse(&mut content, &h, context)),
-            "stbl" => try!(recurse(&mut content, &h, context)),
-            "stco" => {
+            b"minf" => try!(recurse(&mut content, &h, context)),
+            b"stbl" => try!(recurse(&mut content, &h, context)),
+            b"stco" => {
                 let stco = try!(read_stco(&mut content, &h));
                 println!("  {:?}", stco);
             },
-            "co64" => {
+            b"co64" => {
                 let co64 = try!(read_co64(&mut content, &h));
                 println!("  {:?}", co64);
             },
-            "stss" => {
+            b"stss" => {
                 let stss = try!(read_stss(&mut content, &h));
                 println!("  {:?}", stss);
             },
-            "stsc" => {
+            b"stsc" => {
                 let stsc = try!(read_stsc(&mut content, &h));
                 println!("  {:?}", stsc);
             },
-            "stsz" => {
+            b"stsz" => {
                 let stsz = try!(read_stsz(&mut content, &h));
                 println!("  {:?}", stsz);
             },
-            "stts" => {
+            b"stts" => {
                 let stts = try!(read_stts(&mut content, &h));
                 println!("  {:?}", stts);
             },
-            "hdlr" => {
+            b"hdlr" => {
                 let hdlr = try!(read_hdlr(&mut content, &h));
-                let track_type = match &fourcc_to_string(hdlr.handler_type)[..] {
-                    "vide" => Some(TrackType::Video),
-                    "soun" => Some(TrackType::Audio),
+                let track_type = match &hdlr.handler_type.0 {
+                    b"vide" => Some(TrackType::Video),
+                    b"soun" => Some(TrackType::Audio),
                     _ => None
                 };
                 // Save track types with recognized types.
                 match track_type {
                     Some(track_type) =>
                          context.tracks.push(Track { track_type: track_type }),
                     None => println!("unknown track type!"),
                 };
                 println!("  {:?}", hdlr);
             },
-            "stsd" => {
+            b"stsd" => {
                 let track = &context.tracks[context.tracks.len() - 1];
                 let stsd = try!(read_stsd(&mut content, &h, &track));
                 println!("  {:?}", stsd);
             },
             _ => {
                 // Skip the contents of unknown chunks.
                 println!("{:?} (skipped)", h);
                 try!(skip_box_content(&mut content, &h));
@@ -395,76 +450,76 @@ pub fn read_box<T: BufRead>(f: &mut T, c
         };
         assert!(content.limit() == 0);
         println!("read_box context: {:?}", context);
         Ok(()) // and_then needs a Result to return.
     })
 }
 
 /// Parse an ftyp box.
-pub fn read_ftyp<T: ReadBytesExt>(src: &mut T, head: &BoxHeader) -> byteorder::Result<FileTypeBox> {
-    let major = FourCC(try!(be_u32(src)));
+fn read_ftyp<T: ReadBytesExt>(src: &mut T, head: &BoxHeader) -> Result<FileTypeBox> {
+    let major = try!(be_fourcc(src));
     let minor = try!(be_u32(src));
     let brand_count = (head.size - 8 - 8) / 4;
     let mut brands = Vec::new();
     for _ in 0..brand_count {
-        brands.push(FourCC(try!(be_u32(src))));
+        brands.push(try!(be_fourcc(src)));
     }
     Ok(FileTypeBox{
         name: head.name,
         size: head.size,
         major_brand: major,
         minor_version: minor,
         compatible_brands: brands,
     })
 }
 
 /// Parse an mvhd box.
-pub fn read_mvhd<T: ReadBytesExt + BufRead>(src: &mut T, head: &BoxHeader) -> byteorder::Result<MovieHeaderBox> {
+fn read_mvhd<T: ReadBytesExt + BufRead>(src: &mut T, head: &BoxHeader) -> Result<MovieHeaderBox> {
     let (version, _) = try!(read_fullbox_extra(src));
     match version {
         // 64 bit creation and modification times.
         1 => { try!(skip(src, 16)); },
         // 32 bit creation and modification times.
         0 => { try!(skip(src, 8)); },
-        _ => panic!("invalid mhdr version"),
+        _ => return Err(Error::InvalidData),
     }
     let timescale = try!(be_u32(src));
     let duration = match version {
         1 => try!(be_u64(src)),
         0 => try!(be_u32(src)) as u64,
-        _ => panic!("invalid mhdr version"),
+        _ => return Err(Error::InvalidData),
     };
     // Skip remaining fields.
     try!(skip(src, 80));
     Ok(MovieHeaderBox {
         name: head.name,
         size: head.size,
         timescale: timescale,
         duration: duration,
     })
 }
 
 /// Parse a tkhd box.
-pub fn read_tkhd<T: ReadBytesExt + BufRead>(src: &mut T, head: &BoxHeader) -> byteorder::Result<TrackHeaderBox> {
+fn read_tkhd<T: ReadBytesExt + BufRead>(src: &mut T, head: &BoxHeader) -> Result<TrackHeaderBox> {
     let (version, flags) = try!(read_fullbox_extra(src));
     let disabled = flags & 0x1u32 == 0 || flags & 0x2u32 == 0;
     match version {
         // 64 bit creation and modification times.
         1 => { try!(skip(src, 16)); },
         // 32 bit creation and modification times.
         0 => { try!(skip(src, 8)); },
-        _ => panic!("invalid tkhd version"),
+        _ => return Err(Error::InvalidData),
     }
     let track_id = try!(be_u32(src));
     try!(skip(src, 4));
     let duration = match version {
         1 => try!(be_u64(src)),
         0 => try!(be_u32(src)) as u64,
-        _ => panic!("invalid tkhd version"),
+        _ => return Err(Error::InvalidData),
     };
     // Skip uninteresting fields.
     try!(skip(src, 52));
     let width = try!(be_u32(src));
     let height = try!(be_u32(src));
     Ok(TrackHeaderBox {
         name: head.name,
         size: head.size,
@@ -472,33 +527,33 @@ pub fn read_tkhd<T: ReadBytesExt + BufRe
         enabled: !disabled,
         duration: duration,
         width: width,
         height: height,
     })
 }
 
 /// Parse a elst box.
-pub fn read_elst<T: ReadBytesExt>(src: &mut T, head: &BoxHeader) -> byteorder::Result<EditListBox> {
+fn read_elst<T: ReadBytesExt>(src: &mut T, head: &BoxHeader) -> Result<EditListBox> {
     let (version, _) = try!(read_fullbox_extra(src));
     let edit_count = try!(be_u32(src));
     let mut edits = Vec::new();
     for _ in 0..edit_count {
         let (segment_duration, media_time) = match version {
             1 => {
                 // 64 bit segment duration and media times.
                 (try!(be_u64(src)),
                  try!(be_i64(src)))
             },
             0 => {
                 // 32 bit segment duration and media times.
                 (try!(be_u32(src)) as u64,
                  try!(be_i32(src)) as i64)
             },
-            _ => panic!("invalid elst version"),
+            _ => return Err(Error::InvalidData),
         };
         let media_rate_integer = try!(be_i16(src));
         let media_rate_fraction = try!(be_i16(src));
         edits.push(Edit{
             segment_duration: segment_duration,
             media_time: media_time,
             media_rate_integer: media_rate_integer,
             media_rate_fraction: media_rate_fraction,
@@ -508,17 +563,17 @@ pub fn read_elst<T: ReadBytesExt>(src: &
     Ok(EditListBox{
         name: head.name,
         size: head.size,
         edits: edits
     })
 }
 
 /// Parse a mdhd box.
-pub fn read_mdhd<T: ReadBytesExt + BufRead>(src: &mut T, head: &BoxHeader) -> byteorder::Result<MediaHeaderBox> {
+fn read_mdhd<T: ReadBytesExt + BufRead>(src: &mut T, head: &BoxHeader) -> Result<MediaHeaderBox> {
     let (version, _) = try!(read_fullbox_extra(src));
     let (timescale, duration) = match version {
         1 => {
             // Skip 64-bit creation and modification times.
             try!(skip(src, 16));
 
             // 64 bit duration.
             (try!(be_u32(src)),
@@ -527,80 +582,80 @@ pub fn read_mdhd<T: ReadBytesExt + BufRe
         0 => {
             // Skip 32-bit creation and modification times.
             try!(skip(src, 8));
 
             // 32 bit duration.
             (try!(be_u32(src)),
              try!(be_u32(src)) as u64)
         },
-        _ => panic!("invalid mdhd version"),
+        _ => return Err(Error::InvalidData),
     };
 
     // Skip uninteresting fields.
     try!(skip(src, 4));
 
     Ok(MediaHeaderBox{
         name: head.name,
         size: head.size,
         timescale: timescale,
         duration: duration,
     })
 }
 
 /// Parse a stco box.
-pub fn read_stco<T: ReadBytesExt>(src: &mut T, head: &BoxHeader) -> byteorder::Result<ChunkOffsetBox> {
+fn read_stco<T: ReadBytesExt>(src: &mut T, head: &BoxHeader) -> Result<ChunkOffsetBox> {
     let (_, _) = try!(read_fullbox_extra(src));
     let offset_count = try!(be_u32(src));
     let mut offsets = Vec::new();
     for _ in 0..offset_count {
         offsets.push(try!(be_u32(src)) as u64);
     }
 
     Ok(ChunkOffsetBox{
         name: head.name,
         size: head.size,
         offsets: offsets,
     })
 }
 
 /// Parse a stco box.
-pub fn read_co64<T: ReadBytesExt>(src: &mut T, head: &BoxHeader) -> byteorder::Result<ChunkOffsetBox> {
+fn read_co64<T: ReadBytesExt>(src: &mut T, head: &BoxHeader) -> Result<ChunkOffsetBox> {
     let (_, _) = try!(read_fullbox_extra(src));
     let offset_count = try!(be_u32(src));
     let mut offsets = Vec::new();
     for _ in 0..offset_count {
         offsets.push(try!(be_u64(src)));
     }
 
     Ok(ChunkOffsetBox{
         name: head.name,
         size: head.size,
         offsets: offsets,
     })
 }
 
 /// Parse a stss box.
-pub fn read_stss<T: ReadBytesExt>(src: &mut T, head: &BoxHeader) -> byteorder::Result<SyncSampleBox> {
+fn read_stss<T: ReadBytesExt>(src: &mut T, head: &BoxHeader) -> Result<SyncSampleBox> {
     let (_, _) = try!(read_fullbox_extra(src));
     let sample_count = try!(be_u32(src));
     let mut samples = Vec::new();
     for _ in 0..sample_count {
         samples.push(try!(be_u32(src)));
     }
 
     Ok(SyncSampleBox{
         name: head.name,
         size: head.size,
         samples: samples,
     })
 }
 
 /// Parse a stsc box.
-pub fn read_stsc<T: ReadBytesExt>(src: &mut T, head: &BoxHeader) -> byteorder::Result<SampleToChunkBox> {
+fn read_stsc<T: ReadBytesExt>(src: &mut T, head: &BoxHeader) -> Result<SampleToChunkBox> {
     let (_, _) = try!(read_fullbox_extra(src));
     let sample_count = try!(be_u32(src));
     let mut samples = Vec::new();
     for _ in 0..sample_count {
         let first_chunk = try!(be_u32(src));
         let samples_per_chunk = try!(be_u32(src));
         let sample_description_index = try!(be_u32(src));
         samples.push(SampleToChunk{
@@ -613,17 +668,17 @@ pub fn read_stsc<T: ReadBytesExt>(src: &
     Ok(SampleToChunkBox{
         name: head.name,
         size: head.size,
         samples: samples,
     })
 }
 
 /// Parse a stsz box.
-pub fn read_stsz<T: ReadBytesExt>(src: &mut T, head: &BoxHeader) -> byteorder::Result<SampleSizeBox> {
+fn read_stsz<T: ReadBytesExt>(src: &mut T, head: &BoxHeader) -> Result<SampleSizeBox> {
     let (_, _) = try!(read_fullbox_extra(src));
     let sample_size = try!(be_u32(src));
     let sample_count = try!(be_u32(src));
     let mut sample_sizes = Vec::new();
     if sample_size == 0 {
         for _ in 0..sample_count {
             sample_sizes.push(try!(be_u32(src)));
         }
@@ -633,17 +688,17 @@ pub fn read_stsz<T: ReadBytesExt>(src: &
         name: head.name,
         size: head.size,
         sample_size: sample_size,
         sample_sizes: sample_sizes,
     })
 }
 
 /// Parse a stts box.
-pub fn read_stts<T: ReadBytesExt>(src: &mut T, head: &BoxHeader) -> byteorder::Result<TimeToSampleBox> {
+fn read_stts<T: ReadBytesExt>(src: &mut T, head: &BoxHeader) -> Result<TimeToSampleBox> {
     let (_, _) = try!(read_fullbox_extra(src));
     let sample_count = try!(be_u32(src));
     let mut samples = Vec::new();
     for _ in 0..sample_count {
         let sample_count = try!(be_u32(src));
         let sample_delta = try!(be_u32(src));
         samples.push(Sample{
             sample_count: sample_count,
@@ -654,52 +709,52 @@ pub fn read_stts<T: ReadBytesExt>(src: &
     Ok(TimeToSampleBox{
         name: head.name,
         size: head.size,
         samples: samples,
     })
 }
 
 /// Parse a hdlr box.
-pub fn read_hdlr<T: ReadBytesExt + BufRead>(src: &mut T, head: &BoxHeader) -> byteorder::Result<HandlerBox> {
+fn read_hdlr<T: ReadBytesExt + BufRead>(src: &mut T, head: &BoxHeader) -> Result<HandlerBox> {
     let (_, _) = try!(read_fullbox_extra(src));
 
     // Skip uninteresting fields.
     try!(skip(src, 4));
 
-    let handler_type = FourCC(try!(be_u32(src)));
+    let handler_type = try!(be_fourcc(src));
 
     // Skip uninteresting fields.
     try!(skip(src, 12));
 
     // TODO(kinetik): Find a copy of ISO/IEC 14496-1 to work out how strings are encoded.
     // As a hack, just consume the rest of the box.
     try!(skip_remaining_box_content(src, head));
 
     Ok(HandlerBox{
         name: head.name,
         size: head.size,
         handler_type: handler_type,
     })
 }
 
 /// Parse a stsd box.
-pub fn read_stsd<T: ReadBytesExt + BufRead>(src: &mut T, head: &BoxHeader, track: &Track) -> byteorder::Result<SampleDescriptionBox> {
+fn read_stsd<T: ReadBytesExt + BufRead>(src: &mut T, head: &BoxHeader, track: &Track) -> Result<SampleDescriptionBox> {
     let (_, _) = try!(read_fullbox_extra(src));
 
     let description_count = try!(be_u32(src));
     let mut descriptions = Vec::new();
 
     for _ in 0..description_count {
         let description = match track.track_type {
             TrackType::Video => {
                 let h = try!(read_box_header(src));
                 // TODO(kinetik): avc3 and encv here also?
-                if fourcc_to_string(h.name) != "avc1" {
-                    panic!("unsupported SampleEntry::Video subtype");
+                if &h.name.0 != b"avc1" {
+                    return Err(Error::Unsupported);
                 }
 
                 // Skip uninteresting fields.
                 try!(skip(src, 6));
 
                 let data_reference_index = try!(be_u16(src));
 
                 // Skip uninteresting fields.
@@ -708,18 +763,18 @@ pub fn read_stsd<T: ReadBytesExt + BufRe
                 let width = try!(be_u16(src));
                 let height = try!(be_u16(src));
 
                 // Skip uninteresting fields.
                 try!(skip(src, 50));
 
                 // TODO(kinetik): Parse avcC atom?  For now we just stash the data.
                 let h = try!(read_box_header(src));
-                if fourcc_to_string(h.name) != "avcC" {
-                    panic!("expected avcC atom inside avc1");
+                if &h.name.0 != b"avcC" {
+                    return Err(Error::InvalidData);
                 }
                 let mut data: Vec<u8> = vec![0; (h.size - h.offset) as usize];
                 let r = try!(src.read(&mut data));
                 assert!(r == data.len());
                 let avcc = AVCDecoderConfigurationRecord { data: data };
 
                 try!(skip_remaining_box_content(src, head));
 
@@ -728,18 +783,18 @@ pub fn read_stsd<T: ReadBytesExt + BufRe
                     width: width,
                     height: height,
                     avcc: avcc,
                 }
             },
             TrackType::Audio => {
                 let h = try!(read_box_header(src));
                 // TODO(kinetik): enca here also?
-                if fourcc_to_string(h.name) != "mp4a" {
-                    panic!("unsupported SampleEntry::Audio subtype");
+                if &h.name.0 != b"mp4a" {
+                    return Err(Error::Unsupported);
                 }
 
                 // Skip uninteresting fields.
                 try!(skip(src, 6));
 
                 let data_reference_index = try!(be_u16(src));
 
                 // Skip uninteresting fields.
@@ -750,18 +805,18 @@ pub fn read_stsd<T: ReadBytesExt + BufRe
 
                 // Skip uninteresting fields.
                 try!(skip(src, 4));
 
                 let samplerate = try!(be_u32(src));
 
                 // TODO(kinetik): Parse esds atom?  For now we just stash the data.
                 let h = try!(read_box_header(src));
-                if fourcc_to_string(h.name) != "esds" {
-                    panic!("expected esds atom inside mp4a");
+                if &h.name.0 != b"esds" {
+                    return Err(Error::InvalidData);
                 }
                 let (_, _) = try!(read_fullbox_extra(src));
                 let mut data: Vec<u8> = vec![0; (h.size - h.offset - 4) as usize];
                 let r = try!(src.read(&mut data));
                 assert!(r == data.len());
                 let esds = ES_Descriptor { data: data };
 
                 SampleEntry::Audio {
@@ -778,94 +833,90 @@ pub fn read_stsd<T: ReadBytesExt + BufRe
 
     Ok(SampleDescriptionBox{
         name: head.name,
         size: head.size,
         descriptions: descriptions,
     })
 }
 
-/// Convert the iso box type or other 4-character value to a string.
-fn fourcc_to_string(name: FourCC) -> String {
-    let u32_to_vec = |u| {
-        vec!((u >> 24 & 0xffu32) as u8,
-             (u >> 16 & 0xffu32) as u8,
-             (u >>  8 & 0xffu32) as u8,
-             (u & 0xffu32) as u8)
-    };
-    let name_bytes = u32_to_vec(name.0);
-    String::from_utf8_lossy(&name_bytes).into_owned()
-}
-
 /// Skip a number of bytes that we don't care to parse.
-fn skip<T: BufRead>(src: &mut T, bytes: usize) -> byteorder::Result<usize> {
+fn skip<T: BufRead>(src: &mut T, bytes: usize) -> Result<usize> {
     let mut bytes_to_skip = bytes;
     while bytes_to_skip > 0 {
-        let len = {
-            let buf = src.fill_buf().unwrap();
-            buf.len()
-        };
+        let len = try!(src.fill_buf()).len();
         if len == 0 {
-            return Err(byteorder::Error::UnexpectedEOF)
+            return Err(Error::UnexpectedEOF)
         }
         let discard = cmp::min(len, bytes_to_skip);
         src.consume(discard);
         bytes_to_skip -= discard;
     }
     assert!(bytes_to_skip == 0);
     Ok(bytes)
 }
 
 fn be_i16<T: ReadBytesExt>(src: &mut T) -> byteorder::Result<i16> {
-    src.read_i16::<BigEndian>()
+    src.read_i16::<byteorder::BigEndian>()
 }
 
 fn be_i32<T: ReadBytesExt>(src: &mut T) -> byteorder::Result<i32> {
-    src.read_i32::<BigEndian>()
+    src.read_i32::<byteorder::BigEndian>()
 }
 
 fn be_i64<T: ReadBytesExt>(src: &mut T) -> byteorder::Result<i64> {
-    src.read_i64::<BigEndian>()
+    src.read_i64::<byteorder::BigEndian>()
 }
 
 fn be_u16<T: ReadBytesExt>(src: &mut T) -> byteorder::Result<u16> {
-    src.read_u16::<BigEndian>()
+    src.read_u16::<byteorder::BigEndian>()
 }
 
 fn be_u32<T: ReadBytesExt>(src: &mut T) -> byteorder::Result<u32> {
-    src.read_u32::<BigEndian>()
+    src.read_u32::<byteorder::BigEndian>()
 }
 
 fn be_u64<T: ReadBytesExt>(src: &mut T) -> byteorder::Result<u64> {
-    src.read_u64::<BigEndian>()
+    src.read_u64::<byteorder::BigEndian>()
+}
+
+fn be_fourcc<T: Read>(src: &mut T) -> Result<FourCC> {
+    let mut fourcc = [0; 4];
+    match src.read(&mut fourcc) {
+        // Expect all 4 bytes read.
+        Ok(4) => Ok(FourCC(fourcc)),
+        // Short read means EOF.
+        Ok(_) => Err(Error::UnexpectedEOF),
+        // Propagate std::io errors.
+        Err(e) => Err(Error::Io(e)),
+    }
 }
 
 #[test]
 fn test_read_box_header() {
-    use std::io::Cursor;
     use std::io::Write;
     let mut test: Vec<u8> = vec![0, 0, 0, 8]; // minimal box length
     write!(&mut test, "test").unwrap(); // box type
     let mut stream = Cursor::new(test);
     let parsed = read_box_header(&mut stream).unwrap();
-    assert_eq!(parsed.name, FourCC(1952805748));
+    assert_eq!(parsed.name, FourCC(*b"test"));
     assert_eq!(parsed.size, 8);
     println!("box {:?}", parsed);
 }
 
 #[test]
 fn test_read_box_header_long() {
     use std::io::Cursor;
     let mut test: Vec<u8> = vec![0, 0, 0, 1]; // long box extension code
     test.extend("long".to_string().into_bytes()); // box type
     test.extend(vec![0, 0, 0, 0, 0, 0, 16, 0]); // 64 bit size
     // Skip generating box content.
     let mut stream = Cursor::new(test);
     let parsed = read_box_header(&mut stream).unwrap();
-    assert_eq!(parsed.name, FourCC(1819242087));
+    assert_eq!(parsed.name, FourCC(*b"long"));
     assert_eq!(parsed.size, 4096);
     println!("box {:?}", parsed);
 }
 
 #[test]
 fn test_read_ftyp() {
     use std::io::Cursor;
     use std::io::Write;
@@ -875,56 +926,54 @@ fn test_read_ftyp() {
     test.extend(vec![0, 0, 0, 0]);      // minor version
     write!(&mut test, "isom").unwrap(); // compatible brands...
     write!(&mut test, "mp42").unwrap();
     assert_eq!(test.len(), 24);
 
     let mut stream = Cursor::new(test);
     let header = read_box_header(&mut stream).unwrap();
     let parsed = read_ftyp(&mut stream, &header).unwrap();
-    assert_eq!(parsed.name, FourCC(1718909296));
+    assert_eq!(parsed.name, FourCC(*b"ftyp"));
     assert_eq!(parsed.size, 24);
-    assert_eq!(parsed.major_brand, FourCC(1836069938));
+    assert_eq!(parsed.major_brand, FourCC(*b"mp42"));
     assert_eq!(parsed.minor_version, 0);
     assert_eq!(parsed.compatible_brands.len(), 2);
-    assert_eq!(parsed.compatible_brands[0], FourCC(1769172845));
-    assert_eq!(fourcc_to_string(parsed.compatible_brands[1]), "mp42");
+    assert_eq!(parsed.compatible_brands[0], FourCC(*b"isom"));
+    assert_eq!(parsed.compatible_brands[1], FourCC(*b"mp42"));
     println!("box {:?}", parsed);
 }
 
 #[test]
 fn test_read_elst_v0() {
-    use std::io::Cursor;
     use std::io::Write;
     let mut test: Vec<u8> = vec![0, 0, 0, 28]; // size
     write!(&mut test, "elst").unwrap(); // type
     test.extend(vec![0, 0, 0, 0]); // fullbox
     test.extend(vec![0, 0, 0, 1]); // count
     test.extend(vec![1, 2, 3, 4,
                      5, 6, 7, 8,
                      9, 10,
                      11, 12]);
     assert_eq!(test.len(), 28);
 
     let mut stream = Cursor::new(test);
     let header = read_box_header(&mut stream).unwrap();
     let parsed = read_elst(&mut stream, &header).unwrap();
-    assert_eq!(parsed.name, FourCC(1701606260));
+    assert_eq!(parsed.name, FourCC(*b"elst"));
     assert_eq!(parsed.size, 28);
     assert_eq!(parsed.edits.len(), 1);
     assert_eq!(parsed.edits[0].segment_duration, 16909060);
     assert_eq!(parsed.edits[0].media_time, 84281096);
     assert_eq!(parsed.edits[0].media_rate_integer, 2314);
     assert_eq!(parsed.edits[0].media_rate_fraction, 2828);
     println!("box {:?}", parsed);
 }
 
 #[test]
 fn test_read_elst_v1() {
-    use std::io::Cursor;
     use std::io::Write;
     let mut test: Vec<u8> = vec![0, 0, 0, 56]; // size
     write!(&mut test, "elst").unwrap(); // type
     test.extend(vec![1, 0, 0, 0]); // fullbox
     test.extend(vec![0, 0, 0, 2]); // count
     test.extend(vec![1, 2, 3, 4, 1, 2, 3, 4,
                      5, 6, 7, 8, 5, 6, 7, 8,
                      9, 10,
@@ -933,65 +982,63 @@ fn test_read_elst_v1() {
                      5, 6, 7, 8, 5, 6, 7, 8,
                      9, 10,
                      11, 12]);
     assert_eq!(test.len(), 56);
 
     let mut stream = Cursor::new(test);
     let header = read_box_header(&mut stream).unwrap();
     let parsed = read_elst(&mut stream, &header).unwrap();
-    assert_eq!(parsed.name, FourCC(1701606260));
+    assert_eq!(parsed.name, FourCC(*b"elst"));
     assert_eq!(parsed.size, 56);
     assert_eq!(parsed.edits.len(), 2);
     assert_eq!(parsed.edits[1].segment_duration, 72623859723010820);
     assert_eq!(parsed.edits[1].media_time, 361984551075317512);
     assert_eq!(parsed.edits[1].media_rate_integer, 2314);
     assert_eq!(parsed.edits[1].media_rate_fraction, 2828);
     println!("box {:?}", parsed);
 }
 
 #[test]
 fn test_read_mdhd_v0() {
-    use std::io::Cursor;
     use std::io::Write;
     let mut test: Vec<u8> = vec![0, 0, 0, 32]; // size
     write!(&mut test, "mdhd").unwrap(); // type
     test.extend(vec![0, 0, 0, 0]); // fullbox
     test.extend(vec![0, 0, 0, 0,
                      0, 0, 0, 0,
                      1, 2, 3, 4,
                      5, 6, 7, 8,
                      0, 0, 0, 0]);
     assert_eq!(test.len(), 32);
 
     let mut stream = Cursor::new(test);
     let header = read_box_header(&mut stream).unwrap();
     let parsed = read_mdhd(&mut stream, &header).unwrap();
-    assert_eq!(parsed.name, FourCC(1835296868));
+    assert_eq!(parsed.name, FourCC(*b"mdhd"));
     assert_eq!(parsed.size, 32);
     assert_eq!(parsed.timescale, 16909060);
     assert_eq!(parsed.duration, 84281096);
     println!("box {:?}", parsed);
 }
 
 #[test]
 fn test_read_mdhd_v1() {
-    use std::io::Cursor;
     use std::io::Write;
     let mut test: Vec<u8> = vec![0, 0, 0, 44]; // size
     write!(&mut test, "mdhd").unwrap(); // type
     test.extend(vec![1, 0, 0, 0]); // fullbox
     test.extend(vec![0, 0, 0, 0, 0, 0, 0, 0,
                      0, 0, 0, 0, 0, 0, 0, 0,
                      1, 2, 3, 4,
                      5, 6, 7, 8, 5, 6, 7, 8,
                      0, 0, 0, 0]);
     assert_eq!(test.len(), 44);
 
     let mut stream = Cursor::new(test);
     let header = read_box_header(&mut stream).unwrap();
     let parsed = read_mdhd(&mut stream, &header).unwrap();
-    assert_eq!(parsed.name, FourCC(1835296868));
+    assert_eq!(parsed.name, FourCC(*b"mdhd"));
     assert_eq!(parsed.size, 44);
     assert_eq!(parsed.timescale, 16909060);
     assert_eq!(parsed.duration, 361984551075317512);
     println!("box {:?}", parsed);
 }
--- a/media/libstagefright/binding/capi.rs
+++ b/media/libstagefright/binding/capi.rs
@@ -1,41 +1,59 @@
-// C API for mp4parse module.
-// Parses ISO Base Media Format aka video/mp4 streams.
+//! C API for mp4parse module.
+//!
+//! Parses ISO Base Media Format aka video/mp4 streams.
+//!
+//! # Examples
+//!
+//! ```rust
+//! extern crate mp4parse;
+//!
+//! // Minimal valid mp4 containing no tracks.
+//! let data = b"\0\0\0\x0cftypmp42";
+//!
+//! let context = mp4parse::mp4parse_new();
+//! unsafe {
+//!     let rv = mp4parse::mp4parse_read(context, data.as_ptr(), data.len());
+//!     assert_eq!(0, rv);
+//!     mp4parse::mp4parse_free(context);
+//! }
+//! ```
 
 // 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 https://mozilla.org/MPL/2.0/.
 
 use std;
 use std::io::Cursor;
-use byteorder;
 
 // Symbols we need from our rust api.
 use MediaContext;
 use read_box;
+use Error;
 
 /// Allocate an opaque rust-side parser context.
 #[no_mangle]
 pub extern "C" fn mp4parse_new() -> *mut MediaContext {
     let context = Box::new(MediaContext::new());
-    unsafe {
-        // transmute is unsafe, but context is always valid.
-        std::mem::transmute(context)
-    }
+    Box::into_raw(context)
 }
 
 /// Free a rust-side parser context.
 #[no_mangle]
 pub unsafe extern "C" fn mp4parse_free(context: *mut MediaContext) {
     assert!(!context.is_null());
-    let _: Box<MediaContext> = std::mem::transmute(context);
+    let _ = Box::from_raw(context);
 }
 
-/// Feed a buffer through read_box(), returning the number of detected tracks.
+/// Feed a buffer through `read_box()` with the given rust-side
+/// parser context, returning the number of detected tracks.
+///
+/// This is safe to call with NULL arguments but will crash
+/// if given invalid pointers, as is usual for C.
 #[no_mangle]
 pub unsafe extern "C" fn mp4parse_read(context: *mut MediaContext, buffer: *const u8, size: usize) -> i32 {
     // Validate arguments from C.
     if context.is_null() {
         return -1;
     }
     if buffer.is_null() || size < 8 {
         return -1;
@@ -47,17 +65,17 @@ pub unsafe extern "C" fn mp4parse_read(c
     let b = std::slice::from_raw_parts(buffer, size);
     let mut c = Cursor::new(b);
 
     // Parse in a subthread to catch any panics.
     let task = std::thread::spawn(move || {
         loop {
             match read_box(&mut c, &mut context) {
                 Ok(_) => {},
-                Err(byteorder::Error::UnexpectedEOF) => { break },
+                Err(Error::UnexpectedEOF) => { break },
                 Err(e) => { panic!(e); },
             }
         }
         // Make sure the track count fits in an i32 so we can use
         // negative values for failure.
         assert!(context.tracks.len() < std::i32::MAX as usize);
         context.tracks.len() as i32
     });
@@ -87,17 +105,17 @@ fn arg_validation() {
 
     let buffer = vec![0u8; 8];
 
     unsafe {
         assert_eq!(-1, mp4parse_read(null_context, null_buffer, 0));
         assert_eq!(-1, mp4parse_read(context, null_buffer, 0));
     }
 
-    for size in (0..buffer.len()) {
+    for size in 0..buffer.len() {
         println!("testing buffer length {}", size);
         unsafe {
             assert_eq!(-1, mp4parse_read(context, buffer.as_ptr(), size));
         }
     }
 
     unsafe { mp4parse_free(context); }
 }
--- a/media/libstagefright/binding/mp4parse-mod.patch
+++ b/media/libstagefright/binding/mp4parse-mod.patch
@@ -1,13 +1,13 @@
 diff --git a/media/libstagefright/binding/MP4Metadata.rs b/media/libstagefright/binding/MP4Metadata.rs
-index a9ab567..b746f15 100644
+index 2f1b873..d2ad827 100644
 --- a/media/libstagefright/binding/MP4Metadata.rs
 +++ b/media/libstagefright/binding/MP4Metadata.rs
-@@ -214,7 +214,7 @@ pub struct Track {
-     track_type: TrackType,
- }
+@@ -4,7 +4,7 @@
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
  
 -extern crate byteorder;
 +mod byteorder; // 'extern crate' upstream.
- use byteorder::{BigEndian, ReadBytesExt};
+ use byteorder::ReadBytesExt;
+ use std::error::Error as ErrorTrait; // For Err(e) => e.description().
  use std::io::{Read, BufRead, Take};
- use std::io::Cursor;
--- a/media/libstagefright/binding/update-rust.sh
+++ b/media/libstagefright/binding/update-rust.sh
@@ -1,13 +1,13 @@
 #!/bin/sh
 # Script to update mp4parse-rust sources to latest upstream
 
 # Default version.
-VER=v0.1.2
+VER=v0.1.3
 
 # Accept version or commit from the command line.
 if test -n "$1"; then
   VER=$1
 fi
 
 echo "Fetching sources..."
 rm -rf _upstream
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_device_info.cc
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_device_info.cc
@@ -71,16 +71,19 @@ const char *DesktopDisplayDevice::getDev
   return deviceNameUTF8_;
 }
 
 const char *DesktopDisplayDevice::getUniqueIdName() {
   return deviceUniqueIdUTF8_;
 }
 
 DesktopDisplayDevice& DesktopDisplayDevice::operator= (DesktopDisplayDevice& other) {
+  if (&other == this) {
+    return *this;
+  }
   screenId_ = other.getScreenId();
   setUniqueIdName(other.getUniqueIdName());
   setDeviceName(other.getDeviceName());
 
   return *this;
 }
 
 
--- a/mfbt/EnumSet.h
+++ b/mfbt/EnumSet.h
@@ -123,16 +123,24 @@ public:
   EnumSet<T> operator-(const EnumSet<T> aEnumSet) const
   {
     EnumSet<T> result(*this);
     result -= aEnumSet;
     return result;
   }
 
   /**
+   * Clear
+   */
+  void clear()
+  {
+    mBitField = 0;
+  }
+
+  /**
    * Intersection
    */
   void operator&=(const EnumSet<T> aEnumSet)
   {
     mBitField &= aEnumSet.mBitField;
   }
 
   /**
--- a/modules/libjar/InterceptedJARChannel.cpp
+++ b/modules/libjar/InterceptedJARChannel.cpp
@@ -118,16 +118,22 @@ InterceptedJARChannel::SetChannelInfo(mo
 {
   if (!mChannel) {
     return NS_ERROR_FAILURE;
   }
 
   return aChannelInfo->ResurrectInfoOnChannel(mChannel);
 }
 
+NS_IMETHODIMP
+InterceptedJARChannel::GetConsoleReportCollector(nsIConsoleReportCollector**)
+{
+  return NS_ERROR_NOT_AVAILABLE;
+}
+
 void
 InterceptedJARChannel::NotifyController()
 {
   nsresult rv = NS_NewPipe(getter_AddRefs(mSynthesizedInput),
                            getter_AddRefs(mResponseBody),
                            0, UINT32_MAX, true, true);
   NS_ENSURE_SUCCESS_VOID(rv);
 
@@ -141,14 +147,8 @@ InterceptedJARChannel::NotifyController(
   rv = dispatcher->Dispatch();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     rv = ResetInterception();
     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
         "Failed to resume intercepted network request");
   }
   mController = nullptr;
 }
-
-nsIConsoleReportCollector*
-InterceptedJARChannel::GetConsoleReportCollector() const
-{
-  return nullptr;
-}
--- a/modules/libjar/InterceptedJARChannel.h
+++ b/modules/libjar/InterceptedJARChannel.h
@@ -48,17 +48,14 @@ class InterceptedJARChannel : public nsI
 public:
   InterceptedJARChannel(nsJARChannel* aChannel,
                         nsINetworkInterceptController* aController);
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIINTERCEPTEDCHANNEL
 
   void NotifyController();
-
-  virtual nsIConsoleReportCollector*
-  GetConsoleReportCollector() const override;
 };
 
 } // namespace net
 } // namespace mozilla
 
 #endif // InterceptedJARChannel_h
--- a/netwerk/base/BackgroundFileSaver.cpp
+++ b/netwerk/base/BackgroundFileSaver.cpp
@@ -28,19 +28,19 @@
 #include <softpub.h>
 #include <wintrust.h>
 #endif // XP_WIN
 
 namespace mozilla {
 namespace net {
 
 // NSPR_LOG_MODULES=BackgroundFileSaver:5
-PRLogModuleInfo *BackgroundFileSaver::prlog = nullptr;
-#define LOG(args) MOZ_LOG(BackgroundFileSaver::prlog, mozilla::LogLevel::Debug, args)
-#define LOG_ENABLED() MOZ_LOG_TEST(BackgroundFileSaver::prlog, mozilla::LogLevel::Debug)
+static LazyLogModule prlog("BackgroundFileSaver");
+#define LOG(args) MOZ_LOG(prlog, mozilla::LogLevel::Debug, args)
+#define LOG_ENABLED() MOZ_LOG_TEST(prlog, mozilla::LogLevel::Debug)
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Globals
 
 /**
  * Buffer size for writing to the output file or reading from the input file.
  */
 #define BUFFERED_IO_SIZE (1024 * 32)
@@ -105,18 +105,16 @@ BackgroundFileSaver::BackgroundFileSaver
 , mRenamedTargetKeepPartial(false)
 , mAsyncCopyContext(nullptr)
 , mSha256Enabled(false)
 , mSignatureInfoEnabled(false)
 , mActualTarget(nullptr)
 , mActualTargetKeepPartial(false)
 , mDigestContext(nullptr)
 {
-  if (!prlog)
-    prlog = PR_NewLogModule("BackgroundFileSaver");
   LOG(("Created BackgroundFileSaver [this = %p]", this));
 }
 
 BackgroundFileSaver::~BackgroundFileSaver()
 {
   LOG(("Destroying BackgroundFileSaver [this = %p]", this));
   nsNSSShutDownPreventionLock lock;
   if (isAlreadyShutDown()) {
--- a/netwerk/base/BackgroundFileSaver.h
+++ b/netwerk/base/BackgroundFileSaver.h
@@ -20,17 +20,16 @@
 #include "nsIBackgroundFileSaver.h"
 #include "nsIStreamListener.h"
 #include "nsStreamUtils.h"
 #include "ScopedNSSTypes.h"
 
 class nsIAsyncInputStream;
 class nsIThread;
 class nsIX509CertList;
-struct PRLogModuleInfo;
 
 namespace mozilla {
 namespace net {
 
 class DigestOutputStream;
 
 ////////////////////////////////////////////////////////////////////////////////
 //// BackgroundFileSaver
@@ -69,18 +68,16 @@ public:
    * session finished, and this counter is reset.
    */
   static uint32_t sTelemetryMaxThreadCount;
 
 
 protected:
   virtual ~BackgroundFileSaver();
 
-  static PRLogModuleInfo *prlog;
-
   /**
    * Helper function for managing NSS objects (mDigestContext).
    */
   void destructorSafeDestroyNSSReference();
 
   /**
    * Thread that constructed this object.
    */
--- a/netwerk/base/CaptivePortalService.cpp
+++ b/netwerk/base/CaptivePortalService.cpp
@@ -11,23 +11,23 @@
 #define kInterfaceName "captive-portal-inteface"
 
 static const char kOpenCaptivePortalLoginEvent[] = "captive-portal-login";
 static const char kAbortCaptivePortalLoginEvent[] = "captive-portal-login-abort";
 static const char kCaptivePortalLoginSuccessEvent[] = "captive-portal-login-success";
 
 static const uint32_t kDefaultInterval = 60*1000; // check every 60 seconds
 
-static PRLogModuleInfo *gCaptivePortalLog = nullptr;
+namespace mozilla {
+namespace net {
+
+static LazyLogModule gCaptivePortalLog("CaptivePortalService");
 #undef LOG
 #define LOG(args) MOZ_LOG(gCaptivePortalLog, mozilla::LogLevel::Debug, args)
 
-namespace mozilla {
-namespace net {
-
 NS_IMPL_ISUPPORTS(CaptivePortalService, nsICaptivePortalService, nsIObserver,
                   nsISupportsWeakReference, nsITimerCallback,
                   nsICaptivePortalCallback)
 
 CaptivePortalService::CaptivePortalService()
   : mState(UNKNOWN)
   , mStarted(false)
   , mInitialized(false)
@@ -96,20 +96,16 @@ CaptivePortalService::RearmTimer()
 nsresult
 CaptivePortalService::Initialize()
 {
   if (mInitialized || XRE_GetProcessType() != GeckoProcessType_Default) {
     return NS_OK;
   }
   mInitialized = true;
 
-  if (!gCaptivePortalLog) {
-    gCaptivePortalLog = PR_NewLogModule("CaptivePortalService");
-  }
-
   nsCOMPtr<nsIObserverService> observerService =
     mozilla::services::GetObserverService();
   if (observerService) {
     observerService->AddObserver(this, kOpenCaptivePortalLoginEvent, true);
     observerService->AddObserver(this, kAbortCaptivePortalLoginEvent, true);
     observerService->AddObserver(this, kCaptivePortalLoginSuccessEvent, true);
   }
 
--- a/netwerk/base/Predictor.cpp
+++ b/netwerk/base/Predictor.cpp
@@ -51,17 +51,18 @@ static const int32_t ANDROID_23_VERSION 
 
 using namespace mozilla;
 
 namespace mozilla {
 namespace net {
 
 Predictor *Predictor::sSelf = nullptr;
 
-static PRLogModuleInfo *gPredictorLog = nullptr;
+static LazyLogModule gPredictorLog("NetworkPredictor");
+
 #define PREDICTOR_LOG(args) MOZ_LOG(gPredictorLog, mozilla::LogLevel::Debug, args)
 
 #define RETURN_IF_FAILED(_rv) \
   do { \
     if (NS_FAILED(_rv)) { \
       return; \
     } \
   } while (0)
@@ -317,18 +318,16 @@ Predictor::Predictor()
   ,mSubresourceDegradationMax(PREDICTOR_SUB_DELTA_MAX_DEFAULT)
   ,mPreconnectMinConfidence(PRECONNECT_MIN_DEFAULT)
   ,mPreresolveMinConfidence(PRERESOLVE_MIN_DEFAULT)
   ,mRedirectLikelyConfidence(REDIRECT_LIKELY_DEFAULT)
   ,mMaxResourcesPerEntry(PREDICTOR_MAX_RESOURCES_DEFAULT)
   ,mStartupCount(1)
   ,mMaxURILength(PREDICTOR_MAX_URI_LENGTH_DEFAULT)
 {
-  gPredictorLog = PR_NewLogModule("NetworkPredictor");
-
   MOZ_ASSERT(!sSelf, "multiple Predictor instances!");
   sSelf = this;
 }
 
 Predictor::~Predictor()
 {
   if (mInitialized)
     Shutdown();
--- a/netwerk/base/nsAsyncRedirectVerifyHelper.cpp
+++ b/netwerk/base/nsAsyncRedirectVerifyHelper.cpp
@@ -8,26 +8,19 @@
 #include "nsThreadUtils.h"
 #include "nsNetUtil.h"
 
 #include "nsIOService.h"
 #include "nsIChannel.h"
 #include "nsIHttpChannelInternal.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 
+static mozilla::LazyLogModule gRedirectLog("nsRedirect");
 #undef LOG
-static PRLogModuleInfo *
-GetRedirectLog()
-{
-    static PRLogModuleInfo *sLog;
-    if (!sLog)
-        sLog = PR_NewLogModule("nsRedirect");
-    return sLog;
-}
-#define LOG(args) MOZ_LOG(GetRedirectLog(), mozilla::LogLevel::Debug, args)
+#define LOG(args) MOZ_LOG(gRedirectLog, mozilla::LogLevel::Debug, args)
 
 NS_IMPL_ISUPPORTS(nsAsyncRedirectVerifyHelper,
                   nsIAsyncVerifyRedirectCallback,
                   nsIRunnable)
 
 class nsAsyncVerifyRedirectCallbackEvent : public nsRunnable {
 public:
     nsAsyncVerifyRedirectCallbackEvent(nsIAsyncVerifyRedirectCallback *cb,
--- a/netwerk/base/nsAsyncStreamCopier.cpp
+++ b/netwerk/base/nsAsyncStreamCopier.cpp
@@ -14,17 +14,17 @@
 #include "mozilla/Logging.h"
 
 using namespace mozilla;
 
 #undef LOG
 //
 // NSPR_LOG_MODULES=nsStreamCopier:5
 //
-static PRLogModuleInfo *gStreamCopierLog = nullptr;
+static LazyLogModule gStreamCopierLog("nsStreamCopier");
 #define LOG(args) MOZ_LOG(gStreamCopierLog, mozilla::LogLevel::Debug, args)
 
 /**
  * An event used to perform initialization off the main thread.
  */
 class AsyncApplyBufferingPolicyEvent final: public nsRunnable
 {
 public:
@@ -65,18 +65,16 @@ private:
 nsAsyncStreamCopier::nsAsyncStreamCopier()
     : mLock("nsAsyncStreamCopier.mLock")
     , mMode(NS_ASYNCCOPY_VIA_READSEGMENTS)
     , mChunkSize(nsIOService::gDefaultSegmentSize)
     , mStatus(NS_OK)
     , mIsPending(false)
     , mShouldSniffBuffering(false)
 {
-    if (!gStreamCopierLog)
-        gStreamCopierLog = PR_NewLogModule("nsStreamCopier");
     LOG(("Creating nsAsyncStreamCopier @%x\n", this));
 }
 
 nsAsyncStreamCopier::~nsAsyncStreamCopier()
 {
     LOG(("Destroying nsAsyncStreamCopier @%x\n", this));
 }
 
--- a/netwerk/base/nsAutodialWin.cpp
+++ b/netwerk/base/nsAutodialWin.cpp
@@ -24,18 +24,17 @@
 //
 //    set NSPR_LOG_MODULES=Autodial:5
 //    set NSPR_LOG_FILE=nspr.log
 //
 // this enables LogLevel::Debug level information and places all output in
 // the file nspr.log
 //
 
-static PRLogModuleInfo* gLog = nullptr;
-
+static mozilla::LazyLogModule gLog("Autodial");
 #undef LOGD
 #undef LOGE
 #define LOGD(args) MOZ_LOG(gLog, mozilla::LogLevel::Debug, args)
 #define LOGE(args) MOZ_LOG(gLog, mozilla::LogLevel::Error, args)
 
 // Don't try to dial again within a few seconds of when user pressed cancel.
 #define NO_RETRY_PERIOD_SEC 5
 PRIntervalTime nsAutodial::mDontRetryUntil = 0;
@@ -57,19 +56,16 @@ nsAutodial::~nsAutodial()
 }
 
 
 // Get settings from the OS. These are settings that might change during
 // the OS session. Call Init() again to pick up those changes if required.
 // Returns NS_ERROR_FAILURE if error or NS_OK if success.
 nsresult nsAutodial::Init()
 {
-    if (!gLog)
-        gLog = PR_NewLogModule("Autodial");
-
     mDefaultEntryName[0] = '\0';
     mNumRASConnectionEntries = 0;
     mAutodialBehavior = QueryAutodialBehavior();
     
     // No need to continue in this case.
     if (mAutodialBehavior == AUTODIAL_NEVER)
     {
         return NS_OK;
--- a/netwerk/base/nsChannelClassifier.cpp
+++ b/netwerk/base/nsChannelClassifier.cpp
@@ -35,30 +35,29 @@
 #include "mozilla/Preferences.h"
 
 using mozilla::ArrayLength;
 using mozilla::Preferences;
 
 //
 // NSPR_LOG_MODULES=nsChannelClassifier:5
 //
-static PRLogModuleInfo *gChannelClassifierLog;
+static mozilla::LazyLogModule gChannelClassifierLog("nsChannelClassifier");
+
 #undef LOG
 #define LOG(args)     MOZ_LOG(gChannelClassifierLog, mozilla::LogLevel::Debug, args)
 #define LOG_ENABLED() MOZ_LOG_TEST(gChannelClassifierLog, mozilla::LogLevel::Debug)
 
 NS_IMPL_ISUPPORTS(nsChannelClassifier,
                   nsIURIClassifierCallback)
 
 nsChannelClassifier::nsChannelClassifier()
   : mIsAllowListed(false),
     mSuspendedChannel(false)
 {
-    if (!gChannelClassifierLog)
-        gChannelClassifierLog = PR_NewLogModule("nsChannelClassifier");
 }
 
 nsresult
 nsChannelClassifier::ShouldEnableTrackingProtection(nsIChannel *aChannel,
                                                     bool *result)
 {
     // Should only be called in the parent process.
     MOZ_ASSERT(XRE_IsParentProcess());
--- a/netwerk/base/nsDirectoryIndexStream.cpp
+++ b/netwerk/base/nsDirectoryIndexStream.cpp
@@ -13,18 +13,16 @@
   http://www.mozilla.org/projects/netlib/dirindexformat.html
 
  */
 
 #include "nsEscape.h"
 #include "nsDirectoryIndexStream.h"
 #include "mozilla/Logging.h"
 #include "prtime.h"
-static PRLogModuleInfo* gLog;
-
 #include "nsISimpleEnumerator.h"
 #ifdef THREADSAFE_I18N
 #include "nsCollationCID.h"
 #include "nsICollation.h"
 #include "nsILocale.h"
 #include "nsILocaleService.h"
 #endif
 #include "nsIFile.h"
@@ -36,22 +34,21 @@ static PRLogModuleInfo* gLog;
 // we want to do stuff like i18n sorting. However, none of the collation stuff
 // is threadsafe.
 // So THIS CODE IS ASCII ONLY!!!!!!!! This is no worse than the current
 // behaviour, though. See bug 99382.
 // When this is fixed, #define THREADSAFE_I18N to get this code working
 
 //#define THREADSAFE_I18N
 
+static mozilla::LazyLogModule gLog("nsDirectoryIndexStream");
+
 nsDirectoryIndexStream::nsDirectoryIndexStream()
     : mOffset(0), mStatus(NS_OK), mPos(0)
 {
-    if (! gLog)
-        gLog = PR_NewLogModule("nsDirectoryIndexStream");
-
     MOZ_LOG(gLog, LogLevel::Debug,
            ("nsDirectoryIndexStream[%p]: created", this));
 }
 
 static int compare(nsIFile* aElement1, nsIFile* aElement2, void* aData)
 {
     if (!NS_IsNativeUTF8()) {
         // don't check for errors, because we can't report them anyway
--- a/netwerk/base/nsILoadGroup.idl
+++ b/netwerk/base/nsILoadGroup.idl
@@ -8,17 +8,21 @@
 interface nsISimpleEnumerator;
 interface nsIRequestObserver;
 interface nsIInterfaceRequestor;
 interface nsISchedulingContext;
 
 typedef unsigned long nsLoadFlags;
 
 /**
- * A load group maintains a collection of nsIRequest objects. 
+ * A load group maintains a collection of nsIRequest objects.
+ * This is used in lots of places where groups of requests need to be tracked.
+ * For example, nsIDocument::mDocumentLoadGroup is used to track all requests
+ * made for subdocuments in order to track page load progress and allow all
+ * requests made on behalf of the document to be stopped, etc.
  */
 [scriptable, uuid(f0c87725-7a35-463c-9ceb-2c07f23406cc)]
 interface nsILoadGroup : nsIRequest
 {
     /**
      * The group observer is notified when requests are added to and removed
      * from this load group.  The groupObserver is weak referenced.
      */
--- a/netwerk/base/nsINetworkInterceptController.idl
+++ b/netwerk/base/nsINetworkInterceptController.idl
@@ -2,16 +2,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 #include "nsIContentPolicyBase.idl"
 
 interface nsIChannel;
+interface nsIConsoleReportCollector;
 interface nsIOutputStream;
 interface nsIURI;
 
 %{C++
 #include "nsIConsoleReportCollector.h"
 namespace mozilla {
 namespace dom {
 class ChannelInfo;
@@ -23,17 +24,17 @@ class ChannelInfo;
 
 /**
  * Interface to allow implementors of nsINetworkInterceptController to control the behaviour
  * of intercepted channels without tying implementation details of the interception to
  * the actual channel. nsIInterceptedChannel is expected to be implemented by objects
  * which do not implement nsIChannel.
  */
 
-[scriptable, uuid(ea78e439-cc42-4b2d-a42b-85ab55a149d1)]
+[scriptable, uuid(231bb567-90e1-4973-9728-7dab93ab29a8)]
 interface nsIInterceptedChannel : nsISupports
 {
     /**
      * Instruct a channel that has been intercepted to continue with the original
      * network request.
      */
     void resetInterception();
 
@@ -82,26 +83,27 @@ interface nsIInterceptedChannel : nsISup
     void setChannelInfo(in ChannelInfo channelInfo);
 
     /**
      * Get the internal load type from the underlying channel.
      */
     [noscript]
     readonly attribute nsContentPolicyType internalContentPolicyType;
 
+    [noscript]
+    readonly attribute nsIConsoleReportCollector consoleReportCollector;
+
 %{C++
-    // Allow access to the inner channel as a ConsoleReportCollector off
-    // the main thread.  Pure C++ method here to avoid requiring an
-    // AddRef() during QI.  Callers should not save the returned pointer.
-    // May return nullptr.
-    //
-    // Note: Only safe to use OMT prior to resetInterception(),
-    //       finishSynthesizedResponse(), and cancel().
-    virtual nsIConsoleReportCollector*
-    GetConsoleReportCollector() const = 0;
+    already_AddRefed<nsIConsoleReportCollector>
+    GetConsoleReportCollector()
+    {
+      nsCOMPtr<nsIConsoleReportCollector> reporter;
+      GetConsoleReportCollector(getter_AddRefs(reporter));
+      return reporter.forget();
+    }
 %}
 };
 
 /**
  * Interface to allow consumers to dispatch the fetch event asynchronously.
  * Consumers get access to this interface by calling channelIntercepted(),
  * and they can choose to either dispatch() immediately or do that at some
  * later time.
--- a/netwerk/base/nsInputStreamPump.cpp
+++ b/netwerk/base/nsInputStreamPump.cpp
@@ -19,17 +19,17 @@
 #include "nsNetCID.h"
 #include <algorithm>
 
 static NS_DEFINE_CID(kStreamTransportServiceCID, NS_STREAMTRANSPORTSERVICE_CID);
 
 //
 // NSPR_LOG_MODULES=nsStreamPump:5
 //
-static PRLogModuleInfo *gStreamPumpLog = nullptr;
+static mozilla::LazyLogModule gStreamPumpLog("nsStreamPump");
 #undef LOG
 #define LOG(args) MOZ_LOG(gStreamPumpLog, mozilla::LogLevel::Debug, args)
 
 //-----------------------------------------------------------------------------
 // nsInputStreamPump methods
 //-----------------------------------------------------------------------------
 
 nsInputStreamPump::nsInputStreamPump()
@@ -40,18 +40,16 @@ nsInputStreamPump::nsInputStreamPump()
     , mSuspendCount(0)
     , mLoadFlags(LOAD_NORMAL)
     , mProcessingCallbacks(false)
     , mWaitingForInputStreamReady(false)
     , mCloseWhenDone(false)
     , mRetargeting(false)
     , mMonitor("nsInputStreamPump")
 {
-    if (!gStreamPumpLog)
-        gStreamPumpLog = PR_NewLogModule("nsStreamPump");
 }
 
 nsInputStreamPump::~nsInputStreamPump()
 {
 }
 
 nsresult
 nsInputStreamPump::Create(nsInputStreamPump  **result,
--- a/netwerk/base/nsLoadGroup.cpp
+++ b/netwerk/base/nsLoadGroup.cpp
@@ -33,18 +33,17 @@ using namespace mozilla::net;
 // To enable logging (see prlog.h for full details):
 //
 //    set NSPR_LOG_MODULES=LoadGroup:5
 //    set NSPR_LOG_FILE=nspr.log
 //
 // this enables LogLevel::Debug level information and places all output in
 // the file nspr.log
 //
-static PRLogModuleInfo* gLoadGroupLog = nullptr;
-
+static LazyLogModule gLoadGroupLog("LoadGroup");
 #undef LOG
 #define LOG(args) MOZ_LOG(gLoadGroupLog, mozilla::LogLevel::Debug, args)
 
 ////////////////////////////////////////////////////////////////////////////////
 
 class RequestMapEntry : public PLDHashEntryHdr
 {
 public:
@@ -112,21 +111,16 @@ nsLoadGroup::nsLoadGroup(nsISupports* ou
     , mPriority(PRIORITY_NORMAL)
     , mIsCanceling(false)
     , mDefaultLoadIsTimed(false)
     , mTimedRequests(0)
     , mCachedRequests(0)
     , mTimedNonCachedRequestsUntilOnEndPageLoad(0)
 {
     NS_INIT_AGGREGATED(outer);
-
-    // Initialize the global PRLogModule for nsILoadGroup logging
-    if (nullptr == gLoadGroupLog)
-        gLoadGroupLog = PR_NewLogModule("LoadGroup");
-
     LOG(("LOADGROUP [%x]: Created.\n", this));
 }
 
 nsLoadGroup::~nsLoadGroup()
 {
     DebugOnly<nsresult> rv = Cancel(NS_BINDING_ABORTED);
     NS_ASSERTION(NS_SUCCEEDED(rv), "Cancel failed");
 
--- a/netwerk/base/nsNetUtil.cpp
+++ b/netwerk/base/nsNetUtil.cpp
@@ -582,17 +582,16 @@ NS_NewDownloader(nsIStreamListener   **r
 nsresult
 NS_NewStreamLoaderInternal(nsIStreamLoader        **outStream,
                            nsIURI                  *aUri,
                            nsIStreamLoaderObserver *aObserver,
                            nsINode                 *aLoadingNode,
                            nsIPrincipal            *aLoadingPrincipal,
                            nsSecurityFlags          aSecurityFlags,
                            nsContentPolicyType      aContentPolicyType,
-                           nsISupports             *aContext /* = nullptr */,
                            nsILoadGroup            *aLoadGroup /* = nullptr */,
                            nsIInterfaceRequestor   *aCallbacks /* = nullptr */,
                            nsLoadFlags              aLoadFlags /* = nsIRequest::LOAD_NORMAL */,
                            nsIURI                  *aReferrer /* = nullptr */)
 {
    nsCOMPtr<nsIChannel> channel;
    nsresult rv = NS_NewChannelInternal(getter_AddRefs(channel),
                                        aUri,
@@ -607,69 +606,65 @@ NS_NewStreamLoaderInternal(nsIStreamLoad
 
   NS_ENSURE_SUCCESS(rv, rv);
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
   if (httpChannel) {
     httpChannel->SetReferrer(aReferrer);
   }
   rv = NS_NewStreamLoader(outStream, aObserver);
   NS_ENSURE_SUCCESS(rv, rv);
-  return channel->AsyncOpen(*outStream, aContext);
+  return channel->AsyncOpen2(*outStream);
 }
 
 
 nsresult /* NS_NewStreamLoaderNode */
 NS_NewStreamLoader(nsIStreamLoader        **outStream,
                    nsIURI                  *aUri,
                    nsIStreamLoaderObserver *aObserver,
                    nsINode                 *aLoadingNode,
                    nsSecurityFlags          aSecurityFlags,
                    nsContentPolicyType      aContentPolicyType,
-                   nsISupports             *aContext /* = nullptr */,
                    nsILoadGroup            *aLoadGroup /* = nullptr */,
                    nsIInterfaceRequestor   *aCallbacks /* = nullptr */,
                    nsLoadFlags              aLoadFlags /* = nsIRequest::LOAD_NORMAL */,
                    nsIURI                  *aReferrer /* = nullptr */)
 {
   NS_ASSERTION(aLoadingNode, "Can not create stream loader without a loading Node!");
   return NS_NewStreamLoaderInternal(outStream,
                                     aUri,
                                     aObserver,
                                     aLoadingNode,
                                     aLoadingNode->NodePrincipal(),
                                     aSecurityFlags,
                                     aContentPolicyType,
-                                    aContext,
                                     aLoadGroup,
                                     aCallbacks,
                                     aLoadFlags,
                                     aReferrer);
 }
 
 nsresult /* NS_NewStreamLoaderPrincipal */
 NS_NewStreamLoader(nsIStreamLoader        **outStream,
                    nsIURI                  *aUri,
                    nsIStreamLoaderObserver *aObserver,
                    nsIPrincipal            *aLoadingPrincipal,
                    nsSecurityFlags          aSecurityFlags,
                    nsContentPolicyType      aContentPolicyType,
-                   nsISupports             *aContext /* = nullptr */,
                    nsILoadGroup            *aLoadGroup /* = nullptr */,
                    nsIInterfaceRequestor   *aCallbacks /* = nullptr */,
                    nsLoadFlags              aLoadFlags /* = nsIRequest::LOAD_NORMAL */,
                    nsIURI                  *aReferrer /* = nullptr */)
 {
   return NS_NewStreamLoaderInternal(outStream,
                                     aUri,
                                     aObserver,
                                     nullptr, // aLoadingNode
                                     aLoadingPrincipal,
                                     aSecurityFlags,
                                     aContentPolicyType,
-                                    aContext,
                                     aLoadGroup,
                                     aCallbacks,
                                     aLoadFlags,
                                     aReferrer);
 }
 
 nsresult
 NS_NewUnicharStreamLoader(nsIUnicharStreamLoader        **result,
--- a/netwerk/base/nsNetUtil.h
+++ b/netwerk/base/nsNetUtil.h
@@ -371,43 +371,40 @@ nsresult NS_NewStreamLoader(nsIStreamLoa
 
 nsresult NS_NewStreamLoaderInternal(nsIStreamLoader        **outStream,
                                     nsIURI                  *aUri,
                                     nsIStreamLoaderObserver *aObserver,
                                     nsINode                 *aLoadingNode,
                                     nsIPrincipal            *aLoadingPrincipal,
                                     nsSecurityFlags          aSecurityFlags,
                                     nsContentPolicyType      aContentPolicyType,
-                                    nsISupports             *aContext = nullptr,
                                     nsILoadGroup            *aLoadGroup = nullptr,
                                     nsIInterfaceRequestor   *aCallbacks = nullptr,
                                     nsLoadFlags              aLoadFlags = nsIRequest::LOAD_NORMAL,
                                     nsIURI                  *aReferrer = nullptr);
 
 nsresult /* NS_NewStreamLoaderNode */
 NS_NewStreamLoader(nsIStreamLoader        **outStream,
                    nsIURI                  *aUri,
                    nsIStreamLoaderObserver *aObserver,
                    nsINode                 *aLoadingNode,
                    nsSecurityFlags          aSecurityFlags,
                    nsContentPolicyType      aContentPolicyType,
-                   nsISupports             *aContext = nullptr,
                    nsILoadGroup            *aLoadGroup = nullptr,
                    nsIInterfaceRequestor   *aCallbacks = nullptr,
                    nsLoadFlags              aLoadFlags = nsIRequest::LOAD_NORMAL,
                    nsIURI                  *aReferrer = nullptr);
 
 nsresult /* NS_NewStreamLoaderPrincipal */
 NS_NewStreamLoader(nsIStreamLoader        **outStream,
                    nsIURI                  *aUri,
                    nsIStreamLoaderObserver *aObserver,
                    nsIPrincipal            *aLoadingPrincipal,
                    nsSecurityFlags          aSecurityFlags,
                    nsContentPolicyType      aContentPolicyType,
-                   nsISupports             *aContext = nullptr,
                    nsILoadGroup            *aLoadGroup = nullptr,
                    nsIInterfaceRequestor   *aCallbacks = nullptr,
                    nsLoadFlags              aLoadFlags = nsIRequest::LOAD_NORMAL,
                    nsIURI                  *aReferrer = nullptr);
 
 nsresult NS_NewUnicharStreamLoader(nsIUnicharStreamLoader        **result,
                                    nsIUnicharStreamLoaderObserver *observer);
 
--- a/netwerk/base/nsPACMan.cpp
+++ b/netwerk/base/nsPACMan.cpp
@@ -20,18 +20,24 @@
 #ifdef MOZ_NUWA_PROCESS
 #include "ipc/Nuwa.h"
 #endif
 
 //-----------------------------------------------------------------------------
 using namespace mozilla;
 using namespace mozilla::net;
 
+namespace mozilla {
+namespace net {
+LazyLogModule gProxyLog("proxy");
+} // namespace net
+} // namespace mozilla
+
 #undef LOG
-#define LOG(args) MOZ_LOG(GetProxyLog(), mozilla::LogLevel::Debug, args)
+#define LOG(args) MOZ_LOG(mozilla::net::gProxyLog, mozilla::LogLevel::Debug, args)
 
 // The PAC thread does evaluations of both PAC files and
 // nsISystemProxySettings because they can both block the calling thread and we
 // don't want that on the main thread
 
 // Check to see if the underlying request was not an error page in the case of
 // a HTTP request.  For other types of channels, just return true.
 static bool
@@ -763,22 +769,8 @@ nsPACMan::Init(nsISystemProxySettings *s
 
   nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &nsPACMan::NamePACThread);
   // don't check return value as it is not a big deal for this to fail.
   mPACThread->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
 
   return NS_OK;
 }
 
-namespace mozilla {
-namespace net {
-
-PRLogModuleInfo*
-GetProxyLog()
-{
-    static PRLogModuleInfo *sLog;
-    if (!sLog)
-        sLog = PR_NewLogModule("proxy");
-    return sLog;
-}
-
-} // namespace net
-} // namespace mozilla
--- a/netwerk/base/nsPACMan.h
+++ b/netwerk/base/nsPACMan.h
@@ -236,13 +236,13 @@ private:
   mozilla::TimeStamp           mScheduledReload;
   uint32_t                     mLoadFailureCount;
 
   bool                         mInProgress;
 };
 
 namespace mozilla {
 namespace net {
-PRLogModuleInfo* GetProxyLog();
+extern LazyLogModule gProxyLog;
 } // namespace net
 } // namespace mozilla
 
 #endif  // nsPACMan_h__
--- a/netwerk/base/nsProtocolProxyService.cpp
+++ b/netwerk/base/nsProtocolProxyService.cpp
@@ -44,17 +44,17 @@ namespace mozilla {
   extern const char kProxyType_SOCKS5[];
   extern const char kProxyType_DIRECT[];
 } // namespace mozilla
 
 using namespace mozilla;
 
 #include "mozilla/Logging.h"
 #undef LOG
-#define LOG(args) MOZ_LOG(net::GetProxyLog(), mozilla::LogLevel::Debug, args)
+#define LOG(args) MOZ_LOG(net::gProxyLog, mozilla::LogLevel::Debug, args)
 
 //----------------------------------------------------------------------------
 
 #define PROXY_PREF_BRANCH  "network.proxy"
 #define PROXY_PREF(x)      PROXY_PREF_BRANCH "." x
 
 #define WPAD_URL "http://wpad/wpad.dat"
 
--- a/netwerk/base/nsRequestObserverProxy.cpp
+++ b/netwerk/base/nsRequestObserverProxy.cpp
@@ -8,17 +8,17 @@
 #include "nscore.h"
 #include "nsRequestObserverProxy.h"
 #include "nsIRequest.h"
 #include "nsAutoPtr.h"
 #include "mozilla/Logging.h"
 
 using namespace mozilla;
 
-static PRLogModuleInfo *gRequestObserverProxyLog;
+static LazyLogModule gRequestObserverProxyLog("nsRequestObserverProxy");
 
 #undef LOG
 #define LOG(args) MOZ_LOG(gRequestObserverProxyLog, mozilla::LogLevel::Debug, args)
 
 //-----------------------------------------------------------------------------
 // nsARequestObserverEvent internal class...
 //-----------------------------------------------------------------------------
 
@@ -168,20 +168,16 @@ nsRequestObserverProxy::OnStopRequest(ns
 //-----------------------------------------------------------------------------
 // nsRequestObserverProxy::nsIRequestObserverProxy implementation...
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsRequestObserverProxy::Init(nsIRequestObserver *observer, nsISupports *context)
 {
     NS_ENSURE_ARG_POINTER(observer);
-
-    if (!gRequestObserverProxyLog)
-        gRequestObserverProxyLog = PR_NewLogModule("nsRequestObserverProxy");
-
     mObserver = new nsMainThreadPtrHolder<nsIRequestObserver>(observer);
     mContext = new nsMainThreadPtrHolder<nsISupports>(context);
 
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // nsRequestObserverProxy implementation...
--- a/netwerk/base/nsSecCheckWrapChannel.cpp
+++ b/netwerk/base/nsSecCheckWrapChannel.cpp
@@ -3,27 +3,18 @@
  * 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 "nsContentSecurityManager.h"
 #include "nsSecCheckWrapChannel.h"
 #include "nsIForcePendingChannel.h"
 #include "nsCOMPtr.h"
 
-static PRLogModuleInfo*
-GetChannelWrapperLog()
-{
-  static PRLogModuleInfo* gChannelWrapperPRLog;
-  if (!gChannelWrapperPRLog) {
-    gChannelWrapperPRLog = PR_NewLogModule("ChannelWrapper");
-  }
-  return gChannelWrapperPRLog;
-}
-
-#define CHANNELWRAPPERLOG(args) MOZ_LOG(GetChannelWrapperLog(), mozilla::LogLevel::Debug, args)
+static mozilla::LazyLogModule gChannelWrapperLog("ChannelWrapper");
+#define CHANNELWRAPPERLOG(args) MOZ_LOG(gChannelWrapperLog, mozilla::LogLevel::Debug, args)
 
 NS_IMPL_ADDREF(nsSecCheckWrapChannelBase)
 NS_IMPL_RELEASE(nsSecCheckWrapChannelBase)
 
 NS_INTERFACE_MAP_BEGIN(nsSecCheckWrapChannelBase)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIHttpChannel, mHttpChannel)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIHttpChannelInternal, mHttpChannelInternal)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIHttpChannel)
--- a/netwerk/base/nsSocketTransportService2.cpp
+++ b/netwerk/base/nsSocketTransportService2.cpp
@@ -24,18 +24,18 @@
 #include "mozilla/PodOperations.h"
 #include "mozilla/Telemetry.h"
 #include "nsThreadUtils.h"
 #include "nsIFile.h"
 
 using namespace mozilla;
 using namespace mozilla::net;
 
-PRLogModuleInfo *gSocketTransportLog = nullptr;
-PRLogModuleInfo *gUDPSocketLog = nullptr;
+LazyLogModule gSocketTransportLog("nsSocketTransport");
+LazyLogModule gUDPSocketLog("UDPSocket");
 
 nsSocketTransportService *gSocketTransportService = nullptr;
 PRThread                 *gSocketThread           = nullptr;
 
 #define SEND_BUFFER_PREF "network.tcp.sendbuffer"
 #define KEEPALIVE_ENABLED_PREF "network.tcp.keepalive.enabled"
 #define KEEPALIVE_IDLE_TIME_PREF "network.tcp.keepalive.idle_time"
 #define KEEPALIVE_RETRY_INTERVAL_PREF "network.tcp.keepalive.retry_interval"
@@ -106,19 +106,16 @@ nsSocketTransportService::nsSocketTransp
     , mKeepaliveRetryIntervalS(1)
     , mKeepaliveProbeCount(kDefaultTCPKeepCount)
     , mKeepaliveEnabledPref(false)
     , mServingPendingQueue(false)
     , mMaxTimePerPollIter(100)
     , mTelemetryEnabledPref(false)
     , mProbedMaxCount(false)
 {
-    gSocketTransportLog = PR_NewLogModule("nsSocketTransport");
-    gUDPSocketLog = PR_NewLogModule("UDPSocket");
-
     NS_ASSERTION(NS_IsMainThread(), "wrong thread");
 
     PR_CallOnce(&gMaxCountInitOnce, DiscoverMaxCount);
     mActiveList = (SocketContext *)
         moz_xmalloc(sizeof(SocketContext) * mActiveListSize);
     mIdleList = (SocketContext *)
         moz_xmalloc(sizeof(SocketContext) * mIdleListSize);
     mPollList = (PRPollDesc *)
--- a/netwerk/base/nsSocketTransportService2.h
+++ b/netwerk/base/nsSocketTransportService2.h
@@ -23,24 +23,24 @@
 class nsASocketHandler;
 struct PRPollDesc;
 
 //-----------------------------------------------------------------------------
 
 //
 // set NSPR_LOG_MODULES=nsSocketTransport:5
 //
-extern PRLogModuleInfo *gSocketTransportLog;
+extern mozilla::LazyLogModule gSocketTransportLog;
 #define SOCKET_LOG(args)     MOZ_LOG(gSocketTransportLog, mozilla::LogLevel::Debug, args)
 #define SOCKET_LOG_ENABLED() MOZ_LOG_TEST(gSocketTransportLog, mozilla::LogLevel::Debug)
 
 //
 // set NSPR_LOG_MODULES=UDPSocket:5
 //
-extern PRLogModuleInfo *gUDPSocketLog;
+extern mozilla::LazyLogModule gUDPSocketLog;
 #define UDPSOCKET_LOG(args)     MOZ_LOG(gUDPSocketLog, mozilla::LogLevel::Debug, args)
 #define UDPSOCKET_LOG_ENABLED() MOZ_LOG_TEST(gUDPSocketLog, mozilla::LogLevel::Debug)
 
 //-----------------------------------------------------------------------------
 
 #define NS_SOCKET_POLL_TIMEOUT PR_INTERVAL_NO_TIMEOUT
 
 //-----------------------------------------------------------------------------
--- a/netwerk/base/nsStandardURL.cpp
+++ b/netwerk/base/nsStandardURL.cpp
@@ -35,17 +35,17 @@ nsIIDNService *nsStandardURL::gIDN = nul
 bool nsStandardURL::gInitialized = false;
 bool nsStandardURL::gEscapeUTF8 = true;
 bool nsStandardURL::gAlwaysEncodeInUTF8 = true;
 char nsStandardURL::gHostLimitDigits[] = { '/', '\\', '?', '#', 0 };
 
 //
 // setenv NSPR_LOG_MODULES nsStandardURL:5
 //
-static PRLogModuleInfo *gStandardURLLog;
+static mozilla::LazyLogModule gStandardURLLog("nsStandardURL");
 
 // The Chromium code defines its own LOG macro which we don't want
 #undef LOG
 #define LOG(args)     MOZ_LOG(gStandardURLLog, mozilla::LogLevel::Debug, args)
 #undef LOG_ENABLED
 #define LOG_ENABLED() MOZ_LOG_TEST(gStandardURLLog, mozilla::LogLevel::Debug)
 
 //----------------------------------------------------------------------------
@@ -247,19 +247,16 @@ nsStandardURL::nsStandardURL(bool aSuppo
     , mPort(-1)
     , mHostA(nullptr)
     , mHostEncoding(eEncoding_ASCII)
     , mSpecEncoding(eEncoding_Unknown)
     , mURLType(URLTYPE_STANDARD)
     , mMutable(true)
     , mSupportsFileURL(aSupportsFileURL)
 {
-    if (!gStandardURLLog)
-        gStandardURLLog = PR_NewLogModule("nsStandardURL");
-
     LOG(("Creating nsStandardURL @%p\n", this));
 
     if (!gInitialized) {
         gInitialized = true;
         InitGlobalObjects();
     }
 
     // default parser in case nsIStandardURL::Init is never called
--- a/netwerk/cache/nsCache.cpp
+++ b/netwerk/cache/nsCache.cpp
@@ -9,27 +9,17 @@
 #include "nsDependentSubstring.h"
 #include "nsString.h"
 
 
 /**
  * Cache Service Utility Functions
  */
 
-PRLogModuleInfo * gCacheLog = nullptr;
-
-
-void
-CacheLogInit()
-{
-    if (gCacheLog) return;
-    gCacheLog = PR_NewLogModule("cache");
-    NS_ASSERTION(gCacheLog, "\nfailed to allocate cache log.\n");
-}
-
+mozilla::LazyLogModule gCacheLog("cache");
 
 void
 CacheLogPrintPath(mozilla::LogLevel level, const char * format, nsIFile * item)
 {
     nsAutoCString path;
     nsresult rv = item->GetNativePath(path);
     if (NS_SUCCEEDED(rv)) {
         MOZ_LOG(gCacheLog, level, (format, path.get()));
--- a/netwerk/cache/nsCache.h
+++ b/netwerk/cache/nsCache.h
@@ -14,22 +14,20 @@
 #include "mozilla/Logging.h"
 #include "nsISupports.h"
 #include "nsIFile.h"
 #include "nsAString.h"
 #include "prtime.h"
 #include "nsError.h"
 
 // PR_LOG args = "format string", arg, arg, ...
-extern PRLogModuleInfo * gCacheLog;
-void   CacheLogInit();
+extern mozilla::LazyLogModule gCacheLog;
 void   CacheLogPrintPath(mozilla::LogLevel level,
                          const char *     format,
                          nsIFile *        item);
-#define CACHE_LOG_INIT()        CacheLogInit()
 #define CACHE_LOG_INFO(args)  MOZ_LOG(gCacheLog, mozilla::LogLevel::Info, args)
 #define CACHE_LOG_ERROR(args)   MOZ_LOG(gCacheLog, mozilla::LogLevel::Error, args)
 #define CACHE_LOG_WARNING(args) MOZ_LOG(gCacheLog, mozilla::LogLevel::Warning, args)
 #define CACHE_LOG_DEBUG(args)   MOZ_LOG(gCacheLog, mozilla::LogLevel::Debug, args)
 #define CACHE_LOG_PATH(level, format, item) \
                                 CacheLogPrintPath(level, format, item)
 
 
--- a/netwerk/cache/nsCacheService.cpp
+++ b/netwerk/cache/nsCacheService.cpp
@@ -1124,18 +1124,16 @@ nsCacheService::Init()
     NS_ASSERTION(!mInitialized, "nsCacheService already initialized.");
     if (mInitialized)
         return NS_ERROR_ALREADY_INITIALIZED;
 
     if (mozilla::net::IsNeckoChild()) {
         return NS_ERROR_UNEXPECTED;
     }
 
-    CACHE_LOG_INIT();
-
     nsresult rv;
 
     mStorageService = do_GetService("@mozilla.org/storage/service;1", &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = NS_NewNamedThread("Cache I/O",
                            getter_AddRefs(mCacheIOThread));
     if (NS_FAILED(rv)) {
--- a/netwerk/cache/nsDiskCacheDeviceSQL.cpp
+++ b/netwerk/cache/nsDiskCacheDeviceSQL.cpp
@@ -2,16 +2,19 @@
 /* vim:set ts=2 sw=2 sts=2 et cin: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Attributes.h"
 
+#include "mozilla/IntegerPrintfMacros.h"
+#include "mozilla/Snprintf.h"
+
 #include "nsCache.h"
 #include "nsDiskCache.h"
 #include "nsDiskCacheDeviceSQL.h"
 #include "nsCacheService.h"
 #include "nsApplicationCache.h"
 
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
@@ -406,31 +409,31 @@ nsOfflineCacheBinding::Create(nsIFile *c
   char leaf[64];
 
   if (generation == -1)
   {
     file->AppendNative(NS_LITERAL_CSTRING("placeholder"));
 
     for (generation = 0; ; ++generation)
     {
-      PR_snprintf(leaf, sizeof(leaf), "%014llX-%X", hash, generation);
+      snprintf_literal(leaf, "%014" PRIX64 "-%X", hash, generation);
 
       rv = file->SetNativeLeafName(nsDependentCString(leaf));
       if (NS_FAILED(rv))
         return nullptr;
       rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 00600);
       if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS)
         return nullptr;
       if (NS_SUCCEEDED(rv))
         break;
     }
   }
   else
   {
-    PR_snprintf(leaf, sizeof(leaf), "%014llX-%X", hash, generation);
+    snprintf_literal(leaf, "%014" PRIX64 "-%X", hash, generation);
     rv = file->AppendNative(nsDependentCString(leaf));
     if (NS_FAILED(rv))
       return nullptr;
   }
 
   nsOfflineCacheBinding *binding = new nsOfflineCacheBinding;
   if (!binding)
     return nullptr;
--- a/netwerk/cache2/CacheLog.cpp
+++ b/netwerk/cache2/CacheLog.cpp
@@ -11,18 +11,12 @@ namespace net {
 //
 // To enable logging (see prlog.h for full details):
 //
 //    set NSPR_LOG_MODULES=cache2:5
 //    set NSPR_LOG_FILE=nspr.log
 //
 // this enables LogLevel::Debug level information and places all output in
 // the file nspr.log
-PRLogModuleInfo* GetCache2Log()
-{
-  static PRLogModuleInfo *sLog;
-  if (!sLog)
-    sLog = PR_NewLogModule("cache2");
-  return sLog;
-}
+LazyLogModule gCache2Log("cache2");
 
 } // namespace net
 } // namespace mozilla
--- a/netwerk/cache2/CacheLog.h
+++ b/netwerk/cache2/CacheLog.h
@@ -5,16 +5,16 @@
 #ifndef Cache2Log__h__
 #define Cache2Log__h__
 
 #include "mozilla/Logging.h"
 
 namespace mozilla {
 namespace net {
 
-extern PRLogModuleInfo* GetCache2Log();
-#define LOG(x)  MOZ_LOG(GetCache2Log(), mozilla::LogLevel::Debug, x)
-#define LOG_ENABLED() MOZ_LOG_TEST(GetCache2Log(), mozilla::LogLevel::Debug)
+extern LazyLogModule gCache2Log;
+#define LOG(x)  MOZ_LOG(gCache2Log, mozilla::LogLevel::Debug, x)
+#define LOG_ENABLED() MOZ_LOG_TEST(gCache2Log, mozilla::LogLevel::Debug)
 
 } // namespace net
 } // namespace mozilla
 
 #endif
--- a/netwerk/cookie/nsCookieService.cpp
+++ b/netwerk/cookie/nsCookieService.cpp
@@ -180,132 +180,125 @@ struct nsListIter
 //
 #include "mozilla/Logging.h"
 #endif
 
 // define logging macros for convenience
 #define SET_COOKIE true
 #define GET_COOKIE false
 
-static PRLogModuleInfo *
-GetCookieLog()
-{
-  static PRLogModuleInfo *sCookieLog;
-  if (!sCookieLog)
-    sCookieLog = PR_NewLogModule("cookie");
-  return sCookieLog;
-}
+static LazyLogModule gCookieLog("cookie");
 
 #define COOKIE_LOGFAILURE(a, b, c, d)    LogFailure(a, b, c, d)
 #define COOKIE_LOGSUCCESS(a, b, c, d, e) LogSuccess(a, b, c, d, e)
 
 #define COOKIE_LOGEVICTED(a, details)          \
   PR_BEGIN_MACRO                               \
-  if (MOZ_LOG_TEST(GetCookieLog(), LogLevel::Debug))  \
+  if (MOZ_LOG_TEST(gCookieLog, LogLevel::Debug))  \
       LogEvicted(a, details);                  \
   PR_END_MACRO
 
 #define COOKIE_LOGSTRING(lvl, fmt)   \
   PR_BEGIN_MACRO                     \
-    MOZ_LOG(GetCookieLog(), lvl, fmt);  \
-    MOZ_LOG(GetCookieLog(), lvl, ("\n")); \
+    MOZ_LOG(gCookieLog, lvl, fmt);  \
+    MOZ_LOG(gCookieLog, lvl, ("\n")); \
   PR_END_MACRO
 
 static void
 LogFailure(bool aSetCookie, nsIURI *aHostURI, const char *aCookieString, const char *aReason)
 {
   // if logging isn't enabled, return now to save cycles
-  if (!MOZ_LOG_TEST(GetCookieLog(), LogLevel::Warning))
+  if (!MOZ_LOG_TEST(gCookieLog, LogLevel::Warning))
     return;
 
   nsAutoCString spec;
   if (aHostURI)
     aHostURI->GetAsciiSpec(spec);
 
-  MOZ_LOG(GetCookieLog(), LogLevel::Warning,
+  MOZ_LOG(gCookieLog, LogLevel::Warning,
     ("===== %s =====\n", aSetCookie ? "COOKIE NOT ACCEPTED" : "COOKIE NOT SENT"));
-  MOZ_LOG(GetCookieLog(), LogLevel::Warning,("request URL: %s\n", spec.get()));
+  MOZ_LOG(gCookieLog, LogLevel::Warning,("request URL: %s\n", spec.get()));
   if (aSetCookie)
-    MOZ_LOG(GetCookieLog(), LogLevel::Warning,("cookie string: %s\n", aCookieString));
+    MOZ_LOG(gCookieLog, LogLevel::Warning,("cookie string: %s\n", aCookieString));
 
   PRExplodedTime explodedTime;
   PR_ExplodeTime(PR_Now(), PR_GMTParameters, &explodedTime);
   char timeString[40];
   PR_FormatTimeUSEnglish(timeString, 40, "%c GMT", &explodedTime);
 
-  MOZ_LOG(GetCookieLog(), LogLevel::Warning,("current time: %s", timeString));
-  MOZ_LOG(GetCookieLog(), LogLevel::Warning,("rejected because %s\n", aReason));
-  MOZ_LOG(GetCookieLog(), LogLevel::Warning,("\n"));
+  MOZ_LOG(gCookieLog, LogLevel::Warning,("current time: %s", timeString));
+  MOZ_LOG(gCookieLog, LogLevel::Warning,("rejected because %s\n", aReason));
+  MOZ_LOG(gCookieLog, LogLevel::Warning,("\n"));
 }
 
 static void
 LogCookie(nsCookie *aCookie)
 {
   PRExplodedTime explodedTime;
   PR_ExplodeTime(PR_Now(), PR_GMTParameters, &explodedTime);
   char timeString[40];
   PR_FormatTimeUSEnglish(timeString, 40, "%c GMT", &explodedTime);
 
-  MOZ_LOG(GetCookieLog(), LogLevel::Debug,("current time: %s", timeString));
+  MOZ_LOG(gCookieLog, LogLevel::Debug,("current time: %s", timeString));
 
   if (aCookie) {
-    MOZ_LOG(GetCookieLog(), LogLevel::Debug,("----------------\n"));
-    MOZ_LOG(GetCookieLog(), LogLevel::Debug,("name: %s\n", aCookie->Name().get()));
-    MOZ_LOG(GetCookieLog(), LogLevel::Debug,("value: %s\n", aCookie->Value().get()));
-    MOZ_LOG(GetCookieLog(), LogLevel::Debug,("%s: %s\n", aCookie->IsDomain() ? "domain" : "host", aCookie->Host().get()));
-    MOZ_LOG(GetCookieLog(), LogLevel::Debug,("path: %s\n", aCookie->Path().get()));
+    MOZ_LOG(gCookieLog, LogLevel::Debug,("----------------\n"));
+    MOZ_LOG(gCookieLog, LogLevel::Debug,("name: %s\n", aCookie->Name().get()));
+    MOZ_LOG(gCookieLog, LogLevel::Debug,("value: %s\n", aCookie->Value().get()));
+    MOZ_LOG(gCookieLog, LogLevel::Debug,("%s: %s\n", aCookie->IsDomain() ? "domain" : "host", aCookie->Host().get()));
+    MOZ_LOG(gCookieLog, LogLevel::Debug,("path: %s\n", aCookie->Path().get()));
 
     PR_ExplodeTime(aCookie->Expiry() * int64_t(PR_USEC_PER_SEC),
                    PR_GMTParameters, &explodedTime);
     PR_FormatTimeUSEnglish(timeString, 40, "%c GMT", &explodedTime);
-    MOZ_LOG(GetCookieLog(), LogLevel::Debug,
+    MOZ_LOG(gCookieLog, LogLevel::Debug,
       ("expires: %s%s", timeString, aCookie->IsSession() ? " (at end of session)" : ""));
 
     PR_ExplodeTime(aCookie->CreationTime(), PR_GMTParameters, &explodedTime);
     PR_FormatTimeUSEnglish(timeString, 40, "%c GMT", &explodedTime);
-    MOZ_LOG(GetCookieLog(), LogLevel::Debug,("created: %s", timeString));
-
-    MOZ_LOG(GetCookieLog(), LogLevel::Debug,("is secure: %s\n", aCookie->IsSecure() ? "true" : "false"));
-    MOZ_LOG(GetCookieLog(), LogLevel::Debug,("is httpOnly: %s\n", aCookie->IsHttpOnly() ? "true" : "false"));
+    MOZ_LOG(gCookieLog, LogLevel::Debug,("created: %s", timeString));
+
+    MOZ_LOG(gCookieLog, LogLevel::Debug,("is secure: %s\n", aCookie->IsSecure() ? "true" : "false"));
+    MOZ_LOG(gCookieLog, LogLevel::Debug,("is httpOnly: %s\n", aCookie->IsHttpOnly() ? "true" : "false"));
   }
 }
 
 static void
 LogSuccess(bool aSetCookie, nsIURI *aHostURI, const char *aCookieString, nsCookie *aCookie, bool aReplacing)
 {
   // if logging isn't enabled, return now to save cycles
-  if (!MOZ_LOG_TEST(GetCookieLog(), LogLevel::Debug)) {
+  if (!MOZ_LOG_TEST(gCookieLog, LogLevel::Debug)) {
     return;
   }
 
   nsAutoCString spec;
   if (aHostURI)
     aHostURI->GetAsciiSpec(spec);
 
-  MOZ_LOG(GetCookieLog(), LogLevel::Debug,
+  MOZ_LOG(gCookieLog, LogLevel::Debug,
     ("===== %s =====\n", aSetCookie ? "COOKIE ACCEPTED" : "COOKIE SENT"));
-  MOZ_LOG(GetCookieLog(), LogLevel::Debug,("request URL: %s\n", spec.get()));
-  MOZ_LOG(GetCookieLog(), LogLevel::Debug,("cookie string: %s\n", aCookieString));
+  MOZ_LOG(gCookieLog, LogLevel::Debug,("request URL: %s\n", spec.get()));
+  MOZ_LOG(gCookieLog, LogLevel::Debug,("cookie string: %s\n", aCookieString));
   if (aSetCookie)
-    MOZ_LOG(GetCookieLog(), LogLevel::Debug,("replaces existing cookie: %s\n", aReplacing ? "true" : "false"));
+    MOZ_LOG(gCookieLog, LogLevel::Debug,("replaces existing cookie: %s\n", aReplacing ? "true" : "false"));
 
   LogCookie(aCookie);
 
-  MOZ_LOG(GetCookieLog(), LogLevel::Debug,("\n"));
+  MOZ_LOG(gCookieLog, LogLevel::Debug,("\n"));
 }
 
 static void
 LogEvicted(nsCookie *aCookie, const char* details)
 {
-  MOZ_LOG(GetCookieLog(), LogLevel::Debug,("===== COOKIE EVICTED =====\n"));
-  MOZ_LOG(GetCookieLog(), LogLevel::Debug,("%s\n", details));
+  MOZ_LOG(gCookieLog, LogLevel::Debug,("===== COOKIE EVICTED =====\n"));
+  MOZ_LOG(gCookieLog, LogLevel::Debug,("%s\n", details));
 
   LogCookie(aCookie);
 
-  MOZ_LOG(GetCookieLog(), LogLevel::Debug,("\n"));
+  MOZ_LOG(gCookieLog, LogLevel::Debug,("\n"));
 }
 
 // inline wrappers to make passing in nsAFlatCStrings easier
 static inline void
 LogFailure(bool aSetCookie, nsIURI *aHostURI, const nsAFlatCString &aCookieString, const char *aReason)
 {
   LogFailure(aSetCookie, aHostURI, aCookieString.get(), aReason);
 }
@@ -341,17 +334,17 @@ class DBListenerErrorHandler : public mo
 protected:
   explicit DBListenerErrorHandler(DBState* dbState) : mDBState(dbState) { }
   RefPtr<DBState> mDBState;
   virtual const char *GetOpType() = 0;
 
 public:
   NS_IMETHOD HandleError(mozIStorageError* aError) override
   {
-    if (MOZ_LOG_TEST(GetCookieLog(), LogLevel::Warning)) {
+    if (MOZ_LOG_TEST(gCookieLog, LogLevel::Warning)) {
       int32_t result = -1;
       aError->GetResult(&result);
 
       nsAutoCString message;
       aError->GetMessage(message);
       COOKIE_LOGSTRING(LogLevel::Warning,
         ("DBListenerErrorHandler::HandleError(): Error %d occurred while "
          "performing operation '%s' with message '%s'; rebuilding database.",
--- a/netwerk/dns/GetAddrInfo.cpp
+++ b/netwerk/dns/GetAddrInfo.cpp
@@ -18,36 +18,37 @@
 #include <algorithm>
 #include "prerror.h"
 
 #if defined(ANDROID) && ANDROID_VERSION > 19
 #include <resolv_netid.h>
 #endif
 
 #include "mozilla/Logging.h"
-static PRLogModuleInfo *gGetAddrInfoLog = PR_NewLogModule("GetAddrInfo");
-#define LOG(msg, ...) \
-  MOZ_LOG(gGetAddrInfoLog, LogLevel::Debug, ("[DNS]: " msg, ##__VA_ARGS__))
-#define LOG_WARNING(msg, ...) \
-  MOZ_LOG(gGetAddrInfoLog, LogLevel::Warning, ("[DNS]: " msg, ##__VA_ARGS__))
 
 #if DNSQUERY_AVAILABLE
 // There is a bug in windns.h where the type of parameter ppQueryResultsSet for
 // DnsQuery_A is dependent on UNICODE being set. It should *always* be
 // PDNS_RECORDA, but if UNICODE is set it is PDNS_RECORDW. To get around this
 // we make sure that UNICODE is unset.
 #undef UNICODE
 #include <ws2tcpip.h>
 #undef GetAddrInfo
 #include <windns.h>
 #endif
 
 namespace mozilla {
 namespace net {
 
+static LazyLogModule gGetAddrInfoLog("GetAddrInfo");
+#define LOG(msg, ...) \
+  MOZ_LOG(gGetAddrInfoLog, LogLevel::Debug, ("[DNS]: " msg, ##__VA_ARGS__))
+#define LOG_WARNING(msg, ...) \
+  MOZ_LOG(gGetAddrInfoLog, LogLevel::Warning, ("[DNS]: " msg, ##__VA_ARGS__))
+
 #if DNSQUERY_AVAILABLE
 ////////////////////////////
 // WINDOWS IMPLEMENTATION //
 ////////////////////////////
 
 // Ensure consistency of PR_* and AF_* constants to allow for legacy usage of
 // PR_* constants with this API.
 static_assert(PR_AF_INET == AF_INET && PR_AF_INET6 == AF_INET6
--- a/netwerk/dns/mdns/libmdns/MDNSResponderOperator.cpp
+++ b/netwerk/dns/mdns/libmdns/MDNSResponderOperator.cpp
@@ -20,30 +20,25 @@
 #include "nsNetCID.h"
 #include "nsSocketTransportService2.h"
 #include "nsThreadUtils.h"
 #include "nsXPCOMCID.h"
 #include "private/pprio.h"
 
 #include "nsASocketHandler.h"
 
-inline PRLogModuleInfo*
-GetOperatorLog()
-{
-  static PRLogModuleInfo* log = PR_NewLogModule("MDNSResponderOperator");
-  return log;
-}
-#undef LOG_I
-#define LOG_I(...) MOZ_LOG(GetOperatorLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
-#undef LOG_E
-#define LOG_E(...) MOZ_LOG(GetOperatorLog(), mozilla::LogLevel::Error, (__VA_ARGS__))
-
 namespace mozilla {
 namespace net {
 
+static LazyLogModule gMDNSLog("MDNSResponderOperator");
+#undef LOG_I
+#define LOG_I(...) MOZ_LOG(mozilla::net::gMDNSLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
+#undef LOG_E
+#define LOG_E(...) MOZ_LOG(mozilla::net::gMDNSLog, mozilla::LogLevel::Error, (__VA_ARGS__))
+
 class MDNSResponderOperator::ServiceWatcher final
   : public nsASocketHandler
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
 
   // nsASocketHandler methods
   virtual void OnSocketReady(PRFileDesc* fd, int16_t outFlags) override
--- a/netwerk/dns/nsHostResolver.cpp
+++ b/netwerk/dns/nsHostResolver.cpp
@@ -63,17 +63,17 @@ static const unsigned int NEGATIVE_RECOR
 #define HighThreadThreshold     MAX_RESOLVER_THREADS_FOR_ANY_PRIORITY
 #define LongIdleTimeoutSeconds  300           // for threads 1 -> HighThreadThreshold
 #define ShortIdleTimeoutSeconds 60            // for threads HighThreadThreshold+1 -> MAX_RESOLVER_THREADS
 
 PR_STATIC_ASSERT (HighThreadThreshold <= MAX_RESOLVER_THREADS);
 
 //----------------------------------------------------------------------------
 
-static PRLogModuleInfo *gHostResolverLog = nullptr;
+static LazyLogModule gHostResolverLog("nsHostResolver");
 #define LOG(args) MOZ_LOG(gHostResolverLog, mozilla::LogLevel::Debug, args)
 
 #define LOG_HOST(host, interface) host,                                        \
                  (interface && interface[0] != '\0') ? " on interface " : "",  \
                  (interface && interface[0] != '\0') ? interface : ""
 
 //----------------------------------------------------------------------------
 
@@ -1453,19 +1453,16 @@ nsHostResolver::ThreadFunc(void *arg)
 }
 
 nsresult
 nsHostResolver::Create(uint32_t maxCacheEntries,
                        uint32_t defaultCacheEntryLifetime,
                        uint32_t defaultGracePeriod,
                        nsHostResolver **result)
 {
-    if (!gHostResolverLog)
-        gHostResolverLog = PR_NewLogModule("nsHostResolver");
-
     nsHostResolver *res = new nsHostResolver(maxCacheEntries, defaultCacheEntryLifetime,
                                              defaultGracePeriod);
     NS_ADDREF(res);
 
     nsresult rv = res->Init();
     if (NS_FAILED(rv))
         NS_RELEASE(res);
 
--- a/netwerk/protocol/ftp/nsFTPChannel.cpp
+++ b/netwerk/protocol/ftp/nsFTPChannel.cpp
@@ -5,17 +5,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsFTPChannel.h"
 #include "nsFtpConnectionThread.h"  // defines nsFtpState
 
 #include "nsThreadUtils.h"
 #include "mozilla/Attributes.h"
 
-extern PRLogModuleInfo* gFTPLog;
+using namespace mozilla;
+using namespace mozilla::net;
+extern LazyLogModule gFTPLog;
 
 // There are two transport connections established for an 
 // ftp connection. One is used for the command channel , and
 // the other for the data channel. The command channel is the first
 // connection made and is used to negotiate the second, data, channel.
 // The data channel is driven by the command channel and is either
 // initiated by the server (PORT command) or by the client (PASV command).
 // Client initiation is the most common case and is attempted first.
--- a/netwerk/protocol/ftp/nsFtpConnectionThread.cpp
+++ b/netwerk/protocol/ftp/nsFtpConnectionThread.cpp
@@ -42,22 +42,23 @@
 #include "nsIURI.h"
 #include "nsILoadInfo.h"
 #include "nsNullPrincipal.h"
 
 #ifdef MOZ_WIDGET_GONK
 #include "NetStatistics.h"
 #endif
 
-extern PRLogModuleInfo* gFTPLog;
+using namespace mozilla;
+using namespace mozilla::net;
+
+extern LazyLogModule gFTPLog;
 #define LOG(args)         MOZ_LOG(gFTPLog, mozilla::LogLevel::Debug, args)
 #define LOG_INFO(args)  MOZ_LOG(gFTPLog, mozilla::LogLevel::Info, args)
 
-using namespace mozilla::net;
-
 // remove FTP parameters (starting with ";") from the path
 static void
 removeParamsFromPath(nsCString& path)
 {
   int32_t index = path.FindChar(';');
   if (index >= 0) {
     path.SetLength(index);
   }
--- a/netwerk/protocol/ftp/nsFtpControlConnection.cpp
+++ b/netwerk/protocol/ftp/nsFtpControlConnection.cpp
@@ -10,17 +10,20 @@
 #include "nsIInputStream.h"
 #include "nsISocketTransportService.h"
 #include "nsISocketTransport.h"
 #include "nsThreadUtils.h"
 #include "nsIOutputStream.h"
 #include "nsNetCID.h"
 #include <algorithm>
 
-extern PRLogModuleInfo* gFTPLog;
+using namespace mozilla;
+using namespace mozilla::net;
+
+extern LazyLogModule gFTPLog;
 #define LOG(args)         MOZ_LOG(gFTPLog, mozilla::LogLevel::Debug, args)
 #define LOG_INFO(args)  MOZ_LOG(gFTPLog, mozilla::LogLevel::Info, args)
 
 //
 // nsFtpControlConnection implementation ...
 //
 
 NS_IMPL_ISUPPORTS(nsFtpControlConnection, nsIInputStreamCallback)
--- a/netwerk/protocol/ftp/nsFtpProtocolHandler.cpp
+++ b/netwerk/protocol/ftp/nsFtpProtocolHandler.cpp
@@ -40,17 +40,17 @@ using namespace mozilla::net;
 // To enable logging (see prlog.h for full details):
 //
 //    set NSPR_LOG_MODULES=nsFtp:5
 //    set NSPR_LOG_FILE=nspr.log
 //
 // this enables LogLevel::Debug level information and places all output in
 // the file nspr.log
 //
-PRLogModuleInfo* gFTPLog = nullptr;
+LazyLogModule gFTPLog("nsFtp");
 #undef LOG
 #define LOG(args) MOZ_LOG(gFTPLog, mozilla::LogLevel::Debug, args)
 
 //-----------------------------------------------------------------------------
 
 #define IDLE_TIMEOUT_PREF     "network.ftp.idleConnectionTimeout"
 #define IDLE_CONNECTION_LIMIT 8 /* TODO pref me */
 
@@ -62,19 +62,16 @@ nsFtpProtocolHandler *gFtpHandler = null
 //-----------------------------------------------------------------------------
 
 nsFtpProtocolHandler::nsFtpProtocolHandler()
     : mIdleTimeout(-1)
     , mSessionId(0)
     , mControlQoSBits(0x00)
     , mDataQoSBits(0x00)
 {
-    if (!gFTPLog)
-        gFTPLog = PR_NewLogModule("nsFtp");
-
     LOG(("FTP:creating handler @%x\n", this));
 
     gFtpHandler = this;
 }
 
 nsFtpProtocolHandler::~nsFtpProtocolHandler()
 {
     LOG(("FTP:destroying handler @%x\n", this));
--- a/netwerk/protocol/ftp/nsFtpProtocolHandler.h
+++ b/netwerk/protocol/ftp/nsFtpProtocolHandler.h
@@ -77,11 +77,11 @@ private:
     uint8_t mControlQoSBits;
     uint8_t mDataQoSBits;
 };
 
 //-----------------------------------------------------------------------------
 
 extern nsFtpProtocolHandler *gFtpHandler;
 
-extern PRLogModuleInfo* gFTPLog;
+extern mozilla::LazyLogModule gFTPLog;
 
 #endif // !nsFtpProtocolHandler_h__
--- a/netwerk/protocol/http/Http2Session.cpp
+++ b/netwerk/protocol/http/Http2Session.cpp
@@ -27,19 +27,19 @@
 #include "nsHttpConnection.h"
 #include "nsISchedulingContext.h"
 #include "nsISSLSocketControl.h"
 #include "nsISSLStatus.h"
 #include "nsISSLStatusProvider.h"
 #include "nsISupportsPriority.h"
 #include "nsStandardURL.h"
 #include "nsURLHelper.h"
-#include "prprf.h"
 #include "prnetdb.h"
 #include "sslt.h"
+#include "mozilla/Snprintf.h"
 
 #ifdef DEBUG
 // defined by the socket transport service while active
 extern PRThread *gSocketThread;
 #endif
 
 namespace mozilla {
 namespace net {
@@ -220,21 +220,20 @@ Http2Session::LogIO(Http2Session *self, 
 
   for (index = 0; index < datalen; ++index) {
     if (!(index % 16)) {
       if (index) {