Merge inbound to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Fri, 19 Aug 2016 18:16:20 -0700
changeset 403581 f97a056ae6235de7855fd8aaa04fb1c8d183bd06
parent 403580 f0b406c1b36b6b5c881fbb24bc9669d9ef8b9d8a (current diff)
parent 403527 083f1c75c0a45d751552d8474a66fe7b44365f1b (diff)
child 403582 b7ebfdf5bace22d5341300cdd4e91ff5e544176b
child 403586 dd6df28a3647977d54d2b3beb45b18b02bce2221
child 403590 498e23d50daaadf28982fda28f32fdbf25b27af3
child 403593 06e5a77a0a3131a5a7c2c08ed4abdb3960d409ef
child 403599 3441cc99c727cdad2fe587a5668575c851e9a6b5
child 403610 afd27635d16ebe223b9186f0033b77cd2bbb48d5
child 403619 734984bd4be1777f826b6bf55cd7754fd7a896d4
child 403641 67ec09e87534489c6250ea5588d58a290cba601a
child 403652 ad71a0d748874748a52ee1a2c7ce90865d315938
child 403656 0ce0c4d321ad5bd3bca2f5573c8b1a57ca3f61bd
child 403660 04b8e81af8044dbc05423505cc759492bcbda51b
child 403726 066b7df14d37e1197e8481bf574aefb2747e9dc9
child 403733 4c746fe866222e9d05177ba9b1d9210e15e1eac2
child 403734 74aa3f0f20cb80496b6499bb189adc7c76ab1575
child 403736 8aec0dd218429fcb6b0b6c7037b9b4bcfb0df2fb
child 403737 ab02b86a1d9a599876f934dd488b3cf003703347
child 403738 5d7822279291851d869f2f3af2104af6ee22b859
child 403739 d7898bf1e224b880de0aa54f7f824fbd0e6bf8a2
child 403743 59b40ae02511705737a2702924bd798ae06aa833
child 403751 f3232b94848793b754a07b175e818a93a970d214
child 403785 57876c872c66e805851db4834602f85209d84d9d
child 403786 d0f031f534a8ee904282c73d2206073342891d99
child 403787 2a56762b3b6d740d9ed2a2c8d122507c00931622
child 403791 a877209522efeff0d202f31602037d63a230f784
child 403799 06c199b58f09e245983bf76932f474c99a9f3265
child 403805 3e890969a1c48247c6d0f7c867b940f14f5dbd80
child 403809 67dadbf730476f5720935c01ce90c34536d53426
child 403817 43af7d09e51b304c1156a4884bf031d4db66aad4
child 403818 88c95f14e41a47568b6be403c63856c2bf580dce
child 403825 1994f4586036e2a34585854cf98144a0d09c8d92
child 403826 721e299b14e081aec7936412ab15e53b4e59f562
child 403828 c9fb2533a2069700a4f9919316910730f97bfa35
child 403831 1720386f83dc19ec161f70ae46d370edbf1e5e5c
child 403835 408d9c595a5f1153242b86e3f74c4ddddb64752d
child 403836 7953ed8cd13a5446a6bb1d539e07acea934b3ae3
child 403842 cfa81f49e8b98b93c90a6ea436ee106db7a12f00
child 403862 437b29169548131f39993950933a0b9d60e84a94
child 403866 e9239abad45ed74d9d33d8c8ab1f604bc6f8bd95
child 403871 5ebe37f7f7b564d84cf8fae87a012fa53c740114
child 403878 c016db51eb1e7d80bfb01de8ad786c6a771408f7
child 403917 20f608c6da8174b70886715ba4fee3c3d4becf87
child 403918 69a03160d54cb7ed6ce940766bd043f4fa125b40
child 403919 723a64dd51d0077e15990289a628d63896548103
child 403928 258059b7acd8f2ea1448623962a7ca990a2272b6
child 403978 1f8ff67e544c82d47e1d78e61a900319c0c001de
child 403986 ed08fa3eea9d2fd239e376df499913d9d2235742
child 404020 0d70c70df386a00cf948eaca21646b5df22f90c8
child 404036 394248673ca9bdb87db1e9cbe19739aaeb197f0f
child 404106 4f949126e8e9d80358905629003aeebd1318203b
child 404141 32fe6e011cd137dfdd7b22f37f645a73ee6ac9eb
child 404171 a859609a30f1e6f031649184fda2a5b5cc628c0d
child 404174 3a9664853864cd1692c0f67973a803395bfb1fe6
child 404208 267d7f6dee53c4911fce49258fed63283cde0fcf
child 404304 8465e95d65c60c30942c6144a71dd96ed9c82d71
child 404308 8c4a8054eabf169ed8203a3ff1709bb9c53cb5d4
child 404311 6642d96bbf7b3a95fd5af25a8aae14fad5b99a11
child 404327 b259b37aa7cc74a4fcd9800cd9f5d25e3b74c8b2
child 404328 44d457169987f0e8121ed399083f82cf05dc1c97
child 404329 3e7263c13438ff61a2b9d11f7276ec77d49aa012
child 404345 ccf700ed138757d9032a223b13c0e6efdf4672bd
child 404354 5873efa61fdaba35143681572dc69c2eacd41019
child 404495 70e47f6fbebdb0a0476e27b7e9e2ab1f73c82caa
child 404520 5889c577c4f8b214cd5387fb7e2799973c03e1a5
child 404591 7cce4ea81e238930439c5add35d56670eb55628c
child 404770 6d7d8d4473fc49e629cce1c690022ae8c66f8b93
child 404790 837a4f5c79904045f0cbf66aad8dbd46c0587b40
child 404792 4453c94ca457d570d37a17f9a33adc82eeaca8c6
child 404839 8994a50f0d0eb11729507b2deb95db9c7d33abb8
child 404851 ef77b176020d62f8b34d230b6fdc73f34182a56e
child 404852 5ec06ce72d57eb05b7eb0f0b5e945411186df8c5
child 404853 d9450435ffeb910412d3e2e3e952157a2d01228e
child 404854 65f2c8d4fec9a81c227caab1b565dda0c4786e08
child 404857 57f60a2a975eff03b938887bdbb7ef2122596365
child 404861 39e1c7dade2f36e53157c1062db5871cf7f43c32
child 404864 b83f0017fbfc7256da78666e9f07d618ed651074
child 404916 ec3956afd34c8f7404c998a108fd5de2e367c4e0
child 405015 938b46bb0d93c6864ffaa1b6fa264c3a10d42b27
child 405016 b5a6a2a095eaa7cf2213cf15697ec2886201d32b
child 405218 6067f296fd820c6948bb418bf67371b56326338a
child 405260 49ca5b98e357d6fdedd45023fa269a926dbba895
child 405276 80a1dac63e7ea6d776f9924ab0ae96016c838f61
child 405283 7aaec81d0623eef3d6003ed2cb27a11e01f65532
child 405319 3175a549d67d237662983aa6cfe7ba72b762e9ed
child 405464 644836c2582b837b762659c37d9475269e143ba3
child 405949 f3f9ad52c0d6a690e1ce9c8b47cc7bfe848414e2
child 406260 d24aea6ccb264895a14d78cab7431b6244de48ec
child 406674 557eb40b63b5d3c9bad8a73996a48714c5ca63d8
child 407831 0d53c3c4343dab7d9669e3c39dc616f54777e23e
child 416271 a244097b221fdc68384e49908ad544c15cfbd440
child 420299 92338a20fe470932999cc04b613801f286642785
child 426708 7d09f7ab94921935caff669648435f615aa31273
push id26949
push userbmo:rob@robwu.nl
push dateSat, 20 Aug 2016 01:26:39 +0000
reviewersmerge
milestone51.0a1
Merge inbound to central, a=merge
browser/base/content/browser.js
build/moz.configure/ffi.configure
netwerk/base/nsISecretDecoderRing.idl
security/manager/ssl/nsSDR.cpp
security/manager/ssl/nsSDR.h
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 1292323 - clobber due to changes in generated code (bug 1182840)
+Bug 1294803's backout needed a clobber to fix SM builds
--- a/accessible/base/Logging.cpp
+++ b/accessible/base/Logging.cpp
@@ -670,51 +670,51 @@ logging::TreeInfo(const char* aMsg, uint
       AccessibleInfo("child", aParent->GetChildAt(idx));
     }
     MsgEnd();
   }
 }
 
 void
 logging::Tree(const char* aTitle, const char* aMsgText,
-              DocAccessible* aDocument, GetTreePrefix aPrefixFunc,
+              Accessible* aRoot, GetTreePrefix aPrefixFunc,
               void* aGetTreePrefixData)
 {
   logging::MsgBegin(aTitle, aMsgText);
 
   nsAutoString level;
-  Accessible* root = aDocument;
+  Accessible* root = aRoot;
   do {
     const char* prefix = aPrefixFunc ? aPrefixFunc(aGetTreePrefixData, root) : "";
     printf("%s", NS_ConvertUTF16toUTF8(level).get());
     logging::AccessibleInfo(prefix, root);
     if (root->FirstChild() && !root->FirstChild()->IsDoc()) {
       level.Append(NS_LITERAL_STRING("  "));
       root = root->FirstChild();
       continue;
     }
-    int32_t idxInParent = !root->IsDoc() && root->mParent ?
+    int32_t idxInParent = root != aRoot && root->mParent ?
       root->mParent->mChildren.IndexOf(root) : -1;
     if (idxInParent != -1 &&
         idxInParent < static_cast<int32_t>(root->mParent->mChildren.Length() - 1)) {
       root = root->mParent->mChildren.ElementAt(idxInParent + 1);
       continue;
     }
-    while (!root->IsDoc() && (root = root->Parent())) {
+    while (root != aRoot && (root = root->Parent())) {
       level.Cut(0, 2);
       int32_t idxInParent = !root->IsDoc() && root->mParent ?
         root->mParent->mChildren.IndexOf(root) : -1;
       if (idxInParent != -1 &&
           idxInParent < static_cast<int32_t>(root->mParent->mChildren.Length() - 1)) {
         root = root->mParent->mChildren.ElementAt(idxInParent + 1);
         break;
       }
     }
   }
-  while (root && !root->IsDoc());
+  while (root && root != aRoot);
 
   logging::MsgEnd();
 }
 
 void
 logging::DOMTree(const char* aTitle, const char* aMsgText,
                  DocAccessible* aDocument)
 {
--- a/accessible/base/Logging.h
+++ b/accessible/base/Logging.h
@@ -138,17 +138,17 @@ void TreeInfo(const char* aMsg, uint32_t
               const char* aMsg1, Accessible* aAcc,
               const char* aMsg2, nsINode* aNode);
 void TreeInfo(const char* aMsg, uint32_t aExtraFlags, Accessible* aParent);
 
 /**
  * Log the accessible/DOM tree.
  */
 typedef const char* (*GetTreePrefix)(void* aData, Accessible*);
-void Tree(const char* aTitle, const char* aMsgText, DocAccessible* aDoc,
+void Tree(const char* aTitle, const char* aMsgText, Accessible* aRoot,
           GetTreePrefix aPrefixFunc = nullptr, void* aGetTreePrefixData = nullptr);
 void DOMTree(const char* aTitle, const char* aMsgText, DocAccessible* aDoc);
 
 /**
  * Log the message ('title: text' format) on new line. Print the start and end
  * boundaries of the message body designated by '{' and '}' (2 spaces indent for
  * body).
  */
--- a/accessible/generic/Accessible.h
+++ b/accessible/generic/Accessible.h
@@ -49,17 +49,17 @@ class TableAccessible;
 class TableCellAccessible;
 class TextLeafAccessible;
 class XULLabelAccessible;
 class XULTreeAccessible;
 
 #ifdef A11Y_LOG
 namespace logging {
   typedef const char* (*GetTreePrefix)(void* aData, Accessible*);
-  void Tree(const char* aTitle, const char* aMsgText, DocAccessible* aDoc,
+  void Tree(const char* aTitle, const char* aMsgText, Accessible* aRoot,
             GetTreePrefix aPrefixFunc, void* GetTreePrefixData);
 };
 #endif
 
 /**
  * Name type flags.
  */
 enum ENameValueFlag {
@@ -1131,17 +1131,17 @@ protected:
   uint32_t mContextFlags : kContextFlagsBits;
   uint32_t mType : kTypeBits;
   uint32_t mGenericTypes : kGenericTypesBits;
 
   void StaticAsserts() const;
 
 #ifdef A11Y_LOG
   friend void logging::Tree(const char* aTitle, const char* aMsgText,
-                            DocAccessible* aDoc,
+                            Accessible* aRoot,
                             logging::GetTreePrefix aPrefixFunc,
                             void* aGetTreePrefixData);
 #endif
   friend class DocAccessible;
   friend class xpcAccessible;
   friend class TreeMutation;
 
   UniquePtr<mozilla::a11y::EmbeddedObjCollector> mEmbeddedObjCollector;
--- a/accessible/generic/DocAccessible-inl.h
+++ b/accessible/generic/DocAccessible-inl.h
@@ -152,16 +152,22 @@ DocAccessible::CreateSubtree(Accessible*
 {
   // If a focused node has been shown then it could mean its frame was recreated
   // while the node stays focused and we need to fire focus event on
   // the accessible we just created. If the queue contains a focus event for
   // this node already then it will be suppressed by this one.
   Accessible* focusedAcc = nullptr;
   CacheChildrenInSubtree(aChild, &focusedAcc);
 
+#ifdef A11Y_LOG
+  if (logging::IsEnabled(logging::eVerbose)) {
+    logging::Tree("TREE", "Created subtree", aChild);
+  }
+#endif
+
   // Fire events for ARIA elements.
   if (aChild->HasARIARole()) {
     roles::Role role = aChild->ARIARole();
     if (role == roles::MENUPOPUP) {
       FireDelayedEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_START, aChild);
     }
     else if (role == roles::ALERT) {
       FireDelayedEvent(nsIAccessibleEvent::EVENT_ALERT, aChild);
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -759,20 +759,18 @@ DocAccessible::AttributeChanged(nsIDocum
   Accessible* accessible = GetAccessible(aElement);
   if (!accessible) {
     if (mContent != aElement)
       return;
 
     accessible = this;
   }
 
-  if (!accessible->IsBoundToParent()) {
-    MOZ_ASSERT_UNREACHABLE("DOM attribute change on accessible detached from tree");
-    return;
-  }
+  MOZ_ASSERT(accessible->IsBoundToParent() || accessible->IsDoc(),
+             "DOM attribute change on an accessible detached from the tree");
 
   // Fire accessible events iff there's an accessible, otherwise we consider
   // the accessible state wasn't changed, i.e. its state is initial state.
   AttributeChangedImpl(accessible, aNameSpaceID, aAttribute);
 
   // Update dependent IDs cache. Take care of accessible elements because no
   // accessible element means either the element is not accessible at all or
   // its accessible will be created later. It doesn't make sense to keep
@@ -975,17 +973,17 @@ DocAccessible::ARIAAttributeChanged(Acce
   nsIContent* elm = aAccessible->GetContent();
 
   // Update aria-hidden flag for the whole subtree iff aria-hidden is changed
   // on the root, i.e. ignore any affiliated aria-hidden changes in the subtree
   // of top aria-hidden.
   if (aAttribute == nsGkAtoms::aria_hidden) {
     bool isDefined = aria::HasDefinedARIAHidden(elm);
     if (isDefined != aAccessible->IsARIAHidden() &&
-        !aAccessible->Parent()->IsARIAHidden()) {
+        (!aAccessible->Parent() || !aAccessible->Parent()->IsARIAHidden())) {
       aAccessible->SetARIAHidden(isDefined);
 
       RefPtr<AccEvent> event =
         new AccObjectAttrChangedEvent(aAccessible, aAttribute);
       FireDelayedEvent(event);
     }
     return;
   }
@@ -1447,16 +1445,21 @@ DocAccessible::DoInitialUpdate()
 
   mLoadState |= eTreeConstructed;
 
   // Set up a root element and ARIA role mapping.
   UpdateRootElIfNeeded();
 
   // Build initial tree.
   CacheChildrenInSubtree(this);
+#ifdef A11Y_LOG
+  if (logging::IsEnabled(logging::eVerbose)) {
+    logging::Tree("TREE", "Initial subtree", this);
+  }
+#endif
 
   // Fire reorder event after the document tree is constructed. Note, since
   // this reorder event is processed by parent document then events targeted to
   // this document may be fired prior to this reorder event. If this is
   // a problem then consider to keep event processing per tab document.
   if (!IsRoot()) {
     RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(Parent());
     ParentDocument()->FireDelayedEvent(reorderEvent);
@@ -1758,17 +1761,17 @@ InsertIterator::Next()
     }
 
 #ifdef A11Y_LOG
     logging::TreeInfo("traversing an inserted node", logging::eVerbose,
                       "container", container, "node", node);
 #endif
 
     // If inserted nodes are siblings then just move the walker next.
-    if (prevNode && prevNode->GetNextSibling() == node) {
+    if (mChild && prevNode && prevNode->GetNextSibling() == node) {
       Accessible* nextChild = mWalker.Scope(node);
       if (nextChild) {
         mChildBefore = mChild;
         mChild = nextChild;
         return true;
       }
     }
     else {
@@ -2207,19 +2210,16 @@ DocAccessible::CacheChildrenInSubtree(Ac
   // If the accessible is focused then report a focus event after all related
   // mutation events.
   if (aFocusedAcc && !*aFocusedAcc &&
       FocusMgr()->HasDOMFocus(aRoot->GetContent()))
     *aFocusedAcc = aRoot;
 
   Accessible* root = aRoot->IsHTMLCombobox() ? aRoot->FirstChild() : aRoot;
   if (root->KidsFromDOM()) {
-#ifdef A11Y_LOG
-  logging::TreeInfo("caching children", logging::eVerbose, aRoot);
-#endif
     TreeMutation mt(root, TreeMutation::kNoEvents);
     TreeWalker walker(root);
     while (Accessible* child = walker.Next()) {
       if (child->IsBoundToParent()) {
         MoveChild(child, root, root->ChildCount());
         continue;
       }
 
--- a/accessible/tests/mochitest/treeupdate/test_general.html
+++ b/accessible/tests/mochitest/treeupdate/test_general.html
@@ -77,30 +77,61 @@
       }
 
       this.getID = function removeRemove_getID()
       {
         return "remove first and second children";
       }
     }
 
+    function insertInaccessibleAccessibleSiblings()
+    {
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, "c3")
+      ];
+
+      this.invoke = function insertInaccessibleAccessibleSiblings_invoke()
+      {
+        getNode("c3").appendChild(document.createElement("span"));
+        getNode("c3").appendChild(document.createElement("input"));
+      }
+
+      this.finalCheck = function insertInaccessibleAccessibleSiblings_finalCheck()
+      {
+        var accTree =
+          { SECTION: [ // container
+            { PUSHBUTTON: [
+              { TEXT_LEAF: [] }
+            ] },
+            { ENTRY: [ ] }
+          ] };
+        testAccessibleTree("c3", accTree);
+      }
+
+      this.getID = function insertInaccessibleAccessibleSiblings_getID()
+      {
+        return "insert inaccessible and then accessible siblings";
+      }
+    }
+
     ////////////////////////////////////////////////////////////////////////////
     // Do tests
     ////////////////////////////////////////////////////////////////////////////
 
     var gQueue = null;
     //gA11yEventDumpID = "eventdump"; // debug stuff
     //gA11yEventDumpToConsole = true;
 
     function doTests()
     {
       gQueue = new eventQueue();
 
       gQueue.push(new prependAppend("c1"));
       gQueue.push(new removeRemove("c2"));
+      gQueue.push(new insertInaccessibleAccessibleSiblings());
 
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
 </head>
@@ -108,10 +139,12 @@
 <body>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <div id="c1"><input></div>
   <div id="c2"><span><input type="checkbox"><input></span><input type="button"></div>
+
+  <div id="c3"><input type="button" value="button"></div>
 </body>
 </html>
--- a/browser/base/content/aboutNetError.xhtml
+++ b/browser/base/content/aboutNetError.xhtml
@@ -417,19 +417,18 @@
         if (gIsCertError) {
           // Initialize the error code link embedded in the error message to
           // display debug information about the cert error.
           var errorCode = document.getElementById("errorCode");
           if (errorCode) {
             errorCode.href = "javascript:void(0)";
             errorCode.addEventListener("click", () => {
               let debugInfo = document.getElementById("certificateErrorDebugInformation");
-              if (toggleDisplay(debugInfo) == "block") {
-                debugInfo.scrollIntoView({block: "start", behavior: "smooth"});
-              }
+              debugInfo.style.display = "block";
+              debugInfo.scrollIntoView({block: "start", behavior: "smooth"});
             }, false);
           }
         }
 
         // Initialize the cert domain link.
         var link = document.getElementById("cert_domain_link");
         if (!link)
           return;
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -15,17 +15,17 @@
     <stringbundle id="bundle_preferences" src="chrome://browser/locale/preferences/preferences.properties"/>
   </stringbundleset>
 
   <commandset id="mainCommandSet">
     <command id="cmd_newNavigator" oncommand="OpenBrowserWindow()" reserved="true"/>
     <command id="cmd_handleBackspace" oncommand="BrowserHandleBackspace();" />
     <command id="cmd_handleShiftBackspace" oncommand="BrowserHandleShiftBackspace();" />
 
-    <command id="cmd_newNavigatorTab" oncommand="BrowserOpenNewTabOrWindow(event);" reserved="true"/>
+    <command id="cmd_newNavigatorTab" oncommand="BrowserOpenTab(event);" reserved="true"/>
     <command id="Browser:OpenFile"  oncommand="BrowserOpenFileWindow();"/>
     <command id="Browser:SavePage" oncommand="saveBrowser(gBrowser.selectedBrowser);"/>
 
     <command id="Browser:SendLink"
              oncommand="MailIntegration.sendLinkForBrowser(gBrowser.selectedBrowser);"/>
 
     <command id="cmd_pageSetup" oncommand="PrintUtils.showPageSetup();"/>
     <command id="cmd_print" oncommand="PrintUtils.printWindow(window.gBrowser.selectedBrowser.outerWindowID, window.gBrowser.selectedBrowser);"/>
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1885,19 +1885,45 @@ function openLocation() {
     else {
       // If there are no open browser windows, open a new one
       window.openDialog("chrome://browser/content/", "_blank",
                         "chrome,all,dialog=no", BROWSER_NEW_TAB_URL);
     }
   }
 }
 
-function BrowserOpenTab()
-{
-  openUILinkIn(BROWSER_NEW_TAB_URL, "tab");
+function BrowserOpenTab(event) {
+  let where = "tab";
+  let relatedToCurrent = false;
+
+  if (event) {
+    where = whereToOpenLink(event, false, true);
+
+    switch (where) {
+      case "tab":
+      case "tabshifted":
+        // When accel-click or middle-click are used, open the new tab as
+        // related to the current tab. We need to exclude key events here,
+        // where the accel key is required for the shortcut.
+        // 'event' and its sourceEvent are command events, the latter of which
+        // doesn't have its own sourceEvent. These events don't indicate how
+        // they were invoked, except that the sourceEvent for keyboard
+        // shortcuts have <key> targets, and those for clicking a toolbar
+        // button or activating a menu item have that button or menuitem as
+        // their target.
+        relatedToCurrent = !event.sourceEvent ||
+                           event.sourceEvent.target.localName != "key";
+        break;
+      case "current":
+        where = "tab";
+        break;
+    }
+  }
+
+  openUILinkIn(BROWSER_NEW_TAB_URL, where, { relatedToCurrent });
 }
 
 /* Called from the openLocation dialog. This allows that dialog to instruct
    its opener to open a new window and then step completely out of the way.
    Anything less byzantine is causing horrible crashes, rather believably,
    though oddly only on Linux. */
 function delayedOpenWindow(chrome, flags, href, postData)
 {
@@ -7854,24 +7880,16 @@ var MousePosTracker = {
       if (listener.onMouseEnter)
         listener.onMouseEnter();
     } else if (listener.onMouseLeave) {
       listener.onMouseLeave();
     }
   }
 };
 
-function BrowserOpenNewTabOrWindow(event) {
-  if (event.shiftKey) {
-    OpenBrowserWindow();
-  } else {
-    BrowserOpenTab();
-  }
-}
-
 var ToolbarIconColor = {
   init: function () {
     this._initialized = true;
 
     window.addEventListener("activate", this);
     window.addEventListener("deactivate", this);
     Services.obs.addObserver(this, "lightweight-theme-styling-update", false);
 
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1593,16 +1593,18 @@
 
             let wasActive = document.activeElement == aBrowser;
 
             // Unmap the old outerWindowID.
             this._outerWindowIDBrowserMap.delete(aBrowser.outerWindowID);
 
             // Unhook our progress listener.
             let tab = this.getTabForBrowser(aBrowser);
+            // aBrowser needs to be inserted now if it hasn't been already.
+            this._insertBrowser(tab);
             let filter = this._tabFilters.get(tab);
             let listener = this._tabListeners.get(tab);
             aBrowser.webProgress.removeProgressListener(filter);
             filter.removeProgressListener(listener);
 
             // We'll be creating a new listener, so destroy the old one.
             listener.destroy();
 
@@ -1776,22 +1778,22 @@
         </body>
       </method>
 
       <method name="_createBrowser">
         <parameter name="aParams"/>
         <body>
           <![CDATA[
             // Supported parameters:
-            // userContextId, remote, isPreloadBrowser, uriIsAboutBlank, permanentKey
+            // userContextId, remote, isPreloadBrowser, uriIsAboutBlank, relatedBrowser
 
             const NS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
             let b = document.createElementNS(NS_XUL, "browser");
-            b.permanentKey = aParams.permanentKey || {};
+            b.permanentKey = {};
             b.setAttribute("type", "content-targetable");
             b.setAttribute("message", "true");
             b.setAttribute("messagemanagergroup", "browsers");
             b.setAttribute("contextmenu", this.getAttribute("contentcontextmenu"));
             b.setAttribute("tooltip", this.getAttribute("contenttooltip"));
 
             if (aParams.userContextId) {
               b.setAttribute("usercontextid", aParams.userContextId);
@@ -1850,68 +1852,105 @@
               b.setAttribute("nodefaultsrc", "true");
             }
 
             return b;
           ]]>
         </body>
       </method>
 
-      <method name="_linkBrowserToTab">
+      <!--
+           `_createLazyBrowser` will define properties on the unbound lazy
+           browser which correspond to properties defined in XBL which will be
+           bound to the browser when it is inserted into the document.  If any
+           of these properties are accessed by consumers, `_insertBrowser` is
+           called and the browser is inserted to ensure that things don't break.
+           This list provides the names of properties that may be called while
+           the browser is in its unbound (lazy) state.
+      -->
+      <field name="_browserBindingProperties">
+        [
+          "canGoBack", "canGoForward", "goBack", "goForward", "permitUnload",
+          "reload", "reloadWithFlags", "stop", "loadURI", "loadURIWithFlags",
+          "goHome", "homePage", "gotoIndex", "currentURI", "documentURI",
+          "preferences", "imageDocument", "isRemoteBrowser", "messageManager",
+          "getTabBrowser", "finder", "fastFind", "sessionHistory", "contentTitle",
+           "characterSet", "fullZoom", "textZoom", "webProgress",
+          "addProgressListener", "removeProgressListener",
+          "audioPlaybackStarted", "audioPlaybackStopped", "adjustPriority",
+          "pauseMedia", "stopMedia", "blockMedia", "resumeMedia",
+          "audioMuted", "mute", "unmute", "blockedPopups", "mIconURL", "lastURI",
+          "userTypedValue", "purgeSessionHistory", "stopScroll", "startScroll"
+        ]
+      </field>
+
+      <method name="_createLazyBrowser">
         <parameter name="aTab"/>
-        <parameter name="aURI"/>
-        <parameter name="aParams"/>
+        <body>
+          <![CDATA[
+            let browser = aTab.linkedBrowser;
+
+            let names = this._browserBindingProperties;
+
+            for (let i = 0; i < names.length; i++) {
+              let name = names[i];
+              let getter;
+              let setter;
+              switch (name) {
+                default:
+                  getter = () => {
+                    this._insertBrowser(aTab);
+                    return browser[name];
+                  };
+                  setter = (value) => {
+                    this._insertBrowser(aTab);
+                    return browser[name] = value;
+                  };
+              }
+              Object.defineProperty(browser, name, {
+                get: getter,
+                set: setter,
+                configurable: true,
+                enumerable: true
+              });
+            }
+
+          ]]>
+        </body>
+      </method>
+
+      <method name="_insertBrowser">
+        <parameter name="aTab"/>
         <body>
           <![CDATA[
             "use strict";
 
-            // Supported parameters:
-            // forceNotRemote, userContextId
-
-            let uriIsAboutBlank = !aURI || aURI == "about:blank";
-
-            // The new browser should be remote if this is an e10s window and
-            // the uri to load can be loaded remotely.
-            let remote = gMultiProcessBrowser &&
-                         !aParams.forceNotRemote &&
-                         E10SUtils.canLoadURIInProcess(aURI, Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT);
-
-            let browser;
-            let usingPreloadedContent = false;
-
-            // If we open a new tab with the newtab URL in the default
-            // userContext, check if there is a preloaded browser ready.
-            // Private windows are not included because both the label and the
-            // icon for the tab would be set incorrectly (see bug 1195981).
-            if (aURI == BROWSER_NEW_TAB_URL &&
-                !aParams.userContextId &&
-                !PrivateBrowsingUtils.isWindowPrivate(window)) {
-              browser = this._getPreloadedBrowser();
-              if (browser) {
-                usingPreloadedContent = true;
-                aTab.permanentKey = browser.permanentKey;
+            // If browser is already inserted, don't do anything.
+            if (aTab.linkedPanel) {
+              return;
+            }
+
+            let browser = aTab.linkedBrowser;
+
+            // If browser is a lazy browser delete the substitute properties.
+            if (this._browserBindingProperties[0] in browser) {
+              for (let name of this._browserBindingProperties) {
+                delete(browser[name]);
               }
             }
 
-            if (!browser) {
-              // No preloaded browser found, create one.
-              browser = this._createBrowser({permanentKey: aTab.permanentKey,
-                                             remote: remote,
-                                             uriIsAboutBlank: uriIsAboutBlank,
-                                             userContextId: aParams.userContextId,
-                                             relatedBrowser: aParams.relatedBrowser});
-            }
+            let { uri, remote, usingPreloadedContent } = aTab._browserParams;
+            delete(aTab._browserParams);
+
+            let uriIsAboutBlank = !uri || uri == "about:blank";
 
             let notificationbox = this.getNotificationBox(browser);
             let uniqueId = this._generateUniquePanelID();
             notificationbox.id = uniqueId;
             aTab.linkedPanel = uniqueId;
-            aTab.linkedBrowser = browser;
-            aTab.hasBrowser = true;
-            this._tabForBrowser.set(browser, aTab);
 
             // Inject the <browser> into the DOM if necessary.
             if (!notificationbox.parentNode) {
               // NB: this appendChild call causes us to run constructors for the
               // browser element, which fires off a bunch of notifications. Some
               // of those notifications can cause code to run that inspects our
               // state, so it is important that the tab element is fully
               // initialized by this point.
@@ -1937,20 +1976,18 @@
             // set the "nodefaultsrc" attribute that prevents a frameLoader
             // from being created as soon as the linked <browser> is inserted
             // into the DOM. We thus have to register the new outerWindowID
             // for non-remote browsers after we have called browser.loadURI().
             if (!remote) {
               this._outerWindowIDBrowserMap.set(browser.outerWindowID, browser);
             }
 
-            var evt = new CustomEvent("TabBrowserCreated", { bubbles: true, detail: {} });
+            var evt = new CustomEvent("TabBrowserInserted", { bubbles: true, detail: {} });
             aTab.dispatchEvent(evt);
-
-            return { usingPreloadedContent: usingPreloadedContent };
           ]]>
         </body>
       </method>
 
       <method name="addTab">
         <parameter name="aURI"/>
         <parameter name="aReferrerURI"/>
         <parameter name="aCharset"/>
@@ -1994,18 +2031,16 @@
             }
 
             // if we're adding tabs, we're past interrupt mode, ditch the owner
             if (this.mCurrentTab.owner)
               this.mCurrentTab.owner = null;
 
             var t = document.createElementNS(NS_XUL, "tab");
 
-            var uriIsAboutBlank = !aURI || aURI == "about:blank";
-
             if (!aURI || isBlankPageURL(aURI)) {
               t.setAttribute("label", this.mStringBundle.getString("tabs.emptyTabTitle"));
             } else if (aURI.toLowerCase().startsWith("javascript:")) {
               // This can go away when bug 672618 or bug 55696 are fixed.
               t.setAttribute("label", aURI);
             }
 
             if (aUserContextId) {
@@ -2038,35 +2073,67 @@
             this.tabContainer.appendChild(t);
 
             // If this new tab is owned by another, assert that relationship
             if (aOwner)
               t.owner = aOwner;
 
             var position = this.tabs.length - 1;
             t._tPos = position;
-            t.permanentKey = {};
             this.tabContainer._setPositionalAttributes();
 
             this.tabContainer.updateVisibility();
 
-            // Currently in this incarnation of bug 906076, we are forcing the
-            // browser to immediately be linked.  In future incarnations of this
-            // bug this will be removed so we can leave the tab in its "lazy"
-            // state to be exploited for startup optimization.  Note that for
-            // now this must occur before "TabOpen" event is fired, as that will
-            // trigger SessionStore.jsm to run code that expects the existence
-            // of tab.linkedBrowser.
-            let browserParams = {
-              forceNotRemote: aForceNotRemote,
-              userContextId:  aUserContextId,
-              relatedBrowser: aRelatedBrowser
+            // The new browser should be remote if this is an e10s window and
+            // the uri to load can be loaded remotely.
+            let remote = gMultiProcessBrowser &&
+                         !aForceNotRemote &&
+                         E10SUtils.canLoadURIInProcess(aURI, Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT);
+
+            let b;
+            let usingPreloadedContent = false;
+            let uriIsAboutBlank = !aURI || aURI == "about:blank";
+
+            // If we open a new tab with the newtab URL in the default
+            // userContext, check if there is a preloaded browser ready.
+            // Private windows are not included because both the label and the
+            // icon for the tab would be set incorrectly (see bug 1195981).
+            if (aURI == BROWSER_NEW_TAB_URL &&
+                !aUserContextId &&
+                !PrivateBrowsingUtils.isWindowPrivate(window)) {
+              b = this._getPreloadedBrowser();
+              if (b) {
+                usingPreloadedContent = true;
+              }
+            }
+
+            if (!b) {
+              // No preloaded browser found, create one.
+              b = this._createBrowser({remote: remote,
+                                       uriIsAboutBlank: uriIsAboutBlank,
+                                       userContextId: aUserContextId,
+                                       relatedBrowser: aRelatedBrowser});
+            }
+
+            t.linkedBrowser = b;
+            this._tabForBrowser.set(b, t);
+            t.permanentKey = b.permanentKey;
+            t._browserParams = {
+              uri: aURI,
+              remote: remote,
+              usingPreloadedContent: usingPreloadedContent,
             };
-            let { usingPreloadedContent } = this._linkBrowserToTab(t, aURI, browserParams);
-            let b = t.linkedBrowser;
+
+            // If we're creating a blank tab, create a lazy browser.
+            // Otherwise fully instantiate the browser now.
+            if (uriIsAboutBlank) {
+              this._createLazyBrowser(t);
+            } else {
+              this._insertBrowser(t);
+            }
 
             // Dispatch a new tab notification.  We do this once we're
             // entirely done, so that things are in a consistent state
             // even if the event listener opens or closes tabs.
             var detail = aEventDetail || {};
             var evt = new CustomEvent("TabOpen", { bubbles: true, detail });
             t.dispatchEvent(evt);
 
@@ -2712,16 +2779,19 @@
         </body>
       </method>
 
       <method name="_swapBrowserDocShells">
         <parameter name="aOurTab"/>
         <parameter name="aOtherBrowser"/>
         <body>
           <![CDATA[
+            // aOurTab's browser needs to be inserted now if it hasn't already.
+            this._insertBrowser(aOurTab);
+
             // Unhook our progress listener
             const filter = this._tabFilters.get(aOurTab);
             let tabListener = this._tabListeners.get(aOurTab);
             let ourBrowser = this.getBrowserForTab(aOurTab);
             ourBrowser.webProgress.removeProgressListener(filter);
             filter.removeProgressListener(tabListener);
 
             // Make sure to unregister any open URIs.
@@ -4484,17 +4554,16 @@
           var uniqueId = this._generateUniquePanelID();
           this.mPanelContainer.childNodes[0].id = uniqueId;
           this.mCurrentTab.linkedPanel = uniqueId;
           this.mCurrentTab.permanentKey = this.mCurrentBrowser.permanentKey;
           this.mCurrentTab._tPos = 0;
           this.mCurrentTab._fullyOpen = true;
           this.mCurrentTab.cachePosition = 0;
           this.mCurrentTab.linkedBrowser = this.mCurrentBrowser;
-          this.mCurrentTab.hasBrowser = true;
           this._tabForBrowser.set(this.mCurrentBrowser, this.mCurrentTab);
 
           // set up the shared autoscroll popup
           this._autoScrollPopup = this.mCurrentBrowser._createAutoScrollPopup();
           this._autoScrollPopup.id = "autoscroller";
           this.appendChild(this._autoScrollPopup);
           this.mCurrentBrowser.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
           this.mCurrentBrowser.droppedLinkHandler = handleDroppedLink;
@@ -5680,16 +5749,31 @@
               let preview = this._browserNewtabpageEnabled ? "_PREVIEW" : "";
               Services.telemetry.getHistogramById("FX_TAB_ANIM_OPEN" + preview + "_FRAME_INTERVAL_MS").add(averageInterval);
             }
           }
         ]]>
         </body>
       </method>
 
+      <method name="getRelatedElement">
+        <parameter name="aTab"/>
+        <body>
+        <![CDATA[
+          if (!aTab)
+            return null;
+          // If the tab's browser is lazy, we need to `_insertBrowser` in order
+          // to have a linkedPanel.  This will also serve to bind the browser
+          // and make it ready to use when the tab is selected.
+          this.tabbrowser._insertBrowser(aTab);
+          return document.getElementById(aTab.linkedPanel);
+        ]]>
+        </body>
+      </method>
+
       <!-- Deprecated stuff, implemented for backwards compatibility. -->
       <property name="mAllTabsPopup" readonly="true"
                 onget="return document.getElementById('alltabs-popup');"/>
     </implementation>
 
     <handlers>
       <handler event="TabSelect" action="this._handleTabSelect();"/>
 
@@ -6264,17 +6348,17 @@
                      anonid="tab-icon-image"
                      class="tab-icon-image"
                      validate="never"
                      role="presentation"/>
           <xul:image xbl:inherits="sharing,selected"
                      anonid="sharing-icon"
                      class="tab-sharing-icon-overlay"
                      role="presentation"/>
-          <xul:image xbl:inherits="crashed,busy,soundplaying,pinned,muted"
+          <xul:image xbl:inherits="crashed,busy,soundplaying,pinned,muted,visuallyselected"
                      anonid="overlay-icon"
                      class="tab-icon-overlay"
                      role="presentation"/>
           <xul:label flex="1"
                      xbl:inherits="value=label,crop,accesskey,fadein,pinned,selected,visuallyselected,attention"
                      class="tab-text tab-label"
                      role="presentation"/>
           <xul:image xbl:inherits="soundplaying,pinned,muted,visuallyselected"
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -120,17 +120,17 @@ const CLOSED_MESSAGES = new Set([
   "SessionStore:update",
 
   // For a description see above.
   "SessionStore:error",
 ]);
 
 // These are tab events that we listen to.
 const TAB_EVENTS = [
-  "TabOpen", "TabBrowserCreated", "TabClose", "TabSelect", "TabShow", "TabHide", "TabPinned",
+  "TabOpen", "TabBrowserInserted", "TabClose", "TabSelect", "TabShow", "TabHide", "TabPinned",
   "TabUnpinned"
 ];
 
 const NS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 Cu.import("resource://gre/modules/Services.jsm", this);
 Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
 Cu.import("resource://gre/modules/TelemetryTimestamps.jsm", this);
@@ -891,18 +891,18 @@ var SessionStoreInternal = {
    */
   handleEvent: function ssi_handleEvent(aEvent) {
     let win = aEvent.currentTarget.ownerGlobal;
     let target = aEvent.originalTarget;
     switch (aEvent.type) {
       case "TabOpen":
         this.onTabAdd(win);
         break;
-      case "TabBrowserCreated":
-        this.onTabBrowserCreated(win, target);
+      case "TabBrowserInserted":
+        this.onTabBrowserInserted(win, target);
         break;
       case "TabClose":
         // `adoptedBy` will be set if the tab was closed because it is being
         // moved to a new window.
         if (!aEvent.detail.adoptedBy)
           this.onTabClose(win, target);
         this.onTabRemove(win, target);
         break;
@@ -984,17 +984,17 @@ var SessionStoreInternal = {
       this._windows[aWindow.__SSi]._restoring = true;
     if (!aWindow.toolbar.visible)
       this._windows[aWindow.__SSi].isPopup = true;
 
     let tabbrowser = aWindow.gBrowser;
 
     // add tab change listeners to all already existing tabs
     for (let i = 0; i < tabbrowser.tabs.length; i++) {
-      this.onTabBrowserCreated(aWindow, tabbrowser.tabs[i]);
+      this.onTabBrowserInserted(aWindow, tabbrowser.tabs[i]);
     }
     // notification of tab add/remove/selection/show/hide
     TAB_EVENTS.forEach(function(aEvent) {
       tabbrowser.tabContainer.addEventListener(aEvent, this, true);
     }, this);
 
     // Keep track of a browser's latest frameLoader.
     aWindow.gBrowser.addEventListener("XULFrameLoaderCreated", this);
@@ -1693,17 +1693,17 @@ var SessionStoreInternal = {
 
   /**
    * set up listeners for a new tab
    * @param aWindow
    *        Window reference
    * @param aTab
    *        Tab reference
    */
-  onTabBrowserCreated: function ssi_onTabBrowserCreated(aWindow, aTab) {
+  onTabBrowserInserted: function ssi_onTabBrowserInserted(aWindow, aTab) {
     let browser = aTab.linkedBrowser;
     browser.addEventListener("SwapDocShells", this);
     browser.addEventListener("oop-browser-crashed", this);
 
     if (browser.frameLoader) {
       this._lastKnownFrameLoader.set(browser.permanentKey, browser.frameLoader);
     }
   },
--- a/browser/modules/NetworkPrioritizer.jsm
+++ b/browser/modules/NetworkPrioritizer.jsm
@@ -22,17 +22,17 @@ Components.utils.import("resource://gre/
 
 // Lazy getters
 XPCOMUtils.defineLazyServiceGetter(this, "_focusManager",
                                    "@mozilla.org/focus-manager;1",
                                    "nsIFocusManager");
 
 
 // Constants
-const TAB_EVENTS = ["TabBrowserCreated", "TabSelect", "TabRemotenessChange"];
+const TAB_EVENTS = ["TabBrowserInserted", "TabSelect", "TabRemotenessChange"];
 const WINDOW_EVENTS = ["activate", "unload"];
 // lower value means higher priority
 const PRIORITY_DELTA = Ci.nsISupportsPriority.PRIORITY_NORMAL - Ci.nsISupportsPriority.PRIORITY_LOW;
 
 
 // Variables
 var _lastFocusedWindow = null;
 var _windows = [];
@@ -44,17 +44,17 @@ var _priorityBackup = new WeakMap();
 this.trackBrowserWindow = function trackBrowserWindow(aWindow) {
   WindowHelper.addWindow(aWindow);
 }
 
 
 // Global methods
 function _handleEvent(aEvent) {
   switch (aEvent.type) {
-    case "TabBrowserCreated":
+    case "TabBrowserInserted":
       BrowserHelper.onOpen(aEvent.target.linkedBrowser);
       break;
     case "TabSelect":
       BrowserHelper.onSelect(aEvent.target.linkedBrowser);
       break;
     case "activate":
       WindowHelper.onActivate(aEvent.target);
       break;
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -1690,37 +1690,16 @@ html|span.ac-emphasize-text-url {
 .alltabs-item[selected="true"] {
   font-weight: bold;
 }
 
 .alltabs-item[busy] > .menu-iconic-left > .menu-iconic-icon {
   list-style-image: url("chrome://global/skin/icons/loading.png");
 }
 
-.alltabs-item[tabIsVisible] {
-  /* box-shadow instead of background-color to work around native styling */
-  box-shadow: inset -5px 0 ThreeDShadow;
-}
-
-.alltabs-endimage[muted] {
-  list-style-image: url(chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-menu-muted);
-}
-
-.alltabs-endimage[soundplaying] {
-  list-style-image: url(chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-menu);
-}
-
-menuitem:hover > hbox > .alltabs-endimage[muted] {
-  list-style-image: url(chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-menu-muted-hover);
-}
-
-menuitem:hover > hbox > .alltabs-endimage[soundplaying] {
-  list-style-image: url(chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-menu-hover);
-}
-
 /* Sidebar */
 #sidebar-throbber[loading="true"] {
   list-style-image: url("chrome://global/skin/icons/loading.png");
   margin-inline-end: 4px;
 }
 
 toolbarbutton.chevron {
   list-style-image: url("chrome://global/skin/toolbar/chevron.gif") !important;
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -2944,37 +2944,16 @@ toolbarbutton.chevron > .toolbarbutton-m
     list-style-image: url("chrome://mozapps/skin/places/defaultFavicon@2x.png");
   }
 
   .alltabs-item[busy] > .menu-iconic-left > .menu-iconic-icon {
     list-style-image: url("chrome://global/skin/icons/loading@2x.png") !important;
   }
 }
 
-.alltabs-item[tabIsVisible] {
-  /* box-shadow instead of background-color to work around native styling */
-  box-shadow: inset -5px 0 ThreeDShadow;
-}
-
-.alltabs-endimage[muted] {
-  list-style-image: url(chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-menu-muted);
-}
-
-.alltabs-endimage[soundplaying] {
-  list-style-image: url(chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-menu);
-}
-
-menuitem:hover > hbox > .alltabs-endimage[muted] {
-  list-style-image: url(chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-menu-muted-hover);
-}
-
-menuitem:hover > hbox > .alltabs-endimage[soundplaying] {
-  list-style-image: url(chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-menu-hover);
-}
-
 /* Bookmarks toolbar */
 #PlacesToolbarDropIndicator {
   list-style-image: url(chrome://browser/skin/places/toolbarDropMarker.png);
 }
 
 /* Bookmark drag and drop styles */
 
 .bookmark-item[dragover-into="true"] {
--- a/browser/themes/shared/devedition.inc.css
+++ b/browser/themes/shared/devedition.inc.css
@@ -274,38 +274,19 @@ window:not([chromehidden~="toolbar"]) #u
   background-color: var(--tab-hover-background-color);
 }
 
 .tabbrowser-tab[visuallyselected] {
   color: var(--tab-selection-color) !important; /* Override color: inherit */
   background-color: var(--tab-selection-background-color);
 }
 
-.tab-icon-sound[visuallyselected=true][soundplaying] {
-  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-white");
-}
-
-.tab-icon-sound[visuallyselected=true][soundplaying]:hover {
-  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-white-hover");
-}
-
-.tab-icon-sound[visuallyselected=true][soundplaying]:hover:active {
-  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-white-pressed");
-}
-
-.tab-icon-sound[visuallyselected=true][muted] {
-  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted-white");
-}
-
-.tab-icon-sound[visuallyselected=true][muted]:hover {
-  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted-white-hover");
-}
-
-.tab-icon-sound[visuallyselected=true][muted]:hover:active {
-  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted-white-pressed");
+.tab-icon-sound[soundplaying],
+.tab-icon-sound[muted] {
+  filter: url(chrome://browser/skin/filters.svg#fill) !important; /* removes drop-shadow filter */
 }
 
 /* Don't need space for the tab curves (66px - 30px) */
 .tabs-newtab-button {
   width: 36px;
 }
 
 .tabs-newtab-button:hover {
--- a/browser/themes/shared/jar.inc.mn
+++ b/browser/themes/shared/jar.inc.mn
@@ -102,17 +102,17 @@
   skin/classic/browser/gear.svg                                (../shared/search/gear.svg)
   skin/classic/browser/social/chat-icons.svg                   (../shared/social/chat-icons.svg)
   skin/classic/browser/social/gear_default.png                 (../shared/social/gear_default.png)
   skin/classic/browser/social/gear_clicked.png                 (../shared/social/gear_clicked.png)
   skin/classic/browser/tabbrowser/connecting.png               (../shared/tabbrowser/connecting.png)
   skin/classic/browser/tabbrowser/connecting@2x.png            (../shared/tabbrowser/connecting@2x.png)
   skin/classic/browser/tabbrowser/crashed.svg                  (../shared/tabbrowser/crashed.svg)
   skin/classic/browser/tabbrowser/pendingpaint.png             (../shared/tabbrowser/pendingpaint.png)
-* skin/classic/browser/tabbrowser/tab-audio.svg                (../shared/tabbrowser/tab-audio.svg)
+  skin/classic/browser/tabbrowser/tab-audio.svg                (../shared/tabbrowser/tab-audio.svg)
   skin/classic/browser/tabbrowser/tab-audio-small.svg          (../shared/tabbrowser/tab-audio-small.svg)
   skin/classic/browser/tabbrowser/tab-overflow-indicator.png   (../shared/tabbrowser/tab-overflow-indicator.png)
   skin/classic/browser/theme-switcher-icon.png                 (../shared/theme-switcher-icon.png)
   skin/classic/browser/theme-switcher-icon@2x.png              (../shared/theme-switcher-icon@2x.png)
   skin/classic/browser/translating-16.png                      (../shared/translation/translating-16.png)
   skin/classic/browser/translating-16@2x.png                   (../shared/translation/translating-16@2x.png)
   skin/classic/browser/translation-16.png                      (../shared/translation/translation-16.png)
   skin/classic/browser/translation-16@2x.png                   (../shared/translation/translation-16@2x.png)
--- a/browser/themes/shared/tabbrowser/tab-audio.svg
+++ b/browser/themes/shared/tabbrowser/tab-audio.svg
@@ -1,103 +1,15 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
   <style>
-    .icon:not(:target) {
+    path:not(:target) {
       display: none;
     }
-
-    .icon {
-      fill: #333;
-      opacity: .75;
-    }
-    .icon.hover {
-      fill: #1a1a1a;
-      opacity: .85;
-    }
-    .icon.pressed {
-      fill: #0d0d0d;
-      opacity: .95;
-    }
-
-    .icon.white {
-      fill: #fff;
-    }
-    .icon.white.hover {
-      opacity: .9;
-    }
-    .icon.white.pressed {
-      opacity: 1;
-    }
-    .icon.white > .outline {
-      fill: #000;
-      fill-opacity: .5;
-    }
-
-    .icon.menu {
-      fill: MenuText;
-    }
-    .icon.menu.hover {
-#ifdef XP_MACOSX
-      fill: -moz-mac-menutextselect;
-#else
-      fill: -moz-menuhovertext;
-#endif
-    }
-
-    .icon.backgroundTab,
-    .icon.backgroundTab.hover,
-    .icon.backgroundTab.pressed {
-      fill: -moz-MenuBarText;
-    }
   </style>
 
-  <path id="tab-audio" class="icon" d="M4,5C2.9,5,2,5.9,2,7v2c0,1.1,0.9,2,2,2h1.2L9,14V2L5.2,5H4z M11,8c0-0.6-0.4-1-1-1v2C10.6,9,11,8.6,11,8z M13,8 c0-1.4-1-2.6-2.3-2.9L10.4,6C11.3,6.2,12,7,12,8s-0.7,1.8-1.6,2l0.4,0.9C12,10.6,13,9.4,13,8z M11.4,3.2l-0.4,0.9 C12.8,4.6,14,6.2,14,8s-1.2,3.4-2.9,3.8l0.4,0.9C13.5,12.2,15,10.3,15,8S13.5,3.8,11.4,3.2z"/>
-  <path id="tab-audio-hover" class="icon hover" d="M4,5C2.9,5,2,5.9,2,7v2c0,1.1,0.9,2,2,2h1.2L9,14V2L5.2,5H4z M11,8c0-0.6-0.4-1-1-1v2C10.6,9,11,8.6,11,8z M13,8 c0-1.4-1-2.6-2.3-2.9L10.4,6C11.3,6.2,12,7,12,8s-0.7,1.8-1.6,2l0.4,0.9C12,10.6,13,9.4,13,8z M11.4,3.2l-0.4,0.9 C12.8,4.6,14,6.2,14,8s-1.2,3.4-2.9,3.8l0.4,0.9C13.5,12.2,15,10.3,15,8S13.5,3.8,11.4,3.2z"/>
-  <path id="tab-audio-pressed" class="icon pressed" d="M4,5C2.9,5,2,5.9,2,7v2c0,1.1,0.9,2,2,2h1.2L9,14V2L5.2,5H4z M11,8c0-0.6-0.4-1-1-1v2C10.6,9,11,8.6,11,8z M13,8 c0-1.4-1-2.6-2.3-2.9L10.4,6C11.3,6.2,12,7,12,8s-0.7,1.8-1.6,2l0.4,0.9C12,10.6,13,9.4,13,8z M11.4,3.2l-0.4,0.9 C12.8,4.6,14,6.2,14,8s-1.2,3.4-2.9,3.8l0.4,0.9C13.5,12.2,15,10.3,15,8S13.5,3.8,11.4,3.2z"/>
-
-  <path id="tab-audio-muted" class="icon" d="M12.5,3.4L9,6.3V2L5.2,5H4C2.9,5,2,5.9,2,7v2c0,0.9,0.6,1.6,1.4,1.9l-1.9,1.5l1,1.2l11-9L12.5,3.4z M9,14v-4l-2.5,2L9,14z"/>
-  <path id="tab-audio-muted-hover" class="icon hover" d="M12.5,3.4L9,6.3V2L5.2,5H4C2.9,5,2,5.9,2,7v2c0,0.9,0.6,1.6,1.4,1.9l-1.9,1.5l1,1.2l11-9L12.5,3.4z M9,14v-4l-2.5,2L9,14z"/>
-  <path id="tab-audio-muted-pressed" class="icon pressed" d="M12.5,3.4L9,6.3V2L5.2,5H4C2.9,5,2,5.9,2,7v2c0,0.9,0.6,1.6,1.4,1.9l-1.9,1.5l1,1.2l11-9L12.5,3.4z M9,14v-4l-2.5,2L9,14z"/>
-
-  <path id="tab-audio-backgroundTab" class="icon backgroundTab" d="M4,5C2.9,5,2,5.9,2,7v2c0,1.1,0.9,2,2,2h1.2L9,14V2L5.2,5H4z M11,8c0-0.6-0.4-1-1-1v2C10.6,9,11,8.6,11,8z M13,8 c0-1.4-1-2.6-2.3-2.9L10.4,6C11.3,6.2,12,7,12,8s-0.7,1.8-1.6,2l0.4,0.9C12,10.6,13,9.4,13,8z M11.4,3.2l-0.4,0.9 C12.8,4.6,14,6.2,14,8s-1.2,3.4-2.9,3.8l0.4,0.9C13.5,12.2,15,10.3,15,8S13.5,3.8,11.4,3.2z"/>
-  <path id="tab-audio-backgroundTab-hover" class="icon backgroundTab hover" d="M4,5C2.9,5,2,5.9,2,7v2c0,1.1,0.9,2,2,2h1.2L9,14V2L5.2,5H4z M11,8c0-0.6-0.4-1-1-1v2C10.6,9,11,8.6,11,8z M13,8 c0-1.4-1-2.6-2.3-2.9L10.4,6C11.3,6.2,12,7,12,8s-0.7,1.8-1.6,2l0.4,0.9C12,10.6,13,9.4,13,8z M11.4,3.2l-0.4,0.9 C12.8,4.6,14,6.2,14,8s-1.2,3.4-2.9,3.8l0.4,0.9C13.5,12.2,15,10.3,15,8S13.5,3.8,11.4,3.2z"/>
-  <path id="tab-audio-backgroundTab-pressed" class="icon backgroundTab pressed" d="M4,5C2.9,5,2,5.9,2,7v2c0,1.1,0.9,2,2,2h1.2L9,14V2L5.2,5H4z M11,8c0-0.6-0.4-1-1-1v2C10.6,9,11,8.6,11,8z M13,8 c0-1.4-1-2.6-2.3-2.9L10.4,6C11.3,6.2,12,7,12,8s-0.7,1.8-1.6,2l0.4,0.9C12,10.6,13,9.4,13,8z M11.4,3.2l-0.4,0.9 C12.8,4.6,14,6.2,14,8s-1.2,3.4-2.9,3.8l0.4,0.9C13.5,12.2,15,10.3,15,8S13.5,3.8,11.4,3.2z"/>
-
-  <path id="tab-audio-backgroundTab-muted" class="icon backgroundTab" d="M12.5,3.4L9,6.3V2L5.2,5H4C2.9,5,2,5.9,2,7v2c0,0.9,0.6,1.6,1.4,1.9l-1.9,1.5l1,1.2l11-9L12.5,3.4z M9,14v-4l-2.5,2L9,14z"/>
-  <path id="tab-audio-backgroundTab-muted-hover" class="icon backgroundTab hover" d="M12.5,3.4L9,6.3V2L5.2,5H4C2.9,5,2,5.9,2,7v2c0,0.9,0.6,1.6,1.4,1.9l-1.9,1.5l1,1.2l11-9L12.5,3.4z M9,14v-4l-2.5,2L9,14z"/>
-  <path id="tab-audio-backgroundTab-muted-pressed" class="icon backgroundTab pressed" d="M12.5,3.4L9,6.3V2L5.2,5H4C2.9,5,2,5.9,2,7v2c0,0.9,0.6,1.6,1.4,1.9l-1.9,1.5l1,1.2l11-9L12.5,3.4z M9,14v-4l-2.5,2L9,14z"/>
+  <path id="tab-audio" d="M4,5C2.9,5,2,5.9,2,7v2c0,1.1,0.9,2,2,2h1.2L9,14V2L5.2,5H4z M11,8c0-0.6-0.4-1-1-1v2C10.6,9,11,8.6,11,8z M13,8 c0-1.4-1-2.6-2.3-2.9L10.4,6C11.3,6.2,12,7,12,8s-0.7,1.8-1.6,2l0.4,0.9C12,10.6,13,9.4,13,8z M11.4,3.2l-0.4,0.9 C12.8,4.6,14,6.2,14,8s-1.2,3.4-2.9,3.8l0.4,0.9C13.5,12.2,15,10.3,15,8S13.5,3.8,11.4,3.2z"/>
 
-  <g id="tab-audio-white" class="icon white">
-    <path class="outline" d="M9,2v12l-3.8-3H4c-1.1,0-2-0.9-2-2V7c0-1.1,0.9-2,2-2h1.2L9,2 M11.4,3.2C13.5,3.8,15,5.7,15,8 s-1.5,4.2-3.5,4.7l-0.4-0.9c1.7-0.4,2.9-2,2.9-3.8s-1.2-3.4-3-3.9L11.4,3.2 M10.7,5.1C12,5.4,13,6.6,13,8s-1,2.6-2.2,2.9L10.4,10 C11.3,9.8,12,9,12,8s-0.7-1.8-1.6-2L10.7,5.1 M10,7c0.6,0,1,0.4,1,1s-0.4,1-1,1V7 M10-0.1L8.4,1.2L4.9,4H4C2.3,4,1,5.3,1,7v2 c0,1.7,1.3,3,3,3h0.9l3.5,2.8l1.6,1.3V14v-2.2l0.2,0.4l0.4,0.9l0.3,0.8l0.8-0.2C14.2,13,16,10.7,16,8c0-2.7-1.7-5-4.3-5.8L10.8,2 l-0.4,0.8l-0.4,0.9L10,3.9V2V-0.1L10-0.1z"/>
-    <path d="M4,5C2.9,5,2,5.9,2,7v2c0,1.1,0.9,2,2,2h1.2L9,14V2L5.2,5H4z M11,8c0-0.6-0.4-1-1-1v2C10.6,9,11,8.6,11,8z M13,8 c0-1.4-1-2.6-2.3-2.9L10.4,6C11.3,6.2,12,7,12,8s-0.7,1.8-1.6,2l0.4,0.9C12,10.6,13,9.4,13,8z M11.4,3.2l-0.4,0.9 C12.8,4.6,14,6.2,14,8s-1.2,3.4-2.9,3.8l0.4,0.9C13.5,12.2,15,10.3,15,8S13.5,3.8,11.4,3.2z"/>
-  </g>
-  <g id="tab-audio-white-hover" class="icon white hover">
-    <path class="outline" d="M9,2v12l-3.8-3H4c-1.1,0-2-0.9-2-2V7c0-1.1,0.9-2,2-2h1.2L9,2 M11.4,3.2C13.5,3.8,15,5.7,15,8 s-1.5,4.2-3.5,4.7l-0.4-0.9c1.7-0.4,2.9-2,2.9-3.8s-1.2-3.4-3-3.9L11.4,3.2 M10.7,5.1C12,5.4,13,6.6,13,8s-1,2.6-2.2,2.9L10.4,10 C11.3,9.8,12,9,12,8s-0.7-1.8-1.6-2L10.7,5.1 M10,7c0.6,0,1,0.4,1,1s-0.4,1-1,1V7 M10-0.1L8.4,1.2L4.9,4H4C2.3,4,1,5.3,1,7v2 c0,1.7,1.3,3,3,3h0.9l3.5,2.8l1.6,1.3V14v-2.2l0.2,0.4l0.4,0.9l0.3,0.8l0.8-0.2C14.2,13,16,10.7,16,8c0-2.7-1.7-5-4.3-5.8L10.8,2 l-0.4,0.8l-0.4,0.9L10,3.9V2V-0.1L10-0.1z"/>
-    <path d="M4,5C2.9,5,2,5.9,2,7v2c0,1.1,0.9,2,2,2h1.2L9,14V2L5.2,5H4z M11,8c0-0.6-0.4-1-1-1v2C10.6,9,11,8.6,11,8z M13,8 c0-1.4-1-2.6-2.3-2.9L10.4,6C11.3,6.2,12,7,12,8s-0.7,1.8-1.6,2l0.4,0.9C12,10.6,13,9.4,13,8z M11.4,3.2l-0.4,0.9 C12.8,4.6,14,6.2,14,8s-1.2,3.4-2.9,3.8l0.4,0.9C13.5,12.2,15,10.3,15,8S13.5,3.8,11.4,3.2z"/>
-  </g>
-  <g id="tab-audio-white-pressed" class="icon white pressed">
-    <path class="outline" d="M9,2v12l-3.8-3H4c-1.1,0-2-0.9-2-2V7c0-1.1,0.9-2,2-2h1.2L9,2 M11.4,3.2C13.5,3.8,15,5.7,15,8 s-1.5,4.2-3.5,4.7l-0.4-0.9c1.7-0.4,2.9-2,2.9-3.8s-1.2-3.4-3-3.9L11.4,3.2 M10.7,5.1C12,5.4,13,6.6,13,8s-1,2.6-2.2,2.9L10.4,10 C11.3,9.8,12,9,12,8s-0.7-1.8-1.6-2L10.7,5.1 M10,7c0.6,0,1,0.4,1,1s-0.4,1-1,1V7 M10-0.1L8.4,1.2L4.9,4H4C2.3,4,1,5.3,1,7v2 c0,1.7,1.3,3,3,3h0.9l3.5,2.8l1.6,1.3V14v-2.2l0.2,0.4l0.4,0.9l0.3,0.8l0.8-0.2C14.2,13,16,10.7,16,8c0-2.7-1.7-5-4.3-5.8L10.8,2 l-0.4,0.8l-0.4,0.9L10,3.9V2V-0.1L10-0.1z"/>
-    <path d="M4,5C2.9,5,2,5.9,2,7v2c0,1.1,0.9,2,2,2h1.2L9,14V2L5.2,5H4z M11,8c0-0.6-0.4-1-1-1v2C10.6,9,11,8.6,11,8z M13,8 c0-1.4-1-2.6-2.3-2.9L10.4,6C11.3,6.2,12,7,12,8s-0.7,1.8-1.6,2l0.4,0.9C12,10.6,13,9.4,13,8z M11.4,3.2l-0.4,0.9 C12.8,4.6,14,6.2,14,8s-1.2,3.4-2.9,3.8l0.4,0.9C13.5,12.2,15,10.3,15,8S13.5,3.8,11.4,3.2z"/>
-  </g>
-
-  <g id="tab-audio-muted-white" class="icon muted white">
-    <path class="outline" d="M9,2v4.3l3.5-2.9l0.9,1.2l-11,9l-1-1.2l1.9-1.5C2.6,10.6,2,9.9,2,9V7c0-1.1,0.9-2,2-2h1.2L9,2 M9,10v4l-2.5-2L9,10 M10-0.1 L8.4,1.2L4.9,4H4C2.3,4,1,5.3,1,7v2c0,0.7,0.3,1.4,0.7,2l-0.8,0.7l-0.8,0.6l0.6,0.8l1,1.2L2.3,15l0.8-0.6l2.3-1.9l0.4,0.3l2.5,2 l1.6,1.3V14v-4V8.7l4.1-3.4l0.8-0.6l-0.6-0.8l-0.9-1.2L12.7,2l-0.8,0.6L10,4.2V2V-0.1L10-0.1z"/>
-    <path d="M12.5,3.4L9,6.3V2L5.2,5H4C2.9,5,2,5.9,2,7v2c0,0.9,0.6,1.6,1.4,1.9l-1.9,1.5l1,1.2l11-9L12.5,3.4z M9,14v-4l-2.5,2L9,14z"/>
-  </g>
-  <g id="tab-audio-muted-white-hover" class="icon muted white hover">
-    <path class="outline" d="M9,2v4.3l3.5-2.9l0.9,1.2l-11,9l-1-1.2l1.9-1.5C2.6,10.6,2,9.9,2,9V7c0-1.1,0.9-2,2-2h1.2L9,2 M9,10v4l-2.5-2L9,10 M10-0.1 L8.4,1.2L4.9,4H4C2.3,4,1,5.3,1,7v2c0,0.7,0.3,1.4,0.7,2l-0.8,0.7l-0.8,0.6l0.6,0.8l1,1.2L2.3,15l0.8-0.6l2.3-1.9l0.4,0.3l2.5,2 l1.6,1.3V14v-4V8.7l4.1-3.4l0.8-0.6l-0.6-0.8l-0.9-1.2L12.7,2l-0.8,0.6L10,4.2V2V-0.1L10-0.1z"/>
-    <path d="M12.5,3.4L9,6.3V2L5.2,5H4C2.9,5,2,5.9,2,7v2c0,0.9,0.6,1.6,1.4,1.9l-1.9,1.5l1,1.2l11-9L12.5,3.4z M9,14v-4l-2.5,2L9,14z"/>
-  </g>
-  <g id="tab-audio-muted-white-pressed" class="icon muted white pressed">
-    <path class="outline" d="M9,2v4.3l3.5-2.9l0.9,1.2l-11,9l-1-1.2l1.9-1.5C2.6,10.6,2,9.9,2,9V7c0-1.1,0.9-2,2-2h1.2L9,2 M9,10v4l-2.5-2L9,10 M10-0.1 L8.4,1.2L4.9,4H4C2.3,4,1,5.3,1,7v2c0,0.7,0.3,1.4,0.7,2l-0.8,0.7l-0.8,0.6l0.6,0.8l1,1.2L2.3,15l0.8-0.6l2.3-1.9l0.4,0.3l2.5,2 l1.6,1.3V14v-4V8.7l4.1-3.4l0.8-0.6l-0.6-0.8l-0.9-1.2L12.7,2l-0.8,0.6L10,4.2V2V-0.1L10-0.1z"/>
-    <path d="M12.5,3.4L9,6.3V2L5.2,5H4C2.9,5,2,5.9,2,7v2c0,0.9,0.6,1.6,1.4,1.9l-1.9,1.5l1,1.2l11-9L12.5,3.4z M9,14v-4l-2.5,2L9,14z"/>
-  </g>
-
-  <path id="tab-audio-menu" class="icon menu" d="M4,5C2.9,5,2,5.9,2,7v2c0,1.1,0.9,2,2,2h1.2L9,14V2L5.2,5H4z M11,8c0-0.6-0.4-1-1-1v2C10.6,9,11,8.6,11,8z M13,8 c0-1.4-1-2.6-2.3-2.9L10.4,6C11.3,6.2,12,7,12,8s-0.7,1.8-1.6,2l0.4,0.9C12,10.6,13,9.4,13,8z M11.4,3.2l-0.4,0.9 C12.8,4.6,14,6.2,14,8s-1.2,3.4-2.9,3.8l0.4,0.9C13.5,12.2,15,10.3,15,8S13.5,3.8,11.4,3.2z"/>
-  <path id="tab-audio-menu-muted" class="icon menu" d="M12.5,3.4L9,6.3V2L5.2,5H4C2.9,5,2,5.9,2,7v2c0,0.9,0.6,1.6,1.4,1.9l-1.9,1.5l1,1.2l11-9L12.5,3.4z M9,14v-4l-2.5,2L9,14z"/>
-  <path id="tab-audio-menu-hover" class="icon menu hover" d="M4,5C2.9,5,2,5.9,2,7v2c0,1.1,0.9,2,2,2h1.2L9,14V2L5.2,5H4z M11,8c0-0.6-0.4-1-1-1v2C10.6,9,11,8.6,11,8z M13,8 c0-1.4-1-2.6-2.3-2.9L10.4,6C11.3,6.2,12,7,12,8s-0.7,1.8-1.6,2l0.4,0.9C12,10.6,13,9.4,13,8z M11.4,3.2l-0.4,0.9 C12.8,4.6,14,6.2,14,8s-1.2,3.4-2.9,3.8l0.4,0.9C13.5,12.2,15,10.3,15,8S13.5,3.8,11.4,3.2z"/>
-  <path id="tab-audio-menu-muted-hover" class="icon menu hover" d="M12.5,3.4L9,6.3V2L5.2,5H4C2.9,5,2,5.9,2,7v2c0,0.9,0.6,1.6,1.4,1.9l-1.9,1.5l1,1.2l11-9L12.5,3.4z M9,14v-4l-2.5,2L9,14z"/>
-
+  <path id="tab-audio-muted" d="M12.5,3.4L9,6.3V2L5.2,5H4C2.9,5,2,5.9,2,7v2c0,0.9,0.6,1.6,1.4,1.9l-1.9,1.5l1,1.2l11-9L12.5,3.4z M9,14v-4l-2.5,2L9,14z"/>
 </svg>
--- a/browser/themes/shared/tabs.inc.css
+++ b/browser/themes/shared/tabs.inc.css
@@ -156,21 +156,23 @@
 .tab-icon-overlay[soundplaying] {
   list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio");
 }
 
 .tab-icon-overlay[muted]:not([crashed]) {
   list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-muted");
 }
 
-#TabsToolbar[brighttext] .tab-icon-overlay[soundplaying]:not(:hover) {
+#TabsToolbar[brighttext] .tab-icon-overlay[soundplaying]:not([visuallyselected]):not(:hover),
+.tab-icon-overlay[soundplaying][visuallyselected]:-moz-lwtheme-brighttext:not(:hover) {
   list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-white");
 }
 
-#TabsToolbar[brighttext] .tab-icon-overlay[muted]:not([crashed]):not(:hover) {
+#TabsToolbar[brighttext] .tab-icon-overlay[muted]:not([crashed]):not([visuallyselected]):not(:hover),
+.tab-icon-overlay[muted][visuallyselected]:-moz-lwtheme-brighttext:not(:hover) {
   list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-white-muted");
 }
 
 .tab-throbber[busy] {
   list-style-image: url("chrome://browser/skin/tabbrowser/connecting.png");
 }
 
 .tab-throbber[progress] {
@@ -190,92 +192,40 @@
 
 .tab-icon-sound {
   margin-inline-start: 4px;
   width: 16px;
   height: 16px;
   padding: 0;
 }
 
-.tab-icon-sound[soundplaying] {
-  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-backgroundTab");
-}
-
-.tab-icon-sound[soundplaying]:hover {
-  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-backgroundTab-hover");
-}
-
-.tab-icon-sound[soundplaying]:hover:active {
-  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-backgroundTab-pressed");
+.tab-icon-sound[soundplaying],
+.tab-icon-sound[muted] {
+  list-style-image: url(chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio);
+  filter: url(chrome://browser/skin/filters.svg#fill);
+  fill: currentColor;
 }
 
 .tab-icon-sound[muted] {
-  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-backgroundTab-muted");
-}
-
-.tab-icon-sound[muted]:hover {
-  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-backgroundTab-muted-hover");
-}
-
-.tab-icon-sound[muted]:hover:active {
-  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-backgroundTab-muted-pressed");
+  list-style-image: url(chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted);
 }
 
-.tab-icon-sound:-moz-lwtheme,
-.tab-icon-sound[visuallyselected=true][soundplaying] {
-  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio");
-}
-
-.tab-icon-sound:hover:-moz-lwtheme,
-.tab-icon-sound[visuallyselected=true][soundplaying]:hover {
-  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-hover");
-}
-
-.tab-icon-sound:hover:active:-moz-lwtheme,
-.tab-icon-sound[visuallyselected=true][soundplaying]:hover:active {
-  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-pressed");
-}
-
-.tab-icon-sound[muted]:-moz-lwtheme,
-.tab-icon-sound[visuallyselected=true][muted] {
-  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted");
+.tab-icon-sound:-moz-lwtheme-darktext[soundplaying],
+.tab-icon-sound:-moz-lwtheme-darktext[muted] {
+  filter: url(chrome://browser/skin/filters.svg#fill) drop-shadow(1px 1px 1px white);
 }
 
-.tab-icon-sound[muted]:hover:-moz-lwtheme,
-.tab-icon-sound[visuallyselected=true][muted]:hover {
-  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted-hover");
-}
-
-.tab-icon-sound[muted]:hover:active:-moz-lwtheme,
-.tab-icon-sound[visuallyselected=true][muted]:hover:active {
-  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted-pressed");
-}
-
-#TabsToolbar[brighttext] .tab-icon-sound[soundplaying] {
-  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-white");
+.tab-icon-sound:-moz-lwtheme-brighttext[soundplaying],
+.tab-icon-sound:-moz-lwtheme-brighttext[muted] {
+  filter: url(chrome://browser/skin/filters.svg#fill) drop-shadow(1px 1px 1px black);
 }
 
-#TabsToolbar[brighttext] .tab-icon-sound[soundplaying]:hover {
-  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-white-hover");
-}
-
-#TabsToolbar[brighttext] .tab-icon-sound[soundplaying]:hover:active {
-  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-white-pressed");
-}
-
-#TabsToolbar[brighttext] .tab-icon-sound[muted] {
-  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted-white");
-}
-
-#TabsToolbar[brighttext] .tab-icon-sound[muted]:hover {
-  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted-white-hover");
-}
-
-#TabsToolbar[brighttext] .tab-icon-sound[muted]:hover:active {
-  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted-white-pressed");
+.tab-icon-sound[soundplaying]:not(:hover),
+.tab-icon-sound[muted]:not(:hover) {
+  opacity: .8;
 }
 
 .tab-background,
 .tabs-newtab-button {
   /* overlap the tab curves */
   margin-inline-end: -@tabCurveHalfWidth@;
   margin-inline-start: -@tabCurveHalfWidth@;
 }
@@ -557,8 +507,26 @@
   .tab-icon-image {
     list-style-image: url("chrome://mozapps/skin/places/defaultFavicon@2x.png");
   }
 
   .tab-throbber[progress] {
     list-style-image: url("chrome://global/skin/icons/loading@2x.png");
   }
 }
+
+/* All tabs menupopup */
+
+.alltabs-item[tabIsVisible] {
+  /* box-shadow instead of background-color to work around native styling */
+  box-shadow: inset -5px 0 ThreeDShadow;
+}
+
+.alltabs-endimage[soundplaying],
+.alltabs-endimage[muted] {
+  list-style-image: url(chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio);
+  filter: url(chrome://browser/skin/filters.svg#fill);
+  fill: currentColor;
+}
+
+.alltabs-endimage[muted] {
+  list-style-image: url(chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted);
+}
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -2212,37 +2212,16 @@ html|span.ac-emphasize-text-url {
 }
 
 @media (min-resolution: 1.1dppx) {
   .alltabs-item[busy] > .menu-iconic-left > .menu-iconic-icon {
     list-style-image: url("chrome://global/skin/icons/loading@2x.png");
   }
 }
 
-.alltabs-item[tabIsVisible] {
-  /* box-shadow instead of background-color to work around native styling */
-  box-shadow: inset -5px 0 ThreeDShadow;
-}
-
-.alltabs-endimage[muted] {
-  list-style-image: url(chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-menu-muted);
-}
-
-.alltabs-endimage[soundplaying] {
-  list-style-image: url(chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-menu);
-}
-
-menuitem:hover > hbox > .alltabs-endimage[muted] {
-  list-style-image: url(chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-menu-muted-hover);
-}
-
-menuitem:hover > hbox > .alltabs-endimage[soundplaying] {
-  list-style-image: url(chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-menu-hover);
-}
-
 toolbarbutton.chevron {
   list-style-image: url("chrome://global/skin/toolbar/chevron.gif") !important;
 }
 
 toolbar[brighttext] toolbarbutton.chevron {
   list-style-image: url("chrome://global/skin/toolbar/chevron-inverted.png") !important;
 }
 
--- a/build/autoconf/ffi.m4
+++ b/build/autoconf/ffi.m4
@@ -1,12 +1,32 @@
 dnl This Source Code Form is subject to the terms of the Mozilla Public
 dnl License, v. 2.0. If a copy of the MPL was not distributed with this
 dnl file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+AC_DEFUN([MOZ_CONFIG_FFI], [
+
+MOZ_ARG_ENABLE_BOOL(system-ffi,
+[  --enable-system-ffi       Use system libffi (located with pkgconfig)],
+    MOZ_SYSTEM_FFI=1 )
+
+if test -n "$MOZ_SYSTEM_FFI"; then
+    # Vanilla libffi 3.0.9 needs a few patches from upcoming version 3.0.10
+    # for non-GCC compilers.
+    if test -z "$GNU_CC"; then
+        PKG_CHECK_MODULES(MOZ_FFI, libffi > 3.0.9)
+    else
+        PKG_CHECK_MODULES(MOZ_FFI, libffi >= 3.0.9)
+    fi
+fi
+
+AC_SUBST(MOZ_SYSTEM_FFI)
+
+])
+
 AC_DEFUN([MOZ_SUBCONFIGURE_FFI], [
 if test "$MOZ_BUILD_APP" != js -o -n "$JS_STANDALONE"; then
 
   if test "$BUILD_CTYPES" -a -z "$MOZ_SYSTEM_FFI"; then
     # Run the libffi 'configure' script.
     ac_configure_args="--disable-shared --enable-static --disable-raw-api"
     if test "$MOZ_DEBUG"; then
       ac_configure_args="$ac_configure_args --enable-debug"
deleted file mode 100644
--- a/build/moz.configure/ffi.configure
+++ /dev/null
@@ -1,38 +0,0 @@
-# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-js_option('--with-system-ffi',
-          help='Use system libffi (located with pkgconfig)')
-
-use_system_ffi = depends_if('--with-system-ffi')(lambda _: True)
-
-system_ffi = pkg_check_modules('MOZ_FFI', 'libffi > 3.0.9',
-                               when=use_system_ffi)
-
-set_config('MOZ_SYSTEM_FFI', system_ffi)
-add_old_configure_assignment('MOZ_SYSTEM_FFI', system_ffi)
-
-@depends(building_js, '--help')
-def ctypes_default(building_js, _):
-    return not building_js
-
-js_option('--enable-ctypes', help='Enable js-ctypes',
-          default=ctypes_default)
-
-build_ctypes = depends_if('--enable-ctypes')(lambda _: True)
-
-set_config('BUILD_CTYPES', build_ctypes)
-set_define('BUILD_CTYPES', build_ctypes)
-add_old_configure_assignment('BUILD_CTYPES', build_ctypes)
-
-@depends(build_ctypes, building_js)
-def js_has_ctypes(ctypes, js):
-    if ctypes and js:
-        return True
-
-set_config('JS_HAS_CTYPES', js_has_ctypes)
-set_define('JS_HAS_CTYPES', js_has_ctypes)
-add_old_configure_assignment('JS_HAS_CTYPES', js_has_ctypes)
--- a/build/moz.configure/old.configure
+++ b/build/moz.configure/old.configure
@@ -164,16 +164,17 @@ def old_configure_options(*options):
     '--enable-b2g-camera',
     '--enable-b2g-ril',
     '--enable-bundled-fonts',
     '--enable-clang-plugin',
     '--enable-content-sandbox',
     '--enable-cookies',
     '--enable-cpp-rtti',
     '--enable-crashreporter',
+    '--enable-ctypes',
     '--enable-dbus',
     '--enable-debug-js-modules',
     '--enable-directshow',
     '--enable-dtrace',
     '--enable-dump-painting',
     '--enable-elf-hack',
     '--enable-extensions',
     '--enable-faststripe',
@@ -227,16 +228,17 @@ def old_configure_options(*options):
     '--enable-small-chunk-size',
     '--enable-startup-notification',
     '--enable-startupcache',
     '--enable-stdcxx-compat',
     '--enable-strip',
     '--enable-synth-pico',
     '--enable-system-cairo',
     '--enable-system-extension-dirs',
+    '--enable-system-ffi',
     '--enable-system-pixman',
     '--enable-system-sqlite',
     '--enable-tasktracer',
     '--enable-thread-sanitizer',
     '--enable-trace-logging',
     '--enable-tree-freetype',
     '--enable-ui-locale',
     '--enable-universalchardet',
--- a/devtools/client/framework/toolbox-init.js
+++ b/devtools/client/framework/toolbox-init.js
@@ -69,16 +69,16 @@ if (url.search.length > 1) {
     let toolbox = yield gDevTools.showToolbox(target, tool, Toolbox.HostType.CUSTOM, options);
 
     // Watch for toolbox.xul unload in order to cleanup things when we close
     // about:devtools-toolbox tabs
     function onUnload() {
       window.removeEventListener("unload", onUnload);
       toolbox.destroy();
     }
-    window.addEventListener("unload", onUnload, true);
+    window.addEventListener("unload", onUnload);
     toolbox.on("destroy", function () {
       window.removeEventListener("unload", onUnload);
     });
   }).catch(error => {
     console.error("Exception while loading the toolbox", error);
   });
 }
--- a/devtools/client/netmonitor/test/browser.ini
+++ b/devtools/client/netmonitor/test/browser.ini
@@ -4,31 +4,33 @@ subsuite = devtools
 support-files =
   dropmarker.svg
   head.js
   html_cause-test-page.html
   html_content-type-test-page.html
   html_content-type-without-cache-test-page.html
   html_cors-test-page.html
   html_custom-get-page.html
-  html_single-get-page.html
   html_cyrillic-test-page.html
+  html_frame-test-page.html
+  html_frame-subdocument.html
   html_filter-test-page.html
   html_infinite-get-page.html
   html_json-custom-mime-test-page.html
   html_json-long-test-page.html
   html_json-malformed-test-page.html
   html_json-text-mime-test-page.html
   html_jsonp-test-page.html
   html_navigate-test-page.html
   html_params-test-page.html
   html_post-data-test-page.html
   html_post-raw-test-page.html
   html_post-raw-with-headers-test-page.html
   html_simple-test-page.html
+  html_single-get-page.html
   html_send-beacon.html
   html_sorting-test-page.html
   html_statistics-test-page.html
   html_status-codes-test-page.html
   html_api-calls-test-page.html
   html_copy-as-curl.html
   html_curl-utils.html
   sjs_content-type-test-server.sjs
@@ -78,16 +80,17 @@ subsuite = clipboard
 subsuite = clipboard
 [browser_net_copy_as_curl.js]
 subsuite = clipboard
 [browser_net_cors_requests.js]
 [browser_net_cyrillic-01.js]
 [browser_net_cyrillic-02.js]
 [browser_net_details-no-duplicated-content.js]
 skip-if = (os == 'linux' && e10s && debug) # Bug 1242204
+[browser_net_frame.js]
 [browser_net_filter-01.js]
 [browser_net_filter-02.js]
 [browser_net_filter-03.js]
 [browser_net_filter-04.js]
 [browser_net_footer-summary.js]
 [browser_net_html-preview.js]
 [browser_net_icon-preview.js]
 [browser_net_image-tooltip.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/test/browser_net_frame.js
@@ -0,0 +1,218 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Tests for all expected requests when an iframe is loading a subdocument.
+ */
+
+const TOP_FILE_NAME = "html_frame-test-page.html";
+const SUB_FILE_NAME = "html_frame-subdocument.html";
+const TOP_URL = EXAMPLE_URL + TOP_FILE_NAME;
+const SUB_URL = EXAMPLE_URL + SUB_FILE_NAME;
+
+const EXPECTED_REQUESTS_TOP = [
+  {
+    method: "GET",
+    url: TOP_URL,
+    causeType: "document",
+    causeUri: "",
+    stack: true
+  },
+  {
+    method: "GET",
+    url: EXAMPLE_URL + "stylesheet_request",
+    causeType: "stylesheet",
+    causeUri: TOP_URL,
+    stack: false
+  },
+  {
+    method: "GET",
+    url: EXAMPLE_URL + "img_request",
+    causeType: "img",
+    causeUri: TOP_URL,
+    stack: false
+  },
+  {
+    method: "GET",
+    url: EXAMPLE_URL + "xhr_request",
+    causeType: "xhr",
+    causeUri: TOP_URL,
+    stack: [{ fn: "performXhrRequest", file: TOP_FILE_NAME, line: 23 }]
+  },
+  {
+    method: "GET",
+    url: EXAMPLE_URL + "fetch_request",
+    causeType: "fetch",
+    causeUri: TOP_URL,
+    stack: [{ fn: "performFetchRequest", file: TOP_FILE_NAME, line: 27 }]
+  },
+  {
+    method: "GET",
+    url: EXAMPLE_URL + "promise_fetch_request",
+    causeType: "fetch",
+    causeUri: TOP_URL,
+    stack: [
+      { fn: "performPromiseFetchRequest", file: TOP_FILE_NAME, line: 39 },
+      { fn: null, file: TOP_FILE_NAME, line: 38, asyncCause: "promise callback" },
+    ]
+  },
+  {
+    method: "GET",
+    url: EXAMPLE_URL + "timeout_fetch_request",
+    causeType: "fetch",
+    causeUri: TOP_URL,
+    stack: [
+      { fn: "performTimeoutFetchRequest", file: TOP_FILE_NAME, line: 41 },
+      { fn: "performPromiseFetchRequest", file: TOP_FILE_NAME, line: 40,
+        asyncCause: "setTimeout handler" },
+    ]
+  },
+  {
+    method: "POST",
+    url: EXAMPLE_URL + "beacon_request",
+    causeType: "beacon",
+    causeUri: TOP_URL,
+    stack: [{ fn: "performBeaconRequest", file: TOP_FILE_NAME, line: 31 }]
+  },
+];
+
+const EXPECTED_REQUESTS_SUB = [
+  {
+    method: "GET",
+    url: SUB_URL,
+    causeType: "subdocument",
+    causeUri: TOP_URL,
+    stack: false
+  },
+  {
+    method: "GET",
+    url: EXAMPLE_URL + "stylesheet_request",
+    causeType: "stylesheet",
+    causeUri: SUB_URL,
+    stack: false
+  },
+  {
+    method: "GET",
+    url: EXAMPLE_URL + "img_request",
+    causeType: "img",
+    causeUri: SUB_URL,
+    stack: false
+  },
+  {
+    method: "GET",
+    url: EXAMPLE_URL + "xhr_request",
+    causeType: "xhr",
+    causeUri: SUB_URL,
+    stack: [{ fn: "performXhrRequest", file: SUB_FILE_NAME, line: 22 }]
+  },
+  {
+    method: "GET",
+    url: EXAMPLE_URL + "fetch_request",
+    causeType: "fetch",
+    causeUri: SUB_URL,
+    stack: [{ fn: "performFetchRequest", file: SUB_FILE_NAME, line: 26 }]
+  },
+  {
+    method: "GET",
+    url: EXAMPLE_URL + "promise_fetch_request",
+    causeType: "fetch",
+    causeUri: SUB_URL,
+    stack: [
+      { fn: "performPromiseFetchRequest", file: SUB_FILE_NAME, line: 38 },
+      { fn: null, file: SUB_FILE_NAME, line: 37, asyncCause: "promise callback" },
+    ]
+  },
+  {
+    method: "GET",
+    url: EXAMPLE_URL + "timeout_fetch_request",
+    causeType: "fetch",
+    causeUri: SUB_URL,
+    stack: [
+      { fn: "performTimeoutFetchRequest", file: SUB_FILE_NAME, line: 40 },
+      { fn: "performPromiseFetchRequest", file: SUB_FILE_NAME, line: 39,
+        asyncCause: "setTimeout handler" },
+    ]
+  },
+  {
+    method: "POST",
+    url: EXAMPLE_URL + "beacon_request",
+    causeType: "beacon",
+    causeUri: SUB_URL,
+    stack: [{ fn: "performBeaconRequest", file: SUB_FILE_NAME, line: 30 }]
+  },
+];
+
+const REQUEST_COUNT = EXPECTED_REQUESTS_TOP.length + EXPECTED_REQUESTS_SUB.length;
+
+add_task(function* () {
+  // the initNetMonitor function clears the network request list after the
+  // page is loaded. That's why we first load a bogus page from SIMPLE_URL,
+  // and only then load the real thing from TOP_URL - we want to catch
+  // all the requests the page is making, not only the XHRs.
+  // We can't use about:blank here, because initNetMonitor checks that the
+  // page has actually made at least one request.
+  let [ tab, , monitor ] = yield initNetMonitor(SIMPLE_URL);
+  let { NetMonitorView } = monitor.panelWin;
+  let { RequestsMenu } = NetMonitorView;
+  RequestsMenu.lazyUpdate = false;
+
+  tab.linkedBrowser.loadURI(TOP_URL, null, null);
+
+  yield waitForNetworkEvents(monitor, REQUEST_COUNT);
+
+  is(RequestsMenu.itemCount, REQUEST_COUNT,
+    "All the page events should be recorded.");
+
+  // While there is a defined order for requests in each document separately, the requests
+  // from different documents may interleave in various ways that change per test run, so
+  // there is not a single order when considering all the requests together.
+  let currentTop = 0;
+  let currentSub = 0;
+  for (let i = 0; i < REQUEST_COUNT; i++) {
+    let requestItem = RequestsMenu.getItemAtIndex(i);
+
+    let itemUrl = requestItem.attachment.url;
+    let itemCauseUri = requestItem.target.querySelector(".requests-menu-cause-label")
+                                         .getAttribute("tooltiptext");
+    let spec;
+    if (itemUrl == SUB_URL || itemCauseUri == SUB_URL) {
+      spec = EXPECTED_REQUESTS_SUB[currentSub++];
+    } else {
+      spec = EXPECTED_REQUESTS_TOP[currentTop++];
+    }
+    let { method, url, causeType, causeUri, stack } = spec;
+
+    verifyRequestItemTarget(requestItem,
+      method, url, { cause: { type: causeType, loadingDocumentUri: causeUri } }
+    );
+
+    let { stacktrace } = requestItem.attachment.cause;
+    let stackLen = stacktrace ? stacktrace.length : 0;
+
+    if (stack) {
+      ok(stacktrace, `Request #${i} has a stacktrace`);
+      ok(stackLen > 0,
+        `Request #${i} (${causeType}) has a stacktrace with ${stackLen} items`);
+
+      // if "stack" is array, check the details about the top stack frames
+      if (Array.isArray(stack)) {
+        stack.forEach((frame, j) => {
+          is(stacktrace[j].functionName, frame.fn,
+            `Request #${i} has the correct function on JS stack frame #${j}`);
+          is(stacktrace[j].filename.split("/").pop(), frame.file,
+            `Request #${i} has the correct file on JS stack frame #${j}`);
+          is(stacktrace[j].lineNumber, frame.line,
+            `Request #${i} has the correct line number on JS stack frame #${j}`);
+          is(stacktrace[j].asyncCause, frame.asyncCause,
+            `Request #${i} has the correct async cause on JS stack frame #${j}`);
+        });
+      }
+    } else {
+      is(stackLen, 0, `Request #${i} (${causeType}) has an empty stacktrace`);
+    }
+  }
+
+  yield teardown(monitor);
+});
--- a/devtools/client/netmonitor/test/head.js
+++ b/devtools/client/netmonitor/test/head.js
@@ -113,27 +113,28 @@ function initNetMonitor(aUrl, aWindow, a
     info("Net tab added successfully: " + aUrl);
 
     let target = TargetFactory.forTab(tab);
 
     yield target.makeRemote();
     info("Target remoted.");
 
     if (!aEnableCache) {
+      info("Disabling cache and reloading page.");
       yield toggleCache(target, true);
       info("Cache disabled when the current and all future toolboxes are open.");
       // Remove any requests generated by the reload while toggling the cache to
       // avoid interfering with the test.
       isnot([...target.activeConsole.getNetworkEvents()].length, 0,
          "Request to reconfigure the tab was recorded.");
       target.activeConsole.clearNetworkRequests();
     }
 
     let toolbox = yield gDevTools.showToolbox(target, "netmonitor");
-    info("Netork monitor pane shown successfully.");
+    info("Network monitor pane shown successfully.");
 
     let debuggee = tab.linkedBrowser.contentWindow.wrappedJSObject;
     let monitor = toolbox.getCurrentPanel();
     return [tab, debuggee, monitor];
   });
 }
 
 function restartNetMonitor(aMonitor, aNewUrl) {
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/test/html_frame-subdocument.html
@@ -0,0 +1,48 @@
+<!-- Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!doctype html>
+
+<html>
+  <head>
+    <meta charset="utf-8"/>
+    <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
+    <meta http-equiv="Pragma" content="no-cache" />
+    <meta http-equiv="Expires" content="0" />
+    <title>Network Monitor test page</title>
+    <link rel="stylesheet" type="text/css" href="stylesheet_request" />
+  </head>
+
+  <body>
+    <p>Request frame test</p>
+    <img src="img_request" />
+    <script type="text/javascript">
+      function performXhrRequest() {
+        var xhr = new XMLHttpRequest();
+        xhr.open("GET", "xhr_request", true);
+        xhr.send();
+      }
+
+      function performFetchRequest() {
+        fetch("fetch_request");
+      }
+
+      function performBeaconRequest() {
+        navigator.sendBeacon("beacon_request");
+      }
+
+      performXhrRequest();
+      performFetchRequest();
+
+      // Perform some requests with async stacks
+      Promise.resolve().then(function performPromiseFetchRequest() {
+        fetch("promise_fetch_request");
+        setTimeout(function performTimeoutFetchRequest() {
+          fetch("timeout_fetch_request");
+
+          // Finally, send a beacon request
+          performBeaconRequest();
+        }, 0);
+      });
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/test/html_frame-test-page.html
@@ -0,0 +1,49 @@
+<!-- Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!doctype html>
+
+<html>
+  <head>
+    <meta charset="utf-8"/>
+    <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
+    <meta http-equiv="Pragma" content="no-cache" />
+    <meta http-equiv="Expires" content="0" />
+    <title>Network Monitor test page</title>
+    <link rel="stylesheet" type="text/css" href="stylesheet_request" />
+  </head>
+
+  <body>
+    <p>Request frame test</p>
+    <img src="img_request" />
+    <iframe src="html_frame-subdocument.html"></iframe>
+    <script type="text/javascript">
+      function performXhrRequest() {
+        var xhr = new XMLHttpRequest();
+        xhr.open("GET", "xhr_request", true);
+        xhr.send();
+      }
+
+      function performFetchRequest() {
+        fetch("fetch_request");
+      }
+
+      function performBeaconRequest() {
+        navigator.sendBeacon("beacon_request");
+      }
+
+      performXhrRequest();
+      performFetchRequest();
+
+      // Perform some requests with async stacks
+      Promise.resolve().then(function performPromiseFetchRequest() {
+        fetch("promise_fetch_request");
+        setTimeout(function performTimeoutFetchRequest() {
+          fetch("timeout_fetch_request");
+
+          // Finally, send a beacon request
+          performBeaconRequest();
+        }, 0);
+      });
+    </script>
+  </body>
+</html>
--- a/devtools/client/performance/test/unit/test_marker-blueprint.js
+++ b/devtools/client/performance/test/unit/test_marker-blueprint.js
@@ -13,17 +13,17 @@ add_task(function () {
   let { TIMELINE_BLUEPRINT } = require("devtools/client/performance/modules/markers");
 
   ok(TIMELINE_BLUEPRINT,
     "A timeline blueprint should be available.");
 
   ok(Object.keys(TIMELINE_BLUEPRINT).length,
     "The timeline blueprint has at least one entry.");
 
-  for (let [, value] of Iterator(TIMELINE_BLUEPRINT)) {
+  for (let value of Object.values(TIMELINE_BLUEPRINT)) {
     ok("group" in value,
       "Each entry in the timeline blueprint contains a `group` key.");
     ok("colorName" in value,
       "Each entry in the timeline blueprint contains a `colorName` key.");
     ok("label" in value,
       "Each entry in the timeline blueprint contains a `label` key.");
   }
 });
--- a/devtools/client/performance/views/details.js
+++ b/devtools/client/performance/views/details.js
@@ -1,15 +1,15 @@
 /* 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/. */
 /* import-globals-from ../performance-controller.js */
 /* import-globals-from ../performance-view.js */
 /* globals WaterfallView, JsCallTreeView, JsFlameGraphView, MemoryCallTreeView,
-           MemoryFlameGraphView, Iterator */
+           MemoryFlameGraphView */
 "use strict";
 
 /**
  * Details view containing call trees, flamegraphs and markers waterfall.
  * Manages subviews and toggles visibility between them.
  */
 var DetailsView = {
   /**
@@ -70,17 +70,17 @@ var DetailsView = {
   /**
    * Unbinds events, destroys subviews.
    */
   destroy: Task.async(function* () {
     for (let button of $$("toolbarbutton[data-view]", this.toolbar)) {
       button.removeEventListener("command", this._onViewToggle);
     }
 
-    for (let [, component] of Iterator(this.components)) {
+    for (let component of Object.values(this.components)) {
       component.initialized && (yield component.view.destroy());
     }
 
     PerformanceController.off(EVENTS.RECORDING_STATE_CHANGE,
                               this._onRecordingStoppedOrSelected);
     PerformanceController.off(EVENTS.RECORDING_SELECTED,
                               this._onRecordingStoppedOrSelected);
     PerformanceController.off(EVENTS.PREF_CHANGED, this.setAvailableViews);
@@ -92,17 +92,17 @@ var DetailsView = {
    * if currently selected. Called when a preference changes in
    * `devtools.performance.ui.`.
    */
   setAvailableViews: Task.async(function* () {
     let recording = PerformanceController.getCurrentRecording();
     let isCompleted = recording && recording.isCompleted();
     let invalidCurrentView = false;
 
-    for (let [name, { view }] of Iterator(this.components)) {
+    for (let [name, { view }] of Object.entries(this.components)) {
       let isSupported = this._isViewSupported(name);
 
       $(`toolbarbutton[data-view=${name}]`).hidden = !isSupported;
 
       // If the view is currently selected and not supported, go back to the
       // default view.
       if (!isSupported && this.isViewSelected(view)) {
         invalidCurrentView = true;
@@ -199,17 +199,17 @@ var DetailsView = {
     // no views are selected (even though there's a selected panel)
     if (!this._initialized) {
       return false;
     }
 
     let selectedPanel = this.el.selectedPanel;
     let selectedId = selectedPanel.id;
 
-    for (let [, { id, view }] of Iterator(this.components)) {
+    for (let { id, view } of Object.values(this.components)) {
       if (id == selectedId && view == viewObject) {
         return true;
       }
     }
 
     return false;
   },
 
--- a/devtools/client/performance/views/overview.js
+++ b/devtools/client/performance/views/overview.js
@@ -1,14 +1,13 @@
 /* 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/. */
 /* import-globals-from ../performance-controller.js */
 /* import-globals-from ../performance-view.js */
-/* globals Iterator */
 "use strict";
 
 // No sense updating the overview more often than receiving data from the
 // backend. Make sure this isn't lower than DEFAULT_TIMELINE_DATA_PULL_TIMEOUT
 // in devtools/server/actors/timeline.js
 
 // The following units are in milliseconds.
 const OVERVIEW_UPDATE_INTERVAL = 200;
@@ -314,17 +313,17 @@ var OverviewView = {
           graph.refresh({ force: true });
         }
         break;
       }
     }
   }),
 
   _setGraphVisibilityFromRecordingFeatures: function (recording) {
-    for (let [graphName, requirements] of Iterator(GRAPH_REQUIREMENTS)) {
+    for (let [graphName, requirements] of Object.entries(GRAPH_REQUIREMENTS)) {
       this.graphs.enable(graphName,
                          PerformanceController.isFeatureSupported(requirements.features));
     }
   },
 
   /**
    * Fetch the multiprocess status and if e10s is not currently on, disable
    * realtime rendering.
--- a/devtools/client/performance/views/toolbar.js
+++ b/devtools/client/performance/views/toolbar.js
@@ -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/. */
 /* import-globals-from ../performance-controller.js */
 /* import-globals-from ../performance-view.js */
-/* globals document, Iterator */
+/* globals document */
 "use strict";
 
 /**
  * View handler for toolbar events (mostly option toggling and triggering)
  */
 var ToolbarView = {
   /**
    * Sets up the view with event binding.
@@ -54,17 +54,17 @@ var ToolbarView = {
     this.optionsView.off("pref-changed", this._onPrefChanged);
     this.optionsView.destroy();
   },
 
   /**
    * Creates the timeline markers filter popup.
    */
   _buildMarkersFilterPopup: function () {
-    for (let [markerName, markerDetails] of Iterator(TIMELINE_BLUEPRINT)) {
+    for (let [markerName, markerDetails] of Object.entries(TIMELINE_BLUEPRINT)) {
       let menuitem = document.createElement("menuitem");
       menuitem.setAttribute("closemenu", "none");
       menuitem.setAttribute("type", "checkbox");
       menuitem.setAttribute("align", "center");
       menuitem.setAttribute("flex", "1");
       menuitem.setAttribute("label",
                             MarkerBlueprintUtils.getMarkerGenericName(markerName));
       menuitem.setAttribute("marker-type", markerName);
--- a/devtools/client/responsive.html/browser/swap.js
+++ b/devtools/client/responsive.html/browser/swap.js
@@ -30,16 +30,30 @@ const { tunnelToInnerBrowser } = require
  *        container page.  It is called with the outer browser that loaded the
  *        container page.
  */
 function swapToInnerBrowser({ tab, containerURL, getInnerBrowser }) {
   let gBrowser = tab.ownerDocument.defaultView.gBrowser;
   let innerBrowser;
   let tunnel;
 
+  // Dispatch a custom event each time the _viewport content_ is swapped from one browser
+  // to another.  DevTools server code uses this to follow the content if there is an
+  // active DevTools connection.  While browser.xml does dispatch it's own SwapDocShells
+  // event, this one is easier for DevTools to follow because it's only emitted once per
+  // transition, instead of twice like SwapDocShells.
+  let dispatchDevToolsBrowserSwap = (from, to) => {
+    let CustomEvent = tab.ownerDocument.defaultView.CustomEvent;
+    let event = new CustomEvent("DevTools:BrowserSwap", {
+      detail: to,
+      bubbles: true,
+    });
+    from.dispatchEvent(event);
+  };
+
   return {
 
     start: Task.async(function* () {
       // Freeze navigation temporarily to avoid "blinking" in the location bar.
       freezeNavigationState(tab);
 
       // 1. Create a temporary, hidden tab to load the tool UI.
       let containerTab = gBrowser.addTab(containerURL, {
@@ -67,16 +81,17 @@ function swapToInnerBrowser({ tab, conta
       if (innerBrowser.isRemoteBrowser != tab.linkedBrowser.isRemoteBrowser) {
         throw new Error("The inner browser's remoteness must match the " +
                         "original tab.");
       }
 
       // 4. Swap tab content from the regular browser tab to the browser within
       //    the viewport in the tool UI, preserving all state via
       //    `gBrowser._swapBrowserDocShells`.
+      dispatchDevToolsBrowserSwap(tab.linkedBrowser, innerBrowser);
       gBrowser._swapBrowserDocShells(tab, innerBrowser);
 
       // 5. Force the original browser tab to be non-remote since the tool UI
       //    must be loaded in the parent process, and we're about to swap the
       //    tool UI into this tab.
       gBrowser.updateBrowserRemoteness(tab.linkedBrowser, false);
 
       // 6. Swap the tool UI (with viewport showing the content) into the
@@ -110,27 +125,29 @@ function swapToInnerBrowser({ tab, conta
 
       // 3. Mark the content tab browser's docshell as active so the frame
       //    is created eagerly and will be ready to swap.
       contentBrowser.docShellIsActive = true;
 
       // 4. Swap tab content from the browser within the viewport in the tool UI
       //    to the regular browser tab, preserving all state via
       //    `gBrowser._swapBrowserDocShells`.
+      dispatchDevToolsBrowserSwap(innerBrowser, contentBrowser);
       gBrowser._swapBrowserDocShells(contentTab, innerBrowser);
       innerBrowser = null;
 
       // 5. Force the original browser tab to be remote since web content is
       //    loaded in the child process, and we're about to swap the content
       //    into this tab.
       gBrowser.updateBrowserRemoteness(tab.linkedBrowser, true);
 
       // 6. Swap the content into the original browser tab and close the
       //    temporary tab used to hold the content via
       //    `swapBrowsersAndCloseOther`.
+      dispatchDevToolsBrowserSwap(contentBrowser, tab.linkedBrowser);
       gBrowser.swapBrowsersAndCloseOther(tab, contentTab);
       gBrowser = null;
 
       // The focus manager seems to get a little dizzy after all this swapping.  If a
       // content element had been focused inside the viewport before stopping, it will
       // have lost focus.  Activate the frame to restore expected focus.
       tab.linkedBrowser.frameLoader.activateRemoteFrame();
     },
@@ -195,16 +212,25 @@ function addXULBrowserDecorations(browse
     Object.defineProperty(browser, "messageManager", {
       get() {
         return this.frameLoader.messageManager;
       },
       configurable: true,
       enumerable: true,
     });
   }
+  if (browser.outerWindowID == undefined) {
+    Object.defineProperty(browser, "outerWindowID", {
+      get() {
+        return browser._outerWindowID;
+      },
+      configurable: true,
+      enumerable: true,
+    });
+  }
 
   // It's not necessary for these to actually do anything.  These properties are
   // swapped between browsers in browser.xml's `swapDocShells`, and then their
   // `swapBrowser` methods are called, so we define them here for that to work
   // without errors.  During the swap process above, these will move from the
   // the new inner browser to the original tab's browser (step 4) and then to
   // the temporary container tab's browser (step 7), which is then closed.
   if (browser._remoteWebNavigationImpl == undefined) {
--- a/devtools/client/responsive.html/browser/tunnel.js
+++ b/devtools/client/responsive.html/browser/tunnel.js
@@ -514,24 +514,28 @@ MessageManagerTunnel.prototype = {
       this.outerParentMM.sendAsyncMessage(name, ...args);
       return;
     }
 
     debug(`${name} outer -> inner`);
     this.innerParentMM.sendAsyncMessage(name, ...args);
   },
 
-  receiveMessage({ name, data, objects, principal }) {
+  receiveMessage({ name, data, objects, principal, sync }) {
     if (!this._shouldTunnelInnerToOuter(name)) {
       debug(`Received unexpected message ${name}`);
-      return;
+      return undefined;
     }
 
-    debug(`${name} inner -> outer`);
+    debug(`${name} inner -> outer, sync: ${sync}`);
+    if (sync) {
+      return this.outerChildMM.sendSyncMessage(name, data, objects, principal);
+    }
     this.outerChildMM.sendAsyncMessage(name, data, objects, principal);
+    return undefined;
   },
 
   _shouldTunnelOuterToInner(name) {
     return this.OUTER_TO_INNER_MESSAGES.includes(name) ||
            this.OUTER_TO_INNER_MESSAGE_PREFIXES.some(prefix => name.startsWith(prefix));
   },
 
   _shouldTunnelInnerToOuter(name) {
--- a/devtools/client/responsive.html/test/browser/browser.ini
+++ b/devtools/client/responsive.html/test/browser/browser.ini
@@ -23,15 +23,15 @@ support-files =
 [browser_frame_script_active.js]
 [browser_menu_item_01.js]
 [browser_menu_item_02.js]
 [browser_mouse_resize.js]
 [browser_navigation.js]
 [browser_page_state.js]
 [browser_permission_doorhanger.js]
 [browser_resize_cmd.js]
-skip-if = true # GCLI target confused after swap, will fix in bug 1240912
 [browser_screenshot_button.js]
 [browser_shutdown_close_sync.js]
 [browser_toolbox_computed_view.js]
 [browser_toolbox_rule_view.js]
+[browser_toolbox_swap_browsers.js]
 [browser_touch_simulation.js]
 [browser_viewport_basics.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/responsive.html/test/browser/browser_toolbox_swap_browsers.js
@@ -0,0 +1,82 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Verify that toolbox remains open when opening and closing RDM.
+
+const TEST_URL = "http://example.com/";
+
+function getServerConnections(browser) {
+  ok(browser.isRemoteBrowser, "Content browser is remote");
+  return ContentTask.spawn(browser, {}, function* () {
+    const Cu = Components.utils;
+    const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
+    const { DebuggerServer } = require("devtools/server/main");
+    if (!DebuggerServer._connections) {
+      return 0;
+    }
+    return Object.getOwnPropertyNames(DebuggerServer._connections);
+  });
+}
+
+let checkServerConnectionCount = Task.async(function* (browser, expected) {
+  let conns = yield getServerConnections(browser);
+  is(conns.length || 0, expected, "Server connection count");
+});
+
+let checkToolbox = Task.async(function* (tab, location) {
+  let target = TargetFactory.forTab(tab);
+  ok(!!gDevTools.getToolbox(target), `Toolbox exists ${location}`);
+});
+
+add_task(function* () {
+  let tab = yield addTab(TEST_URL);
+
+  // Open toolbox outside RDM
+  {
+    // 0: No DevTools connections yet
+    yield checkServerConnectionCount(tab.linkedBrowser, 0);
+    let { toolbox } = yield openInspector();
+    // 2: One for each tab (starting tab plus the one we opened).  Only one truly needed,
+    //    but calling listTabs will create one for each tab.  `registerTestActor` calls
+    //    this, triggering the extra tab's actor to be made.
+    yield checkServerConnectionCount(tab.linkedBrowser, 2);
+    yield checkToolbox(tab, "outside RDM");
+    let { ui } = yield openRDM(tab);
+    // 3: RDM UI uses an extra connection
+    yield checkServerConnectionCount(ui.getViewportBrowser(), 3);
+    yield checkToolbox(tab, "after opening RDM");
+    yield closeRDM(tab);
+    // 2: RDM UI closed, back to one for each tab
+    yield checkServerConnectionCount(tab.linkedBrowser, 2);
+    yield checkToolbox(tab, tab.linkedBrowser, "after closing RDM");
+    yield toolbox.destroy();
+    // 0: All DevTools usage closed
+    yield checkServerConnectionCount(tab.linkedBrowser, 0);
+  }
+
+  // Open toolbox inside RDM
+  {
+    // 0: No DevTools connections yet
+    yield checkServerConnectionCount(tab.linkedBrowser, 0);
+    let { ui } = yield openRDM(tab);
+    // 1: RDM UI uses an extra connection
+    yield checkServerConnectionCount(ui.getViewportBrowser(), 1);
+    let { toolbox } = yield openInspector();
+    // 3: One for each tab (starting tab plus the one we opened).  Only one truly needed,
+    //    but calling listTabs will create one for each tab.  `registerTestActor` calls
+    //    this, triggering the extra tab's actor to be made.
+    yield checkServerConnectionCount(ui.getViewportBrowser(), 3);
+    yield checkToolbox(tab, ui.getViewportBrowser(), "inside RDM");
+    yield closeRDM(tab);
+    // 2: RDM UI closed, back to one for each tab
+    yield checkServerConnectionCount(tab.linkedBrowser, 2);
+    yield checkToolbox(tab, tab.linkedBrowser, "after closing RDM");
+    yield toolbox.destroy();
+    // 0: All DevTools usage closed
+    yield checkServerConnectionCount(tab.linkedBrowser, 0);
+  }
+
+  yield removeTab(tab);
+});
--- a/devtools/client/shared/AppCacheUtils.jsm
+++ b/devtools/client/shared/AppCacheUtils.jsm
@@ -108,17 +108,17 @@ AppCacheUtils.prototype = {
     for (let parsedUri of parsed.uris) {
       dupes[parsedUri.uri] = dupes[parsedUri.uri] || [];
       dupes[parsedUri.uri].push({
         line: parsedUri.line,
         section: parsedUri.section,
         original: parsedUri.original
       });
     }
-    for (let [uri, value] of Iterator(dupes)) {
+    for (let [uri, value] of Object.entries(dupes)) {
       if (value.length > 1) {
         this._addError(0, "duplicateURI", uri, JSON.stringify(value));
       }
     }
 
     // Loop through network entries making sure that fallback and cache don't
     // contain uris starting with the network uri.
     for (let neturi of parsed.uris) {
--- a/devtools/client/shared/test/browser_telemetry_button_eyedropper.js
+++ b/devtools/client/shared/test/browser_telemetry_button_eyedropper.js
@@ -28,17 +28,17 @@ function* testButton(toolbox, Telemetry)
   yield toolbox.getPanel("inspector").showEyeDropper();
 
   checkResults("_EYEDROPPER_", Telemetry);
 }
 
 function checkResults(histIdFocus, Telemetry) {
   let result = Telemetry.prototype.telemetryInfo;
 
-  for (let [histId, value] of Iterator(result)) {
+  for (let [histId, value] of Object.entries(result)) {
     if (histId.startsWith("DEVTOOLS_INSPECTOR_") ||
         !histId.includes(histIdFocus)) {
       // Inspector stats are tested in
       // browser_telemetry_toolboxtabs_{toolname}.js so we skip them here
       // because we only open the inspector once for this test.
       continue;
     }
 
--- a/devtools/client/shared/test/browser_telemetry_button_paintflashing.js
+++ b/devtools/client/shared/test/browser_telemetry_button_paintflashing.js
@@ -54,17 +54,17 @@ function* delayedClicks(toolbox, node, c
     // promise gets resolved once execution finishes and output is ready
     yield outputEvent.output.promise;
   }
 }
 
 function checkResults(histIdFocus, Telemetry) {
   let result = Telemetry.prototype.telemetryInfo;
 
-  for (let [histId, value] of Iterator(result)) {
+  for (let [histId, value] of Object.entries(result)) {
     if (histId.startsWith("DEVTOOLS_INSPECTOR_") ||
         !histId.includes(histIdFocus)) {
       // Inspector stats are tested in
       // browser_telemetry_toolboxtabs_{toolname}.js so we skip them here
       // because we only open the inspector once for this test.
       continue;
     }
 
--- a/devtools/client/shared/test/browser_telemetry_button_responsive.js
+++ b/devtools/client/shared/test/browser_telemetry_button_responsive.js
@@ -58,17 +58,17 @@ var delayedClicks = Task.async(function*
     // See TOOL_DELAY for why we need setTimeout here
     yield DevToolsUtils.waitForTime(TOOL_DELAY);
   }
 });
 
 function checkResults(histIdFocus, Telemetry) {
   let result = Telemetry.prototype.telemetryInfo;
 
-  for (let [histId, value] of Iterator(result)) {
+  for (let [histId, value] of Object.entries(result)) {
     if (histId.startsWith("DEVTOOLS_INSPECTOR_") ||
         !histId.includes(histIdFocus)) {
       // Inspector stats are tested in
       // browser_telemetry_toolboxtabs_{toolname}.js so we skip them here
       // because we only open the inspector once for this test.
       continue;
     }
 
--- a/devtools/client/shared/test/browser_telemetry_button_scratchpad.js
+++ b/devtools/client/shared/test/browser_telemetry_button_scratchpad.js
@@ -89,17 +89,17 @@ function delayedClicks(node, clicks) {
       }
     }, TOOL_DELAY);
   });
 }
 
 function checkResults(histIdFocus, Telemetry) {
   let result = Telemetry.prototype.telemetryInfo;
 
-  for (let [histId, value] of Iterator(result)) {
+  for (let [histId, value] of Object.entries(result)) {
     if (histId.startsWith("DEVTOOLS_INSPECTOR_") ||
         !histId.includes(histIdFocus)) {
       // Inspector stats are tested in
       // browser_telemetry_toolboxtabs_{toolname}.js so we skip them here
       // because we only open the inspector once for this test.
       continue;
     }
 
--- a/devtools/client/shared/test/browser_telemetry_sidebar.js
+++ b/devtools/client/shared/test/browser_telemetry_sidebar.js
@@ -47,17 +47,17 @@ function* testSidebar(toolbox) {
       }
     }, TOOL_DELAY);
   });
 }
 
 function checkResults(Telemetry) {
   let result = Telemetry.prototype.telemetryInfo;
 
-  for (let [histId, value] of Iterator(result)) {
+  for (let [histId, value] of Object.entries(result)) {
     if (histId.startsWith("DEVTOOLS_INSPECTOR_")) {
       // Inspector stats are tested in browser_telemetry_toolboxtabs.js so we
       // skip them here because we only open the inspector once for this test.
       continue;
     }
 
     if (histId === "DEVTOOLS_TOOLBOX_OPENED_COUNT") {
       is(value.length, 1, histId + " has only one entry");
--- a/devtools/client/shared/widgets/Chart.jsm
+++ b/devtools/client/shared/widgets/Chart.jsm
@@ -376,17 +376,17 @@ function createTableChart(document, { ti
     rowNode.className = "table-chart-row";
     rowNode.setAttribute("align", "center");
 
     let boxNode = document.createElement("hbox");
     boxNode.className = "table-chart-row-box chart-colored-blob";
     boxNode.setAttribute("name", rowInfo.label);
     rowNode.appendChild(boxNode);
 
-    for (let [key, value] in Iterator(rowInfo)) {
+    for (let [key, value] of Object.entries(rowInfo)) {
       let index = data.indexOf(rowInfo);
       let stringified = strings[key] ? strings[key](value, index) : value;
       let labelNode = document.createElement("label");
       labelNode.className = "plain table-chart-row-label";
       labelNode.setAttribute("name", key);
       labelNode.setAttribute("value", stringified);
       rowNode.appendChild(labelNode);
     }
@@ -394,17 +394,17 @@ function createTableChart(document, { ti
     proxy.rows.set(rowInfo, rowNode);
     delegate(proxy, ["click", "mouseover", "mouseout"], rowNode, rowInfo);
     tableNode.appendChild(rowNode);
   }
 
   let totalsNode = document.createElement("vbox");
   totalsNode.className = "table-chart-totals";
 
-  for (let [key, value] in Iterator(totals)) {
+  for (let [key, value] of Object.entries(totals)) {
     let total = data.reduce((acc, e) => acc + e[key], 0);
     let stringified = totals[key] ? totals[key](total || 0) : total;
     let labelNode = document.createElement("label");
     labelNode.className = "plain table-chart-summary-label";
     labelNode.setAttribute("name", key);
     labelNode.setAttribute("value", stringified);
     totalsNode.appendChild(labelNode);
   }
--- a/devtools/server/actors/director-registry.js
+++ b/devtools/server/actors/director-registry.js
@@ -105,55 +105,48 @@ const DirectorRegistry = exports.Directo
     gDirectorScripts = Object.create(null);
   }
 };
 
 /**
  * E10S parent/child setup helpers
  */
 
-var gTrackedMessageManager = new Set();
-
-exports.setupParentProcess = function setupParentProcess({mm, prefix}) {
-  // prevents multiple subscriptions on the same messagemanager
-  if (gTrackedMessageManager.has(mm)) {
-    return;
-  }
-  gTrackedMessageManager.add(mm);
-
+exports.setupParentProcess = function setupParentProcess({ mm, prefix }) {
   // listen for director-script requests from the child process
-  mm.addMessageListener("debug:director-registry-request", handleChildRequest);
-
-  DebuggerServer.once("disconnected-from-child:" + prefix, handleMessageManagerDisconnected);
+  setMessageManager(mm);
 
   /* parent process helpers */
 
-  function handleMessageManagerDisconnected(evt, { mm: disconnected_mm }) {
-    // filter out not subscribed message managers
-    if (disconnected_mm !== mm || !gTrackedMessageManager.has(mm)) {
-      return;
-    }
-
-    gTrackedMessageManager.delete(mm);
-
-    // unregister for director-script requests handlers from the parent process (if any)
-    mm.removeMessageListener("debug:director-registry-request", handleChildRequest);
-  }
-
   function handleChildRequest(msg) {
     switch (msg.json.method) {
       case "get":
         return DirectorRegistry.get(msg.json.args[0]);
       case "list":
         return DirectorRegistry.list();
       default:
         console.error(ERR_DIRECTOR_PARENT_UNKNOWN_METHOD, msg.json.method);
         throw new Error(ERR_DIRECTOR_PARENT_UNKNOWN_METHOD);
     }
   }
+
+  function setMessageManager(newMM) {
+    if (mm) {
+      mm.removeMessageListener("debug:director-registry-request", handleChildRequest);
+    }
+    mm = newMM;
+    if (mm) {
+      mm.addMessageListener("debug:director-registry-request", handleChildRequest);
+    }
+  }
+
+  return {
+    onBrowserSwap: setMessageManager,
+    onDisconnected: () => setMessageManager(null),
+  };
 };
 
 /**
  * The DirectorRegistry Actor is a global actor which manages install/uninstall of
  * director scripts definitions.
  */
 const DirectorRegistryActor = exports.DirectorRegistryActor = protocol.ActorClass(directorRegistrySpec, {
   /* init & destroy methods */
--- a/devtools/server/actors/storage.js
+++ b/devtools/server/actors/storage.js
@@ -27,18 +27,16 @@ loader.lazyGetter(this, "indexedDBForSto
       = Cu.Sandbox(CC("@mozilla.org/systemprincipal;1", "nsIPrincipal")(),
                    {wantGlobalProperties: ["indexedDB"]});
     return sandbox.indexedDB;
   } catch (e) {
     return {};
   }
 });
 
-var gTrackedMessageManager = new Map();
-
 // Maximum number of cookies/local storage key-value-pairs that can be sent
 // over the wire to the client in one request.
 const MAX_STORE_OBJECT_COUNT = 50;
 // Delay for the batch job that sends the accumulated update packets to the
 // client (ms).
 const BATCH_DELAY = 200;
 
 // MAX_COOKIE_EXPIRY should be 2^63-1, but JavaScript can't handle that
@@ -640,21 +638,21 @@ StorageActors.createActor({
       callParentProcess.bind(null, "removeCookieObservers");
     this.editCookie =
       callParentProcess.bind(null, "editCookie");
     this.removeCookie =
       callParentProcess.bind(null, "removeCookie");
     this.removeAllCookies =
       callParentProcess.bind(null, "removeAllCookies");
 
-    addMessageListener("storage:storage-cookie-request-child",
+    addMessageListener("debug:storage-cookie-request-child",
                        cookieHelpers.handleParentRequest);
 
     function callParentProcess(methodName, ...args) {
-      let reply = sendSyncMessage("storage:storage-cookie-request-parent", {
+      let reply = sendSyncMessage("debug:storage-cookie-request-parent", {
         method: methodName,
         args: args
       });
 
       if (reply.length === 0) {
         console.error("ERR_DIRECTOR_CHILD_NO_REPLY from " + methodName);
       } else if (reply.length > 1) {
         console.error("ERR_DIRECTOR_CHILD_MULTIPLE_REPLIES from " + methodName);
@@ -918,64 +916,62 @@ var cookieHelpers = {
     }
   },
 };
 
 /**
  * E10S parent/child setup helpers
  */
 
-exports.setupParentProcessForCookies = function ({mm, prefix}) {
+exports.setupParentProcessForCookies = function ({ mm, prefix }) {
   cookieHelpers.onCookieChanged =
     callChildProcess.bind(null, "onCookieChanged");
 
   // listen for director-script requests from the child process
-  mm.addMessageListener("storage:storage-cookie-request-parent",
-                        cookieHelpers.handleChildRequest);
-
-  DebuggerServer.once("disconnected-from-child:" + prefix,
-                      handleMessageManagerDisconnected);
-
-  gTrackedMessageManager.set("cookies", mm);
-
-  function handleMessageManagerDisconnected(evt, { mm: disconnectedMm }) {
-    // filter out not subscribed message managers
-    if (disconnectedMm !== mm || !gTrackedMessageManager.has("cookies")) {
-      return;
-    }
-
-    // Although "disconnected-from-child" implies that the child is already
-    // disconnected this is not the case. The disconnection takes place after
-    // this method has finished. This gives us chance to clean up items within
-    // the parent process e.g. observers.
-    cookieHelpers.removeCookieObservers();
-
-    gTrackedMessageManager.delete("cookies");
-
-    // unregister for director-script requests handlers from the parent process
-    // (if any)
-    mm.removeMessageListener("storage:storage-cookie-request-parent",
-                             cookieHelpers.handleChildRequest);
-  }
+  setMessageManager(mm);
 
   function callChildProcess(methodName, ...args) {
     if (methodName === "onCookieChanged") {
       args[0] = JSON.stringify(args[0]);
     }
 
     try {
-      mm.sendAsyncMessage("storage:storage-cookie-request-child", {
+      mm.sendAsyncMessage("debug:storage-cookie-request-child", {
         method: methodName,
         args: args
       });
     } catch (e) {
       // We may receive a NS_ERROR_NOT_INITIALIZED if the target window has
       // been closed. This can legitimately happen in between test runs.
     }
   }
+
+  function setMessageManager(newMM) {
+    if (mm) {
+      mm.removeMessageListener("debug:storage-cookie-request-parent",
+                               cookieHelpers.handleChildRequest);
+    }
+    mm = newMM;
+    if (mm) {
+      mm.addMessageListener("debug:storage-cookie-request-parent",
+                            cookieHelpers.handleChildRequest);
+    }
+  }
+
+  return {
+    onBrowserSwap: setMessageManager,
+    onDisconnected: () => {
+      // Although "disconnected-from-child" implies that the child is already
+      // disconnected this is not the case. The disconnection takes place after
+      // this method has finished. This gives us chance to clean up items within
+      // the parent process e.g. observers.
+      cookieHelpers.removeCookieObservers();
+      setMessageManager(null);
+    }
+  };
 };
 
 /**
  * Helper method to create the overriden object required in
  * StorageActors.createActor for Local Storage and Session Storage.
  * This method exists as both Local Storage and Session Storage have almost
  * identical actors.
  */
@@ -1631,17 +1627,17 @@ StorageActors.createActor({
 
     this.getDBMetaData = callParentProcessAsync.bind(null, "getDBMetaData");
     this.getDBNamesForHost = callParentProcessAsync.bind(null, "getDBNamesForHost");
     this.getValuesForHost = callParentProcessAsync.bind(null, "getValuesForHost");
     this.removeDB = callParentProcessAsync.bind(null, "removeDB");
     this.removeDBRecord = callParentProcessAsync.bind(null, "removeDBRecord");
     this.clearDBStore = callParentProcessAsync.bind(null, "clearDBStore");
 
-    addMessageListener("storage:storage-indexedDB-request-child", msg => {
+    addMessageListener("debug:storage-indexedDB-request-child", msg => {
       switch (msg.json.method) {
         case "backToChild": {
           let [func, rv] = msg.json.args;
           let deferred = unresolvedPromises.get(func);
           if (deferred) {
             unresolvedPromises.delete(func);
             deferred.resolve(rv);
           }
@@ -1655,17 +1651,17 @@ StorageActors.createActor({
     });
 
     let unresolvedPromises = new Map();
     function callParentProcessAsync(methodName, ...args) {
       let deferred = promise.defer();
 
       unresolvedPromises.set(methodName, deferred);
 
-      sendAsyncMessage("storage:storage-indexedDB-request-parent", {
+      sendAsyncMessage("debug:storage-indexedDB-request-parent", {
         method: methodName,
         args: args
       });
 
       return deferred.promise;
     }
   },
 
@@ -1699,27 +1695,27 @@ StorageActors.createActor({
   })
 });
 
 var indexedDBHelpers = {
   backToChild(...args) {
     let mm = Cc["@mozilla.org/globalmessagemanager;1"]
                .getService(Ci.nsIMessageListenerManager);
 
-    mm.broadcastAsyncMessage("storage:storage-indexedDB-request-child", {
+    mm.broadcastAsyncMessage("debug:storage-indexedDB-request-child", {
       method: "backToChild",
       args: args
     });
   },
 
   onItemUpdated(action, host, path) {
     let mm = Cc["@mozilla.org/globalmessagemanager;1"]
                .getService(Ci.nsIMessageListenerManager);
 
-    mm.broadcastAsyncMessage("storage:storage-indexedDB-request-child", {
+    mm.broadcastAsyncMessage("debug:storage-indexedDB-request-child", {
       method: "onItemUpdated",
       args: [ action, host, path ]
     });
   },
 
   /**
    * Fetches and stores all the metadata information for the given database
    * `name` for the given `host` with its `principal`. The stored metadata
@@ -2136,39 +2132,36 @@ var indexedDBHelpers = {
     }
   }
 };
 
 /**
  * E10S parent/child setup helpers
  */
 
-exports.setupParentProcessForIndexedDB = function ({mm, prefix}) {
+exports.setupParentProcessForIndexedDB = function ({ mm, prefix }) {
   // listen for director-script requests from the child process
-  mm.addMessageListener("storage:storage-indexedDB-request-parent",
-                        indexedDBHelpers.handleChildRequest);
-
-  DebuggerServer.once("disconnected-from-child:" + prefix,
-                      handleMessageManagerDisconnected);
-
-  gTrackedMessageManager.set("indexedDB", mm);
+  setMessageManager(mm);
 
-  function handleMessageManagerDisconnected(evt, { mm: disconnectedMm }) {
-    // filter out not subscribed message managers
-    if (disconnectedMm !== mm || !gTrackedMessageManager.has("indexedDB")) {
-      return;
+  function setMessageManager(newMM) {
+    if (mm) {
+      mm.removeMessageListener("debug:storage-indexedDB-request-parent",
+                               indexedDBHelpers.handleChildRequest);
     }
+    mm = newMM;
+    if (mm) {
+      mm.addMessageListener("debug:storage-indexedDB-request-parent",
+                            indexedDBHelpers.handleChildRequest);
+    }
+  }
 
-    gTrackedMessageManager.delete("indexedDB");
-
-    // unregister for director-script requests handlers from the parent process
-    // (if any)
-    mm.removeMessageListener("storage:storage-indexedDB-request-parent",
-                             indexedDBHelpers.handleChildRequest);
-  }
+  return {
+    onBrowserSwap: setMessageManager,
+    onDisconnected: () => setMessageManager(null),
+  };
 };
 
 /**
  * The main Storage Actor.
  */
 let StorageActor = protocol.ActorClass(specs.storageSpec, {
   typeName: "storage",
 
--- a/devtools/server/actors/webbrowser.js
+++ b/devtools/server/actors/webbrowser.js
@@ -864,19 +864,18 @@ function TabActor(connection) {
   this.makeDebugger = makeDebugger.bind(null, {
     findDebuggees: () => {
       return this.windows.concat(this.webextensionsContentScriptGlobals);
     },
     shouldAddNewGlobalAsDebuggee: this._shouldAddNewGlobalAsDebuggee
   });
 
   // Flag eventually overloaded by sub classes in order to watch new docshells
-  // Used on b2g to catch activity frames and in chrome to list all frames
-  this.listenForNewDocShells =
-    Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT;
+  // Used by the ChromeActor to list all frames in the Browser Toolbox
+  this.listenForNewDocShells = false;
 
   this.traits = {
     reconfigure: true,
     // Supports frame listing via `listFrames` request and `frameUpdate` events
     // as well as frame switching via `switchToFrame` request
     frames: true,
     // Do not require to send reconfigure request to reset the document state
     // to what it was before using the TabActor
@@ -933,19 +932,23 @@ TabActor.prototype = {
   get chromeEventHandler() {
     return getDocShellChromeEventHandler(this.docShell);
   },
 
   /**
    * Getter for the nsIMessageManager associated to the tab.
    */
   get messageManager() {
-    return this.docShell
-      .QueryInterface(Ci.nsIInterfaceRequestor)
-      .getInterface(Ci.nsIContentFrameMessageManager);
+    try {
+      return this.docShell
+        .QueryInterface(Ci.nsIInterfaceRequestor)
+        .getInterface(Ci.nsIContentFrameMessageManager);
+    } catch (e) {
+      return null;
+    }
   },
 
   /**
    * Getter for the tab's doc shell.
    */
   get docShell() {
     throw new Error(
       "The docShell getter should be implemented by a subclass of TabActor");
@@ -967,16 +970,25 @@ TabActor.prototype = {
     if (this.docShell) {
       return this.docShell
         .QueryInterface(Ci.nsIInterfaceRequestor)
         .getInterface(Ci.nsIDOMWindow);
     }
     return null;
   },
 
+  get outerWindowID() {
+    if (this.window) {
+      return this.window.QueryInterface(Ci.nsIInterfaceRequestor)
+                        .getInterface(Ci.nsIDOMWindowUtils)
+                        .outerWindowID;
+    }
+    return null;
+  },
+
   /**
    * Getter for the WebExtensions ContentScript globals related to the
    * current tab content's DOM window.
    */
   get webextensionsContentScriptGlobals() {
     // Ignore xpcshell runtime which spawn TabActors without a window.
     if (this.window) {
       return ExtensionContent.getContentScriptGlobalsForWindow(this.window);
@@ -1099,20 +1111,17 @@ TabActor.prototype = {
     };
 
     // We may try to access window while the document is closing, then
     // accessing window throws. Also on xpcshell we are using tabactor even if
     // there is no valid document.
     if (this.docShell && !this.docShell.isBeingDestroyed()) {
       response.title = this.title;
       response.url = this.url;
-      let windowUtils = this.window
-        .QueryInterface(Ci.nsIInterfaceRequestor)
-        .getInterface(Ci.nsIDOMWindowUtils);
-      response.outerWindowID = windowUtils.outerWindowID;
+      response.outerWindowID = this.outerWindowID;
     }
 
     // Always use the same ActorPool, so existing actor instances
     // (created in createExtraActors) are not lost.
     if (!this._tabActorPool) {
       this._tabActorPool = new ActorPool(this.conn);
       this.conn.addActorPool(this._tabActorPool);
     }
@@ -1396,39 +1405,41 @@ TabActor.prototype = {
   _notifyDocShellsUpdate(docshells) {
     let windows = this._docShellsToWindows(docshells);
 
     // Do not send the `frameUpdate` event if the windows array is empty.
     if (windows.length == 0) {
       return;
     }
 
-    this.conn.send({ from: this.actorID,
-                     type: "frameUpdate",
-                     frames: windows
-                   });
+    this.conn.send({
+      from: this.actorID,
+      type: "frameUpdate",
+      frames: windows
+    });
   },
 
   _updateChildDocShells() {
     this._notifyDocShellsUpdate(this.docShells);
   },
 
   _notifyDocShellDestroy(webProgress) {
     webProgress = webProgress.QueryInterface(Ci.nsIWebProgress);
     let id = webProgress.DOMWindow
                         .QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIDOMWindowUtils)
                         .outerWindowID;
-    this.conn.send({ from: this.actorID,
-                     type: "frameUpdate",
-                     frames: [{
-                       id: id,
-                       destroy: true
-                     }]
-                   });
+    this.conn.send({
+      from: this.actorID,
+      type: "frameUpdate",
+      frames: [{
+        id,
+        destroy: true
+      }]
+    });
 
     // Stop watching this docshell (the unwatch() method will check if we
     // started watching it before).
     webProgress.QueryInterface(Ci.nsIDocShell);
     this._progressListener.unwatch(webProgress);
 
     if (webProgress.DOMWindow == this._originalWindow) {
       // If the original top level document we connected to is removed,
@@ -1457,20 +1468,21 @@ TabActor.prototype = {
     // we have to switch to the top-level one.
     if (webProgress.DOMWindow == this.window &&
         this.window != this._originalWindow) {
       this._changeTopLevelDocument(this._originalWindow);
     }
   },
 
   _notifyDocShellDestroyAll() {
-    this.conn.send({ from: this.actorID,
-                     type: "frameUpdate",
-                     destroyAll: true
-                   });
+    this.conn.send({
+      from: this.actorID,
+      type: "frameUpdate",
+      destroyAll: true
+    });
   },
 
   /**
    * Creates a thread actor and a pool for context-lifetime actors. It then sets
    * up the content window for debugging.
    */
   _pushContext() {
     assert(!this._contextPool, "Can't push multiple contexts");
@@ -1867,23 +1879,21 @@ TabActor.prototype = {
     // targeted context (it will indirectly update this.window and
     // many other attributes defined from docShell).
     Object.defineProperty(this, "docShell", {
       value: docShell,
       enumerable: true,
       configurable: true
     });
     events.emit(this, "changed-toplevel-document");
-    let id = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                   .getInterface(Ci.nsIDOMWindowUtils)
-                   .outerWindowID;
-    this.conn.send({ from: this.actorID,
-                     type: "frameUpdate",
-                     selected: id
-                   });
+    this.conn.send({
+      from: this.actorID,
+      type: "frameUpdate",
+      selected: this.outerWindowID
+    });
   },
 
   /**
    * Handle location changes, by clearing the previous debuggees and enabling
    * debugging, which may have been disabled temporarily by the
    * DebuggerProgressListener.
    */
   _windowReady(window, isFrameSwitching = false) {
--- a/devtools/server/actors/webconsole.js
+++ b/devtools/server/actors/webconsole.js
@@ -610,18 +610,18 @@ WebConsoleActor.prototype =
             this.stackTraceCollector.init();
 
             let processBoundary = Services.appinfo.processType !=
                                   Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
             if ((appId || messageManager) && processBoundary) {
               // Start a network monitor in the parent process to listen to
               // most requests than happen in parent
               this.networkMonitor =
-                new NetworkMonitorChild(appId, messageManager,
-                                        this.parentActor.actorID, this);
+                new NetworkMonitorChild(appId, this.parentActor.outerWindowID,
+                                        messageManager, this.conn, this);
               this.networkMonitor.init();
               // Spawn also one in the child to listen to service workers
               this.networkMonitorChild = new NetworkMonitor({ window }, this);
               this.networkMonitorChild.init();
             } else {
               this.networkMonitor = new NetworkMonitor({ window }, this);
               this.networkMonitor.init();
             }
--- a/devtools/server/child.js
+++ b/devtools/server/child.js
@@ -57,17 +57,17 @@ try {
     let onSetupInChild = DevToolsUtils.makeInfallible(msg => {
       let { module, setupChild, args } = msg.data;
       let m;
 
       try {
         m = require(module);
 
         if (!setupChild in m) {
-          dumpn(`ERROR: module '${module}' does not export 'setupChild'`);
+          dumpn(`ERROR: module '${module}' does not export '${setupChild}'`);
           return false;
         }
 
         m[setupChild].apply(m, args);
       } catch (e) {
         let errorMessage =
           "Exception during actor module setup running in the child process: ";
         DevToolsUtils.reportException(errorMessage + e);
@@ -80,27 +80,31 @@ try {
         sendAsyncMessage("debug:setup-in-child-response", {id: msg.data.id});
       }
       return true;
     });
 
     addMessageListener("debug:setup-in-child", onSetupInChild);
 
     let onDisconnect = DevToolsUtils.makeInfallible(function (msg) {
+      let prefix = msg.data.prefix;
+      let conn = connections.get(prefix);
+      if (!conn) {
+        // Several copies of this frame script can be running for a single frame since it
+        // is loaded once for each DevTools connection to the frame.  If this disconnect
+        // request doesn't match a connection known here, ignore it.
+        return;
+      }
+
       removeMessageListener("debug:disconnect", onDisconnect);
-
       // Call DebuggerServerConnection.close to destroy all child actors. It should end up
       // calling DebuggerServerConnection.onClosed that would actually cleanup all actor
       // pools.
-      let prefix = msg.data.prefix;
-      let conn = connections.get(prefix);
-      if (conn) {
-        conn.close();
-        connections.delete(prefix);
-      }
+      conn.close();
+      connections.delete(prefix);
     });
     addMessageListener("debug:disconnect", onDisconnect);
 
     // In non-e10s mode, the "debug:disconnect" message isn't always received before the
     // messageManager connection goes away.  Watching for "unload" here ensures we close
     // any connections when the frame is unloaded.
     addEventListener("unload", () => {
       for (let conn of connections.values()) {
--- a/devtools/server/docs/actor-e10s-handling.md
+++ b/devtools/server/docs/actor-e10s-handling.md
@@ -39,66 +39,68 @@ The `setupChildProcess` helper defined a
 
 With this, the `DebuggerServer` running in the parent process will require the requested module (**director-registry**) and call its `setupParentProcess` function (which should be exported on the module).
 
 The `setupParentProcess` function will receive a parameter that contains a reference to the **MessageManager** and a prefix that should be used to send/receive messages between the child and parent processes.
 
 See below an example implementation of a `setupParent` function in the parent process:
 
 ```
-let gTrackedMessageManager = new Set();
 exports.setupParentProcess = function setupParentProcess({ mm, prefix }) {
-  // Prevent multiple subscriptions on the same messagemanager.
-  if (gTrackedMessageManager.has(mm)) { return; }
-  gTrackedMessageManager.add(mm);
-
   // Start listening for messages from the actor in the child process.
-  mm.addMessageListener("debug:some-message-name", handleChildRequest);
+  setMessageManager(mm);
 
   function handleChildRequest(msg) {
     switch (msg.json.method) {
       case "get":
         return doGetInParentProcess(msg.json.args[0]);
         break;
       case "list":
         return doListInParentProcess();
         break;
       default:
         console.error("Unknown method name", msg.json.method);
         throw new Error("Unknown method name");
     }
   }
 
-  // Listen to the disconnection message to clean-up.
-  DebuggerServer.once("disconnected-from-child:" + prefix, handleMessageManagerDisconnected);
+  function setMessageManager(newMM) {
+    if (mm) {
+      // Remove listener from old message manager
+      mm.removeMessageListener("debug:some-message-name", handleChildRequest);
+    }
+    // Switch to the new message manager for future use
+    // Note: Make sure that any other functions also use the new reference.
+    mm = newMM;
+    if (mm) {
+      // Add listener to new message manager
+      mm.addMessageListener("debug:some-message-name", handleChildRequest);
+    }
+  }
 
-  function handleMessageManagerDisconnected(evt, { mm: disconnected_mm }) {
-    // filter out not subscribed message managers
-    if (disconnected_mm !== mm || !gTrackedMessageManager.has(mm)) {
-      return;
-    }
-
-    gTrackedMessageManager.delete(mm);
-
-    // unregister for director-script requests handlers from the parent process (if any)
-    mm.removeMessageListener("debug:director-registry-request", handleChildRequest);
-  }
+  return {
+    onBrowserSwap: setMessageManager,
+    onDisconnected: () => setMessageManager(null),
+  };
+};
 ```
 
-The `DebuggerServer` emits "disconnected-from-child:PREFIX" events to give the actor modules the chance to cleanup their handlers registered on the disconnected message manager.
+The server will call the `onDisconnected` method returned by the parent process setup flow to give the actor modules the chance to cleanup their handlers registered on the disconnected message manager.
+
+The server will call the `onBrowserSwap` method returned by the parent process setup flow to notify actor modules when the message manager for the target frame has changed.  The parent process code should remove any message listeners from the previous message manager and add them to the new one.
 
 ## Summary of the setup flow
 
 In the child process:
 
 * The `DebuggerServer` loads an actor module,
 * the actor module checks `DebuggerServer.isInChildProcess` to know whether it runs in a child process or not,
 * the actor module then uses the `DebuggerServerConnection.setupInParent` helper to start setting up a parent-process counterpart,
 * the `DebuggerServerConnection.setupInParent` helper asks the parent process to run the required module's setup function,
 * the actor module uses the `DebuggerServerConnection.parentMessageManager.sendSyncMessage` and `DebuggerServerConnection.parentMessageManager.addMessageListener` helpers to send or listen to message.
 
 In the parent process:
 
 * The DebuggerServer receives the `DebuggerServerConnection.setupInParent` request,
 * tries to load the required module,
 * tries to call the `module[setupParent]` function with the frame message manager and the prefix as parameters `{ mm, prefix }`,
-* the `setupParent` function then uses the mm to subscribe the messagemanager events,
-* the `setupParent` function also uses the DebuggerServer object to subscribe *once* to the `"disconnected-from-child:PREFIX"` event to unsubscribe from messagemanager events.
+* the `setupParent` function then uses the mm to subscribe the message manager events,
+* the `setupParent` function returns an object with `onDisconnected` and `onBrowserSwap` methods which the server can use to notify the module of various lifecycle events
--- a/devtools/server/main.js
+++ b/devtools/server/main.js
@@ -1000,56 +1000,73 @@ var DebuggerServer = {
    */
   connectToChild(connection, frame, onDestroy) {
     let deferred = SyncPromise.defer();
 
     // Get messageManager from XUL browser (which might be a specialized tunnel for RDM)
     // or else fallback to asking the frameLoader itself.
     let mm = frame.messageManager || frame.frameLoader.messageManager;
     mm.loadFrameScript("resource://devtools/server/child.js", false);
-    this._childMessageManagers.add(mm);
+
+    let trackMessageManager = () => {
+      frame.addEventListener("DevTools:BrowserSwap", onBrowserSwap);
+      mm.addMessageListener("debug:setup-in-parent", onSetupInParent);
+      if (!actor) {
+        mm.addMessageListener("debug:actor", onActorCreated);
+      }
+      DebuggerServer._childMessageManagers.add(mm);
+    };
+
+    let untrackMessageManager = () => {
+      frame.removeEventListener("DevTools:BrowserSwap", onBrowserSwap);
+      mm.removeMessageListener("debug:setup-in-parent", onSetupInParent);
+      if (!actor) {
+        mm.removeMessageListener("debug:actor", onActorCreated);
+      }
+      DebuggerServer._childMessageManagers.delete(mm);
+    };
 
     let actor, childTransport;
     let prefix = connection.allocID("child");
-    let netMonitor = null;
+    // Compute the same prefix that's used by DebuggerServerConnection
+    let connPrefix = prefix + "/";
 
     // provides hook to actor modules that need to exchange messages
     // between e10s parent and child processes
+    let parentModules = [];
     let onSetupInParent = function (msg) {
       // We may have multiple connectToChild instance running for the same tab
-      // and need to filter the messages. Also the DebuggerServerConnection's
-      // prefix has an additional '/' and the end, so use `includes`.
-      if (!msg.json.prefix.includes(prefix)) {
+      // and need to filter the messages.
+      if (msg.json.prefix != connPrefix) {
         return false;
       }
 
       let { module, setupParent } = msg.json;
       let m;
 
       try {
         m = require(module);
 
         if (!setupParent in m) {
-          dumpn(`ERROR: module '${module}' does not export 'setupParent'`);
+          dumpn(`ERROR: module '${module}' does not export '${setupParent}'`);
           return false;
         }
 
-        m[setupParent]({ mm, prefix });
+        parentModules.push(m[setupParent]({ mm, prefix: connPrefix }));
 
         return true;
       } catch (e) {
         let errorMessage =
           "Exception during actor module setup running in the parent process: ";
         DevToolsUtils.reportException(errorMessage + e);
         dumpn(`ERROR: ${errorMessage}\n\t module: '${module}'\n\t ` +
               `setupParent: '${setupParent}'\n${DevToolsUtils.safeErrorString(e)}`);
         return false;
       }
     };
-    mm.addMessageListener("debug:setup-in-parent", onSetupInParent);
 
     let onActorCreated = DevToolsUtils.makeInfallible(function (msg) {
       if (msg.json.prefix != prefix) {
         return;
       }
       mm.removeMessageListener("debug:actor", onActorCreated);
 
       // Pipe Debugger message from/to parent/child via the message manager
@@ -1060,30 +1077,55 @@ var DebuggerServer = {
       };
       childTransport.ready();
 
       connection.setForwarding(prefix, childTransport);
 
       dumpn("establishing forwarding for app with prefix " + prefix);
 
       actor = msg.json.actor;
-
-      let { NetworkMonitorManager } = require("devtools/shared/webconsole/network-monitor");
-      netMonitor = new NetworkMonitorManager(frame, actor.actor);
-
-      events.emit(DebuggerServer, "new-child-process", { mm });
-
       deferred.resolve(actor);
     }).bind(this);
-    mm.addMessageListener("debug:actor", onActorCreated);
+
+    // Listen for browser frame swap
+    let onBrowserSwap = ({ detail: newFrame }) => {
+      // Remove listeners from old frame and mm
+      untrackMessageManager();
+      // Update frame and mm to point to the new browser frame
+      frame = newFrame;
+      // Get messageManager from XUL browser (which might be a specialized tunnel for RDM)
+      // or else fallback to asking the frameLoader itself.
+      mm = frame.messageManager || frame.frameLoader.messageManager;
+      // Add listeners to new frame and mm
+      trackMessageManager();
+
+      // provides hook to actor modules that need to exchange messages
+      // between e10s parent and child processes
+      parentModules.forEach(mod => {
+        if (mod.onBrowserSwap) {
+          mod.onBrowserSwap(mm);
+        }
+      });
+
+      if (childTransport) {
+        childTransport.swapBrowser(mm);
+      }
+    };
 
     let destroy = DevToolsUtils.makeInfallible(function () {
       // provides hook to actor modules that need to exchange messages
       // between e10s parent and child processes
-      DebuggerServer.emit("disconnected-from-child:" + prefix, { mm, prefix });
+      parentModules.forEach(mod => {
+        if (mod.onDisconnected) {
+          mod.onDisconnected();
+        }
+      });
+      // TODO: Remove this deprecated path once it's no longer needed by add-ons.
+      DebuggerServer.emit("disconnected-from-child:" + connPrefix,
+                          { mm, prefix: connPrefix });
 
       if (childTransport) {
         // If we have a child transport, the actor has already
         // been created. We need to stop using this message manager.
         childTransport.close();
         childTransport = null;
         connection.cancelForwarding(prefix);
 
@@ -1104,35 +1146,28 @@ var DebuggerServer = {
       if (actor) {
         // The ContentActor within the child process doesn't necessary
         // have time to uninitialize itself when the app is closed/killed.
         // So ensure telling the client that the related actor is detached.
         connection.send({ from: actor.actor, type: "tabDetached" });
         actor = null;
       }
 
-      if (netMonitor) {
-        netMonitor.destroy();
-        netMonitor = null;
-      }
-
       if (onDestroy) {
         onDestroy(mm);
       }
 
       // Cleanup all listeners
+      untrackMessageManager();
       Services.obs.removeObserver(onMessageManagerClose, "message-manager-close");
-      mm.removeMessageListener("debug:setup-in-parent", onSetupInParent);
-      if (!actor) {
-        mm.removeMessageListener("debug:actor", onActorCreated);
-      }
       events.off(connection, "closed", destroy);
+    });
 
-      DebuggerServer._childMessageManagers.delete(mm);
-    });
+    // Listen for various messages and frame events
+    trackMessageManager();
 
     // Listen for app process exit
     let onMessageManagerClose = function (subject, topic, data) {
       if (subject == mm) {
         destroy();
       }
     };
     Services.obs.addObserver(onMessageManagerClose,
--- a/devtools/shared/transport/transport.js
+++ b/devtools/shared/transport/transport.js
@@ -694,87 +694,101 @@
         }
       }
     }
   };
 
   exports.LocalDebuggerTransport = LocalDebuggerTransport;
 
   /**
-   * A transport for the debugging protocol that uses nsIMessageSenders to
+   * A transport for the debugging protocol that uses nsIMessageManagers to
    * exchange packets with servers running in child processes.
    *
-   * In the parent process, |sender| should be the nsIMessageSender for the
-   * child process. In a child process, |sender| should be the child process
+   * In the parent process, |mm| should be the nsIMessageSender for the
+   * child process. In a child process, |mm| should be the child process
    * message manager, which sends packets to the parent.
    *
    * |prefix| is a string included in the message names, to distinguish
    * multiple servers running in the same child process.
    *
    * This transport exchanges messages named 'debug:<prefix>:packet', where
    * <prefix> is |prefix|, whose data is the protocol packet.
    */
-  function ChildDebuggerTransport(sender, prefix) {
+  function ChildDebuggerTransport(mm, prefix) {
     EventEmitter.decorate(this);
 
-    this._sender = sender;
+    this._mm = mm;
     this._messageName = "debug:" + prefix + ":packet";
   }
 
   /*
    * To avoid confusion, we use 'message' to mean something that
    * nsIMessageSender conveys, and 'packet' to mean a remote debugging
    * protocol packet.
    */
   ChildDebuggerTransport.prototype = {
     constructor: ChildDebuggerTransport,
 
     hooks: null,
 
-    ready: function () {
-      this._sender.addMessageListener(this._messageName, this);
+    _addListener() {
+      this._mm.addMessageListener(this._messageName, this);
     },
 
-    close: function () {
+    _removeListener() {
       try {
-        this._sender.removeMessageListener(this._messageName, this);
+        this._mm.removeMessageListener(this._messageName, this);
       } catch (e) {
         if (e.result != Cr.NS_ERROR_NULL_POINTER) {
           throw e;
         }
         // In some cases, especially when using messageManagers in non-e10s mode, we reach
         // this point with a dead messageManager which only throws errors but does not
         // seem to indicate in any other way that it is dead.
       }
+    },
+
+    ready: function () {
+      this._addListener();
+    },
+
+    close: function () {
+      this._removeListener();
       this.emit("close");
       this.hooks.onClosed();
     },
 
     receiveMessage: function ({data}) {
       this.emit("packet", data);
       this.hooks.onPacket(data);
     },
 
     send: function (packet) {
       this.emit("send", packet);
       try {
-        this._sender.sendAsyncMessage(this._messageName, packet);
+        this._mm.sendAsyncMessage(this._messageName, packet);
       } catch (e) {
         if (e.result != Cr.NS_ERROR_NULL_POINTER) {
           throw e;
         }
         // In some cases, especially when using messageManagers in non-e10s mode, we reach
         // this point with a dead messageManager which only throws errors but does not
         // seem to indicate in any other way that it is dead.
       }
     },
 
     startBulkSend: function () {
       throw new Error("Can't send bulk data to child processes.");
-    }
+    },
+
+    swapBrowser(mm) {
+      this._removeListener();
+      this._mm = mm;
+      this._addListener();
+    },
   };
 
   exports.ChildDebuggerTransport = ChildDebuggerTransport;
 
   // WorkerDebuggerTransport is defined differently depending on whether we are
   // on the main thread or a worker thread. In the former case, we are required
   // by the devtools loader, and isWorker will be false. Otherwise, we are
   // required by the worker loader, and isWorker will be true.
--- a/devtools/shared/webconsole/network-monitor.js
+++ b/devtools/shared/webconsole/network-monitor.js
@@ -11,16 +11,18 @@ const Services = require("Services");
 const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
 
 loader.lazyRequireGetter(this, "NetworkHelper",
                          "devtools/shared/webconsole/network-helper");
 loader.lazyRequireGetter(this, "DevToolsUtils",
                          "devtools/shared/DevToolsUtils");
 loader.lazyRequireGetter(this, "flags",
                          "devtools/shared/flags");
+loader.lazyRequireGetter(this, "DebuggerServer",
+                         "devtools/server/main", true);
 loader.lazyImporter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm");
 loader.lazyServiceGetter(this, "gActivityDistributor",
                          "@mozilla.org/network/http-activity-distributor;1",
                          "nsIHttpActivityDistributor");
 
 // /////////////////////////////////////////////////////////////////////////////
 // Network logging
 // /////////////////////////////////////////////////////////////////////////////
@@ -45,17 +47,17 @@ const RESPONSE_BODY_LIMIT = 1048576;
  *        Request to check.
  * @param filters
  *        NetworkMonitor filters to match against.
  * @return boolean
  *         True if the network request should be logged, false otherwise.
  */
 function matchRequest(channel, filters) {
   // Log everything if no filter is specified
-  if (!filters.topFrame && !filters.window && !filters.appId) {
+  if (!filters.outerWindowID && !filters.window && !filters.appId) {
     return true;
   }
 
   // Ignore requests from chrome or add-on code when we are monitoring
   // content.
   // TODO: one particular test (browser_styleeditor_fetch-from-cache.js) needs
   // the flags.testing check. We will move to a better way to serve
   // its needs in bug 1167188, where this check should be removed.
@@ -76,66 +78,31 @@ function matchRequest(channel, filters) 
       }
       if (win.parent == win) {
         break;
       }
       win = win.parent;
     }
   }
 
-  if (filters.topFrame) {
+  if (filters.outerWindowID) {
     let topFrame = NetworkHelper.getTopFrameForRequest(channel);
-    while (topFrame) {
-      // In the normal case, a topFrame filter should match the request's topFrame if it
-      // will match at all.
-      if (topFrame === filters.topFrame) {
-        return true;
-      }
-      // As a stop gap approach for RDM, where `filters.topFrame` will be the
-      // <xul:browser> frame for an entire tab and the request's `topFrame` is the
-      // <iframe mozbrower> that triggered the request, we try to climb up parent frames
-      // above the request's `topFrame` to see if they might also match the filter.
-      // In bug 1240912, we want to rework this, since we don't really want to be passing
-      // a frame down to the network monitor.
-      if (!topFrame.ownerGlobal) {
-        break;
-      }
-      topFrame = topFrame.ownerGlobal
-                         .QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIDOMWindowUtils)
-                         .containerElement;
+    if (topFrame && topFrame.outerWindowID &&
+        topFrame.outerWindowID == filters.outerWindowID) {
+      return true;
     }
   }
 
   if (filters.appId) {
     let appId = NetworkHelper.getAppIdForRequest(channel);
     if (appId && appId == filters.appId) {
       return true;
     }
   }
 
-  // The following check is necessary because beacon channels don't come
-  // associated with a load group. Bug 1160837 will hopefully introduce a
-  // platform fix that will render the following code entirely useless.
-  if (channel.loadInfo &&
-      channel.loadInfo.externalContentPolicyType ==
-      Ci.nsIContentPolicy.TYPE_BEACON) {
-    let nonE10sMatch = filters.window &&
-        channel.loadInfo.loadingDocument === filters.window.document;
-    const loadingPrincipal = channel.loadInfo.loadingPrincipal;
-    let e10sMatch = filters.topFrame &&
-        filters.topFrame.contentPrincipal &&
-        filters.topFrame.contentPrincipal.equals(loadingPrincipal) &&
-        filters.topFrame.contentPrincipal.URI.spec == channel.referrer.spec;
-    let b2gMatch = filters.appId && loadingPrincipal.appId === filters.appId;
-    if (nonE10sMatch || e10sMatch || b2gMatch) {
-      return true;
-    }
-  }
-
   return false;
 }
 
 /**
  * This is a nsIChannelEventSink implementation that monitors channel redirects and
  * informs the registered StackTraceCollector about the old and new channels.
  */
 const SINK_CLASS_DESCRIPTION = "NetworkMonitor Channel Event Sink";
@@ -711,17 +678,17 @@ NetworkResponseListener.prototype = {
  * routed to the remote Web Console.
  *
  * @constructor
  * @param object filters
  *        Object with the filters to use for network requests:
  *        - window (nsIDOMWindow): filter network requests by the associated
  *          window object.
  *        - appId (number): filter requests by the appId.
- *        - topFrame (nsIDOMElement): filter requests by their topFrameElement.
+ *        - outerWindowID (number): filter requests by their top frame's outerWindowID.
  *        Filters are optional. If any of these filters match the request is
  *        logged (OR is applied). If no filter is provided then all requests are
  *        logged.
  * @param object owner
  *        The network monitor owner. This object needs to hold:
  *        - onNetworkEvent(requestInfo)
  *          This method is invoked once for every new network request and it is
  *          given the initial network request information as an argument.
@@ -1421,26 +1388,29 @@ NetworkMonitor.prototype = {
  *
  * The main process creates NetworkEventActorProxy instances per request. These
  * send the data to this object using the nsIMessageManager. Here we proxy the
  * data to the WebConsoleActor or to a NetworkEventActor.
  *
  * @constructor
  * @param number appId
  *        The web appId of the child process.
+ * @param number outerWindowID
+ *        The outerWindowID of the TabActor's main window.
  * @param nsIMessageManager messageManager
  *        The nsIMessageManager to use to communicate with the parent process.
- * @param string connID
- *        The connection ID to use for send messages to the parent process.
+ * @param object DebuggerServerConnection
+ *        The RDP connection to the client.
  * @param object owner
  *        The WebConsoleActor that is listening for the network requests.
  */
-function NetworkMonitorChild(appId, messageManager, connID, owner) {
+function NetworkMonitorChild(appId, outerWindowID, messageManager, conn, owner) {
   this.appId = appId;
-  this.connID = connID;
+  this.outerWindowID = outerWindowID;
+  this.conn = conn;
   this.owner = owner;
   this._messageManager = messageManager;
   this._onNewEvent = this._onNewEvent.bind(this);
   this._onUpdateEvent = this._onUpdateEvent.bind(this);
   this._netEvents = new Map();
 }
 
 exports.NetworkMonitorChild = NetworkMonitorChild;
@@ -1453,32 +1423,38 @@ NetworkMonitorChild.prototype = {
 
   get saveRequestAndResponseBodies() {
     return this._saveRequestAndResponseBodies;
   },
 
   set saveRequestAndResponseBodies(val) {
     this._saveRequestAndResponseBodies = val;
 
-    this._messageManager.sendAsyncMessage("debug:netmonitor:" + this.connID, {
+    this._messageManager.sendAsyncMessage("debug:netmonitor", {
       action: "setPreferences",
       preferences: {
         saveRequestAndResponseBodies: this._saveRequestAndResponseBodies,
       },
     });
   },
 
   init: function () {
+    this.conn.setupInParent({
+      module: "devtools/shared/webconsole/network-monitor",
+      setupParent: "setupParentProcess"
+    });
+
     let mm = this._messageManager;
-    mm.addMessageListener("debug:netmonitor:" + this.connID + ":newEvent",
+    mm.addMessageListener("debug:netmonitor:newEvent",
                           this._onNewEvent);
-    mm.addMessageListener("debug:netmonitor:" + this.connID + ":updateEvent",
+    mm.addMessageListener("debug:netmonitor:updateEvent",
                           this._onUpdateEvent);
-    mm.sendAsyncMessage("debug:netmonitor:" + this.connID, {
+    mm.sendAsyncMessage("debug:netmonitor", {
       appId: this.appId,
+      outerWindowID: this.outerWindowID,
       action: "start",
     });
   },
 
   _onNewEvent: DevToolsUtils.makeInfallible(function _onNewEvent(msg) {
     let {id, event} = msg.data;
 
     // Try to add stack trace to the event data received from parent
@@ -1506,30 +1482,30 @@ NetworkMonitorChild.prototype = {
       return;
     }
     actor[method].apply(actor, args);
   }),
 
   destroy: function () {
     let mm = this._messageManager;
     try {
-      mm.removeMessageListener("debug:netmonitor:" + this.connID + ":newEvent",
+      mm.removeMessageListener("debug:netmonitor:newEvent",
                                this._onNewEvent);
-      mm.removeMessageListener("debug:netmonitor:" + this.connID +
-                               ":updateEvent",
+      mm.removeMessageListener("debug:netmonitor:updateEvent",
                                this._onUpdateEvent);
     } catch (e) {
       // On b2g, when registered to a new root docshell,
       // all message manager functions throw when trying to call them during
       // message-manager-disconnect event.
       // As there is no attribute/method on message manager to know
       // if they are still usable or not, we can only catch the exception...
     }
     this._netEvents.clear();
     this._messageManager = null;
+    this.conn = null;
     this.owner = null;
   },
 };
 
 /**
  * The NetworkEventActorProxy is used to send network request information from
  * the main process to the child app process. One proxy is used per request.
  * Similarly, one NetworkEventActor in the child app process is used per
@@ -1538,31 +1514,28 @@ NetworkMonitorChild.prototype = {
  * The child process has a NetworkMonitorChild instance that is listening for
  * all network logging from the main process. The net monitor shim is used to
  * proxy the data to the WebConsoleActor instance of the child process.
  *
  * @constructor
  * @param nsIMessageManager messageManager
  *        The message manager for the child app process. This is used for
  *        communication with the NetworkMonitorChild instance of the process.
- * @param string connID
- *        The connection ID to use to send messages to the child process.
  */
-function NetworkEventActorProxy(messageManager, connID) {
+function NetworkEventActorProxy(messageManager) {
   this.id = gSequenceId();
-  this.connID = connID;
   this.messageManager = messageManager;
 }
 exports.NetworkEventActorProxy = NetworkEventActorProxy;
 
 NetworkEventActorProxy.methodFactory = function (method) {
   return DevToolsUtils.makeInfallible(function () {
     let args = Array.slice(arguments);
     let mm = this.messageManager;
-    mm.sendAsyncMessage("debug:netmonitor:" + this.connID + ":updateEvent", {
+    mm.sendAsyncMessage("debug:netmonitor:updateEvent", {
       id: this.id,
       method: method,
       args: args,
     });
   }, "NetworkEventActorProxy." + method);
 };
 
 NetworkEventActorProxy.prototype = {
@@ -1572,17 +1545,17 @@ NetworkEventActorProxy.prototype = {
    *
    * @param object event
    *        Object describing the network request.
    * @return object
    *         This object.
    */
   init: DevToolsUtils.makeInfallible(function (event) {
     let mm = this.messageManager;
-    mm.sendAsyncMessage("debug:netmonitor:" + this.connID + ":newEvent", {
+    mm.sendAsyncMessage("debug:netmonitor:newEvent", {
       id: this.id,
       event: event,
     });
     return this;
   }),
 };
 
 (function () {
@@ -1592,62 +1565,81 @@ NetworkEventActorProxy.prototype = {
                  "addResponseCookies", "addResponseContent", "addEventTimings"];
   let factory = NetworkEventActorProxy.methodFactory;
   for (let method of methods) {
     NetworkEventActorProxy.prototype[method] = factory(method);
   }
 })();
 
 /**
- * The NetworkMonitor manager used by the Webapps actor in the main process.
- * This object uses the message manager to listen for requests from the child
- * process to start/stop the network monitor.
+ * This is triggered by the child calling `setupInParent` when the child's network monitor
+ * is starting up.  This initializes the parent process side of the monitoring.
+ */
+function setupParentProcess({ mm, prefix }) {
+  let networkMonitor = new NetworkMonitorParent(mm, prefix);
+  return {
+    onBrowserSwap: newMM => networkMonitor.setMessageManager(newMM),
+    onDisconnected: () => {
+      networkMonitor.destroy();
+      networkMonitor = null;
+    }
+  };
+}
+
+exports.setupParentProcess = setupParentProcess;
+
+/**
+ * The NetworkMonitorParent runs in the parent process and uses the message manager to
+ * listen for requests from the child process to start/stop the network monitor.  Most
+ * request data is only available from the parent process, so that's why the network
+ * monitor needs to run there when debugging tabs that are in the child.
  *
- * @constructor
- * @param nsIDOMElement frame
- *        The browser frame to work with (mozbrowser).
- * @param string id
- *        Instance identifier to use for messages.
+ * @param nsIMessageManager mm
+ *        The message manager for the browser we're filtering on.
+ * @param string prefix
+ *        The RDP connection prefix that uniquely identifies the connection.
  */
-function NetworkMonitorManager(frame, id) {
-  this.id = id;
-  // Get messageManager from XUL browser (which might be a specialized tunnel for RDM)
-  // or else fallback to asking the frameLoader itself.
-  let mm = frame.messageManager || frame.frameLoader.messageManager;
-  this.messageManager = mm;
-  this.frame = frame;
+function NetworkMonitorParent(mm, prefix) {
   this.onNetMonitorMessage = this.onNetMonitorMessage.bind(this);
   this.onNetworkEvent = this.onNetworkEvent.bind(this);
-
-  mm.addMessageListener("debug:netmonitor:" + id, this.onNetMonitorMessage);
+  this.setMessageManager(mm);
 }
-exports.NetworkMonitorManager = NetworkMonitorManager;
 
-NetworkMonitorManager.prototype = {
+NetworkMonitorParent.prototype = {
   netMonitor: null,
-  frame: null,
   messageManager: null,
 
+  setMessageManager(mm) {
+    if (this.messageManager) {
+      let oldMM = this.messageManager;
+      oldMM.removeMessageListener("debug:netmonitor", this.onNetMonitorMessage);
+    }
+    this.messageManager = mm;
+    if (mm) {
+      mm.addMessageListener("debug:netmonitor", this.onNetMonitorMessage);
+    }
+  },
+
   /**
-   * Handler for "debug:monitor" messages received through the message manager
+   * Handler for "debug:netmonitor" messages received through the message manager
    * from the content process.
    *
    * @param object msg
    *        Message from the content.
    */
   onNetMonitorMessage: DevToolsUtils.makeInfallible(function (msg) {
     let {action} = msg.json;
     // Pipe network monitor data from parent to child via the message manager.
     switch (action) {
       case "start":
         if (!this.netMonitor) {
-          let {appId} = msg.json;
+          let {appId, outerWindowID} = msg.json;
           this.netMonitor = new NetworkMonitor({
-            topFrame: this.frame,
-            appId: appId,
+            outerWindowID,
+            appId,
           }, this);
           this.netMonitor.init();
         }
         break;
       case "setPreferences": {
         let {preferences} = msg.json;
         for (let key of Object.keys(preferences)) {
           if (key == "saveRequestAndResponseBodies" && this.netMonitor) {
@@ -1675,27 +1667,22 @@ NetworkMonitorManager.prototype = {
    * NetworkMonitor instance.
    *
    * @param object event
    *        Object describing the network request.
    * @return object
    *         A NetworkEventActorProxy instance which is notified when further
    *         data about the request is available.
    */
-  onNetworkEvent: DevToolsUtils.makeInfallible(function _onNetworkEvent(event) {
-    return new NetworkEventActorProxy(this.messageManager, this.id).init(event);
+  onNetworkEvent: DevToolsUtils.makeInfallible(function (event) {
+    return new NetworkEventActorProxy(this.messageManager).init(event);
   }),
 
   destroy: function () {
-    if (this.messageManager) {
-      this.messageManager.removeMessageListener("debug:netmonitor:" + this.id,
-                                                this.onNetMonitorMessage);
-    }
-    this.messageManager = null;
-    this.filters = null;
+    this.setMessageManager(null);
 
     if (this.netMonitor) {
       this.netMonitor.destroy();
       this.netMonitor = null;
     }
   },
 };
 
--- a/devtools/shared/webconsole/server-logger-monitor.js
+++ b/devtools/shared/webconsole/server-logger-monitor.js
@@ -4,17 +4,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {Ci} = require("chrome");
 const Services = require("Services");
 
-const {DebuggerServer} = require("devtools/server/main");
 const {makeInfallible} = require("devtools/shared/DevToolsUtils");
 
 loader.lazyGetter(this, "NetworkHelper", () => require("devtools/shared/webconsole/network-helper"));
 
 // Helper tracer. Should be generic sharable by other modules (bug 1171927)
 const trace = {
   log: function (...args) {
   }
@@ -36,71 +35,49 @@ const acceptableHeaders = ["x-chromelogg
  * Read more about the architecture:
  * https://github.com/mozilla/gecko-dev/blob/fx-team/devtools/server/docs/actor-e10s-handling.md
  */
 var ServerLoggerMonitor = {
   // Initialization
 
   initialize: function () {
     this.onChildMessage = this.onChildMessage.bind(this);
-    this.onDisconnectChild = this.onDisconnectChild.bind(this);
     this.onExamineResponse = this.onExamineResponse.bind(this);
 
-    // Set of tracked message managers.
-    this.messageManagers = new Set();
-
     // Set of registered child frames (loggers).
     this.targets = new Set();
   },
 
   // Parent Child Relationship
 
-  attach: makeInfallible(function ({mm, prefix}) {
-    let size = this.messageManagers.size;
-
-    trace.log("ServerLoggerMonitor.attach; ", size, arguments);
+  attach: makeInfallible(function ({ mm, prefix }) {
+    trace.log("ServerLoggerMonitor.attach; ", arguments);
 
-    if (this.messageManagers.has(mm)) {
-      return;
-    }
-
-    this.messageManagers.add(mm);
+    let setMessageManager = newMM => {
+      if (mm) {
+        mm.removeMessageListener("debug:server-logger", this.onChildMessage);
+      }
+      mm = newMM;
+      if (mm) {
+        mm.addMessageListener("debug:server-logger", this.onChildMessage);
+      }
+    };
 
     // Start listening for messages from the {@ServerLogger} actor
     // living in the child process.
-    mm.addMessageListener("debug:server-logger", this.onChildMessage);
-
-    // Listen to the disconnection message to clean-up.
-    DebuggerServer.once("disconnected-from-child:" + prefix,
-      this.onDisconnectChild);
-  }),
-
-  detach: function (mm) {
-    let size = this.messageManagers.size;
-
-    trace.log("ServerLoggerMonitor.detach; ", size);
+    setMessageManager(mm);
 
-    // Unregister message listeners
-    mm.removeMessageListener("debug:server-logger", this.onChildMessage);
-  },
-
-  onDisconnectChild: function (event, mm) {
-    let size = this.messageManagers.size;
-
-    trace.log("ServerLoggerMonitor.onDisconnectChild; ",
-      size, arguments);
-
-    if (!this.messageManagers.has(mm)) {
-      return;
-    }
-
-    this.detach(mm);
-
-    this.messageManagers.delete(mm);
-  },
+    return {
+      onBrowserSwap: setMessageManager,
+      onDisconnected: () => {
+        trace.log("ServerLoggerMonitor.onDisconnectChild; ", arguments);
+        setMessageManager(null);
+      }
+    };
+  }),
 
   // Child Message Handling
 
   onChildMessage: function (msg) {
     let method = msg.data.method;
 
     trace.log("ServerLoggerMonitor.onChildMessage; ", method, msg);
 
@@ -199,16 +176,16 @@ var ServerLoggerMonitor = {
       headers.length, ", ", headers);
   }),
 };
 
 /**
  * Executed automatically by the framework.
  */
 function setupParentProcess(event) {
-  ServerLoggerMonitor.attach(event);
+  return ServerLoggerMonitor.attach(event);
 }
 
 // Monitor initialization.
 ServerLoggerMonitor.initialize();
 
 // Exports from this module
 exports.setupParentProcess = setupParentProcess;
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -8789,21 +8789,28 @@ struct SubDocEnumArgs
 
 void
 nsDocument::EnumerateSubDocuments(nsSubDocEnumFunc aCallback, void *aData)
 {
   if (!mSubDocuments) {
     return;
   }
 
+  // PLDHashTable::Iterator can't handle modifications while iterating so we
+  // copy all entries to an array first before calling any callbacks.
+  AutoTArray<nsCOMPtr<nsIDocument>, 8> subdocs;
   for (auto iter = mSubDocuments->Iter(); !iter.Done(); iter.Next()) {
     auto entry = static_cast<SubDocMapEntry*>(iter.Get());
     nsIDocument* subdoc = entry->mSubDocument;
-    bool next = subdoc ? aCallback(subdoc, aData) : true;
-    if (!next) {
+    if (subdoc) {
+      subdocs.AppendElement(subdoc);
+    }
+  }
+  for (auto subdoc : subdocs) {
+    if (!aCallback(subdoc, aData)) {
       break;
     }
   }
 }
 
 #ifdef DEBUG_bryner
 #define DEBUG_PAGE_CACHE
 #endif
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -3922,17 +3922,17 @@ struct MOZ_STACK_CLASS CanvasBidiProcess
       params.strokeOpts = &strokeOpts;
       params.drawOpts = &drawOpts;
     }
 
     mTextRun->Draw(gfxTextRun::Range(mTextRun.get()), point, params);
   }
 
   // current text run
-  UniquePtr<gfxTextRun> mTextRun;
+  RefPtr<gfxTextRun> mTextRun;
 
   // pointer to a screen reference context used to measure text and such
   RefPtr<DrawTarget> mDrawTarget;
 
   // Pointer to the draw target we should fill our text to
   CanvasRenderingContext2D *mCtx;
 
   // position of the left side of the string, alphabetic baseline
@@ -5039,17 +5039,22 @@ CanvasRenderingContext2D::DrawWindow(nsG
 
   // protect against too-large surfaces that will cause allocation
   // or overflow issues
   if (!Factory::CheckSurfaceSize(IntSize(int32_t(aW), int32_t(aH)), 0xffff)) {
     aError.Throw(NS_ERROR_FAILURE);
     return;
   }
 
-  EnsureTarget();
+  CompositionOp op = UsedOperation();
+  bool discardContent = GlobalAlpha() == 1.0f
+    && (op == CompositionOp::OP_OVER || op == CompositionOp::OP_SOURCE);
+  const gfx::Rect drawRect(aX, aY, aW, aH);
+  EnsureTarget(discardContent ? &drawRect : nullptr);
+
   // We can't allow web apps to call this until we fix at least the
   // following potential security issues:
   // -- rendering cross-domain IFRAMEs and then extracting the results
   // -- rendering the user's theme and then extracting the results
   // -- rendering native anonymous content (e.g., file input paths;
   // scrollbars should be allowed)
   if (!nsContentUtils::IsCallerChrome()) {
     // not permitted to use DrawWindow
@@ -5142,17 +5147,17 @@ CanvasRenderingContext2D::DrawWindow(nsG
     thebes->SetMatrix(gfxMatrix::Scaling(matrix._11, matrix._22));
   }
 
   nsCOMPtr<nsIPresShell> shell = presContext->PresShell();
 
   Unused << shell->RenderDocument(r, renderDocFlags, backgroundColor, thebes);
   // If this canvas was contained in the drawn window, the pre-transaction callback
   // may have returned its DT. If so, we must reacquire it here.
-  EnsureTarget();
+  EnsureTarget(discardContent ? &drawRect : nullptr);
 
   if (drawDT) {
     RefPtr<SourceSurface> snapshot = drawDT->Snapshot();
     if (NS_WARN_IF(!snapshot)) {
       aError.Throw(NS_ERROR_FAILURE);
       return;
     }
     RefPtr<DataSourceSurface> data = snapshot->GetDataSurface();
@@ -5552,17 +5557,16 @@ CanvasRenderingContext2D::PutImageData(I
 }
 
 nsresult
 CanvasRenderingContext2D::PutImageData_explicit(int32_t aX, int32_t aY, uint32_t aW, uint32_t aH,
                                                 dom::Uint8ClampedArray* aArray,
                                                 bool aHasDirtyRect, int32_t aDirtyX, int32_t aDirtyY,
                                                 int32_t aDirtyWidth, int32_t aDirtyHeight)
 {
-  EnsureTarget();
   if (mDrawObserver) {
     mDrawObserver->DidDrawCall(CanvasDrawObserver::DrawCallType::PutImageData);
   }
 
   if (aW == 0 || aH == 0) {
     return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
@@ -5659,16 +5663,22 @@ CanvasRenderingContext2D::PutImageData_e
       *dst++ = gfxUtils::sPremultiplyTable[a * 256 + r];
       *dst++ = gfxUtils::sPremultiplyTable[a * 256 + g];
       *dst++ = gfxUtils::sPremultiplyTable[a * 256 + b];
 #endif
     }
     srcLine += aW * 4;
   }
 
+  // The canvas spec says that the current path, transformation matrix, shadow attributes,
+  // global alpha, the clipping region, and global composition operator must not affect the
+  // getImageData() and putImageData() methods.
+  const gfx::Rect putRect(dirtyRect);
+  EnsureTarget(&putRect);
+
   if (!IsTargetValid()) {
     return NS_ERROR_FAILURE;
   }
 
   RefPtr<SourceSurface> sourceSurface =
     mTarget->CreateSourceSurfaceFromData(imgsurf->Data(), IntSize(copyWidth, copyHeight), imgsurf->Stride(), SurfaceFormat::B8G8R8A8);
 
   // In certain scenarios, requesting larger than 8k image fails.  Bug 803568
--- a/dom/html/HTMLTrackElement.cpp
+++ b/dom/html/HTMLTrackElement.cpp
@@ -125,18 +125,21 @@ private:
   uint64_t mInnerID;
 };
 NS_IMPL_ISUPPORTS(WindowDestroyObserver, nsIObserver);
 
 /** HTMLTrackElement */
 HTMLTrackElement::HTMLTrackElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
   : nsGenericHTMLElement(aNodeInfo)
   , mLoadResourceDispatched(false)
-  , mWindowDestroyObserver(nullptr)
 {
+  nsISupports* parentObject = OwnerDoc()->GetParentObject();
+  NS_ENSURE_TRUE_VOID(parentObject);
+  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(parentObject);
+  mWindowDestroyObserver = new WindowDestroyObserver(this, window->WindowID());
 }
 
 HTMLTrackElement::~HTMLTrackElement()
 {
   if (mWindowDestroyObserver) {
     mWindowDestroyObserver->UnRegisterWindowDestroyObserver();
   }
   NotifyShutdown();
@@ -316,20 +319,16 @@ HTMLTrackElement::LoadResource()
   NS_ENSURE_TRUE_VOID(NS_SUCCEEDED(rv));
   channel->SetNotificationCallbacks(mListener);
 
   LOG(LogLevel::Debug, ("opening webvtt channel"));
   rv = channel->AsyncOpen2(mListener);
   NS_ENSURE_TRUE_VOID(NS_SUCCEEDED(rv));
 
   mChannel = channel;
-  nsISupports* parentObject = OwnerDoc()->GetParentObject();
-  NS_ENSURE_TRUE_VOID(parentObject);
-  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(parentObject);
-  mWindowDestroyObserver = new WindowDestroyObserver(this, window->WindowID());
 }
 
 nsresult
 HTMLTrackElement::BindToTree(nsIDocument* aDocument,
                              nsIContent* aParent,
                              nsIContent* aBindingParent,
                              bool aCompileEventHandlers)
 {
--- a/dom/security/nsCSPParser.cpp
+++ b/dom/security/nsCSPParser.cpp
@@ -923,17 +923,20 @@ nsCSPParser::referrerDirectiveValue(nsCS
   }
 
   // the referrer policy is valid, so go ahead and use it.
   mPolicy->setReferrerPolicy(&mCurDir[1]);
   mPolicy->addDirective(aDir);
 }
 
 void
-nsCSPParser::requireSRIForDirectiveValue(nsRequireSRIForDirective* aDir) {
+nsCSPParser::requireSRIForDirectiveValue(nsRequireSRIForDirective* aDir)
+{
+  CSPPARSERLOG(("nsCSPParser::requireSRIForDirectiveValue"));
+
   // directive-value = "style" / "script"
   // directive name is token 0, we need to examine the remaining tokens
   for (uint32_t i = 1; i < mCurDir.Length(); i++) {
     // mCurToken is only set here and remains the current token
     // to be processed, which avoid passing arguments between functions.
     mCurToken = mCurDir[i];
     resetCurValue();
     CSPPARSERLOG(("nsCSPParser:::directive (require-sri-for directive), "
@@ -951,30 +954,35 @@ nsCSPParser::requireSRIForDirectiveValue
       logWarningErrorToConsole(nsIScriptError::warningFlag, "failedToParseUnrecognizedSource",
                             invalidTokenName, ArrayLength(invalidTokenName));
       CSPPARSERLOG(("nsCSPParser:::directive (require-sri-for directive), "
                     "mCurToken: %s (invalid), mCurValue: %s",
                     NS_ConvertUTF16toUTF8(mCurToken).get(),
                     NS_ConvertUTF16toUTF8(mCurValue).get()));
     }
   }
+
   if (!(aDir->hasType(nsIContentPolicy::TYPE_STYLESHEET)) &&
       !(aDir->hasType(nsIContentPolicy::TYPE_SCRIPT))) {
     const char16_t* directiveName[] = { mCurToken.get() };
     logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringDirectiveWithNoValues",
                                directiveName, ArrayLength(directiveName));
+    delete aDir;
     return;
-  } else {
-    mPolicy->addDirective(aDir);
   }
+  
+  mPolicy->addDirective(aDir);
 }
 
 void
-nsCSPParser::reportURIList(nsTArray<nsCSPBaseSrc*>& outSrcs)
+nsCSPParser::reportURIList(nsCSPDirective* aDir)
 {
+  CSPPARSERLOG(("nsCSPParser::reportURIList"));
+
+  nsTArray<nsCSPBaseSrc*> srcs;
   nsCOMPtr<nsIURI> uri;
   nsresult rv;
 
   // remember, srcs start at index 1
   for (uint32_t i = 1; i < mCurDir.Length(); i++) {
     mCurToken = mCurDir[i];
 
     CSPPARSERLOG(("nsCSPParser::reportURIList, mCurToken: %s, mCurValue: %s",
@@ -988,27 +996,40 @@ nsCSPParser::reportURIList(nsTArray<nsCS
       const char16_t* params[] = { mCurToken.get() };
       logWarningErrorToConsole(nsIScriptError::warningFlag, "couldNotParseReportURI",
                                params, ArrayLength(params));
       continue;
     }
 
     // Create new nsCSPReportURI and append to the list.
     nsCSPReportURI* reportURI = new nsCSPReportURI(uri);
-    outSrcs.AppendElement(reportURI);
+    srcs.AppendElement(reportURI);
   }
+
+  if (srcs.Length() == 0) {
+    const char16_t* directiveName[] = { mCurToken.get() };
+    logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringDirectiveWithNoValues",
+                             directiveName, ArrayLength(directiveName));
+    delete aDir;
+    return;
+  }
+
+  aDir->addSrcs(srcs);
+  mPolicy->addDirective(aDir);
 }
 
 /* Helper function for parsing sandbox flags. This function solely concatenates
  * all the source list tokens (the sandbox flags) so the attribute parser
  * (nsContentUtils::ParseSandboxAttributeToFlags) can parse them.
  */
 void
-nsCSPParser::sandboxFlagList(nsTArray<nsCSPBaseSrc*>& outSrcs)
+nsCSPParser::sandboxFlagList(nsCSPDirective* aDir)
 {
+  CSPPARSERLOG(("nsCSPParser::sandboxFlagList"));
+
   nsAutoString flags;
 
   // remember, srcs start at index 1
   for (uint32_t i = 1; i < mCurDir.Length(); i++) {
     mCurToken = mCurDir[i];
 
     CSPPARSERLOG(("nsCSPParser::sandboxFlagList, mCurToken: %s, mCurValue: %s",
                  NS_ConvertUTF16toUTF8(mCurToken).get(),
@@ -1023,42 +1044,31 @@ nsCSPParser::sandboxFlagList(nsTArray<ns
     }
 
     flags.Append(mCurToken);
     if (i != mCurDir.Length() - 1) {
       flags.AppendASCII(" ");
     }
   }
 
-  nsCSPSandboxFlags* sandboxFlags = new nsCSPSandboxFlags(flags);
-  outSrcs.AppendElement(sandboxFlags);
+  // Please note that the sandbox directive can exist
+  // by itself (not containing any flags).
+  nsTArray<nsCSPBaseSrc*> srcs;
+  srcs.AppendElement(new nsCSPSandboxFlags(flags));
+  aDir->addSrcs(srcs);
+  mPolicy->addDirective(aDir);
 }
 
 // directive-value = *( WSP / <VCHAR except ";" and ","> )
 void
 nsCSPParser::directiveValue(nsTArray<nsCSPBaseSrc*>& outSrcs)
 {
   CSPPARSERLOG(("nsCSPParser::directiveValue"));
 
-  // The tokenzier already generated an array in the form of
-  // [ name, src, src, ... ], no need to parse again, but
-  // special case handling in case the directive is report-uri.
-  if (CSP_IsDirective(mCurDir[0], nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE)) {
-    reportURIList(outSrcs);
-    return;
-  }
-
-  // For the sandbox flag the source list is a list of flags, so we're special
-  // casing this directive
-  if (CSP_IsDirective(mCurDir[0], nsIContentSecurityPolicy::SANDBOX_DIRECTIVE)) {
-    sandboxFlagList(outSrcs);
-    return;
-  }
-
-  // Otherwise just forward to sourceList
+  // Just forward to sourceList
   sourceList(outSrcs);
 }
 
 // directive-name = 1*( ALPHA / DIGIT / "-" )
 nsCSPDirective*
 nsCSPParser::directiveName()
 {
   CSPPARSERLOG(("nsCSPParser::directiveName, mCurToken: %s, mCurValue: %s",
@@ -1207,16 +1217,30 @@ nsCSPParser::directive()
 
   // special case handling of the referrer directive (since it doesn't contain
   // source lists)
   if (cspDir->equals(nsIContentSecurityPolicy::REFERRER_DIRECTIVE)) {
     referrerDirectiveValue(cspDir);
     return;
   }
 
+  // special case handling for report-uri directive (since it doesn't contain
+  // a valid source list but rather actual URIs)
+  if (CSP_IsDirective(mCurDir[0], nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE)) {
+    reportURIList(cspDir);
+    return;
+  }
+
+  // special case handling for sandbox directive (since it doe4sn't contain
+  // a valid source list but rather special sandbox flags)
+  if (CSP_IsDirective(mCurDir[0], nsIContentSecurityPolicy::SANDBOX_DIRECTIVE)) {
+    sandboxFlagList(cspDir);
+    return;
+  }
+
   // make sure to reset cache variables when trying to invalidate unsafe-inline;
   // unsafe-inline might not only appear in script-src, but also in default-src
   mHasHashOrNonce = false;
   mUnsafeInlineKeywordSrc = nullptr;
 
   // Try to parse all the srcs by handing the array off to directiveValue
   nsTArray<nsCSPBaseSrc*> srcs;
   directiveValue(srcs);
--- a/dom/security/nsCSPParser.h
+++ b/dom/security/nsCSPParser.h
@@ -118,16 +118,18 @@ class nsCSPParser {
 
     // Parsing the CSP using the source-list from http://www.w3.org/TR/CSP11/#source-list
     nsCSPPolicy*        policy();
     void                directive();
     nsCSPDirective*     directiveName();
     void                directiveValue(nsTArray<nsCSPBaseSrc*>& outSrcs);
     void                requireSRIForDirectiveValue(nsRequireSRIForDirective* aDir);
     void                referrerDirectiveValue(nsCSPDirective* aDir);
+    void                reportURIList(nsCSPDirective* aDir);
+    void                sandboxFlagList(nsCSPDirective* aDir);
     void                sourceList(nsTArray<nsCSPBaseSrc*>& outSrcs);
     nsCSPBaseSrc*       sourceExpression();
     nsCSPSchemeSrc*     schemeSource();
     nsCSPHostSrc*       hostSource();
     nsCSPBaseSrc*       keywordSource();
     nsCSPNonceSrc*      nonceSource();
     nsCSPHashSrc*       hashSource();
     nsCSPHostSrc*       appHost(); // helper function to support app specific hosts
@@ -137,20 +139,18 @@ class nsCSPParser {
     bool                port();
     bool                path(nsCSPHostSrc* aCspHost);
 
     bool subHost();                                         // helper function to parse subDomains
     bool atValidUnreservedChar();                           // helper function to parse unreserved
     bool atValidSubDelimChar();                             // helper function to parse sub-delims
     bool atValidPctEncodedChar();                           // helper function to parse pct-encoded
     bool subPath(nsCSPHostSrc* aCspHost);                   // helper function to parse paths
-    void reportURIList(nsTArray<nsCSPBaseSrc*>& outSrcs);   // helper function to parse report-uris
     void percentDecodeStr(const nsAString& aEncStr,         // helper function to percent-decode
                           nsAString& outDecStr);
-    void sandboxFlagList(nsTArray<nsCSPBaseSrc*>& outSrcs); // helper function to parse sandbox flags
 
     inline bool atEnd()
     {
       return mCurChar >= mEndChar;
     }
 
     inline bool accept(char16_t aSymbol)
     {
--- a/dom/security/nsCSPUtils.cpp
+++ b/dom/security/nsCSPUtils.cpp
@@ -880,16 +880,17 @@ nsCSPReportURI::toString(nsAString& outS
   outStr.AppendASCII(spec.get());
 }
 
 /* ===== nsCSPSandboxFlags ===================== */
 
 nsCSPSandboxFlags::nsCSPSandboxFlags(const nsAString& aFlags)
   : mFlags(aFlags)
 {
+  ToLowerCase(mFlags);
 }
 
 nsCSPSandboxFlags::~nsCSPSandboxFlags()
 {
 }
 
 bool
 nsCSPSandboxFlags::visit(nsCSPSrcVisitor* aVisitor) const
--- a/dom/security/nsCSPUtils.h
+++ b/dom/security/nsCSPUtils.h
@@ -559,17 +559,20 @@ class nsCSPPolicy {
 
     inline void setReportOnlyFlag(bool aFlag)
       { mReportOnly = aFlag; }
 
     inline bool getReportOnlyFlag() const
       { return mReportOnly; }
 
     inline void setReferrerPolicy(const nsAString* aValue)
-      { mReferrerPolicy = *aValue; }
+      {
+        mReferrerPolicy = *aValue;
+        ToLowerCase(mReferrerPolicy);
+      }
 
     inline void getReferrerPolicy(nsAString& outPolicy) const
       { outPolicy.Assign(mReferrerPolicy); }
 
     void getReportURIs(nsTArray<nsString> &outReportURIs) const;
 
     void getDirectiveStringForContentType(nsContentPolicyType aContentType,
                                           nsAString& outDirective) const;
--- a/dom/security/test/TestCSPParser.cpp
+++ b/dom/security/test/TestCSPParser.cpp
@@ -280,21 +280,23 @@ nsresult TestIgnoreUpperLowerCasePolicie
       "script-src http://test.com" },
     { "script-src 'NoNCE-correctscriptnonce'",
       "script-src 'nonce-correctscriptnonce'" },
     { "script-src 'NoncE-NONCENEEDSTOBEUPPERCASE'",
       "script-src 'nonce-NONCENEEDSTOBEUPPERCASE'" },
     { "script-src 'SHA256-siVR8vAcqP06h2ppeNwqgjr0yZ6yned4X2VF84j4GmI='",
       "script-src 'sha256-siVR8vAcqP06h2ppeNwqgjr0yZ6yned4X2VF84j4GmI='" },
     { "refERRer No-refeRRer",
-      "referrer No-refeRRer" },
+      "referrer no-referrer" },
     { "upgrade-INSECURE-requests",
       "upgrade-insecure-requests" },
+    { "sanDBox alloW-foRMs",
+      "sandbox allow-forms"},
     { "require-SRI-for sCript stYle",
-        "require-sri-for script style"}
+      "require-sri-for script style"},
   };
 
   uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
   return runTestSuite(policies, policyCount, 1);
 }
 
 // ============================= TestPaths ========================
 
@@ -458,17 +460,19 @@ nsresult TestSimplePolicies() {
       "default-src http://127.0.0.1:*" },
     { "default-src -; ",
       "default-src http://-" },
     { "script-src 1",
       "script-src http://1" },
     { "upgrade-insecure-requests",
       "upgrade-insecure-requests" },
     { "upgrade-insecure-requests https:",
-      "upgrade-insecure-requests" }
+      "upgrade-insecure-requests" },
+    { "sandbox allow-scripts allow-forms  ",
+      "sandbox allow-scripts allow-forms" },
   };
 
   uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
   return runTestSuite(policies, policyCount, 1);
 }
 
 // ============================= TestPoliciesWithInvalidSrc ========================
 
@@ -523,18 +527,18 @@ nsresult TestPoliciesWithInvalidSrc() {
     { "script-src http://www.example.com:*.",
       "script-src 'none'" },
     { "connect-src http://www.example.com/foo%zz;",
       "connect-src 'none'" },
     { "script-src https://foo.com/%$",
       "script-src 'none'" },
     { "require-SRI-for script elephants",
       "require-sri-for script"},
-    { "require-sri-for paul",
-      ""}
+    { "sandbox    foo",
+      "sandbox"},
   };
 
   // amount of tests - 1, because the latest should be ignored.
   uint32_t policyCount = (sizeof(policies) / sizeof(PolicyTest)) -1;
   return runTestSuite(policies, policyCount, 1);
 }
 
 // ============================= TestBadPolicies ========================
@@ -545,16 +549,21 @@ nsresult TestBadPolicies() {
   {
     { "script-sr 'self", "" },
     { "", "" },
     { "; ; ; ; ; ; ;", "" },
     { "defaut-src asdf", "" },
     { "default-src: aaa", "" },
     { "asdf http://test.com", ""},
     { "referrer", ""},
+    { "referrer foo", ""},
+    { "require-sri-for", ""},
+    { "require-sri-for foo", ""},
+    { "report-uri", ""},
+    { "report-uri http://:foo", ""},
   };
 
   uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
   return runTestSuite(policies, policyCount, 0);
 }
 
 // ============================= TestGoodGeneratedPolicies ========================
 
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -1530,17 +1530,20 @@ XMLHttpRequestMainThread::OpenInternal(c
   mAuthorRequestHeaders.Clear();
   ResetResponse();
 
   // Gecko-specific
   mFlagHadUploadListenersOnSend = false;
   mFlagAborted = false;
   mFlagTimedOut = false;
 
-  rv = InitChannel();
+  // The channel should really be created on send(), but we have a chrome-only
+  // XHR.channel API which necessitates creating the channel now, while doing
+  // the rest of the channel-setup later at send-time.
+  rv = CreateChannel();
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Step 12
   if (mState != State::opened) {
     mState = State::opened;
     FireReadystatechangeEvent();
   }
 
@@ -2331,17 +2334,17 @@ XMLHttpRequestMainThread::RequestBody<co
 {
   mBody->ComputeLengthAndData();
   return GetBufferDataAsStream(mBody->Data(), mBody->Length(),
                                aResult, aContentLength, aContentType, aCharset);
 }
 
 
 nsresult
-XMLHttpRequestMainThread::InitChannel()
+XMLHttpRequestMainThread::CreateChannel()
 {
   // When we are called from JS we can find the load group for the page,
   // and add ourselves to it. This way any pending requests
   // will be automatically aborted if the user leaves the page.
   nsCOMPtr<nsILoadGroup> loadGroup = GetLoadGroup();
 
   nsSecurityFlags secFlags;
   nsLoadFlags loadFlags = nsIRequest::LOAD_BACKGROUND |
@@ -2405,16 +2408,215 @@ XMLHttpRequestMainThread::InitChannel()
     if (timedChannel) {
       timedChannel->SetInitiatorType(NS_LITERAL_STRING("xmlhttprequest"));
     }
   }
 
   return NS_OK;
 }
 
+nsresult
+XMLHttpRequestMainThread::InitiateFetch(nsIInputStream* aUploadStream,
+                                        int64_t aUploadLength,
+                                        nsACString& aUploadContentType)
+{
+  nsresult rv;
+
+  // nsIRequest::LOAD_BACKGROUND prevents throbber from becoming active, which
+  // in turn keeps STOP button from becoming active.  If the consumer passed in
+  // a progress event handler we must load with nsIRequest::LOAD_NORMAL or
+  // necko won't generate any progress notifications.
+  if (HasListenersFor(nsGkAtoms::onprogress) ||
+      (mUpload && mUpload->HasListenersFor(nsGkAtoms::onprogress))) {
+    nsLoadFlags loadFlags;
+    mChannel->GetLoadFlags(&loadFlags);
+    loadFlags &= ~nsIRequest::LOAD_BACKGROUND;
+    loadFlags |= nsIRequest::LOAD_NORMAL;
+    mChannel->SetLoadFlags(loadFlags);
+  }
+
+  nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
+  if (httpChannel) {
+    // If the user hasn't overridden the Accept header, set it to */* per spec.
+    if (!mAuthorRequestHeaders.Has("accept")) {
+      mAuthorRequestHeaders.Set("accept", NS_LITERAL_CSTRING("*/*"));
+    }
+
+    mAuthorRequestHeaders.ApplyToChannel(httpChannel);
+
+    if (!IsSystemXHR()) {
+      nsCOMPtr<nsPIDOMWindowInner> owner = GetOwner();
+      nsCOMPtr<nsIDocument> doc = owner ? owner->GetExtantDoc() : nullptr;
+      nsContentUtils::SetFetchReferrerURIWithPolicy(mPrincipal, doc,
+                                                    httpChannel,
+                                                    mozilla::net::RP_Default);
+    }
+
+    // Some extensions override the http protocol handler and provide their own
+    // implementation. The channels returned from that implementation don't
+    // always seem to implement the nsIUploadChannel2 interface, presumably
+    // because it's a new interface. Eventually we should remove this and simply
+    // require that http channels implement the new interface (see bug 529041).
+    nsCOMPtr<nsIUploadChannel2> uploadChannel2 = do_QueryInterface(httpChannel);
+    if (!uploadChannel2) {
+      nsCOMPtr<nsIConsoleService> consoleService =
+        do_GetService(NS_CONSOLESERVICE_CONTRACTID);
+      if (consoleService) {
+        consoleService->LogStringMessage(NS_LITERAL_STRING(
+          "Http channel implementation doesn't support nsIUploadChannel2. "
+          "An extension has supplied a non-functional http protocol handler. "
+          "This will break behavior and in future releases not work at all."
+        ).get());
+      }
+    }
+
+    if (aUploadStream) {
+      // If necessary, wrap the stream in a buffered stream so as to guarantee
+      // support for our upload when calling ExplicitSetUploadStream.
+      nsCOMPtr<nsIInputStream> bufferedStream;
+      if (!NS_InputStreamIsBuffered(aUploadStream)) {
+        rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream),
+                                       aUploadStream, 4096);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        aUploadStream = bufferedStream;
+      }
+
+      // We want to use a newer version of the upload channel that won't
+      // ignore the necessary headers for an empty Content-Type.
+      nsCOMPtr<nsIUploadChannel2> uploadChannel2(do_QueryInterface(httpChannel));
+      // This assertion will fire if buggy extensions are installed
+      NS_ASSERTION(uploadChannel2, "http must support nsIUploadChannel2");
+      if (uploadChannel2) {
+          uploadChannel2->ExplicitSetUploadStream(aUploadStream,
+                                                  aUploadContentType,
+                                                  mUploadTotal, mRequestMethod,
+                                                  false);
+      } else {
+        // The http channel doesn't support the new nsIUploadChannel2.
+        // Emulate it as best we can using nsIUploadChannel.
+        if (aUploadContentType.IsEmpty()) {
+          aUploadContentType.AssignLiteral("application/octet-stream");
+        }
+        nsCOMPtr<nsIUploadChannel> uploadChannel =
+          do_QueryInterface(httpChannel);
+        uploadChannel->SetUploadStream(aUploadStream, aUploadContentType,
+                                       mUploadTotal);
+        // Reset the method to its original value
+        httpChannel->SetRequestMethod(mRequestMethod);
+      }
+    }
+  }
+
+  // Due to the chrome-only XHR.channel API, we need a hacky way to set the
+  // SEC_COOKIES_INCLUDE *after* the channel has been has been created, since
+  // .withCredentials can be called after open() is called.
+  // Not doing this for privileged system XHRs since those don't use CORS.
+  if (!IsSystemXHR() && !mIsAnon && mFlagACwithCredentials) {
+    nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
+    static_cast<net::LoadInfo*>(loadInfo.get())->SetIncludeCookiesSecFlag();
+  }
+
+  // Blocking gets are common enough out of XHR that we should mark
+  // the channel slow by default for pipeline purposes
+  AddLoadFlags(mChannel, nsIRequest::INHIBIT_PIPELINE);
+
+  // We never let XHR be blocked by head CSS/JS loads to avoid potential
+  // deadlock where server generation of CSS/JS requires an XHR signal.
+  nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(mChannel));
+  if (cos) {
+    cos->AddClassFlags(nsIClassOfService::Unblocked);
+  }
+
+  // Disable Necko-internal response timeouts.
+  nsCOMPtr<nsIHttpChannelInternal>
+    internalHttpChannel(do_QueryInterface(mChannel));
+  if (internalHttpChannel) {
+    internalHttpChannel->SetResponseTimeoutEnabled(false);
+  }
+
+  if (!mIsAnon) {
+    AddLoadFlags(mChannel, nsIChannel::LOAD_EXPLICIT_CREDENTIALS);
+  }
+
+  // Bypass the network cache in cases where it makes no sense:
+  // POST responses are always unique, and we provide no API that would
+  // allow our consumers to specify a "cache key" to access old POST
+  // responses, so they are not worth caching.
+  if (mRequestMethod.EqualsLiteral("POST")) {
+    AddLoadFlags(mChannel,
+                 nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE |
+                 nsIRequest::INHIBIT_CACHING);
+  } else {
+    // When we are sync loading, we need to bypass the local cache when it would
+    // otherwise block us waiting for exclusive access to the cache.  If we don't
+    // do this, then we could dead lock in some cases (see bug 309424).
+    //
+    // Also don't block on the cache entry on async if it is busy - favoring parallelism
+    // over cache hit rate for xhr. This does not disable the cache everywhere -
+    // only in cases where more than one channel for the same URI is accessed
+    // simultanously.
+    AddLoadFlags(mChannel, nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
+  }
+
+  // Since we expect XML data, set the type hint accordingly
+  // if the channel doesn't know any content type.
+  // This means that we always try to parse local files as XML
+  // ignoring return value, as this is not critical
+  nsAutoCString contentType;
+  if (NS_FAILED(mChannel->GetContentType(contentType)) ||
+      contentType.IsEmpty() ||
+      contentType.Equals(UNKNOWN_CONTENT_TYPE)) {
+    mChannel->SetContentType(NS_LITERAL_CSTRING("application/xml"));
+  }
+
+  // Set up the preflight if needed
+  if (!IsSystemXHR()) {
+    nsTArray<nsCString> CORSUnsafeHeaders;
+    mAuthorRequestHeaders.GetCORSUnsafeHeaders(CORSUnsafeHeaders);
+    nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
+    loadInfo->SetCorsPreflightInfo(CORSUnsafeHeaders,
+                                   mFlagHadUploadListenersOnSend);
+  }
+
+  // Hook us up to listen to redirects and the like. Only do this very late
+  // since this creates a cycle between the channel and us. This cycle has
+  // to be manually broken if anything below fails.
+  mChannel->GetNotificationCallbacks(getter_AddRefs(mNotificationCallbacks));
+  mChannel->SetNotificationCallbacks(this);
+
+  if (internalHttpChannel) {
+    internalHttpChannel->SetBlockAuthPrompt(ShouldBlockAuthPrompt());
+  }
+
+  // Because of bug 682305, we can't let listener be the XHR object itself
+  // because JS wouldn't be able to use it. So create a listener around 'this'.
+  // Make sure to hold a strong reference so that we don't leak the wrapper.
+  nsCOMPtr<nsIStreamListener> listener = new net::nsStreamListenerWrapper(this);
+
+  // Start reading from the channel
+  rv = mChannel->AsyncOpen2(listener);
+  listener = nullptr;
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    // Drop our ref to the channel to avoid cycles. Also drop channel's
+    // ref to us to be extra safe.
+    mChannel->SetNotificationCallbacks(mNotificationCallbacks);
+    mChannel = nullptr;
+
+    mErrorLoad = true;
+
+    // Per spec, we throw on sync errors, but not async.
+    if (mFlagSynchronous) {
+      return rv;
+    }
+  }
+
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 XMLHttpRequestMainThread::Send(nsIVariant* aVariant)
 {
   if (!aVariant) {
     return SendInternal(nullptr);
   }
 
   uint16_t dataType;
@@ -2503,360 +2705,104 @@ XMLHttpRequestMainThread::SendInternal(c
   }
 
   if (mState != State::opened || // Step 1
       mFlagSend || // Step 2
       !mChannel) { // Gecko-specific
     return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
-  // nsIRequest::LOAD_BACKGROUND prevents throbber from becoming active, which
-  // in turn keeps STOP button from becoming active.  If the consumer passed in
-  // a progress event handler we must load with nsIRequest::LOAD_NORMAL or
-  // necko won't generate any progress notifications.
-  if (HasListenersFor(nsGkAtoms::onprogress) ||
-      (mUpload && mUpload->HasListenersFor(nsGkAtoms::onprogress))) {
-    nsLoadFlags loadFlags;
-    mChannel->GetLoadFlags(&loadFlags);
-    loadFlags &= ~nsIRequest::LOAD_BACKGROUND;
-    loadFlags |= nsIRequest::LOAD_NORMAL;
-    mChannel->SetLoadFlags(loadFlags);
-  }
-
   // XXX We should probably send a warning to the JS console
   //     if there are no event listeners set and we are doing
   //     an asynchronous call.
 
-  // Ignore argument if method is GET, there is no point in trying to
-  // upload anything
-  nsAutoCString method;
-  nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
-
-  if (httpChannel) {
-    // Spec step 5
-    SetAuthorRequestHeadersOnChannel(httpChannel);
-
-    httpChannel->GetRequestMethod(method); // If GET, method name will be uppercase
-
-    if (!IsSystemXHR()) {
-      nsCOMPtr<nsPIDOMWindowInner> owner = GetOwner();
-      nsCOMPtr<nsIDocument> doc = owner ? owner->GetExtantDoc() : nullptr;
-      nsContentUtils::SetFetchReferrerURIWithPolicy(mPrincipal, doc,
-                                                    httpChannel, mozilla::net::RP_Default);
-    }
-
-    // If the user hasn't overridden the Accept header, set it to */* as per spec
-    nsAutoCString acceptHeader;
-    GetAuthorRequestHeaderValue("accept", acceptHeader);
-    if (acceptHeader.IsVoid()) {
-      httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
-                                    NS_LITERAL_CSTRING("*/*"),
-                                    false);
-    }
-
-    // Some extensions override the http protocol handler and provide their own
-    // implementation. The channels returned from that implementation doesn't
-    // seem to always implement the nsIUploadChannel2 interface, presumably
-    // because it's a new interface.
-    // Eventually we should remove this and simply require that http channels
-    // implement the new interface.
-    // See bug 529041
-    nsCOMPtr<nsIUploadChannel2> uploadChannel2 =
-      do_QueryInterface(httpChannel);
-    if (!uploadChannel2) {
-      nsCOMPtr<nsIConsoleService> consoleService =
-        do_GetService(NS_CONSOLESERVICE_CONTRACTID);
-      if (consoleService) {
-        consoleService->LogStringMessage(NS_LITERAL_STRING(
-          "Http channel implementation doesn't support nsIUploadChannel2. An extension has supplied a non-functional http protocol handler. This will break behavior and in future releases not work at all."
-                                                           ).get());
-      }
-    }
-  }
-
   mUploadTransferred = 0;
   mUploadTotal = 0;
   // By default we don't have any upload, so mark upload complete.
   mUploadComplete = true;
   mErrorLoad = false;
   mLoadTotal = 0;
+  nsCOMPtr<nsIInputStream> uploadStream;
+  nsAutoCString uploadContentType;
+  nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
   if (aBody && httpChannel &&
-      !method.LowerCaseEqualsLiteral("get") &&
-      !method.LowerCaseEqualsLiteral("head")) {
+      !mRequestMethod.EqualsLiteral("GET") &&
+      !mRequestMethod.EqualsLiteral("HEAD")) {
 
     nsAutoCString charset;
     nsAutoCString defaultContentType;
-    nsCOMPtr<nsIInputStream> postDataStream;
-
     uint64_t size_u64;
-    rv = aBody->GetAsStream(getter_AddRefs(postDataStream),
+    rv = aBody->GetAsStream(getter_AddRefs(uploadStream),
                             &size_u64, defaultContentType, charset);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // make sure it fits within js MAX_SAFE_INTEGER
     mUploadTotal =
       net::InScriptableRange(size_u64) ? static_cast<int64_t>(size_u64) : -1;
 
-    if (postDataStream) {
+    if (uploadStream) {
       // If author set no Content-Type, use the default from GetAsStream().
-      nsAutoCString contentType;
-
-      GetAuthorRequestHeaderValue("content-type", contentType);
-      if (contentType.IsVoid()) {
-        contentType = defaultContentType;
+      mAuthorRequestHeaders.Get("content-type", uploadContentType);
+      if (uploadContentType.IsVoid()) {
+        uploadContentType = defaultContentType;
 
         if (!charset.IsEmpty()) {
           // If we are providing the default content type, then we also need to
           // provide a charset declaration.
-          contentType.Append(NS_LITERAL_CSTRING(";charset="));
-          contentType.Append(charset);
+          uploadContentType.Append(NS_LITERAL_CSTRING(";charset="));
+          uploadContentType.Append(charset);
         }
       }
 
       // We don't want to set a charset for streams.
       if (!charset.IsEmpty()) {
-        nsAutoCString specifiedCharset;
-        bool haveCharset;
-        int32_t charsetStart, charsetEnd;
-        rv = NS_ExtractCharsetFromContentType(contentType, specifiedCharset,
-                                              &haveCharset, &charsetStart,
-                                              &charsetEnd);
-        while (NS_SUCCEEDED(rv) && haveCharset) {
-          // special case: the extracted charset is quoted with single quotes
-          // -- for the purpose of preserving what was set we want to handle
-          // them as delimiters (although they aren't really)
-          if (specifiedCharset.Length() >= 2 &&
-              specifiedCharset.First() == '\'' &&
-              specifiedCharset.Last() == '\'') {
-            specifiedCharset = Substring(specifiedCharset, 1,
-                                         specifiedCharset.Length() - 2);
+        // Replace all case-insensitive matches of the charset in the
+        // content-type with the correct case.
+        RequestHeaders::CharsetIterator iter(uploadContentType);
+        const nsCaseInsensitiveCStringComparator cmp;
+        while (iter.Next()) {
+          if (!iter.Equals(charset, cmp)) {
+            iter.Replace(charset);
           }
-
-          // If the content-type the page set already has a charset parameter,
-          // and it's the same charset, up to case, as |charset|, just send the
-          // page-set content-type header.  Apparently at least
-          // google-web-toolkit is broken and relies on the exact case of its
-          // charset parameter, which makes things break if we use |charset|
-          // (which is always a fully resolved charset per our charset alias
-          // table, hence might be differently cased).
-          if (!specifiedCharset.Equals(charset,
-                                       nsCaseInsensitiveCStringComparator())) {
-            // Find the start of the actual charset declaration, skipping the
-            // "; charset=" to avoid modifying whitespace.
-            int32_t charIdx =
-              Substring(contentType, charsetStart,
-                        charsetEnd - charsetStart).FindChar('=') + 1;
-            MOZ_ASSERT(charIdx != -1);
-
-            contentType.Replace(charsetStart + charIdx,
-                                charsetEnd - charsetStart - charIdx,
-                                charset);
-          }
-
-          // Look for another charset declaration in the string, limiting the
-          // search to only look for charsets before the current charset, to
-          // prevent finding the same charset twice.
-          nsDependentCSubstring interestingSection =
-            Substring(contentType, 0, charsetStart);
-          rv = NS_ExtractCharsetFromContentType(interestingSection,
-                                                specifiedCharset, &haveCharset,
-                                                &charsetStart, &charsetEnd);
         }
       }
 
-      // If necessary, wrap the stream in a buffered stream so as to guarantee
-      // support for our upload when calling ExplicitSetUploadStream.
-      if (!NS_InputStreamIsBuffered(postDataStream)) {
-        nsCOMPtr<nsIInputStream> bufferedStream;
-        rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream),
-                                       postDataStream,
-                                       4096);
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        postDataStream = bufferedStream;
-      }
-
       mUploadComplete = false;
-
-      // We want to use a newer version of the upload channel that won't
-      // ignore the necessary headers for an empty Content-Type.
-      nsCOMPtr<nsIUploadChannel2> uploadChannel2(do_QueryInterface(httpChannel));
-      // This assertion will fire if buggy extensions are installed
-      NS_ASSERTION(uploadChannel2, "http must support nsIUploadChannel2");
-      if (uploadChannel2) {
-          uploadChannel2->ExplicitSetUploadStream(postDataStream, contentType,
-                                                 mUploadTotal, method, false);
-      }
-      else {
-        // http channel doesn't support the new nsIUploadChannel2. Emulate
-        // as best we can using nsIUploadChannel
-        if (contentType.IsEmpty()) {
-          contentType.AssignLiteral("application/octet-stream");
-        }
-        nsCOMPtr<nsIUploadChannel> uploadChannel =
-          do_QueryInterface(httpChannel);
-        uploadChannel->SetUploadStream(postDataStream, contentType, mUploadTotal);
-        // Reset the method to its original value
-        httpChannel->SetRequestMethod(method);
-      }
     }
   }
 
   ResetResponse();
 
-  if (!IsSystemXHR() && !mIsAnon && mFlagACwithCredentials) {
-    // This is quite sad. We have to create the channel in .open(), since the
-    // chrome-only xhr.channel API depends on that. However .withCredentials
-    // can be modified after, so we don't know what to set the
-    // SEC_COOKIES_INCLUDE flag to when the channel is
-    // created. So set it here using a hacky internal API.
-
-    // Not doing this for system XHR uses since those don't use CORS.
-    nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
-    static_cast<net::LoadInfo*>(loadInfo.get())->SetIncludeCookiesSecFlag();
-  }
-
-  // Blocking gets are common enough out of XHR that we should mark
-  // the channel slow by default for pipeline purposes
-  AddLoadFlags(mChannel, nsIRequest::INHIBIT_PIPELINE);
-
-  nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(mChannel));
-  if (cos) {
-    // we never let XHR be blocked by head CSS/JS loads to avoid
-    // potential deadlock where server generation of CSS/JS requires
-    // an XHR signal.
-    cos->AddClassFlags(nsIClassOfService::Unblocked);
-  }
-
-  nsCOMPtr<nsIHttpChannelInternal>
-    internalHttpChannel(do_QueryInterface(mChannel));
-  if (internalHttpChannel) {
-    // Disable Necko-internal response timeouts.
-    internalHttpChannel->SetResponseTimeoutEnabled(false);
-  }
-
-  if (!mIsAnon) {
-    AddLoadFlags(mChannel, nsIChannel::LOAD_EXPLICIT_CREDENTIALS);
-  }
-
-  // Bypass the network cache in cases where it makes no sense:
-  // POST responses are always unique, and we provide no API that would
-  // allow our consumers to specify a "cache key" to access old POST
-  // responses, so they are not worth caching.
-  if (method.EqualsLiteral("POST")) {
-    AddLoadFlags(mChannel,
-                 nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE |
-                 nsIRequest::INHIBIT_CACHING);
-  } else {
-    // When we are sync loading, we need to bypass the local cache when it would
-    // otherwise block us waiting for exclusive access to the cache.  If we don't
-    // do this, then we could dead lock in some cases (see bug 309424).
-    //
-    // Also don't block on the cache entry on async if it is busy - favoring parallelism
-    // over cache hit rate for xhr. This does not disable the cache everywhere -
-    // only in cases where more than one channel for the same URI is accessed
-    // simultanously.
-
-    AddLoadFlags(mChannel,
-                 nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
-  }
-
-  // Since we expect XML data, set the type hint accordingly
-  // if the channel doesn't know any content type.
-  // This means that we always try to parse local files as XML
-  // ignoring return value, as this is not critical
-  nsAutoCString contentType;
-  if (NS_FAILED(mChannel->GetContentType(contentType)) ||
-      contentType.IsEmpty() ||
-      contentType.Equals(UNKNOWN_CONTENT_TYPE)) {
-    mChannel->SetContentType(NS_LITERAL_CSTRING("application/xml"));
-  }
-
-  // We're about to send the request.  Start our timeout.
-  mRequestSentTime = PR_Now();
-  StartTimeoutTimer();
-
-  // Check if we should enabled cross-origin upload listeners.
+  // Check if we should enable cross-origin upload listeners.
   if (mUpload && mUpload->HasListeners()) {
     mFlagHadUploadListenersOnSend = true;
   }
 
-  // Set up the preflight if needed
-  if (!IsSystemXHR()) {
-    nsTArray<nsCString> CORSUnsafeHeaders;
-    const char *kCrossOriginSafeHeaders[] = {
-      "accept", "accept-language", "content-language", "content-type",
-      "last-event-id"
-    };
-    for (RequestHeader& header : mAuthorRequestHeaders) {
-      bool safe = false;
-      for (uint32_t i = 0; i < ArrayLength(kCrossOriginSafeHeaders); ++i) {
-        if (header.name.LowerCaseEqualsASCII(kCrossOriginSafeHeaders[i])) {
-          safe = true;
-          break;
-        }
-      }
-      if (!safe) {
-        CORSUnsafeHeaders.AppendElement(header.name);
-      }
-    }
-
-    nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
-    loadInfo->SetCorsPreflightInfo(CORSUnsafeHeaders,
-                                   mFlagHadUploadListenersOnSend);
-  }
-
   mIsMappedArrayBuffer = false;
   if (mResponseType == XMLHttpRequestResponseType::Arraybuffer &&
       Preferences::GetBool("dom.mapped_arraybuffer.enabled", true)) {
     nsCOMPtr<nsIURI> uri;
     nsAutoCString scheme;
 
     rv = mChannel->GetURI(getter_AddRefs(uri));
     if (NS_SUCCEEDED(rv)) {
       uri->GetScheme(scheme);
       if (scheme.LowerCaseEqualsLiteral("app") ||
           scheme.LowerCaseEqualsLiteral("jar")) {
         mIsMappedArrayBuffer = true;
       }
     }
   }
 
-  // Hook us up to listen to redirects and the like
-  // Only do this very late since this creates a cycle between
-  // the channel and us. This cycle has to be manually broken if anything
-  // below fails.
-  mChannel->GetNotificationCallbacks(getter_AddRefs(mNotificationCallbacks));
-  mChannel->SetNotificationCallbacks(this);
-
-  if (internalHttpChannel) {
-    internalHttpChannel->SetBlockAuthPrompt(ShouldBlockAuthPrompt());
-  }
-
-  // Start reading from the channel
-  // Because of bug 682305, we can't let listener be the XHR object itself
-  // because JS wouldn't be able to use it. So create a listener around 'this'.
-  // Make sure to hold a strong reference so that we don't leak the wrapper.
-  nsCOMPtr<nsIStreamListener> listener = new net::nsStreamListenerWrapper(this);
-  rv = mChannel->AsyncOpen2(listener);
-  listener = nullptr;
-
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    // Drop our ref to the channel to avoid cycles. Also drop channel's
-    // ref to us to be extra safe.
-    mChannel->SetNotificationCallbacks(mNotificationCallbacks);
-    mChannel = nullptr;
-
-    mErrorLoad = true;
-
-    // Per spec, we throw on sync errors, but not async.
-    if (mFlagSynchronous) {
-      return rv;
-    }
-  }
+  rv = InitiateFetch(uploadStream, mUploadTotal, uploadContentType);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Start our timeout
+  mRequestSentTime = PR_Now();
+  StartTimeoutTimer();
 
   mWaitingForOnStopRequest = true;
 
   // Step 8
   mFlagSend = true;
 
   // If we're synchronous, spin an event loop here and wait
   if (mFlagSynchronous) {
@@ -2963,40 +2909,23 @@ XMLHttpRequestMainThread::SetRequestHead
     LogMessage("ForbiddenHeaderWarning", GetOwner(), params, ArrayLength(params));
     return NS_OK;
   }
 
   // Step 6.1
   // Skipping for now, as normalizing the case of header names may not be
   // web-compatible. See bug 1285036.
 
-  // Step 6.2
-  bool notAlreadySet = true;
-  const nsCaseInsensitiveCStringComparator ignoreCase;
-  for (RequestHeader& header : mAuthorRequestHeaders) {
-    if (header.name.Equals(aName, ignoreCase)) {
-      // Gecko-specific: invalid headers can be set by privileged
-      //                 callers, but will not merge.
-      if (isPrivilegedCaller && isForbiddenHeader) {
-        header.value.Assign(value);
-      } else {
-        header.value.AppendLiteral(", ");
-        header.value.Append(value);
-      }
-      notAlreadySet = false;
-      break;
-    }
-  }
-
-  // Step 6.3
-  if (notAlreadySet) {
-    RequestHeader newHeader = {
-      nsCString(aName), nsCString(value)
-    };
-    mAuthorRequestHeaders.AppendElement(newHeader);
+  // Step 6.2-6.3
+  // Gecko-specific: invalid headers can be set by privileged
+  //                 callers, but will not merge.
+  if (isPrivilegedCaller && isForbiddenHeader) {
+    mAuthorRequestHeaders.Set(aName, value);
+  } else {
+    mAuthorRequestHeaders.MergeOrSet(aName, value);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 XMLHttpRequestMainThread::GetTimeout(uint32_t *aTimeout)
 {
@@ -3227,55 +3156,29 @@ XMLHttpRequestMainThread::AsyncOnChannel
         mNewRedirectChannel = nullptr;
     }
     return rv;
   }
   OnRedirectVerifyCallback(NS_OK);
   return NS_OK;
 }
 
-void
-XMLHttpRequestMainThread::GetAuthorRequestHeaderValue(const char* aName,
-                                                      nsACString& outValue)
-{
-  for (RequestHeader& header : mAuthorRequestHeaders) {
-    if (header.name.EqualsIgnoreCase(aName)) {
-      outValue.Assign(header.value);
-      return;
-    }
-  }
-  outValue.SetIsVoid(true);
-}
-
-void
-XMLHttpRequestMainThread::SetAuthorRequestHeadersOnChannel(
-  nsCOMPtr<nsIHttpChannel> aHttpChannel)
-{
-  for (RequestHeader& header : mAuthorRequestHeaders) {
-    if (header.value.IsEmpty()) {
-      aHttpChannel->SetEmptyRequestHeader(header.name);
-    } else {
-      aHttpChannel->SetRequestHeader(header.name, header.value, false);
-    }
-  }
-}
-
 nsresult
 XMLHttpRequestMainThread::OnRedirectVerifyCallback(nsresult result)
 {
   NS_ASSERTION(mRedirectCallback, "mRedirectCallback not set in callback");
   NS_ASSERTION(mNewRedirectChannel, "mNewRedirectChannel not set in callback");
 
   if (NS_SUCCEEDED(result)) {
     mChannel = mNewRedirectChannel;
 
     nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
     if (httpChannel) {
       // Ensure all original headers are duplicated for the new channel (bug #553888)
-      SetAuthorRequestHeadersOnChannel(httpChannel);
+      mAuthorRequestHeaders.ApplyToChannel(httpChannel);
     }
   } else {
     mErrorLoad = true;
   }
 
   mNewRedirectChannel = nullptr;
 
   mRedirectCallback->OnRedirectVerifyCallback(result);
@@ -3558,19 +3461,17 @@ XMLHttpRequestMainThread::EnsureXPCOMifi
 }
 
 bool
 XMLHttpRequestMainThread::ShouldBlockAuthPrompt()
 {
   // Verify that it's ok to prompt for credentials here, per spec
   // http://xhr.spec.whatwg.org/#the-send%28%29-method
 
-  nsAutoCString contentType;
-  GetAuthorRequestHeaderValue("authorization", contentType);
-  if (!contentType.IsVoid()) {
+  if (mAuthorRequestHeaders.Has("authorization")) {
     return true;
   }
 
   nsCOMPtr<nsIURI> uri;
   nsresult rv = mChannel->GetURI(getter_AddRefs(uri));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return false;
   }
@@ -3832,10 +3733,197 @@ ArrayBufferBuilder::areOverlappingRegion
   const uint8_t* end2 = aStart2 + aLength2;
 
   const uint8_t* max_start = aStart1 > aStart2 ? aStart1 : aStart2;
   const uint8_t* min_end   = end1 < end2 ? end1 : end2;
 
   return max_start < min_end;
 }
 
+RequestHeaders::RequestHeader*
+RequestHeaders::Find(const nsACString& aName)
+{
+  const nsCaseInsensitiveCStringComparator ignoreCase;
+  for (RequestHeaders::RequestHeader& header : mHeaders) {
+    if (header.mName.Equals(aName, ignoreCase)) {
+      return &header;
+    }
+  }
+  return nullptr;
+}
+
+bool
+RequestHeaders::Has(const char* aName)
+{
+  return Has(nsDependentCString(aName));
+}
+
+bool
+RequestHeaders::Has(const nsACString& aName)
+{
+  return !!Find(aName);
+}
+
+void
+RequestHeaders::Get(const char* aName, nsACString& aValue)
+{
+  Get(nsDependentCString(aName), aValue);
+}
+
+void
+RequestHeaders::Get(const nsACString& aName, nsACString& aValue)
+{
+  RequestHeader* header = Find(aName);
+  if (header) {
+    aValue = header->mValue;
+  } else {
+    aValue.SetIsVoid(true);
+  }
+}
+
+void
+RequestHeaders::Set(const char* aName, const nsACString& aValue)
+{
+  Set(nsDependentCString(aName), aValue);
+}
+
+void
+RequestHeaders::Set(const nsACString& aName, const nsACString& aValue)
+{
+  RequestHeader* header = Find(aName);
+  if (header) {
+    header->mValue.Assign(aValue);
+  } else {
+    RequestHeader newHeader = {
+      nsCString(aName), nsCString(aValue)
+    };
+    mHeaders.AppendElement(newHeader);
+  }
+}
+
+void
+RequestHeaders::MergeOrSet(const char* aName, const nsACString& aValue)
+{
+  MergeOrSet(nsDependentCString(aName), aValue);
+}
+
+void
+RequestHeaders::MergeOrSet(const nsACString& aName, const nsACString& aValue)
+{
+  RequestHeader* header = Find(aName);
+  if (header) {
+    header->mValue.AppendLiteral(", ");
+    header->mValue.Append(aValue);
+  } else {
+    RequestHeader newHeader = {
+      nsCString(aName), nsCString(aValue)
+    };
+    mHeaders.AppendElement(newHeader);
+  }
+}
+
+void
+RequestHeaders::Clear()
+{
+  mHeaders.Clear();
+}
+
+void
+RequestHeaders::ApplyToChannel(nsIHttpChannel* aHttpChannel) const
+{
+  for (const RequestHeader& header : mHeaders) {
+    if (header.mValue.IsEmpty()) {
+      aHttpChannel->SetEmptyRequestHeader(header.mName);
+    } else {
+      aHttpChannel->SetRequestHeader(header.mName, header.mValue, false);
+    }
+  }
+}
+
+void
+RequestHeaders::GetCORSUnsafeHeaders(nsTArray<nsCString>& aArray) const
+{
+  static const char *kCrossOriginSafeHeaders[] = {
+    "accept", "accept-language", "content-language", "content-type",
+    "last-event-id"
+  };
+  const uint32_t kCrossOriginSafeHeadersLength =
+    ArrayLength(kCrossOriginSafeHeaders);
+  for (const RequestHeader& header : mHeaders) {
+    bool safe = false;
+    for (uint32_t i = 0; i < kCrossOriginSafeHeadersLength; ++i) {
+      if (header.mName.LowerCaseEqualsASCII(kCrossOriginSafeHeaders[i])) {
+        safe = true;
+        break;
+      }
+    }
+    if (!safe) {
+      aArray.AppendElement(header.mName);
+    }
+  }
+}
+
+RequestHeaders::CharsetIterator::CharsetIterator(nsACString& aSource) :
+  mValid(false),
+  mCurPos(-1),
+  mCurLen(-1),
+  mCutoff(aSource.Length()),
+  mSource(aSource)
+{
+}
+
+bool
+RequestHeaders::CharsetIterator::Equals(const nsACString& aOther,
+                                        const nsCStringComparator& aCmp) const
+{
+  if (mValid) {
+    return Substring(mSource, mCurPos, mCurLen).Equals(aOther, aCmp);
+  } else {
+    return false;
+  }
+}
+
+void
+RequestHeaders::CharsetIterator::Replace(const nsACString& aReplacement)
+{
+  if (mValid) {
+    mSource.Replace(mCurPos, mCurLen, aReplacement);
+    mCurLen = aReplacement.Length();
+  }
+}
+
+bool
+RequestHeaders::CharsetIterator::Next()
+{
+  int32_t start, end;
+  nsAutoCString charset;
+
+  // Look for another charset declaration in the string, limiting the
+  // search to only the characters before the parts we've already searched
+  // (before mCutoff), so that we don't find the same charset twice.
+  NS_ExtractCharsetFromContentType(Substring(mSource, 0, mCutoff),
+                                   charset, &mValid, &start, &end);
+
+  if (!mValid) {
+    return false;
+  }
+
+  // Everything after the = sign is the part of the charset we want.
+  mCurPos = mSource.FindChar('=', start) + 1;
+  mCurLen = end - mCurPos;
+
+  // Special case: the extracted charset is quoted with single quotes.
+  // For the purpose of preserving what was set we want to handle them
+  // as delimiters (although they aren't really).
+  if (charset.Length() >= 2 &&
+      charset.First() == '\'' &&
+      charset.Last() == '\'') {
+    ++mCurPos;
+    mCurLen -= 2;
+  }
+
+  mCutoff = start;
+
+  return true;
+}
+
 } // dom namespace
 } // mozilla namespaceo
--- a/dom/xhr/XMLHttpRequestMainThread.h
+++ b/dom/xhr/XMLHttpRequestMainThread.h
@@ -107,16 +107,53 @@ public:
 
 protected:
   static bool areOverlappingRegions(const uint8_t* aStart1, uint32_t aLength1,
                                     const uint8_t* aStart2, uint32_t aLength2);
 };
 
 class nsXMLHttpRequestXPCOMifier;
 
+class RequestHeaders
+{
+  struct RequestHeader
+  {
+    nsCString mName;
+    nsCString mValue;
+  };
+  nsTArray<RequestHeader> mHeaders;
+  RequestHeader* Find(const nsACString& aName);
+
+public:
+  class CharsetIterator
+  {
+    bool mValid;
+    int32_t mCurPos, mCurLen, mCutoff;
+    nsACString& mSource;
+
+  public:
+    explicit CharsetIterator(nsACString& aSource);
+    bool Equals(const nsACString& aOther, const nsCStringComparator& aCmp) const;
+    void Replace(const nsACString& aReplacement);
+    bool Next();
+  };
+
+  bool Has(const char* aName);
+  bool Has(const nsACString& aName);
+  void Get(const char* aName, nsACString& aValue);
+  void Get(const nsACString& aName, nsACString& aValue);
+  void Set(const char* aName, const nsACString& aValue);
+  void Set(const nsACString& aName, const nsACString& aValue);
+  void MergeOrSet(const char* aName, const nsACString& aValue);
+  void MergeOrSet(const nsACString& aName, const nsACString& aValue);
+  void Clear();
+  void ApplyToChannel(nsIHttpChannel* aChannel) const;
+  void GetCORSUnsafeHeaders(nsTArray<nsCString>& aArray) const;
+};
+
 // Make sure that any non-DOM interfaces added here are also added to
 // nsXMLHttpRequestXPCOMifier.
 class XMLHttpRequestMainThread final : public XMLHttpRequest,
                                        public nsIXMLHttpRequest,
                                        public nsIJSXMLHttpRequest,
                                        public nsIStreamListener,
                                        public nsIChannelEventSink,
                                        public nsIProgressEventSink,
@@ -194,17 +231,20 @@ public:
     SizeOfEventTargetIncludingThis(MallocSizeOf aMallocSizeOf) const override;
 
   NS_REALLY_FORWARD_NSIDOMEVENTTARGET(XMLHttpRequestEventTarget)
 
   // states
   virtual uint16_t ReadyState() const override;
 
   // request
-  nsresult InitChannel();
+  nsresult CreateChannel();
+  nsresult InitiateFetch(nsIInputStream* aUploadStream,
+                         int64_t aUploadLength,
+                         nsACString& aUploadContentType);
 
   virtual void
   Open(const nsACString& aMethod, const nsAString& aUrl,
        ErrorResult& aRv) override;
 
   virtual void
   Open(const nsACString& aMethod, const nsAString& aUrl, bool aAsync,
        const Optional<nsAString>& aUser,
@@ -715,25 +755,17 @@ protected:
   ArrayBufferBuilder mArrayBufferBuilder;
   JS::Heap<JSObject*> mResultArrayBuffer;
   bool mIsMappedArrayBuffer;
 
   void ResetResponse();
 
   bool ShouldBlockAuthPrompt();
 
-  struct RequestHeader
-  {
-    nsCString name;
-    nsCString value;
-  };
-  nsTArray<RequestHeader> mAuthorRequestHeaders;
-
-  void GetAuthorRequestHeaderValue(const char* aName, nsACString& outValue);
-  void SetAuthorRequestHeadersOnChannel(nsCOMPtr<nsIHttpChannel> aChannel);
+  RequestHeaders mAuthorRequestHeaders;
 
   // Helper object to manage our XPCOM scriptability bits
   nsXMLHttpRequestXPCOMifier* mXPCOMifier;
 
   static bool sDontWarnAboutSyncXHR;
 };
 
 class MOZ_STACK_CLASS AutoDontWarnAboutSyncXHR
--- a/gfx/layers/basic/BasicCompositor.cpp
+++ b/gfx/layers/basic/BasicCompositor.cpp
@@ -481,16 +481,21 @@ AttemptVideoScale(TextureSourceBasic* aS
 static bool
 AttemptVideoConvertAndScale(TextureSource* aSource, const SourceSurface* aSourceMask,
                             gfx::Float aOpacity, CompositionOp aBlendMode,
                             const TexturedEffect* aTexturedEffect,
                             const Matrix& aNewTransform, const gfx::Rect& aRect,
                             const gfx::Rect& aClipRect,
                             DrawTarget* aDest, const DrawTarget* aBuffer)
 {
+#if defined(XP_WIN) && defined(_M_X64)
+  // libyuv does not support SIMD scaling on win 64bit. See Bug 1295927.
+  return false;
+#endif
+
   WrappingTextureSourceYCbCrBasic* wrappingSource = aSource->AsWrappingTextureSourceYCbCrBasic();
   if (!wrappingSource)
     return false;
 #ifdef MOZILLA_SSE_HAVE_CPUID_DETECTION
   if (!mozilla::supports_ssse3()) // libyuv requests SSSE3 for fast YUV conversion.
     return false;
   if (aNewTransform.HasNonAxisAlignedTransform() || aNewTransform.HasNegativeScaling())
       return false;
--- a/gfx/src/nsFontMetrics.cpp
+++ b/gfx/src/nsFontMetrics.cpp
@@ -73,17 +73,17 @@ private:
             case NS_STYLE_TEXT_ORIENTATION_SIDEWAYS:
                 flags |= gfxTextRunFactory::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
                 break;
             }
         }
         return flags;
     }
 
-    UniquePtr<gfxTextRun> mTextRun;
+    RefPtr<gfxTextRun> mTextRun;
 };
 
 class StubPropertyProvider : public gfxTextRun::PropertyProvider {
 public:
     virtual void GetHyphenationBreaks(gfxTextRun::Range aRange,
                                       bool* aBreakBefore) {
         NS_ERROR("This shouldn't be called because we never call BreakAndMeasureText");
     }
--- a/gfx/thebes/gfxFT2FontList.cpp
+++ b/gfx/thebes/gfxFT2FontList.cpp
@@ -539,17 +539,18 @@ hb_blob_t*
 FT2FontEntry::GetFontTable(uint32_t aTableTag)
 {
     if (mFontFace) {
         // if there's a cairo font face, we may be able to return a blob
         // that just wraps a range of the attached user font data
         FTUserFontData *userFontData = static_cast<FTUserFontData*>(
             cairo_font_face_get_user_data(mFontFace, &sFTUserFontDataKey));
         if (userFontData && userFontData->FontData()) {
-            return GetTableFromFontData(userFontData->FontData(), aTableTag);
+            return gfxFontUtils::GetTableFromFontData(userFontData->FontData(),
+                                                      aTableTag);
         }
     }
 
     // otherwise, use the default method (which in turn will call our
     // implementation of CopyFontTable)
     return gfxFontEntry::GetFontTable(aTableTag);
 }
 
--- a/gfx/thebes/gfxFcPlatformFontList.cpp
+++ b/gfx/thebes/gfxFcPlatformFontList.cpp
@@ -417,17 +417,17 @@ gfxFontconfigFontEntry::TestCharacterMap
     return HasChar(mFontPattern, aCh);
 }
 
 hb_blob_t*
 gfxFontconfigFontEntry::GetFontTable(uint32_t aTableTag)
 {
     // for data fonts, read directly from the font data
     if (mFontData) {
-        return GetTableFromFontData(mFontData, aTableTag);
+        return gfxFontUtils::GetTableFromFontData(mFontData, aTableTag);
     }
 
     return gfxFontEntry::GetFontTable(aTableTag);
 }
 
 void
 gfxFontconfigFontEntry::MaybeReleaseFTFace()
 {
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -3202,27 +3202,27 @@ gfxFont::InitFakeSmallCapsRun(DrawTarget
                     // This is the hard case: the transformation caused chars
                     // to be inserted or deleted, so we can't shape directly
                     // into the destination textrun but have to handle the
                     // mismatch of character positions.
                     gfxTextRunFactory::Parameters params = {
                         aDrawTarget, nullptr, nullptr, nullptr, 0,
                         aTextRun->GetAppUnitsPerDevUnit()
                     };
-                    UniquePtr<gfxTextRun> tempRun(
+                    RefPtr<gfxTextRun> tempRun(
                         gfxTextRun::Create(&params, convertedString.Length(),
                                            aTextRun->GetFontGroup(), 0));
                     tempRun->AddGlyphRun(f, aMatchType, 0, true, aOrientation);
                     if (!f->SplitAndInitTextRun(aDrawTarget, tempRun.get(),
                                                 convertedString.BeginReading(),
                                                 0, convertedString.Length(),
                                                 aScript, vertical)) {
                         ok = false;
                     } else {
-                        UniquePtr<gfxTextRun> mergedRun(
+                        RefPtr<gfxTextRun> mergedRun(
                             gfxTextRun::Create(&params, runLength,
                                                aTextRun->GetFontGroup(), 0));
                         MergeCharactersInTextRun(mergedRun.get(), tempRun.get(),
                                                  charsToMergeArray.Elements(),
                                                  deletedCharsArray.Elements());
                         gfxTextRun::Range runRange(0, runLength);
                         aTextRun->CopyGlyphDataFrom(mergedRun.get(), runRange,
                                                     aOffset + runStart);
--- a/gfx/thebes/gfxFontEntry.cpp
+++ b/gfx/thebes/gfxFontEntry.cpp
@@ -669,43 +669,16 @@ gfxFontEntry::ShareFontTableAndGetBlob(u
         // ensure the entry is null
         entry->Clear();
         return nullptr;
     }
 
     return entry->ShareTableAndGetBlob(Move(*aBuffer), mFontTableCache.get());
 }
 
-static int
-DirEntryCmp(const void* aKey, const void* aItem)
-{
-    int32_t tag = *static_cast<const int32_t*>(aKey);
-    const TableDirEntry* entry = static_cast<const TableDirEntry*>(aItem);
-    return tag - int32_t(entry->tag);
-}
-
-hb_blob_t*
-gfxFontEntry::GetTableFromFontData(const void* aFontData, uint32_t aTableTag)
-{
-    const SFNTHeader* header =
-        reinterpret_cast<const SFNTHeader*>(aFontData);
-    const TableDirEntry* dir =
-        reinterpret_cast<const TableDirEntry*>(header + 1);
-    dir = static_cast<const TableDirEntry*>
-        (bsearch(&aTableTag, dir, uint16_t(header->numTables),
-                 sizeof(TableDirEntry), DirEntryCmp));
-    if (dir) {
-        return hb_blob_create(reinterpret_cast<const char*>(aFontData) +
-                                  dir->offset, dir->length,
-                              HB_MEMORY_MODE_READONLY, nullptr, nullptr);
-
-    }
-    return nullptr;
-}
-
 already_AddRefed<gfxCharacterMap>
 gfxFontEntry::GetCMAPFromFontInfo(FontInfoData *aFontInfoData,
                                   uint32_t& aUVSOffset,
                                   bool& aSymbolFont)
 {
     if (!aFontInfoData || !aFontInfoData->mLoadCmaps) {
         return nullptr;
     }
--- a/gfx/thebes/gfxFontEntry.h
+++ b/gfx/thebes/gfxFontEntry.h
@@ -479,24 +479,16 @@ protected:
     // Copy a font table into aBuffer.
     // The caller will be responsible for ownership of the data.
     virtual nsresult CopyFontTable(uint32_t aTableTag,
                                    nsTArray<uint8_t>& aBuffer) {
         NS_NOTREACHED("forgot to override either GetFontTable or CopyFontTable?");
         return NS_ERROR_FAILURE;
     }
 
-    // Return a blob that wraps a table found within a buffer of font data.
-    // The blob does NOT own its data; caller guarantees that the buffer
-    // will remain valid at least as long as the blob.
-    // Returns null if the specified table is not found.
-    // This method assumes aFontData is valid 'sfnt' data; before using this,
-    // caller is responsible to do any sanitization/validation necessary.
-    hb_blob_t* GetTableFromFontData(const void* aFontData, uint32_t aTableTag);
-
     // lookup the cmap in cached font data
     virtual already_AddRefed<gfxCharacterMap>
     GetCMAPFromFontInfo(FontInfoData *aFontInfoData,
                         uint32_t& aUVSOffset,
                         bool& aSymbolFont);
 
     // helper for HasCharacter(), which is what client code should call
     virtual bool TestCharacterMap(uint32_t aCh);
--- a/gfx/thebes/gfxFontUtils.cpp
+++ b/gfx/thebes/gfxFontUtils.cpp
@@ -957,16 +957,51 @@ gfxFontUtils::DetermineFontDataType(cons
         }
     }
     
     // tests for other formats here
     
     return GFX_USERFONT_UNKNOWN;
 }
 
+static int
+DirEntryCmp(const void* aKey, const void* aItem)
+{
+    int32_t tag = *static_cast<const int32_t*>(aKey);
+    const TableDirEntry* entry = static_cast<const TableDirEntry*>(aItem);
+    return tag - int32_t(entry->tag);
+}
+
+/* static */
+TableDirEntry*
+gfxFontUtils::FindTableDirEntry(const void* aFontData, uint32_t aTableTag)
+{
+    const SFNTHeader* header =
+        reinterpret_cast<const SFNTHeader*>(aFontData);
+    const TableDirEntry* dir =
+        reinterpret_cast<const TableDirEntry*>(header + 1);
+    return static_cast<TableDirEntry*>
+        (bsearch(&aTableTag, dir, uint16_t(header->numTables),
+                 sizeof(TableDirEntry), DirEntryCmp));
+}
+
+/* static */
+hb_blob_t*
+gfxFontUtils::GetTableFromFontData(const void* aFontData, uint32_t aTableTag)
+{
+    const TableDirEntry* dir = FindTableDirEntry(aFontData, aTableTag);
+    if (dir) {
+        return hb_blob_create(reinterpret_cast<const char*>(aFontData) +
+                                  dir->offset, dir->length,
+                              HB_MEMORY_MODE_READONLY, nullptr, nullptr);
+
+    }
+    return nullptr;
+}
+
 nsresult
 gfxFontUtils::RenameFont(const nsAString& aName, const uint8_t *aFontData, 
                          uint32_t aFontDataLength, FallibleTArray<uint8_t> *aNewFont)
 {
     NS_ASSERTION(aNewFont, "null font data array");
     
     uint64_t dataLength(aFontDataLength);
 
@@ -1040,31 +1075,24 @@ gfxFontUtils::RenameFont(const nsAString
                                                   aName.BeginReading(),
                                                   aName.Length());
     strData[aName.Length()] = 0; // add null termination
     
     // adjust name table header to point to the new name table
     SFNTHeader *sfntHeader = reinterpret_cast<SFNTHeader*>(newFontData);
 
     // table directory entries begin immediately following SFNT header
-    TableDirEntry *dirEntry = 
-        reinterpret_cast<TableDirEntry*>(newFontData + sizeof(SFNTHeader));
+    TableDirEntry *dirEntry =
+        FindTableDirEntry(newFontData, TRUETYPE_TAG('n','a','m','e'));
+    // function only called if font validates, so this should always be true
+    MOZ_ASSERT(dirEntry, "attempt to rename font with no name table");
 
     uint32_t numTables = sfntHeader->numTables;
     
-    for (i = 0; i < numTables; i++, dirEntry++) {
-        if (dirEntry->tag == TRUETYPE_TAG('n','a','m','e')) {
-            break;
-        }
-    }
-    
-    // function only called if font validates, so this should always be true
-    NS_ASSERTION(i < numTables, "attempt to rename font with no name table");
-
-    // note: dirEntry now points to name record
+    // note: dirEntry now points to 'name' table record
     
     // recalculate name table checksum
     uint32_t checkSum = 0;
     AutoSwap_PRUint32 *nameData = reinterpret_cast<AutoSwap_PRUint32*> (nameHeader);
     AutoSwap_PRUint32 *nameDataEnd = nameData + (nameTableSize >> 2);
     
     while (nameData < nameDataEnd)
         checkSum = checkSum + *nameData++;
@@ -1111,35 +1139,21 @@ gfxFontUtils::RenameFont(const nsAString
 // (though it might fail to read it, if memory isn't available);
 // other checks here are just for extra paranoia.
 nsresult
 gfxFontUtils::GetFullNameFromSFNT(const uint8_t* aFontData, uint32_t aLength,
                                   nsAString& aFullName)
 {
     aFullName.AssignLiteral("(MISSING NAME)"); // should always get replaced
 
-    NS_ENSURE_TRUE(aLength >= sizeof(SFNTHeader), NS_ERROR_UNEXPECTED);
-    const SFNTHeader *sfntHeader =
-        reinterpret_cast<const SFNTHeader*>(aFontData);
     const TableDirEntry *dirEntry =
-        reinterpret_cast<const TableDirEntry*>(aFontData + sizeof(SFNTHeader));
-    uint32_t numTables = sfntHeader->numTables;
-    NS_ENSURE_TRUE(aLength >=
-                   sizeof(SFNTHeader) + numTables * sizeof(TableDirEntry),
-                   NS_ERROR_UNEXPECTED);
-    bool foundName = false;
-    for (uint32_t i = 0; i < numTables; i++, dirEntry++) {
-        if (dirEntry->tag == TRUETYPE_TAG('n','a','m','e')) {
-            foundName = true;
-            break;
-        }
-    }
+        FindTableDirEntry(aFontData, TRUETYPE_TAG('n','a','m','e'));
     
     // should never fail, as we're only called after font validation succeeded
-    NS_ENSURE_TRUE(foundName, NS_ERROR_NOT_AVAILABLE);
+    NS_ENSURE_TRUE(dirEntry, NS_ERROR_NOT_AVAILABLE);
 
     uint32_t len = dirEntry->length;
     NS_ENSURE_TRUE(aLength > len && aLength - len >= dirEntry->offset,
                    NS_ERROR_UNEXPECTED);
 
     hb_blob_t *nameBlob =
         hb_blob_create((const char*)aFontData + dirEntry->offset, len,
                        HB_MEMORY_MODE_READONLY, nullptr, nullptr);
--- a/gfx/thebes/gfxFontUtils.h
+++ b/gfx/thebes/gfxFontUtils.h
@@ -853,16 +853,30 @@ public:
     GetFullNameFromTable(hb_blob_t *aNameTable,
                          nsAString& aFullName);
 
     // helper to get family name from name table
     static nsresult
     GetFamilyNameFromTable(hb_blob_t *aNameTable,
                            nsAString& aFamilyName);
 
+    // Find the table directory entry for a given table tag, in a (validated)
+    // buffer of 'sfnt' data. Returns null if the tag is not present.
+    static mozilla::TableDirEntry*
+    FindTableDirEntry(const void* aFontData, uint32_t aTableTag);
+
+    // Return a blob that wraps a table found within a buffer of font data.
+    // The blob does NOT own its data; caller guarantees that the buffer
+    // will remain valid at least as long as the blob.
+    // Returns null if the specified table is not found.
+    // This method assumes aFontData is valid 'sfnt' data; before using this,
+    // caller is responsible to do any sanitization/validation necessary.
+    static hb_blob_t*
+    GetTableFromFontData(const void* aFontData, uint32_t aTableTag);
+
     // create a new name table and build a new font with that name table
     // appended on the end, returns true on success
     static nsresult
     RenameFont(const nsAString& aName, const uint8_t *aFontData, 
                uint32_t aFontDataLength, FallibleTArray<uint8_t> *aNewFont);
     
     // read all names matching aNameID, returning in aNames array
     static nsresult
--- a/gfx/thebes/gfxFontconfigFonts.cpp
+++ b/gfx/thebes/gfxFontconfigFonts.cpp
@@ -647,17 +647,17 @@ bool gfxDownloadedFcFontEntry::SetCairoF
 
 hb_blob_t *
 gfxDownloadedFcFontEntry::GetFontTable(uint32_t aTableTag)
 {
     // The entry already owns the (sanitized) sfnt data in mFontData,
     // so we can just return a blob that "wraps" the appropriate chunk of it.
     // The blob should not attempt to free its data, as the entire sfnt data
     // will be freed when the font entry is deleted.
-    return GetTableFromFontData(mFontData, aTableTag);
+    return gfxFontUtils::GetTableFromFontData(mFontData, aTableTag);
 }
 
 /*
  * gfxFcFont
  *
  * This is a gfxFont implementation using a CAIRO_FONT_TYPE_FT
  * cairo_scaled_font created from an FcPattern.
  */
--- a/gfx/thebes/gfxGDIFontList.cpp
+++ b/gfx/thebes/gfxGDIFontList.cpp
@@ -747,16 +747,73 @@ gfxGDIFontList::LookupLocalFont(const ns
 
     // make the new font entry match the userfont entry style characteristics
     fe->mWeight = (aWeight == 0 ? 400 : aWeight);
     fe->mStyle = aStyle;
 
     return fe;
 }
 
+// If aFontData contains only a MS/Symbol cmap subtable, not MS/Unicode,
+// we modify the subtable header to mark it as Unicode instead, because
+// otherwise GDI will refuse to load the font.
+// NOTE that this function does not bounds-check every access to the font data.
+// This is OK because we only use it on data that has already been validated
+// by OTS, and therefore we will not hit out-of-bounds accesses here.
+static bool
+FixupSymbolEncodedFont(uint8_t* aFontData, uint32_t aLength)
+{
+    struct CmapHeader {
+        AutoSwap_PRUint16 version;
+        AutoSwap_PRUint16 numTables;
+    };
+    struct CmapEncodingRecord {
+        AutoSwap_PRUint16 platformID;
+        AutoSwap_PRUint16 encodingID;
+        AutoSwap_PRUint32 offset;
+    };
+    const uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p');
+    const TableDirEntry* dir =
+        gfxFontUtils::FindTableDirEntry(aFontData, kCMAP);
+    if (dir && uint32_t(dir->length) >= sizeof(CmapHeader)) {
+        CmapHeader *cmap =
+            reinterpret_cast<CmapHeader*>(aFontData + uint32_t(dir->offset));
+        CmapEncodingRecord *encRec =
+            reinterpret_cast<CmapEncodingRecord*>(cmap + 1);
+        int32_t symbolSubtable = -1;
+        for (uint32_t i = 0; i < (uint16_t)cmap->numTables; ++i) {
+            if (uint16_t(encRec[i].platformID) !=
+                gfxFontUtils::PLATFORM_ID_MICROSOFT) {
+                continue; // only interested in MS platform
+            }
+            if (uint16_t(encRec[i].encodingID) ==
+                gfxFontUtils::ENCODING_ID_MICROSOFT_UNICODEBMP) {
+                // We've got a Microsoft/Unicode table, so don't interfere.
+                symbolSubtable = -1;
+                break;
+            }
+            if (uint16_t(encRec[i].encodingID) ==
+                gfxFontUtils::ENCODING_ID_MICROSOFT_SYMBOL) {
+                // Found a symbol subtable; remember it for possible fixup,
+                // but if we subsequently find a Microsoft/Unicode subtable,
+                // we'll cancel this.
+                symbolSubtable = i;
+            }
+        }
+        if (symbolSubtable != -1) {
+            // We found a windows/symbol cmap table, and no windows/unicode one;
+            // change the encoding ID so that AddFontMemResourceEx will accept it
+            encRec[symbolSubtable].encodingID =
+                gfxFontUtils::ENCODING_ID_MICROSOFT_UNICODEBMP;
+            return true;
+        }
+    }
+    return false;
+}
+
 gfxFontEntry*
 gfxGDIFontList::MakePlatformFont(const nsAString& aFontName,
                                  uint16_t aWeight,
                                  int16_t aStretch,
                                  uint8_t aStyle,
                                  const uint8_t* aFontData,
                                  uint32_t aLength)
 {
@@ -794,18 +851,24 @@ gfxGDIFontList::MakePlatformFont(const n
     uint32_t fontLength = newFontData.Length();
     NS_ASSERTION(fontData, "null font data after renaming");
 
     // http://msdn.microsoft.com/en-us/library/ms533942(VS.85).aspx
     // "A font that is added by AddFontMemResourceEx is always private 
     //  to the process that made the call and is not enumerable."
     fontRef = AddFontMemResourceEx(fontData, fontLength, 
                                     0 /* reserved */, &numFonts);
-    if (!fontRef)
+    if (!fontRef) {
+        if (FixupSymbolEncodedFont(fontData, fontLength)) {
+            fontRef = AddFontMemResourceEx(fontData, fontLength, 0, &numFonts);
+        }
+    }
+    if (!fontRef) {
         return nullptr;
+    }
 
     // only load fonts with a single face contained in the data
     // AddFontMemResourceEx generates an additional face name for
     // vertical text if the font supports vertical writing but since
     // the font is referenced via the name this can be ignored
     if (fontRef && numFonts > 2) {
         RemoveFontMemResourceEx(fontRef);
         return nullptr;
--- a/gfx/thebes/gfxTextRun.cpp
+++ b/gfx/thebes/gfxTextRun.cpp
@@ -126,27 +126,28 @@ gfxTextRun::AllocateStorageForTextRun(si
 
     // Initialize the glyph storage (beyond aSize) to zero
     memset(reinterpret_cast<char*>(storage) + aSize, 0,
            aLength * sizeof(CompressedGlyph));
 
     return storage;
 }
 
-UniquePtr<gfxTextRun>
+already_AddRefed<gfxTextRun>
 gfxTextRun::Create(const gfxTextRunFactory::Parameters *aParams,
                    uint32_t aLength, gfxFontGroup *aFontGroup, uint32_t aFlags)
 {
     void *storage = AllocateStorageForTextRun(sizeof(gfxTextRun), aLength);
     if (!storage) {
         return nullptr;
     }
 
-    return UniquePtr<gfxTextRun>(new (storage) gfxTextRun(aParams, aLength,
-                                                          aFontGroup, aFlags));
+    RefPtr<gfxTextRun> result = new (storage) gfxTextRun(aParams, aLength,
+                                                         aFontGroup, aFlags);
+    return result.forget();
 }
 
 gfxTextRun::gfxTextRun(const gfxTextRunFactory::Parameters *aParams,
                        uint32_t aLength, gfxFontGroup *aFontGroup, uint32_t aFlags)
     : gfxShapedText(aLength, aFlags, aParams->mAppUnitsPerDevUnit)
     , mUserData(aParams->mUserData)
     , mFontGroup(aFontGroup)
     , mReleasedFontGroup(false)
@@ -1471,69 +1472,16 @@ gfxTextRun::FetchGlyphExtents(DrawTarget
                     }
                 }
             }
         }
     }
 }
 
 
-gfxTextRun::ClusterIterator::ClusterIterator(gfxTextRun *aTextRun)
-    : mTextRun(aTextRun), mCurrentChar(uint32_t(-1))
-{
-}
-
-void
-gfxTextRun::ClusterIterator::Reset()
-{
-    mCurrentChar = uint32_t(-1);
-}
-
-bool
-gfxTextRun::ClusterIterator::NextCluster()
-{
-    uint32_t len = mTextRun->GetLength();
-    while (++mCurrentChar < len) {
-        if (mTextRun->IsClusterStart(mCurrentChar)) {
-            return true;
-        }
-    }
-
-    mCurrentChar = uint32_t(-1);
-    return false;
-}
-
-gfxTextRun::Range
-gfxTextRun::ClusterIterator::ClusterRange() const
-{
-    if (mCurrentChar == uint32_t(-1)) {
-        return Range(0, 0);
-    }
-
-    uint32_t i = mCurrentChar,
-             len = mTextRun->GetLength();
-    while (++i < len) {
-        if (mTextRun->IsClusterStart(i)) {
-            break;
-        }
-    }
-
-    return Range(mCurrentChar, i);
-}
-
-gfxFloat
-gfxTextRun::ClusterIterator::ClusterAdvance(PropertyProvider *aProvider) const
-{
-    if (mCurrentChar == uint32_t(-1)) {
-        return 0;
-    }
-
-    return mTextRun->GetAdvanceWidth(ClusterRange(), aProvider);
-}
-
 size_t
 gfxTextRun::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf)
 {
     // The second arg is how much gfxTextRun::AllocateStorage would have
     // allocated.
     size_t total = mGlyphRuns.ShallowSizeOfExcludingThis(aMallocSizeOf);
 
     if (mDetailedGlyphs) {
@@ -1963,29 +1911,29 @@ gfxFontGroup::IsInvalidChar(char16_t ch)
     if (ch <= 0x9f) {
         return true;
     }
     return (((ch & 0xFF00) == 0x2000 /* Unicode control character */ &&
              (ch == 0x200B/*ZWSP*/ || ch == 0x2028/*LSEP*/ || ch == 0x2029/*PSEP*/)) ||
             IsBidiControl(ch));
 }
 
-UniquePtr<gfxTextRun>
+already_AddRefed<gfxTextRun>
 gfxFontGroup::MakeEmptyTextRun(const Parameters *aParams, uint32_t aFlags)
 {
     aFlags |= TEXT_IS_8BIT | TEXT_IS_ASCII | TEXT_IS_PERSISTENT;
     return gfxTextRun::Create(aParams, 0, this, aFlags);
 }
 
-UniquePtr<gfxTextRun>
+already_AddRefed<gfxTextRun>
 gfxFontGroup::MakeSpaceTextRun(const Parameters *aParams, uint32_t aFlags)
 {
     aFlags |= TEXT_IS_8BIT | TEXT_IS_ASCII | TEXT_IS_PERSISTENT;
 
-    UniquePtr<gfxTextRun> textRun =
+    RefPtr<gfxTextRun> textRun =
         gfxTextRun::Create(aParams, 1, this, aFlags);
     if (!textRun) {
         return nullptr;
     }
 
     uint16_t orientation = aFlags & TEXT_ORIENT_MASK;
     if (orientation == TEXT_ORIENT_VERTICAL_MIXED) {
         orientation = TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
@@ -2017,39 +1965,39 @@ gfxFontGroup::MakeSpaceTextRun(const Par
                                        orientation);
             }
         }
     }
 
     // Note that the gfxGlyphExtents glyph bounds storage for the font will
     // always contain an entry for the font's space glyph, so we don't have
     // to call FetchGlyphExtents here.
-    return textRun;
+    return textRun.forget();
 }
 
-UniquePtr<gfxTextRun>
+already_AddRefed<gfxTextRun>
 gfxFontGroup::MakeBlankTextRun(uint32_t aLength,
                                const Parameters *aParams, uint32_t aFlags)
 {
-    UniquePtr<gfxTextRun> textRun =
+    RefPtr<gfxTextRun> textRun =
         gfxTextRun::Create(aParams, aLength, this, aFlags);
     if (!textRun) {
         return nullptr;
     }
 
     uint16_t orientation = aFlags & TEXT_ORIENT_MASK;
     if (orientation == TEXT_ORIENT_VERTICAL_MIXED) {
         orientation = TEXT_ORIENT_VERTICAL_UPRIGHT;
     }
     textRun->AddGlyphRun(GetFirstValidFont(), gfxTextRange::kFontGroup, 0, false,
                          orientation);
-    return textRun;
+    return textRun.forget();
 }
 
-UniquePtr<gfxTextRun>
+already_AddRefed<gfxTextRun>
 gfxFontGroup::MakeHyphenTextRun(DrawTarget* aDrawTarget,
                                 uint32_t aAppUnitsPerDevUnit)
 {
     // only use U+2010 if it is supported by the first font in the group;
     // it's better to use ASCII '-' from the primary font than to fall back to
     // U+2010 from some other, possibly poorly-matching face
     static const char16_t hyphen = 0x2010;
     gfxFont *font = GetFirstValidFont(uint32_t(hyphen));
@@ -2064,26 +2012,26 @@ gfxFontGroup::MakeHyphenTextRun(DrawTarg
 }
 
 gfxFloat
 gfxFontGroup::GetHyphenWidth(gfxTextRun::PropertyProvider *aProvider)
 {
     if (mHyphenWidth < 0) {
         RefPtr<DrawTarget> dt(aProvider->GetDrawTarget());
         if (dt) {
-            UniquePtr<gfxTextRun>
+            RefPtr<gfxTextRun>
                 hyphRun(MakeHyphenTextRun(dt,
                                           aProvider->GetAppUnitsPerDevUnit()));
             mHyphenWidth = hyphRun.get() ? hyphRun->GetAdvanceWidth() : 0;
         }
     }
     return mHyphenWidth;
 }
 
-UniquePtr<gfxTextRun>
+already_AddRefed<gfxTextRun>
 gfxFontGroup::MakeTextRun(const uint8_t *aString, uint32_t aLength,
                           const Parameters *aParams, uint32_t aFlags,
                           gfxMissingFontRecorder *aMFR)
 {
     if (aLength == 0) {
         return MakeEmptyTextRun(aParams, aFlags);
     }
     if (aLength == 1 && aString[0] == ' ') {
@@ -2095,56 +2043,56 @@ gfxFontGroup::MakeTextRun(const uint8_t 
     if (MOZ_UNLIKELY(GetStyle()->size == 0) ||
         MOZ_UNLIKELY(GetStyle()->sizeAdjust == 0.0f)) {
         // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
         // them, and always create at least size 1 fonts, i.e. they still
         // render something for size 0 fonts.
         return MakeBlankTextRun(aLength, aParams, aFlags);
     }
 
-    UniquePtr<gfxTextRun> textRun = gfxTextRun::Create(aParams, aLength,
-                                                       this, aFlags);
+    RefPtr<gfxTextRun> textRun = gfxTextRun::Create(aParams, aLength, this,
+                                                    aFlags);
     if (!textRun) {
         return nullptr;
     }
 
     InitTextRun(aParams->mDrawTarget, textRun.get(), aString, aLength, aMFR);
 
     textRun->FetchGlyphExtents(aParams->mDrawTarget);
 
-    return textRun;
+    return textRun.forget();
 }
 
-UniquePtr<gfxTextRun>
+already_AddRefed<gfxTextRun>
 gfxFontGroup::MakeTextRun(const char16_t *aString, uint32_t aLength,
                           const Parameters *aParams, uint32_t aFlags,
                           gfxMissingFontRecorder *aMFR)
 {
     if (aLength == 0) {
         return MakeEmptyTextRun(aParams, aFlags);
     }
     if (aLength == 1 && aString[0] == ' ') {
         return MakeSpaceTextRun(aParams, aFlags);
     }
     if (MOZ_UNLIKELY(GetStyle()->size == 0) ||
         MOZ_UNLIKELY(GetStyle()->sizeAdjust == 0.0f)) {
         return MakeBlankTextRun(aLength, aParams, aFlags);
     }
 
-    UniquePtr<gfxTextRun> textRun = gfxTextRun::Create(aParams, aLength,
-                                                       this, aFlags);
+    RefPtr<gfxTextRun> textRun = gfxTextRun::Create(aParams, aLength, this,
+                                                    aFlags);
     if (!textRun) {
         return nullptr;
     }
 
     InitTextRun(aParams->mDrawTarget, textRun.get(), aString, aLength, aMFR);
 
     textRun->FetchGlyphExtents(aParams->mDrawTarget);
 
-    return textRun;
+    return textRun.forget();
 }
 
 template<typename T>
 void
 gfxFontGroup::InitTextRun(DrawTarget* aDrawTarget,
                           gfxTextRun *aTextRun,
                           const T *aString,
                           uint32_t aLength,
--- a/gfx/thebes/gfxTextRun.h
+++ b/gfx/thebes/gfxTextRun.h
@@ -66,39 +66,41 @@ struct gfxTextRunDrawCallbacks {
 };
 
 /**
  * gfxTextRun is an abstraction for drawing and measuring substrings of a run
  * of text. It stores runs of positioned glyph data, each run having a single
  * gfxFont. The glyphs are associated with a string of source text, and the
  * gfxTextRun APIs take parameters that are offsets into that source text.
  * 
- * gfxTextRuns are not refcounted. They should be deleted when no longer required.
- * 
  * gfxTextRuns are mostly immutable. The only things that can change are
  * inter-cluster spacing and line break placement. Spacing is always obtained
  * lazily by methods that need it, it is not cached. Line breaks are stored
  * persistently (insofar as they affect the shaping of glyphs; gfxTextRun does
  * not actually do anything to explicitly account for line breaks). Initially
  * there are no line breaks. The textrun can record line breaks before or after
  * any given cluster. (Line breaks specified inside clusters are ignored.)
  * 
  * It is important that zero-length substrings are handled correctly. This will
  * be on the test!
  */
-class gfxTextRun : public gfxShapedText {
-public:
+class gfxTextRun : public gfxShapedText
+{
+    NS_INLINE_DECL_REFCOUNTING(gfxTextRun);
+
+protected:
     // Override operator delete to properly free the object that was
     // allocated via malloc.
     void operator delete(void* p) {
         free(p);
     }
 
     virtual ~gfxTextRun();
 
+public:
     typedef gfxFont::RunMetrics Metrics;
     typedef mozilla::gfx::DrawTarget DrawTarget;
 
     // Public textrun API for general use
 
     bool IsClusterStart(uint32_t aPos) const {
         MOZ_ASSERT(aPos < GetLength());
         return mCharacterGlyphs[aPos].IsClusterStart();
@@ -210,37 +212,16 @@ public:
         // Only called if the hyphen width is requested.
         virtual already_AddRefed<DrawTarget> GetDrawTarget() = 0;
 
         // Return the appUnitsPerDevUnit value to be used when measuring.
         // Only called if the hyphen width is requested.
         virtual uint32_t GetAppUnitsPerDevUnit() = 0;
     };
 
-    class ClusterIterator {
-    public:
-        explicit ClusterIterator(gfxTextRun *aTextRun);
-
-        void Reset();
-
-        bool NextCluster();
-
-        uint32_t Position() const {
-            return mCurrentChar;
-        }
-
-        Range ClusterRange() const;
-
-        gfxFloat ClusterAdvance(PropertyProvider *aProvider) const;
-
-    private:
-        gfxTextRun *mTextRun;
-        uint32_t    mCurrentChar;
-    };
-
     struct DrawParams
     {
         gfxContext* context;
         DrawMode drawMode = DrawMode::GLYPH_FILL;
         nscolor textStrokeColor = 0;
         gfxPattern* textStrokePattern = nullptr;
         const mozilla::gfx::StrokeOptions *strokeOpts = nullptr;
         const mozilla::gfx::DrawOptions *drawOpts = nullptr;
@@ -438,17 +419,17 @@ public:
       mFlags &= ~aFlags;
     }
     const gfxSkipChars& GetSkipChars() const { return mSkipChars; }
     gfxFontGroup *GetFontGroup() const { return mFontGroup; }
 
 
     // Call this, don't call "new gfxTextRun" directly. This does custom
     // allocation and initialization
-    static mozilla::UniquePtr<gfxTextRun>
+    static already_AddRefed<gfxTextRun>
     Create(const gfxTextRunFactory::Parameters *aParams,
            uint32_t aLength, gfxFontGroup *aFontGroup,
            uint32_t aFlags);
 
     // The text is divided into GlyphRuns as necessary
     struct GlyphRun {
         RefPtr<gfxFont> mFont;   // never null
         uint32_t          mCharacterOffset; // into original UTF16 string
@@ -569,17 +550,17 @@ public:
      * Prefetch all the glyph extents needed to ensure that Measure calls
      * on this textrun not requesting tight boundingBoxes will succeed. Note
      * that some glyph extents might not be fetched due to OOM or other
      * errors.
      */
     void FetchGlyphExtents(DrawTarget* aRefDrawTarget);
 
     uint32_t CountMissingGlyphs() const;
-    const GlyphRun *GetGlyphRuns(uint32_t *aNumGlyphRuns) {
+    const GlyphRun* GetGlyphRuns(uint32_t* aNumGlyphRuns) const {
         *aNumGlyphRuns = mGlyphRuns.Length();
         return mGlyphRuns.Elements();
     }
     // Returns the index of the GlyphRun containing the given offset.
     // Returns mGlyphRuns.Length() when aOffset is mCharacterCount.
     uint32_t FindFirstGlyphRunContaining(uint32_t aOffset) const;
 
     // Copy glyph data from a ShapedWord into this textrun.
@@ -808,37 +789,37 @@ public:
     static bool IsInvalidChar(char16_t ch);
 
     /**
      * Make a textrun for a given string.
      * If aText is not persistent (aFlags & TEXT_IS_PERSISTENT), the
      * textrun will copy it.
      * This calls FetchGlyphExtents on the textrun.
      */
-    virtual mozilla::UniquePtr<gfxTextRun>
+    virtual already_AddRefed<gfxTextRun>
     MakeTextRun(const char16_t *aString, uint32_t aLength,
                 const Parameters *aParams, uint32_t aFlags,
                 gfxMissingFontRecorder *aMFR);
     /**
      * Make a textrun for a given string.
      * If aText is not persistent (aFlags & TEXT_IS_PERSISTENT), the
      * textrun will copy it.
      * This calls FetchGlyphExtents on the textrun.
      */
-    virtual mozilla::UniquePtr<gfxTextRun>
+    virtual already_AddRefed<gfxTextRun>
     MakeTextRun(const uint8_t *aString, uint32_t aLength,
                 const Parameters *aParams, uint32_t aFlags,
                 gfxMissingFontRecorder *aMFR);
 
     /**
      * Textrun creation helper for clients that don't want to pass
      * a full Parameters record.
      */
     template<typename T>
-    mozilla::UniquePtr<gfxTextRun>
+    already_AddRefed<gfxTextRun>
     MakeTextRun(const T* aString, uint32_t aLength,
                 DrawTarget* aRefDrawTarget,
                 int32_t aAppUnitsPerDevUnit,
                 uint32_t aFlags,
                 gfxMissingFontRecorder *aMFR)
     {
         gfxTextRunFactory::Parameters params = {
             aRefDrawTarget, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevUnit
@@ -856,17 +837,17 @@ public:
 
     /**
      * Make a text run representing a single hyphen character.
      * This will use U+2010 HYPHEN if available in the first font,
      * otherwise fall back to U+002D HYPHEN-MINUS.
      * The caller is responsible for deleting the returned text run
      * when no longer required.
      */
-    mozilla::UniquePtr<gfxTextRun>
+    already_AddRefed<gfxTextRun>
     MakeHyphenTextRun(DrawTarget* aDrawTarget, uint32_t aAppUnitsPerDevUnit);
 
     /**
      * Check whether a given font (specified by its gfxFontEntry)
      * is already in the fontgroup's list of actual fonts
      */
     bool HasFont(const gfxFontEntry *aFontEntry);
 
@@ -1098,17 +1079,17 @@ protected:
 
     RefPtr<gfxUserFontSet> mUserFontSet;
     uint64_t mCurrGeneration;  // track the current user font set generation, rebuild font list if needed
 
     gfxTextPerfMetrics *mTextPerf;
 
     // Cache a textrun representing an ellipsis (useful for CSS text-overflow)
     // at a specific appUnitsPerDevPixel size and orientation
-    mozilla::UniquePtr<gfxTextRun>   mCachedEllipsisTextRun;
+    RefPtr<gfxTextRun>   mCachedEllipsisTextRun;
 
     // cache the most recent pref font to avoid general pref font lookup
     RefPtr<gfxFontFamily> mLastPrefFamily;
     RefPtr<gfxFont>       mLastPrefFont;
     eFontPrefLang           mLastPrefLang;       // lang group for last pref font
     eFontPrefLang           mPageLang;
     bool                    mLastPrefFirstFont;  // is this the first font in the list of pref fonts for this lang group?
 
@@ -1118,23 +1099,23 @@ protected:
 
     // xxx - gfxPangoFontGroup skips UpdateUserFonts
     bool                    mSkipUpdateUserFonts;
 
     /**
      * Textrun creation short-cuts for special cases where we don't need to
      * call a font shaper to generate glyphs.
      */
-    mozilla::UniquePtr<gfxTextRun>
+    already_AddRefed<gfxTextRun>
     MakeEmptyTextRun(const Parameters *aParams, uint32_t aFlags);
 
-    mozilla::UniquePtr<gfxTextRun>
+    already_AddRefed<gfxTextRun>
     MakeSpaceTextRun(const Parameters *aParams, uint32_t aFlags);
 
-    mozilla::UniquePtr<gfxTextRun>
+    already_AddRefed<gfxTextRun>
     MakeBlankTextRun(uint32_t aLength, const Parameters *aParams,
                      uint32_t aFlags);
 
     // Initialize the list of fonts
     void BuildFontList();
 
     // Get the font at index i within the fontlist.
     // Will initiate userfont load if not already loaded.
--- a/gfx/ycbcr/yuv_convert.cpp
+++ b/gfx/ycbcr/yuv_convert.cpp
@@ -255,16 +255,20 @@ void ScaleYCbCrToRGB32(const uint8* y_bu
                        int height,
                        int y_pitch,
                        int uv_pitch,
                        int rgb_pitch,
                        YUVType yuv_type,
                        ScaleFilter filter) {
 
   bool use_deprecated = gfxPrefs::YCbCrAccurateConversion() ||
+#if defined(XP_WIN) && defined(_M_X64)
+                        // libyuv does not support SIMD scaling on win 64bit. See Bug 1295927.
+                        supports_sse3() ||
+#endif
                         (supports_mmx() && supports_sse() && !supports_sse3());
   if (use_deprecated) {
     ScaleYCbCrToRGB32_deprecated(y_buf, u_buf, v_buf,
                                  rgb_buf,
                                  source_width, source_height,
                                  width, height,
                                  y_pitch, uv_pitch,
                                  rgb_pitch,
--- a/ipc/chromium/src/base/pickle.cc
+++ b/ipc/chromium/src/base/pickle.cc
@@ -14,20 +14,16 @@
 #include <stdlib.h>
 
 #include <limits>
 #include <string>
 #include <algorithm>
 
 #include "nsDebug.h"
 
-#if !defined(RELEASE_BUILD) || defined(DEBUG)
-#define SENTINEL_CHECKING
-#endif
-
 //------------------------------------------------------------------------------
 
 static_assert(MOZ_ALIGNOF(Pickle::memberAlignmentType) >= MOZ_ALIGNOF(uint32_t),
               "Insufficient alignment");
 
 static const uint32_t kHeaderSegmentCapacity = 64;
 
 static const uint32_t kDefaultSegmentCapacity = 4096;
@@ -432,35 +428,29 @@ bool Pickle::ReadBytesInto(PickleIterato
 
   if (!buffers_.ReadBytes(iter->iter_, reinterpret_cast<char*>(data), length)) {
     return false;
   }
 
   return iter->iter_.AdvanceAcrossSegments(buffers_, AlignInt(length) - length);
 }
 
+#ifdef MOZ_PICKLE_SENTINEL_CHECKING
 bool Pickle::ReadSentinel(PickleIterator* iter, uint32_t sentinel) const {
-#ifdef SENTINEL_CHECKING
   uint32_t found;
   if (!ReadUInt32(iter, &found)) {
     return false;
   }
   return found == sentinel;
-#else
-  return true;
-#endif
 }
 
 bool Pickle::WriteSentinel(uint32_t sentinel) {
-#ifdef SENTINEL_CHECKING
   return WriteUInt32(sentinel);
-#else
-  return true;
+}
 #endif
-}
 
 void Pickle::EndRead(PickleIterator& iter) const {
   DCHECK(iter.iter_.Done());
 }
 
 void Pickle::BeginWrite(uint32_t length, uint32_t alignment) {
   DCHECK(alignment % 4 == 0) << "Must be at least 32-bit aligned!";
 
--- a/ipc/chromium/src/base/pickle.h
+++ b/ipc/chromium/src/base/pickle.h
@@ -17,16 +17,20 @@
 #include "mozilla/BufferList.h"
 #include "mozilla/mozalloc.h"
 
 #ifdef MOZ_FAULTY
 #include "base/singleton.h"
 #include "mozilla/ipc/Faulty.h"
 #endif
 
+#if !defined(RELEASE_BUILD) || defined(DEBUG)
+#define MOZ_PICKLE_SENTINEL_CHECKING
+#endif
+
 class Pickle;
 
 class PickleIterator {
 public:
   explicit PickleIterator(const Pickle& pickle);
 
 private:
   friend class Pickle;
@@ -109,17 +113,24 @@ class Pickle {
   MOZ_MUST_USE bool ReadBytesInto(PickleIterator* iter, void* data, uint32_t length) const;
   MOZ_MUST_USE bool FlattenBytes(PickleIterator* iter, const char** data, uint32_t length,
                                  uint32_t alignment = sizeof(memberAlignmentType));
 
   // Safer version of ReadInt() checks for the result not being negative.
   // Use it for reading the object sizes.
   MOZ_MUST_USE bool ReadLength(PickleIterator* iter, int* result) const;
 
-  MOZ_MUST_USE bool ReadSentinel(PickleIterator* iter, uint32_t sentinel) const;
+  MOZ_MUST_USE bool ReadSentinel(PickleIterator* iter, uint32_t sentinel) const
+#ifdef MOZ_PICKLE_SENTINEL_CHECKING
+    ;
+#else
+  {
+    return true;
+  }
+#endif
 
   void EndRead(PickleIterator& iter) const;
 
   // Methods for adding to the payload of the Pickle.  These values are
   // appended to the end of the Pickle's payload.  When reading values from a
   // Pickle, it is important to read them in the order in which they were added
   // to the Pickle.
   bool WriteBool(bool value) {
@@ -212,17 +223,24 @@ class Pickle {
     return WriteBytes(&value, sizeof(value));
   }
   bool WriteString(const std::string& value);
   bool WriteWString(const std::wstring& value);
   bool WriteData(const char* data, uint32_t length);
   bool WriteBytes(const void* data, uint32_t data_len,
                   uint32_t alignment = sizeof(memberAlignmentType));
 
-  bool WriteSentinel(uint32_t sentinel);
+  bool WriteSentinel(uint32_t sentinel)
+#ifdef MOZ_PICKLE_SENTINEL_CHECKING
+    ;
+#else
+  {
+    return true;
+  }
+#endif
 
   int32_t* GetInt32PtrForTest(uint32_t offset);
 
   void InputBytes(const char* data, uint32_t length);
 
   // Payload follows after allocation of Header (header size is customizable).
   struct Header {
     uint32_t payload_size;  // Specifies the size of the payload.
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -648,18 +648,18 @@ class GCRuntime
 
     void runDebugGC();
     inline void poke();
 
     enum TraceOrMarkRuntime {
         TraceRuntime,
         MarkRuntime
     };
-    void markRuntime(JSTracer* trc, TraceOrMarkRuntime traceOrMark,
-                     AutoLockForExclusiveAccess& lock);
+    void traceRuntime(JSTracer* trc, AutoLockForExclusiveAccess& lock);
+    void traceRuntimeForMinorGC(JSTracer* trc, AutoLockForExclusiveAccess& lock);
 
     void notifyDidPaint();
     void shrinkBuffers();
     void onOutOfMallocMemory();
     void onOutOfMallocMemory(const AutoLockGC& lock);
 
 #ifdef JS_GC_ZEAL
     const void* addressOfZealModeBits() { return &zealModeBits; }
@@ -947,16 +947,19 @@ class GCRuntime
     void incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason reason,
                                  AutoLockForExclusiveAccess& lock);
 
     void pushZealSelectedObjects();
     void purgeRuntime(AutoLockForExclusiveAccess& lock);
     MOZ_MUST_USE bool beginMarkPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAccess& lock);
     bool shouldPreserveJITCode(JSCompartment* comp, int64_t currentTime,
                                JS::gcreason::Reason reason);
+    void traceRuntimeForMajorGC(JSTracer* trc, AutoLockForExclusiveAccess& lock);
+    void traceRuntimeCommon(JSTracer* trc, TraceOrMarkRuntime traceOrMark,
+                            AutoLockForExclusiveAccess& lock);
     void bufferGrayRoots();
     void markCompartments();
     IncrementalProgress drainMarkStack(SliceBudget& sliceBudget, gcstats::Phase phase);
     template <class CompartmentIterT> void markWeakReferences(gcstats::Phase phase);
     void markWeakReferencesInCurrentGroup(gcstats::Phase phase);
     template <class ZoneIterT, class CompartmentIterT> void markGrayReferences(gcstats::Phase phase);
     void markBufferedGrayRoots(JS::Zone* zone);
     void markGrayReferencesInCurrentGroup(gcstats::Phase phase);
--- a/js/src/gc/Iteration.cpp
+++ b/js/src/gc/Iteration.cpp
@@ -12,28 +12,16 @@
 #include "vm/Runtime.h"
 
 #include "jscntxtinlines.h"
 #include "jsgcinlines.h"
 
 using namespace js;
 using namespace js::gc;
 
-void
-js::TraceRuntime(JSTracer* trc)
-{
-    MOZ_ASSERT(!trc->isMarkingTracer());
-
-    JSRuntime* rt = trc->runtime();
-    rt->gc.evictNursery();
-    AutoPrepareForTracing prep(rt->contextFromMainThread(), WithAtoms);
-    gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_TRACE_HEAP);
-    rt->gc.markRuntime(trc, GCRuntime::TraceRuntime, prep.session().lock);
-}
-
 static void
 IterateCompartmentsArenasCells(JSContext* cx, Zone* zone, void* data,
                                JSIterateCompartmentCallback compartmentCallback,
                                IterateArenaCallback arenaCallback,
                                IterateCellCallback cellCallback)
 {
     for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
         (*compartmentCallback)(cx, data, comp);
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -670,17 +670,17 @@ js::Nursery::doCollection(JSRuntime* rt,
     sb.traceWholeCells(mover);
     maybeEndProfile(ProfileKey::TraceWholeCells);
 
     maybeStartProfile(ProfileKey::TraceGenericEntries);
     sb.traceGenericEntries(&mover);
     maybeEndProfile(ProfileKey::TraceGenericEntries);
 
     maybeStartProfile(ProfileKey::MarkRuntime);
-    rt->gc.markRuntime(&mover, GCRuntime::TraceRuntime, session.lock);
+    rt->gc.traceRuntimeForMinorGC(&mover, session.lock);
     maybeEndProfile(ProfileKey::MarkRuntime);
 
     maybeStartProfile(ProfileKey::MarkDebugger);
     {
         gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_MARK_ROOTS);
         Debugger::markAll(&mover);
     }
     maybeEndProfile(ProfileKey::MarkDebugger);
@@ -780,16 +780,20 @@ js::Nursery::freeMallocedBuffers()
         freeMallocedBuffersTask->runFromMainThread(runtime());
 
     MOZ_ASSERT(mallocedBuffers.empty());
 }
 
 void
 js::Nursery::waitBackgroundFreeEnd()
 {
+    // We may finishRoots before nursery init if runtime init fails.
+    if (!isEnabled())
+        return;
+
     MOZ_ASSERT(freeMallocedBuffersTask);
     freeMallocedBuffersTask->join();
 }
 
 void
 js::Nursery::sweep()
 {
     /* Sweep unique id's in all in-use chunks. */
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -218,17 +218,18 @@ AutoGCRooter::trace(JSTracer* trc)
     MOZ_ASSERT(tag_ >= 0);
     if (Value* vp = static_cast<AutoArrayRooter*>(this)->array)
         TraceRootRange(trc, tag_, vp, "JS::AutoArrayRooter.array");
 }
 
 /* static */ void
 AutoGCRooter::traceAll(JSTracer* trc)
 {
-    traceAllInContext(trc->runtime()->contextFromMainThread(), trc);
+    for (AutoGCRooter* gcr = trc->runtime()->contextFromMainThread()->roots.autoGCRooters_; gcr; gcr = gcr->down)
+        gcr->trace(trc);
 }
 
 /* static */ void
 AutoGCRooter::traceAllWrappers(JSTracer* trc)
 {
     JSContext* cx = trc->runtime()->contextFromMainThread();
 
     for (AutoGCRooter* gcr = cx->roots.autoGCRooters_; gcr; gcr = gcr->down) {
@@ -266,30 +267,60 @@ PropertyDescriptor::trace(JSTracer* trc)
     if ((attrs & JSPROP_SETTER) && setter) {
         JSObject* tmp = JS_FUNC_TO_DATA_PTR(JSObject*, setter);
         TraceRoot(trc, &tmp, "Descriptor::set");
         setter = JS_DATA_TO_FUNC_PTR(JSSetterOp, tmp);
     }
 }
 
 void
-js::gc::GCRuntime::markRuntime(JSTracer* trc, TraceOrMarkRuntime traceOrMark,
-                               AutoLockForExclusiveAccess& lock)
+js::gc::GCRuntime::traceRuntimeForMajorGC(JSTracer* trc, AutoLockForExclusiveAccess& lock)
+{
+    traceRuntimeCommon(trc, MarkRuntime, lock);
+}
+
+void
+js::gc::GCRuntime::traceRuntimeForMinorGC(JSTracer* trc, AutoLockForExclusiveAccess& lock)
+{
+    traceRuntimeCommon(trc, TraceRuntime, lock);
+}
+
+void
+js::TraceRuntime(JSTracer* trc)
+{
+    MOZ_ASSERT(!trc->isMarkingTracer());
+
+    JSRuntime* rt = trc->runtime();
+    rt->gc.evictNursery();
+    AutoPrepareForTracing prep(rt->contextFromMainThread(), WithAtoms);
+    gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_TRACE_HEAP);
+    rt->gc.traceRuntime(trc, prep.session().lock);
+}
+
+void
+js::gc::GCRuntime::traceRuntime(JSTracer* trc, AutoLockForExclusiveAccess& lock)
+{
+    traceRuntimeCommon(trc, TraceRuntime, lock);
+}
+
+void
+js::gc::GCRuntime::traceRuntimeCommon(JSTracer* trc, TraceOrMarkRuntime traceOrMark,
+                                      AutoLockForExclusiveAccess& lock)
 {
     gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_ROOTS);
 
-    MOZ_ASSERT(traceOrMark == TraceRuntime || traceOrMark == MarkRuntime);
-
     MOZ_ASSERT(!rt->mainThread.suppressGC);
 
+    // Trace incoming CCW edges.
     if (traceOrMark == MarkRuntime) {
         gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_CCWS);
         JSCompartment::traceIncomingCrossCompartmentEdgesForZoneGC(trc);
     }
 
+    // Trace C stack roots.
     {
         gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_ROOTERS);
 
         AutoGCRooter::traceAll(trc);
 
         if (!rt->isBeingDestroyed()) {
             MarkExactStackRoots(rt, trc);
             rt->markSelfHostingGlobal(trc);
@@ -298,41 +329,49 @@ js::gc::GCRuntime::markRuntime(JSTracer*
         for (RootRange r = rootsHash.all(); !r.empty(); r.popFront()) {
             const RootEntry& entry = r.front();
             TraceRoot(trc, entry.key(), entry.value());
         }
 
         MarkPersistentRooted(rt, trc);
     }
 
+    // Trace the atoms Compartment.
     if (!rt->isBeingDestroyed() && !rt->isHeapMinorCollecting()) {
         gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_RUNTIME_DATA);
 
         if (traceOrMark == TraceRuntime || rt->atomsCompartment(lock)->zone()->isCollecting()) {
             MarkPermanentAtoms(trc);
             MarkAtoms(trc, lock);
             MarkWellKnownSymbols(trc);
             jit::JitRuntime::Mark(trc, lock);
         }
     }
 
+    // This table is weak in major GC's.
     if (rt->isHeapMinorCollecting())
         jit::JitRuntime::MarkJitcodeGlobalTableUnconditionally(trc);
 
+    // Trace anything in the single context. Note that this is actually the
+    // same struct as the JSRuntime, but is still split for historical reasons.
     rt->contextFromMainThread()->mark(trc);
 
+    // Trace all compartment roots, but not the compartment itself; it is
+    // marked via the parent pointer if traceRoots actually traces anything.
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
         c->traceRoots(trc, traceOrMark);
 
+    // Trace JS stack roots.
     MarkInterpreterActivations(rt, trc);
-
     jit::MarkJitActivations(rt, trc);
 
+    // Trace SPS.
     rt->spsProfiler.trace(trc);
 
+    // Trace the embeddings black and gray roots.
     if (!rt->isHeapMinorCollecting()) {
         gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_EMBEDDING);
 
         /*
          * The embedding can register additional roots here.
          *
          * We don't need to trace these in a minor GC because all pointers into
          * the nursery should be in the store buffer, and we want to avoid the
@@ -346,16 +385,62 @@ js::gc::GCRuntime::markRuntime(JSTracer*
         /* During GC, we don't mark gray roots at this stage. */
         if (JSTraceDataOp op = grayRootTracer.op) {
             if (traceOrMark == TraceRuntime)
                 (*op)(trc, grayRootTracer.data);
         }
     }
 }
 
+#ifdef DEBUG
+class AssertNoRootsTracer : public JS::CallbackTracer
+{
+    void onChild(const JS::GCCellPtr& thing) override {
+        MOZ_CRASH("There should not be any roots after finishRoots");
+    }
+
+  public:
+    AssertNoRootsTracer(JSRuntime* rt, WeakMapTraceKind weakTraceKind)
+      : JS::CallbackTracer(rt, weakTraceKind)
+    {}
+};
+#endif // DEBUG
+
+void
+js::gc::GCRuntime::finishRoots()
+{
+    rt->finishAtoms();
+
+    if (rootsHash.initialized())
+        rootsHash.clear();
+
+    rt->contextFromMainThread()->roots.finishPersistentRoots();
+
+    rt->finishSelfHosting();
+
+    for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
+        c->finishRoots();
+
+#ifdef DEBUG
+    // The nsWrapperCache may not be empty before our shutdown GC, so we have
+    // to skip that table when verifying that we are fully unrooted.
+    auto prior = grayRootTracer;
+    grayRootTracer = Callback<JSTraceDataOp>(nullptr, nullptr);
+
+    AssertNoRootsTracer trc(rt, TraceWeakMapKeysValues);
+    AutoPrepareForTracing prep(rt->contextFromMainThread(), WithAtoms);
+    gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_TRACE_HEAP);
+    traceRuntime(&trc, prep.session().lock);
+
+    // Restore the wrapper tracing so that we leak instead of leaving dangling
+    // pointers.
+    grayRootTracer = prior;
+#endif // DEBUG
+}
+
 // Append traced things to a buffer on the zone for use later in the GC.
 // See the comment in GCRuntime.h above grayBufferState for details.
 class BufferGrayRootsTracer : public JS::CallbackTracer
 {
     // Set to false if we OOM while buffering gray roots.
     bool bufferingGrayRootsFailed;
 
     void onChild(const JS::GCCellPtr& thing) override;
--- a/js/src/gc/Verifier.cpp
+++ b/js/src/gc/Verifier.cpp
@@ -201,17 +201,17 @@ gc::GCRuntime::startVerifyPreBarriers()
         goto oom;
 
     /* Create the root node. */
     trc->curnode = MakeNode(trc, nullptr, JS::TraceKind(0));
 
     incrementalState = State::MarkRoots;
 
     /* Make all the roots be edges emanating from the root node. */
-    markRuntime(trc, TraceRuntime, prep.session().lock);
+    traceRuntime(trc, prep.session().lock);
 
     VerifyNode* node;
     node = trc->curnode;
     if (trc->edgeptr == trc->term)
         goto oom;
 
     /* For each edge, make a node for it if one doesn't already exist. */
     while ((char*)node < trc->edgeptr) {
@@ -502,19 +502,19 @@ CheckHeapTracer::onChild(const JS::GCCel
     WorkItem item(thing, contextName(), parentIndex);
     if (!stack.append(item))
         oom = true;
 }
 
 bool
 CheckHeapTracer::check(AutoLockForExclusiveAccess& lock)
 {
-    // The analysis thinks that markRuntime might GC by calling a GC callback.
+    // The analysis thinks that traceRuntime might GC by calling a GC callback.
     JS::AutoSuppressGCAnalysis nogc;
-    rt->gc.markRuntime(this, GCRuntime::TraceRuntime, lock);
+    rt->gc.traceRuntime(this, lock);
 
     while (!stack.empty()) {
         WorkItem item = stack.back();
         if (item.processed) {
             stack.popBack();
         } else {
             parentIndex = stack.length() - 1;
             TraceChildren(this, item.thing);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1293127.js
@@ -0,0 +1,11 @@
+// Test that we can create 1000 cross compartment wrappers to nursery objects
+// without trigger a minor GC.
+gczeal(0);
+let g = newGlobal();
+evalcx("function f(x) { return {x: x}; }", g);
+gc();
+let initial = gcparam("gcNumber");
+for (let i = 0; i < 1000; i++)
+    g.f(i);
+let final = gcparam("gcNumber");
+assertEq(final, initial);
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -608,16 +608,22 @@ jit::LazyLinkTopActivation(JSContext* cx
 
     return calleeScript->baselineOrIonRawPointer();
 }
 
 /* static */ void
 JitRuntime::Mark(JSTracer* trc, AutoLockForExclusiveAccess& lock)
 {
     MOZ_ASSERT(!trc->runtime()->isHeapMinorCollecting());
+
+    // Shared stubs are allocated in the atoms compartment, so do not iterate
+    // them after the atoms heap after it has been "finished."
+    if (trc->runtime()->atomsAreFinished())
+        return;
+
     Zone* zone = trc->runtime()->atomsCompartment(lock)->zone();
     for (auto i = zone->cellIter<JitCode>(); !i.done(); i.next()) {
         JitCode* code = i;
         TraceRoot(trc, &code, "wrapper");
     }
 }
 
 /* static */ void
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -5575,16 +5575,27 @@ JS_ReportError(JSContext* cx, const char
 
     AssertHeapIsIdle(cx);
     va_start(ap, format);
     ReportErrorVA(cx, JSREPORT_ERROR, format, ap);
     va_end(ap);
 }
 
 JS_PUBLIC_API(void)
+JS_ReportErrorLatin1(JSContext* cx, const char* format, ...)
+{
+    va_list ap;
+
+    AssertHeapIsIdle(cx);
+    va_start(ap, format);
+    ReportErrorVA(cx, JSREPORT_ERROR, format, ap);
+    va_end(ap);
+}
+
+JS_PUBLIC_API(void)
 JS_ReportErrorNumber(JSContext* cx, JSErrorCallback errorCallback,
                      void* userRef, const unsigned errorNumber, ...)
 {
     va_list ap;
     va_start(ap, errorNumber);
     JS_ReportErrorNumberVA(cx, errorCallback, userRef, errorNumber, ap);
     va_end(ap);
 }
@@ -5595,16 +5606,36 @@ JS_ReportErrorNumberVA(JSContext* cx, JS
                        va_list ap)
 {
     AssertHeapIsIdle(cx);
     ReportErrorNumberVA(cx, JSREPORT_ERROR, errorCallback, userRef,
                         errorNumber, ArgumentsAreASCII, ap);
 }
 
 JS_PUBLIC_API(void)
+JS_ReportErrorNumberLatin1(JSContext* cx, JSErrorCallback errorCallback,
+                           void* userRef, const unsigned errorNumber, ...)
+{
+    va_list ap;
+    va_start(ap, errorNumber);
+    JS_ReportErrorNumberLatin1VA(cx, errorCallback, userRef, errorNumber, ap);
+    va_end(ap);
+}
+
+JS_PUBLIC_API(void)
+JS_ReportErrorNumberLatin1VA(JSContext* cx, JSErrorCallback errorCallback,
+                             void* userRef, const unsigned errorNumber,
+                             va_list ap)
+{
+    AssertHeapIsIdle(cx);
+    ReportErrorNumberVA(cx, JSREPORT_ERROR, errorCallback, userRef,
+                        errorNumber, ArgumentsAreLatin1, ap);
+}
+
+JS_PUBLIC_API(void)
 JS_ReportErrorNumberUC(JSContext* cx, JSErrorCallback errorCallback,
                        void* userRef, const unsigned errorNumber, ...)
 {
     va_list ap;
 
     AssertHeapIsIdle(cx);
     va_start(ap, errorNumber);
     ReportErrorNumberVA(cx, JSREPORT_ERROR, errorCallback, userRef,
@@ -5631,32 +5662,61 @@ JS_ReportWarning(JSContext* cx, const ch
     AssertHeapIsIdle(cx);
     va_start(ap, format);
     ok = ReportErrorVA(cx, JSREPORT_WARNING, format, ap);
     va_end(ap);
     return ok;
 }
 
 JS_PUBLIC_API(bool)
+JS_ReportWarningLatin1(JSContext* cx, const char* format, ...)
+{
+    va_list ap;
+    bool ok;
+
+    AssertHeapIsIdle(cx);
+    va_start(ap, format);
+    ok = ReportErrorVA(cx, JSREPORT_WARNING, format, ap);
+    va_end(ap);
+    return ok;
+}
+
+JS_PUBLIC_API(bool)
 JS_ReportErrorFlagsAndNumber(JSContext* cx, unsigned flags,
                              JSErrorCallback errorCallback, void* userRef,
                              const unsigned errorNumber, ...)
 {
     va_list ap;
     bool ok;
 
     AssertHeapIsIdle(cx);
     va_start(ap, errorNumber);
     ok = ReportErrorNumberVA(cx, flags, errorCallback, userRef,
                              errorNumber, ArgumentsAreASCII, ap);
     va_end(ap);
     return ok;
 }
 
 JS_PUBLIC_API(bool)
+JS_ReportErrorFlagsAndNumberLatin1(JSContext* cx, unsigned flags,
+                                   JSErrorCallback errorCallback, void* userRef,
+                                   const unsigned errorNumber, ...)
+{
+    va_list ap;
+    bool ok;
+
+    AssertHeapIsIdle(cx);
+    va_start(ap, errorNumber);
+    ok = ReportErrorNumberVA(cx, flags, errorCallback, userRef,
+                             errorNumber, ArgumentsAreLatin1, ap);
+    va_end(ap);
+    return ok;
+}
+
+JS_PUBLIC_API(bool)
 JS_ReportErrorFlagsAndNumberUC(JSContext* cx, unsigned flags,
                                JSErrorCallback errorCallback, void* userRef,
                                const unsigned errorNumber, ...)
 {
     va_list ap;
     bool ok;
 
     AssertHeapIsIdle(cx);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -5161,29 +5161,42 @@ const uint16_t MaxNumErrorArguments = 10
 
 /**
  * Report an exception represented by the sprintf-like conversion of format
  * and its arguments.
  */
 extern JS_PUBLIC_API(void)
 JS_ReportError(JSContext* cx, const char* format, ...);
 
+extern JS_PUBLIC_API(void)
+JS_ReportErrorLatin1(JSContext* cx, const char* format, ...);
+
 /*
  * Use an errorNumber to retrieve the format string, args are char*
  */
 extern JS_PUBLIC_API(void)
 JS_ReportErrorNumber(JSContext* cx, JSErrorCallback errorCallback,
                      void* userRef, const unsigned errorNumber, ...);
 
 #ifdef va_start
 extern JS_PUBLIC_API(void)
 JS_ReportErrorNumberVA(JSContext* cx, JSErrorCallback errorCallback,
                        void* userRef, const unsigned errorNumber, va_list ap);
 #endif
 
+extern JS_PUBLIC_API(void)
+JS_ReportErrorNumberLatin1(JSContext* cx, JSErrorCallback errorCallback,
+                           void* userRef, const unsigned errorNumber, ...);
+
+#ifdef va_start
+extern JS_PUBLIC_API(void)
+JS_ReportErrorNumberLatin1VA(JSContext* cx, JSErrorCallback errorCallback,
+                             void* userRef, const unsigned errorNumber, va_list ap);
+#endif
+
 /*
  * Use an errorNumber to retrieve the format string, args are char16_t*
  */
 extern JS_PUBLIC_API(void)
 JS_ReportErrorNumberUC(JSContext* cx, JSErrorCallback errorCallback,
                      void* userRef, const unsigned errorNumber, ...);
 
 extern JS_PUBLIC_API(void)
@@ -5196,21 +5209,29 @@ JS_ReportErrorNumberUCArray(JSContext* c
  * Return true if there was no error trying to issue the warning, and if the
  * warning was not converted into an error due to the JSOPTION_WERROR option
  * being set, false otherwise.
  */
 extern JS_PUBLIC_API(bool)
 JS_ReportWarning(JSContext* cx, const char* format, ...);
 
 extern JS_PUBLIC_API(bool)
+JS_ReportWarningLatin1(JSContext* cx, const char* format, ...);
+
+extern JS_PUBLIC_API(bool)
 JS_ReportErrorFlagsAndNumber(JSContext* cx, unsigned flags,
                              JSErrorCallback errorCallback, void* userRef,
                              const unsigned errorNumber, ...);
 
 extern JS_PUBLIC_API(bool)
+JS_ReportErrorFlagsAndNumberLatin1(JSContext* cx, unsigned flags,
+                                   JSErrorCallback errorCallback, void* userRef,
+                                   const unsigned errorNumber, ...);
+
+extern JS_PUBLIC_API(bool)
 JS_ReportErrorFlagsAndNumberUC(JSContext* cx, unsigned flags,
                                JSErrorCallback errorCallback, void* userRef,
                                const unsigned errorNumber, ...);
 
 /**
  * Complain when out of memory.
  */
 extern JS_PUBLIC_API(void)
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -195,16 +195,20 @@ JSRuntime::finishAtoms()
     wellKnownSymbols = nullptr;
     emptyString = nullptr;
 }
 
 void
 js::MarkAtoms(JSTracer* trc, AutoLockForExclusiveAccess& lock)
 {
     JSRuntime* rt = trc->runtime();
+
+    if (rt->atomsAreFinished())
+        return;
+
     for (AtomSet::Enum e(rt->atoms(lock)); !e.empty(); e.popFront()) {
         const AtomStateEntry& entry = e.front();
         if (!entry.isPinned())
             continue;
 
         JSAtom* atom = entry.asPtrUnbarriered();
         TraceRoot(trc, &atom, "interned_atom");
         MOZ_ASSERT(entry.asPtrUnbarriered() == atom);
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -532,17 +532,17 @@ class MOZ_RAII AutoMessageArgs
             args_ = cx->pod_malloc<const char16_t*>(count_ + 1);
             if (!args_)
                 return false;
             args_[count_] = nullptr;
         }
         for (uint16_t i = 0; i < count_; i++) {
             if (passed_) {
                 lengths_[i] = js_strlen(args_[i]);
-            } else if (typeArg == ArgumentsAreASCII) {
+            } else if (typeArg == ArgumentsAreASCII || typeArg == ArgumentsAreLatin1) {
                 char* charArg = va_arg(ap, char*);
                 size_t charArgLength = strlen(charArg);
                 args_[i] = InflateString(cx, charArg, &charArgLength);
                 if (!args_[i])
                     return false;
                 allocatedElements_ = true;
                 MOZ_ASSERT(charArgLength == js_strlen(args_[i]));
                 lengths_[i] = charArgLength;
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -572,17 +572,18 @@ struct MOZ_RAII AutoResolving {
 extern JSContext*
 NewContext(uint32_t maxBytes, uint32_t maxNurseryBytes, JSRuntime* parentRuntime);
 
 extern void
 DestroyContext(JSContext* cx);
 
 enum ErrorArgumentsType {
     ArgumentsAreUnicode,
-    ArgumentsAreASCII
+    ArgumentsAreASCII,
+    ArgumentsAreLatin1
 };
 
 /*
  * Loads and returns a self-hosted function by name. For performance, define
  * the property name in vm/CommonPropertyNames.h.
  *
  * Defined in SelfHosting.cpp.
  */
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -204,54 +204,16 @@ JSCompartment::ensureJitCompartmentExist
         js_delete(jitCompartment_);
         jitCompartment_ = nullptr;
         return false;
     }
 
     return true;
 }
 
-/*
- * This class is used to add a post barrier on the crossCompartmentWrappers map,
- * as the key is calculated based on objects which may be moved by generational
- * GC.
- */
-class WrapperMapRef : public BufferableRef
-{
-    WrapperMap* map;
-    CrossCompartmentKey key;
-
-  public:
-    WrapperMapRef(WrapperMap* map, const CrossCompartmentKey& key)
-      : map(map), key(key) {}
-
-    struct TraceFunctor {
-        JSTracer* trc_;
-        const char* name_;
-        TraceFunctor(JSTracer *trc, const char* name) : trc_(trc), name_(name) {}
-
-        template <class T> void operator()(T* t) { TraceManuallyBarrieredEdge(trc_, t, name_); }
-    };
-    void trace(JSTracer* trc) override {
-        CrossCompartmentKey prior = key;
-        key.applyToWrapped(TraceFunctor(trc, "ccw wrapped"));
-        key.applyToDebugger(TraceFunctor(trc, "ccw debugger"));
-        if (key == prior)
-            return;
-
-        /* Look for the original entry, which might have been removed. */
-        WrapperMap::Ptr p = map->lookup(prior);
-        if (!p)
-            return;
-
-        /* Rekey the entry. */
-        map->rekeyAs(prior, key, key);
-    }
-};
-
 #ifdef JSGC_HASH_TABLE_CHECKS
 namespace {
 struct CheckGCThingAfterMovingGCFunctor {
     template <class T> void operator()(T* t) { CheckGCThingAfterMovingGC(*t); }
 };
 } // namespace (anonymous)
 
 void
@@ -283,26 +245,30 @@ JSCompartment::putWrapper(JSContext* cx,
                           const js::Value& wrapper)
 {
     MOZ_ASSERT(wrapped.is<JSString*>() == wrapper.isString());
     MOZ_ASSERT_IF(!wrapped.is<JSString*>(), wrapper.isObject());
 
     /* There's no point allocating wrappers in the nursery since we will tenure them anyway. */
     MOZ_ASSERT(!IsInsideNursery(static_cast<gc::Cell*>(wrapper.toGCThing())));
 
-    if (!crossCompartmentWrappers.put(wrapped, ReadBarriered<Value>(wrapper))) {
+    bool isNuseryKey =
+        const_cast<CrossCompartmentKey&>(wrapped).applyToWrapped(IsInsideNurseryFunctor()) ||
+        const_cast<CrossCompartmentKey&>(wrapped).applyToDebugger(IsInsideNurseryFunctor());
+
+    if (isNuseryKey && !nurseryCCKeys.append(wrapped)) {
         ReportOutOfMemory(cx);
         return false;
     }
 
-    if (const_cast<CrossCompartmentKey&>(wrapped).applyToWrapped(IsInsideNurseryFunctor()) ||
-        const_cast<CrossCompartmentKey&>(wrapped).applyToDebugger(IsInsideNurseryFunctor()))
-    {
-        WrapperMapRef ref(&crossCompartmentWrappers, wrapped);
-        cx->runtime()->gc.storeBuffer.putGeneric(ref);
+    if (!crossCompartmentWrappers.put(wrapped, ReadBarriered<Value>(wrapper))) {
+        if (isNuseryKey)
+            nurseryCCKeys.popBack();
+        ReportOutOfMemory(cx);
+        return false;
     }
 
     return true;
 }
 
 static JSString*
 CopyStringPure(JSContext* cx, JSString* str)
 {
@@ -623,16 +589,26 @@ JSCompartment::traceIncomingCrossCompart
 }
 
 void
 JSCompartment::trace(JSTracer* trc)
 {
     savedStacks_.trace(trc);
 }
 
+struct TraceFunctor {
+    JSTracer* trc_;
+    const char* name_;
+    TraceFunctor(JSTracer *trc, const char* name)
+      : trc_(trc), name_(name) {}
+    template <class T> void operator()(T* t) {
+        TraceManuallyBarrieredEdge(trc_, t, name_);
+    }
+};
+
 void
 JSCompartment::traceRoots(JSTracer* trc, js::gc::GCRuntime::TraceOrMarkRuntime traceOrMark)
 {
     if (objectMetadataState.is<PendingMetadata>()) {
         TraceRoot(trc,
                   &objectMetadataState.as<PendingMetadata>(),
                   "on-stack object pending metadata");
     }
@@ -694,20 +670,53 @@ JSCompartment::traceRoots(JSTracer* trc,
             TraceRoot(trc, &script, "profilingScripts");
             MOZ_ASSERT(script == r.front().key(), "const_cast is only a work-around");
         }
     }
 
     if (nonSyntacticLexicalScopes_)
         nonSyntacticLexicalScopes_->trace(trc);
 
+    // In a minor GC we need to mark nursery objects that are the targets of
+    // cross compartment wrappers.
+    if (trc->runtime()->isHeapMinorCollecting()) {
+        for (auto key : nurseryCCKeys) {
+            CrossCompartmentKey prior = key;
+            key.applyToWrapped(TraceFunctor(trc, "ccw wrapped"));
+            key.applyToDebugger(TraceFunctor(trc, "ccw debugger"));
+            crossCompartmentWrappers.rekeyIfMoved(prior, key);
+        }
+        nurseryCCKeys.clear();
+    }
+
     wasm.trace(trc);
 }
 
 void
+JSCompartment::finishRoots()
+{
+    if (watchpointMap)
+        watchpointMap->clear();
+
+    if (debugScopes)
+        debugScopes->finish();
+
+    if (lazyArrayBuffers)
+        lazyArrayBuffers->clear();
+
+    if (objectMetadataTable)
+        objectMetadataTable->clear();
+
+    clearScriptCounts();
+
+    if (nonSyntacticLexicalScopes_)
+        nonSyntacticLexicalScopes_->clear();
+}
+
+void
 JSCompartment::sweepAfterMinorGC()
 {
     globalWriteBarriered = 0;
 
     if (innerViews.needsSweepAfterMinorGC())
         innerViews.sweepAfterMinorGC();
 }
 
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -410,16 +410,19 @@ struct JSCompartment
 
   private:
     const js::AllocationMetadataBuilder *allocationMetadataBuilder;
 
     js::SavedStacks              savedStacks_;
 
     js::WrapperMap               crossCompartmentWrappers;
 
+    using CCKeyVector = mozilla::Vector<js::CrossCompartmentKey, 0, js::SystemAllocPolicy>;
+    CCKeyVector                  nurseryCCKeys;
+
   public:
     /* Last time at which an animation was played for a global in this compartment. */
     int64_t                      lastAnimationTime;
 
     js::RegExpCompartment        regExps;
 
     /*
      * For generational GC, record whether a write barrier has added this
@@ -597,16 +600,20 @@ struct JSCompartment
      */
     void trace(JSTracer* trc);
     /*
      * This method traces JSCompartment-owned GC roots that are considered live
      * regardless of whether the JSCompartment itself is still live.
      */
     void traceRoots(JSTracer* trc, js::gc::GCRuntime::TraceOrMarkRuntime traceOrMark);
     /*
+     * This method clears out tables of roots in preparation for the final GC.
+     */
+    void finishRoots();
+    /*
      * These methods mark pointers that cross compartment boundaries. They are
      * called in per-zone GCs to prevent the wrappers' outgoing edges from
      * dangling (full GCs naturally follow pointers across compartments) and
      * when compacting to update cross-compartment pointers.
      */
     void traceOutgoingCrossCompartmentWrappers(JSTracer* trc);
     static void traceIncomingCrossCompartmentEdgesForZoneGC(JSTracer* trc);
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1112,25 +1112,16 @@ GCRuntime::finish()
     FreeChunkPool(rt, availableChunks_);
     FreeChunkPool(rt, emptyChunks_);
 
     FinishTrace();
 
     nursery.printTotalProfileTimes();
 }
 
-void
-GCRuntime::finishRoots()
-{
-    if (rootsHash.initialized())
-        rootsHash.clear();
-
-    rt->contextFromMainThread()->roots.finishPersistentRoots();
-}
-
 bool
 GCRuntime::setParameter(JSGCParamKey key, uint32_t value, AutoLockGC& lock)
 {
     switch (key) {
       case JSGC_MAX_MALLOC_BYTES:
         setMaxMallocBytes(value);
         for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next())
             zone->setGCMaxMallocBytes(maxMallocBytesAllocated() * 0.9);
@@ -2527,17 +2518,17 @@ GCRuntime::updatePointersToRelocatedCell
 
     // Iterate through all cells that can contain relocatable pointers to update
     // them. Since updating each cell is independent we try to parallelize this
     // as much as possible.
     updateAllCellPointers(&trc, zone);
 
     // Mark roots to update them.
     {
-        markRuntime(&trc, MarkRuntime, lock);
+        traceRuntimeForMajorGC(&trc, lock);
 
         gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_ROOTS);
         Debugger::markAll(&trc);
         Debugger::markIncomingCrossCompartmentEdges(&trc);
 
         WeakMapBase::markAll(zone, &trc);
         for (CompartmentsInZoneIter c(zone); !c.done(); c.next()) {
             c->trace(&trc);
@@ -3894,17 +3885,17 @@ GCRuntime::beginMarkPhase(JS::gcreason::
         }
 
         for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
             /* Unmark all weak maps in the zones being collected. */
             WeakMapBase::unmarkZone(zone);
         }
     }
 
-    markRuntime(gcmarker, MarkRuntime, lock);
+    traceRuntimeForMajorGC(gcmarker, lock);
 
     gcstats::AutoPhase ap2(stats, gcstats::PHASE_MARK_ROOTS);
 
     if (isIncremental) {
         gcstats::AutoPhase ap3(stats, gcstats::PHASE_BUFFER_GRAY_ROOTS);
         bufferGrayRoots();
     }
 
@@ -4179,17 +4170,17 @@ js::gc::MarkingValidator::nonIncremental
 
             MOZ_ASSERT(gcmarker->isDrained());
             gcmarker->reset();
 
             for (auto chunk = gc->allNonEmptyChunks(); !chunk.done(); chunk.next())
                 chunk->bitmap.clear();
         }
 
-        gc->markRuntime(gcmarker, GCRuntime::MarkRuntime, lock);
+        gc->traceRuntimeForMajorGC(gcmarker, lock);
 
         gc->incrementalState = State::Mark;
         auto unlimited = SliceBudget::unlimited();
         MOZ_RELEASE_ASSERT(gc->marker.drainMarkStack(unlimited));
     }
 
     gc->incrementalState = State::Sweep;
     {
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -191,23 +191,16 @@ class JS_PUBLIC_API(AutoGCRooter)
         *stackTop = down;
     }
 
     /* Implemented in gc/RootMarking.cpp. */
     inline void trace(JSTracer* trc);
     static void traceAll(JSTracer* trc);
     static void traceAllWrappers(JSTracer* trc);
 
-    /* T must be a context type */
-    template<typename T>
-    static void traceAllInContext(T* cx, JSTracer* trc) {
-        for (AutoGCRooter* gcr = cx->roots.autoGCRooters_; gcr; gcr = gcr->down)
-            gcr->trace(trc);
-    }
-
   protected:
     AutoGCRooter * const down;
 
     /*
      * Discriminates actual subclass of this being used.  If non-negative, the
      * subclass roots an array of values of the length stored in this field.
      * If negative, meaning is indicated by the corresponding value in the enum
      * below.  Any other negative value indicates some deeper problem such as
--- a/js/src/old-configure.in
+++ b/js/src/old-configure.in
@@ -1534,16 +1534,21 @@ fi
 MOZ_ZLIB_CHECK([1.2.3])
 
 if test -n "$ZLIB_IN_MOZGLUE"; then
     AC_DEFINE(ZLIB_IN_MOZGLUE)
 fi
 AC_SUBST(ZLIB_IN_MOZGLUE)
 
 dnl ========================================================
+dnl system libffi Support
+dnl ========================================================
+MOZ_CONFIG_FFI()
+
+dnl ========================================================
 dnl =
 dnl = Application
 dnl =
 dnl ========================================================
 
 MOZ_ARG_HEADER(Application)
 
 dnl ========================================================
@@ -2092,23 +2097,32 @@ AC_SUBST_LIST(EDITLINE_LIBS)
 
 dnl ========================================================
 dnl =
 dnl = Standalone module options
 dnl =
 dnl ========================================================
 MOZ_ARG_HEADER(Standalone module options (Not for building Mozilla))
 
+dnl ========================================================
+dnl = Build jsctypes if it's enabled
+dnl ========================================================
+MOZ_ARG_ENABLE_BOOL(ctypes,
+[  --enable-ctypes         Enable js-ctypes (default=no)],
+    BUILD_CTYPES=1,
+    BUILD_CTYPES= )
+JS_HAS_CTYPES=$BUILD_CTYPES
+AC_SUBST(JS_HAS_CTYPES)
+AC_SUBST(BUILD_CTYPES)
 if test "$JS_HAS_CTYPES"; then
-  dnl JS_HAS_CTYPES is defined by Python configure. This check remains
-  dnl as long as determining $AS remains in old-configure.
   dnl Error out if we're on MSVC and MASM is unavailable.
   if test -n "$_MSC_VER" -a \( "$AS" != "ml.exe" -a "$AS" != "ml64.exe" \); then
     AC_MSG_ERROR([\"$AS\" is not a suitable assembler to build js-ctypes. If you are building with MS Visual Studio 8 Express, you may download the MASM 8.0 package, upgrade to Visual Studio 9 Express, or install the Vista SDK. Or do not use --enable-ctypes.])
   fi
+  AC_DEFINE(JS_HAS_CTYPES)
 fi
 
 dnl ========================================================
 dnl =
 dnl = Options for generating the shell as a script
 dnl =
 dnl ========================================================
 
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -380,32 +380,16 @@ JSRuntime::destroyRuntime()
          * parse tasks. Waiting for AsmJS and compression tasks is done
          * synchronously (on the main thread or during parse tasks), so no
          * explicit canceling is needed for these.
          */
         for (CompartmentsIter comp(this, SkipAtoms); !comp.done(); comp.next())
             CancelOffThreadIonCompile(comp, nullptr);
         CancelOffThreadParses(this);
 
-        /* Clear debugging state to remove GC roots. */
-        for (CompartmentsIter comp(this, SkipAtoms); !comp.done(); comp.next()) {
-            if (WatchpointMap* wpmap = comp->watchpointMap)
-                wpmap->clear();
-        }
-
-        /*
-         * Clear script counts map, to remove the strong reference on the
-         * JSScript key.
-         */
-        for (CompartmentsIter comp(this, SkipAtoms); !comp.done(); comp.next())
-            comp->clearScriptCounts();
-
-        /* Clear atoms to remove GC roots and heap allocations. */
-        finishAtoms();
-
         /* Remove persistent GC roots. */
         gc.finishRoots();
 
         /*
          * Flag us as being destroyed. This allows the GC to free things like
          * interned atoms and Ion trampolines.
          */
         beingDestroyed_ = true;
@@ -418,22 +402,16 @@ JSRuntime::destroyRuntime()
 
         JS::PrepareForFullGC(contextFromMainThread());
         gc.gc(GC_NORMAL, JS::gcreason::DESTROY_RUNTIME);
     }
 
     MOZ_ASSERT(ionLazyLinkListSize_ == 0);
     MOZ_ASSERT(ionLazyLinkList_.isEmpty());
 
-    /*
-     * Clear the self-hosted global and delete self-hosted classes *after*
-     * GC, as finalizers for objects check for clasp->finalize during GC.
-     */
-    finishSelfHosting();
-
     MOZ_ASSERT(!numExclusiveThreads);
     AutoLockForExclusiveAccess lock(this);
 
     /*
      * Even though all objects in the compartment are dead, we may have keep
      * some filenames around because of gcKeepAtoms.
      */
     FreeScriptData(this, lock);
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -1041,16 +1041,17 @@ struct JSRuntime : public JS::shadow::Ru
     // allocated in the atomsCompartment. Reading or writing the symbol
     // registry requires the calling thread to have an ExclusiveContext and
     // hold a lock. Use AutoLockForExclusiveAccess.
     js::SymbolRegistry symbolRegistry_;
 
   public:
     bool initializeAtoms(JSContext* cx);
     void finishAtoms();
+    bool atomsAreFinished() const { return !atoms_; }
 
     void sweepAtoms();
 
     js::AtomSet& atoms(js::AutoLockForExclusiveAccess& lock) {
         return *atoms_;
     }
     JSCompartment* atomsCompartment(js::AutoLockForExclusiveAccess& lock) {
         return atomsCompartment_;
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -2547,16 +2547,22 @@ DebugScopes::sweep(JSRuntime* rt)
 
     /*
      * Scopes can be finalized when a debugger-synthesized ScopeObject is
      * no longer reachable via its DebugScopeObject.
      */
     liveScopes.sweep();
 }
 
+void
+DebugScopes::finish()
+{
+    proxiedScopes.clear();
+}
+
 #ifdef JSGC_HASH_TABLE_CHECKS
 void
 DebugScopes::checkHashTablesAfterMovingGC(JSRuntime* runtime)
 {
     /*
      * This is called at the end of StoreBuffer::mark() to check that our
      * postbarriers have worked and that no hashtable keys (or values) are left
      * pointing into the nursery.
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -1347,16 +1347,17 @@ class DebugScopes
   private:
     bool init();
 
     static DebugScopes* ensureCompartmentData(JSContext* cx);
 
   public:
     void mark(JSTracer* trc);
     void sweep(JSRuntime* rt);
+    void finish();
 #ifdef JS_GC_ZEAL
     void checkHashTablesAfterMovingGC(JSRuntime* rt);
 #endif
 
     static DebugScopeObject* hasDebugScope(JSContext* cx, ScopeObject& scope);
     static bool addDebugScope(JSContext* cx, Handle<ScopeObject*> scope,
                               Handle<DebugScopeObject*> debugScope);
 
--- a/layout/base/RestyleManagerBase.cpp
+++ b/layout/base/RestyleManagerBase.cpp
@@ -513,17 +513,24 @@ RecomputePosition(nsIFrame* aFrame)
         ReflowInput::ComputeRelativeOffsets(wm, cont, size, newOffsets);
         NS_ASSERTION(newOffsets.left == -newOffsets.right &&
                      newOffsets.top == -newOffsets.bottom,
                      "ComputeRelativeOffsets should return valid results");
 
         // ReflowInput::ApplyRelativePositioning would work here, but
         // since we've already checked mPosition and aren't changing the frame's
         // normal position, go ahead and add the offsets directly.
-        cont->SetPosition(cont->GetNormalPosition() +
+        // First, we need to ensure that the normal position is stored though.
+        nsPoint normalPosition = cont->GetNormalPosition();
+        auto props = cont->Properties();
+        const auto& prop = nsIFrame::NormalPositionProperty();
+        if (!props.Get(prop)) {
+          props.Set(prop, new nsPoint(normalPosition));
+        }
+        cont->SetPosition(normalPosition +
                           nsPoint(newOffsets.left, newOffsets.top));
       }
     }
 
     return true;
   }
 
   // For the absolute positioning case, set up a fake HTML reflow state for
new file mode 100644
--- /dev/null
+++ b/layout/base/crashtests/1288608.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<script>
+
+function boom()
+{
+    document.body.style.overflow = "scroll";
+    c.style.visibility = "";
+}
+
+</script>
+</head>
+<body onload="boom();">
+<div id="c" style="position: relative; transition: 2s; display: table-cell; bottom: 0.1vw;"></div>
+</body>
+</html>
--- a/layout/base/crashtests/crashtests.list
+++ b/layout/base/crashtests/crashtests.list
@@ -471,8 +471,9 @@ pref(layout.css.grid.enabled,true) load 
 load 1162813.xul
 load 1163583.html
 load 1234622-1.html
 load 1235467-1.html
 pref(dom.webcomponents.enabled,true) load 1261351.html
 load 1270797-1.html
 load 1278455-1.html
 load 1286889.html
+load 1288608.html
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -3676,16 +3676,17 @@ PresShell::ScheduleViewManagerFlush(Pain
   }
 }
 
 bool
 FlushLayoutRecursive(nsIDocument* aDocument,
                      void* aData = nullptr)
 {
   MOZ_ASSERT(!aData);
+  nsCOMPtr<nsIDocument> kungFuDeathGrip(aDocument);
   aDocument->EnumerateSubDocuments(FlushLayoutRecursive, nullptr);
   aDocument->FlushPendingNotifications(Flush_Layout);
   return true;
 }
 
 void
 PresShell::DispatchSynthMouseMove(WidgetGUIEvent* aEvent,
                                   bool aFlushOnHoverChange)
--- a/layout/generic/MathMLTextRunFactory.cpp
+++ b/layout/generic/MathMLTextRunFactory.cpp
@@ -709,18 +709,18 @@ MathMLTextRunFactory::RebuildTextRun(nsT
       canBreakBeforeArray.AppendElement(false);
     }
   }
 
   uint32_t flags;
   gfxTextRunFactory::Parameters innerParams =
       GetParametersForInner(aTextRun, &flags, aRefDrawTarget);
 
-  UniquePtr<nsTransformedTextRun> transformedChild;
-  UniquePtr<gfxTextRun> cachedChild;
+  RefPtr<nsTransformedTextRun> transformedChild;
+  RefPtr<gfxTextRun> cachedChild;
   gfxTextRun* child;
 
   if (mathVar == NS_MATHML_MATHVARIANT_BOLD && doMathvariantStyling) {
     font.style = NS_FONT_STYLE_NORMAL;
     font.weight = NS_FONT_WEIGHT_BOLD;
   } else if (mathVar == NS_MATHML_MATHVARIANT_ITALIC && doMathvariantStyling) {
     font.style = NS_FONT_STYLE_ITALIC;
     font.weight = NS_FONT_WEIGHT_NORMAL;
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -171,17 +171,17 @@ TabWidthStore::ApplySpacing(gfxTextRun::
     i++;
   }
 }
 
 NS_DECLARE_FRAME_PROPERTY_DELETABLE(TabWidthProperty, TabWidthStore)
 
 NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(OffsetToFrameProperty, nsTextFrame)
 
-NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(UninflatedTextRunProperty, gfxTextRun)
+NS_DECLARE_FRAME_PROPERTY_RELEASABLE(UninflatedTextRunProperty, gfxTextRun)
 
 NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(FontSizeInflationProperty, float)
 
 /**
  * A glyph observer for the change of a font glyph in a text run.
  *
  * This is stored in {Simple, Complex}TextRunUserData.
  */
@@ -516,17 +516,17 @@ DestroyTextRunUserData(gfxTextRun* aText
   aTextRun->SetUserData(nullptr);
 }
 
 /**
  * These are utility functions just for helping with the complexity related with
  * the text runs user data.
  */
 static nsTextFrame*
-GetFrameForSimpleFlow(gfxTextRun* aTextRun)
+GetFrameForSimpleFlow(const gfxTextRun* aTextRun)
 {
   MOZ_ASSERT(aTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SIMPLE_FLOW,
              "Not so simple flow?");
   if (aTextRun->GetFlags() & nsTextFrameUtils::TEXT_MIGHT_HAVE_GLYPH_CHANGES) {
     return static_cast<SimpleTextRunUserData*>(aTextRun->GetUserData())->mFrame;
   }
 
   return static_cast<nsTextFrame*>(aTextRun->GetUserData());
@@ -948,31 +948,29 @@ CreateObserversForAnimatedGlyphs(gfxText
  * and hard line breaks) and at each boundary runs the linebreaker to compute
  * potential line breaks. It also records actual line breaks to store them in
  * the textruns.
  */
 class BuildTextRunsScanner {
 public:
   BuildTextRunsScanner(nsPresContext* aPresContext, DrawTarget* aDrawTarget,
       nsIFrame* aLineContainer, nsTextFrame::TextRunType aWhichTextRun) :
-    mCurrentFramesAllSameTextRun(nullptr),
     mDrawTarget(aDrawTarget),
     mLineContainer(aLineContainer),
     mCommonAncestorWithLastFrame(nullptr),
     mMissingFonts(aPresContext->MissingFontRecorder()),
     mBidiEnabled(aPresContext->BidiEnabled()),
     mSkipIncompleteTextRuns(false),
     mWhichTextRun(aWhichTextRun),
     mNextRunContextInfo(nsTextFrameUtils::INCOMING_NONE),
     mCurrentRunContextInfo(nsTextFrameUtils::INCOMING_NONE) {
     ResetRunInfo();
   }
   ~BuildTextRunsScanner() {
     NS_ASSERTION(mBreakSinks.IsEmpty(), "Should have been cleared");
-    NS_ASSERTION(mTextRunsToDelete.IsEmpty(), "Should have been cleared");
     NS_ASSERTION(mLineBreakBeforeFrames.IsEmpty(), "Should have been cleared");
     NS_ASSERTION(mMappedFlows.IsEmpty(), "Should have been cleared");
   }
 
   void SetAtStartOfLine() {
     mStartOfLine = true;
     mCanStopOnThisLine = false;
   }
@@ -990,33 +988,33 @@ public:
   }
   void LiftCommonAncestorWithLastFrameToParent(nsIFrame* aFrame) {
     if (mCommonAncestorWithLastFrame &&
         mCommonAncestorWithLastFrame->GetParent() == aFrame) {
       mCommonAncestorWithLastFrame = aFrame;
     }
   }
   void ScanFrame(nsIFrame* aFrame);
-  bool IsTextRunValidForMappedFlows(gfxTextRun* aTextRun);
+  bool IsTextRunValidForMappedFlows(const gfxTextRun* aTextRun);
   void FlushFrames(bool aFlushLineBreaks, bool aSuppressTrailingBreak);
   void FlushLineBreaks(gfxTextRun* aTrailingTextRun);
   void ResetRunInfo() {
     mLastFrame = nullptr;
     mMappedFlows.Clear();
     mLineBreakBeforeFrames.Clear();
     mMaxTextLength = 0;
     mDoubleByteText = false;
   }
   void AccumulateRunInfo(nsTextFrame* aFrame);
   /**
    * @return null to indicate either textrun construction failed or
    * we constructed just a partial textrun to set up linebreaker and other
    * state for following textruns.
    */
-  gfxTextRun* BuildTextRunForFrames(void* aTextBuffer);
+  already_AddRefed<gfxTextRun> BuildTextRunForFrames(void* aTextBuffer);
   bool SetupLineBreakerContext(gfxTextRun *aTextRun);
   void AssignTextRun(gfxTextRun* aTextRun, float aInflation);
   nsTextFrame* GetNextBreakBeforeFrame(uint32_t* aIndex);
   void SetupBreakSinksForTextRun(gfxTextRun* aTextRun, const void* aTextPtr);
   void SetupTextEmphasisForTextRun(gfxTextRun* aTextRun, const void* aTextPtr);
   struct FindBoundaryState {
     nsIFrame*    mStopAtFrame;
     nsTextFrame* mFirstTextFrame;
@@ -1073,48 +1071,47 @@ public:
     }
     
     virtual void SetCapitalization(uint32_t aOffset, uint32_t aLength,
                                    bool* aCapitalize) override {
       MOZ_ASSERT(mTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_TRANSFORMED,
                  "Text run should be transformed!");
       if (mTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_TRANSFORMED) {
         nsTransformedTextRun* transformedTextRun =
-          static_cast<nsTransformedTextRun*>(mTextRun);
+          static_cast<nsTransformedTextRun*>(mTextRun.get());
         transformedTextRun->SetCapitalization(aOffset + mOffsetIntoTextRun, aLength,
                                               aCapitalize);
       }
     }
 
     void Finish(gfxMissingFontRecorder* aMFR) {
       MOZ_ASSERT(!(mTextRun->GetFlags() & nsTextFrameUtils::TEXT_UNUSED_FLAG),
                    "Flag set that should never be set! (memory safety error?)");
       if (mTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_TRANSFORMED) {
         nsTransformedTextRun* transformedTextRun =
-          static_cast<nsTransformedTextRun*>(mTextRun);
+          static_cast<nsTransformedTextRun*>(mTextRun.get());
         transformedTextRun->FinishSettingProperties(mDrawTarget, aMFR);
       }
       // The way nsTransformedTextRun is implemented, its glyph runs aren't
       // available until after nsTransformedTextRun::FinishSettingProperties()
       // is called. So that's why we defer checking for animated glyphs to here.
       CreateObserversForAnimatedGlyphs(mTextRun);
     }
 
-    gfxTextRun*  mTextRun;
+    RefPtr<gfxTextRun> mTextRun;
     DrawTarget*  mDrawTarget;
     uint32_t     mOffsetIntoTextRun;
   };
 
 private:
   AutoTArray<MappedFlow,10>   mMappedFlows;
   AutoTArray<nsTextFrame*,50> mLineBreakBeforeFrames;
   AutoTArray<UniquePtr<BreakSink>,10> mBreakSinks;
-  AutoTArray<UniquePtr<gfxTextRun>,5> mTextRunsToDelete;
   nsLineBreaker                 mLineBreaker;
-  gfxTextRun*                   mCurrentFramesAllSameTextRun;
+  RefPtr<gfxTextRun>            mCurrentFramesAllSameTextRun;
   DrawTarget*                   mDrawTarget;
   nsIFrame*                     mLineContainer;
   nsTextFrame*                  mLastFrame;
   // The common ancestor of the current frame and the previous leaf frame
   // on the line, or null if there was no previous leaf frame.
   nsIFrame*                     mCommonAncestorWithLastFrame;
   gfxMissingFontRecorder*       mMissingFonts;
   // mMaxTextLength is an upper bound on the size of the text in all mapped frames
@@ -1531,17 +1528,18 @@ ExpandBuffer(char16_t* aDest, uint8_t* a
     *aDest = *aSrc;
     ++aDest;
     ++aSrc;
     --aCount;
   }
   return aDest;
 }
 
-bool BuildTextRunsScanner::IsTextRunValidForMappedFlows(gfxTextRun* aTextRun)
+bool
+BuildTextRunsScanner::IsTextRunValidForMappedFlows(const gfxTextRun* aTextRun)
 {
   if (aTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SIMPLE_FLOW) {
     return mMappedFlows.Length() == 1 &&
       mMappedFlows[0].mStartFrame == GetFrameForSimpleFlow(aTextRun) &&
       mMappedFlows[0].mEndFrame == nullptr;
   }
 
   auto userData = static_cast<TextRunUserData*>(aTextRun->GetUserData());
@@ -1557,17 +1555,17 @@ bool BuildTextRunsScanner::IsTextRunVali
 }
 
 /**
  * This gets called when we need to make a text run for the current list of
  * frames.
  */
 void BuildTextRunsScanner::FlushFrames(bool aFlushLineBreaks, bool aSuppressTrailingBreak)
 {
-  gfxTextRun* textRun = nullptr;
+  RefPtr<gfxTextRun> textRun;
   if (!mMappedFlows.IsEmpty()) {
     if (!mSkipIncompleteTextRuns && mCurrentFramesAllSameTextRun &&
         ((mCurrentFramesAllSameTextRun->GetFlags() & nsTextFrameUtils::TEXT_INCOMING_WHITESPACE) != 0) ==
         ((mCurrentRunContextInfo & nsTextFrameUtils::INCOMING_WHITESPACE) != 0) &&
         ((mCurrentFramesAllSameTextRun->GetFlags() & gfxTextRunFactory::TEXT_INCOMING_ARABICCHAR) != 0) ==
         ((mCurrentRunContextInfo & nsTextFrameUtils::INCOMING_ARABICCHAR) != 0) &&
         IsTextRunValidForMappedFlows(mCurrentFramesAllSameTextRun)) {
       // Optimization: We do not need to (re)build the textrun.
@@ -1593,17 +1591,17 @@ void BuildTextRunsScanner::FlushFrames(b
           !buffer.AppendElements(bufferSize, fallible)) {
         return;
       }
       textRun = BuildTextRunForFrames(buffer.Elements());
     }
   }
 
   if (aFlushLineBreaks) {
-    FlushLineBreaks(aSuppressTrailingBreak ? nullptr : textRun);
+    FlushLineBreaks(aSuppressTrailingBreak ? nullptr : textRun.get());
   }
 
   mCanStopOnThisLine = true;
   ResetRunInfo();
 }
 
 void BuildTextRunsScanner::FlushLineBreaks(gfxTextRun* aTrailingTextRun)
 {
@@ -1617,17 +1615,16 @@ void BuildTextRunsScanner::FlushLineBrea
   }
 
   for (uint32_t i = 0; i < mBreakSinks.Length(); ++i) {
     // TODO cause frames associated with the textrun to be reflowed, if they
     // aren't being reflowed already!
     mBreakSinks[i]->Finish(mMissingFonts);
   }
   mBreakSinks.Clear();
-  mTextRunsToDelete.Clear();
 }
 
 void BuildTextRunsScanner::AccumulateRunInfo(nsTextFrame* aFrame)
 {
   if (mMaxTextLength != UINT32_MAX) {
     NS_ASSERTION(mMaxTextLength < UINT32_MAX - aFrame->GetContentLength(), "integer overflow");
     if (mMaxTextLength >= UINT32_MAX - aFrame->GetContentLength()) {
       mMaxTextLength = UINT32_MAX;
@@ -1669,17 +1666,17 @@ GetFirstFontMetrics(gfxFontGroup* aFontG
   if (!aFontGroup)
     return gfxFont::Metrics();
   gfxFont* font = aFontGroup->GetFirstValidFont();
   return font->GetMetrics(aVerticalMetrics ? gfxFont::eVertical
                                            : gfxFont::eHorizontal);
 }
 
 static gfxFloat
-GetSpaceWidthAppUnits(gfxTextRun* aTextRun)
+GetSpaceWidthAppUnits(const gfxTextRun* aTextRun)
 {
   // Round the space width when converting to appunits the same way textruns
   // do.
   gfxFloat spaceWidthAppUnits =
     NS_round(GetFirstFontMetrics(aTextRun->GetFontGroup(),
                                  aTextRun->UseCenterBaseline()).spaceWidth *
              aTextRun->GetAppUnitsPerDevUnit());
 
@@ -1700,17 +1697,17 @@ LetterSpacing(nsIFrame* aFrame, const ns
   if (eStyleUnit_Coord == coord.GetUnit()) {
     return coord.GetCoordValue();
   }
   return 0;
 }
 
 // This function converts non-coord values (e.g. percentages) to nscoord.
 static nscoord
-WordSpacing(nsIFrame* aFrame, gfxTextRun* aTextRun,
+WordSpacing(nsIFrame* aFrame, const gfxTextRun* aTextRun,
             const nsStyleText* aStyleText = nullptr)
 {
   if (aFrame->IsSVGText()) {
     return 0;
   }
   if (!aStyleText) {
     aStyleText = aFrame->StyleText();
   }
@@ -1929,18 +1926,18 @@ static already_AddRefed<DrawTarget>
 CreateReferenceDrawTarget(nsTextFrame* aTextFrame)
 {
   RefPtr<gfxContext> ctx =
     aTextFrame->PresContext()->PresShell()->CreateReferenceRenderingContext();
   RefPtr<DrawTarget> dt = ctx->GetDrawTarget();
   return dt.forget();
 }
 
-static UniquePtr<gfxTextRun>
-GetHyphenTextRun(gfxTextRun* aTextRun, DrawTarget* aDrawTarget,
+static already_AddRefed<gfxTextRun>
+GetHyphenTextRun(const gfxTextRun* aTextRun, DrawTarget* aDrawTarget,
                  nsTextFrame* aTextFrame)
 {
   RefPtr<DrawTarget> dt = aDrawTarget;
   if (!dt) {
     dt = CreateReferenceDrawTarget(aTextFrame);
     if (!dt) {
       return nullptr;
     }
@@ -1976,17 +1973,17 @@ GetCSSWhitespaceToCompressionMode(nsText
       !aStyleText->NewlineIsSignificant(aFrame)) {
     // If newline is set to be preserved, but then suppressed,
     // transform newline to space.
     compression = nsTextFrameUtils::COMPRESS_NONE_TRANSFORM_TO_SPACE;
   }
   return compression;
 }
 
-gfxTextRun*
+already_AddRefed<gfxTextRun>
 BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
 {
   gfxSkipChars skipChars;
 
   const void* textPtr = aTextBuffer;
   bool anyTextTransformStyle = false;
   bool anyMathMLStyling = false;
   bool anyTextEmphasis = false;
@@ -2302,17 +2299,17 @@ BuildTextRunsScanner::BuildTextRunForFra
         }
       }
     }
     textFlags |= nsTextFrameUtils::TEXT_IS_TRANSFORMED;
     NS_ASSERTION(iter.GetSkippedOffset() == transformedLength,
                  "We didn't cover all the characters in the text run!");
   }
 
-  UniquePtr<gfxTextRun> textRun;
+  RefPtr<gfxTextRun> textRun;
   gfxTextRunFactory::Parameters params =
       { mDrawTarget, finalUserData, &skipChars,
         textBreakPointsAfterTransform.Elements(),
         uint32_t(textBreakPointsAfterTransform.Length()),
         int32_t(firstFrame->PresContext()->AppUnitsPerDevPixel())};
 
   if (mDoubleByteText) {
     const char16_t* text = static_cast<const char16_t*>(textPtr);
@@ -2368,26 +2365,23 @@ BuildTextRunsScanner::BuildTextRunForFra
         transformedLength, mDoubleByteText);
     // Since we're doing to destroy the user data now, avoid a dangling
     // pointer. Strictly speaking we don't need to do this since it should
     // not be used (since this textrun will not be used and will be
     // itself deleted soon), but it's always better to not have dangling
     // pointers around.
     textRun->SetUserData(nullptr);
     DestroyUserData(userDataToDestroy);
-    // Arrange for this textrun to be deleted the next time the linebreaker
-    // is flushed out
-    mTextRunsToDelete.AppendElement(Move(textRun));
     return nullptr;
   }
 
   // Actually wipe out the textruns associated with the mapped frames and associate
   // those frames with this text run.
   AssignTextRun(textRun.get(), fontInflation);
-  return textRun.release();
+  return textRun.forget();
 }
 
 // This is a cut-down version of BuildTextRunForFrames used to set up
 // context for the line-breaker, when the textrun has already been created.
 // So it does the same walk over the mMappedFlows, but doesn't actually
 // build a new textrun.
 bool
 BuildTextRunsScanner::SetupLineBreakerContext(gfxTextRun *aTextRun)
@@ -3135,17 +3129,17 @@ protected:
 
   void InitFontGroupAndFontMetrics() {
     float inflation = (mWhichTextRun == nsTextFrame::eInflated)
       ? mFrame->GetFontSizeInflation() : 1.0f;
     mFontGroup = GetFontGroupForFrame(mFrame, inflation,
                                       getter_AddRefs(mFontMetrics));
   }
 
-  gfxTextRun*           mTextRun;
+  RefPtr<gfxTextRun>    mTextRun;
   gfxFontGroup*         mFontGroup;
   RefPtr<nsFontMetrics> mFontMetrics;
   const nsStyleText*    mTextStyle;
   const nsTextFragment* mFrag;
   nsIFrame*             mLineContainer;
   nsTextFrame*          mFrame;
   gfxSkipCharsIterator  mStart;  // Offset in original and transformed string
   gfxSkipCharsIterator  mTempIterator;
@@ -3170,34 +3164,36 @@ protected:
 
   bool                  mReflowing;
   nsTextFrame::TextRunType mWhichTextRun;
 };
 
 /**
  * Finds the offset of the first character of the cluster containing aPos
  */
-static void FindClusterStart(gfxTextRun* aTextRun, int32_t aOriginalStart,
+static void FindClusterStart(const gfxTextRun* aTextRun,
+                             int32_t aOriginalStart,
                              gfxSkipCharsIterator* aPos)
 {
   while (aPos->GetOriginalOffset() > aOriginalStart) {
     if (aPos->IsOriginalCharSkipped() ||
         aTextRun->IsClusterStart(aPos->GetSkippedOffset())) {
       break;
     }
     aPos->AdvanceOriginal(-1);
   }
 }
 
 /**
  * Finds the offset of the last character of the cluster containing aPos.
  * If aAllowSplitLigature is false, we also check for a ligature-group
  * start.
  */
-static void FindClusterEnd(gfxTextRun* aTextRun, int32_t aOriginalEnd,
+static void FindClusterEnd(const gfxTextRun* aTextRun,
+                           int32_t aOriginalEnd,
                            gfxSkipCharsIterator* aPos,
                            bool aAllowSplitLigature = true)
 {
   NS_PRECONDITION(aPos->GetOriginalOffset() < aOriginalEnd,
                   "character outside string");
   aPos->AdvanceOriginal(1);
   while (aPos->GetOriginalOffset() < aOriginalEnd) {
     if (aPos->IsOriginalCharSkipped() ||
@@ -3297,17 +3293,17 @@ PropertyProvider::ComputeJustification(
 void
 PropertyProvider::GetSpacing(Range aRange, Spacing* aSpacing)
 {
   GetSpacingInternal(aRange, aSpacing,
                      (mTextRun->GetFlags() & nsTextFrameUtils::TEXT_HAS_TAB) == 0);
 }
 
 static bool
-CanAddSpacingAfter(gfxTextRun* aTextRun, uint32_t aOffset)
+CanAddSpacingAfter(const gfxTextRun* aTextRun, uint32_t aOffset)
 {
   if (aOffset + 1 >= aTextRun->GetLength())
     return true;
   return aTextRun->IsClusterStart(aOffset + 1) &&
     aTextRun->IsLigatureGroupStart(aOffset + 1);
 }
 
 void
@@ -3386,28 +3382,28 @@ PropertyProvider::GetSpacingInternal(Ran
       uint32_t offset = i - aRange.start;
       aSpacing[offset].mBefore += spacing.mBefore;
       aSpacing[offset].mAfter += spacing.mAfter;
     }
   }
 }
 
 static gfxFloat
-ComputeTabWidthAppUnits(nsIFrame* aFrame, gfxTextRun* aTextRun)
+ComputeTabWidthAppUnits(nsIFrame* aFrame, const gfxTextRun* aTextRun)
 {
   // Get the number of spaces from CSS -moz-tab-size
   const nsStyleText* textStyle = aFrame->StyleText();
 
   return textStyle->mTabSize * GetSpaceWidthAppUnits(aTextRun);
 }
 
 // aX and the result are in whole appunits.
 static gfxFloat
 AdvanceToNextTab(gfxFloat aX, nsIFrame* aFrame,
-                 gfxTextRun* aTextRun, gfxFloat* aCachedTabWidth)
+                 const gfxTextRun* aTextRun, gfxFloat* aCachedTabWidth)
 {
   if (*aCachedTabWidth < 0) {
     *aCachedTabWidth = ComputeTabWidthAppUnits(aFrame, aTextRun);
   }
 
   // Advance aX to the next multiple of *aCachedTabWidth. We must advance
   // by at least 1 appunit.
   // XXX should we make this 1 CSS pixel?
@@ -4586,16 +4582,19 @@ nsTextFrame::SetTextRun(gfxTextRun* aTex
       // FIXME: Probably shouldn't do this within each SetTextRun
       // method, but it doesn't hurt.
       ClearTextRun(nullptr, nsTextFrame::eNotInflated);
     }
     SetFontSizeInflation(aInflation);
   } else {
     MOZ_ASSERT(aInflation == 1.0f, "unexpected inflation");
     if (HasFontSizeInflation()) {
+      // Setting the property will not automatically increment the textrun's
+      // reference count, so we need to do it here.
+      aTextRun->AddRef();
       Properties().Set(UninflatedTextRunProperty(), aTextRun);
       return;
     }
     // fall through to setting mTextRun
   }
 
   mTextRun = aTextRun;
 
@@ -4619,30 +4618,25 @@ nsTextFrame::RemoveTextRun(gfxTextRun* a
   }
   return false;
 }
 
 void
 nsTextFrame::ClearTextRun(nsTextFrame* aStartContinuation,
                           TextRunType aWhichTextRun)
 {
-  gfxTextRun* textRun = GetTextRun(aWhichTextRun);
+  RefPtr<gfxTextRun> textRun = GetTextRun(aWhichTextRun);
   if (!textRun) {
     return;
   }
 
   DebugOnly<bool> checkmTextrun = textRun == mTextRun;
   UnhookTextRunFromFrames(textRun, aStartContinuation);
   MOZ_ASSERT(checkmTextrun ? !mTextRun
                            : !Properties().Get(UninflatedTextRunProperty()));
-
-  if (!textRun->GetUserData()) {
-    // Delete it now because it's not doing anything useful
-    delete textRun;
-  }
 }
 
 void
 nsTextFrame::DisconnectTextRuns()
 {
   MOZ_ASSERT(!IsInTextRunUserData(),
              "Textrun mentions this frame in its user data so we can't just disconnect");
   mTextRun = nullptr;
@@ -5265,24 +5259,24 @@ GetInflationForTextDecorations(nsIFrame*
     return
       static_cast<const SVGTextFrame*>(container)->GetFontSizeScaleFactor();
   }
   return nsLayoutUtils::FontSizeInflationInner(aFrame, aInflationMinFontSize);
 }
 
 struct EmphasisMarkInfo
 {
-  UniquePtr<gfxTextRun> textRun;
+  RefPtr<gfxTextRun> textRun;
   gfxFloat advance;
   gfxFloat baselineOffset;
 };
 
 NS_DECLARE_FRAME_PROPERTY_DELETABLE(EmphasisMarkProperty, EmphasisMarkInfo)
 
-UniquePtr<gfxTextRun>
+already_AddRefed<gfxTextRun>
 GenerateTextRunForEmphasisMarks(nsTextFrame* aFrame,
                                 nsFontMetrics* aFontMetrics,
                                 nsStyleContext* aStyleContext,
                                 const nsStyleText* aStyleText)
 {
   const nsString& emphasisString = aStyleText->mTextEmphasisStyleString;
   RefPtr<DrawTarget> dt = CreateReferenceDrawTarget(aFrame);
   auto appUnitsPerDevUnit = aFrame->PresContext()->AppUnitsPerDevPixel();
@@ -5905,17 +5899,17 @@ public:
                       TextRangeStyle* aStyle);
   void UpdateWithAdvance(gfxFloat aAdvance) {
     mXOffset += aAdvance*mTextRun->GetDirection();
   }
 
 private:
   SelectionDetails**      mSelectionDetails;
   PropertyProvider&       mProvider;
-  gfxTextRun*             mTextRun;
+  RefPtr<gfxTextRun>      mTextRun;
   gfxSkipCharsIterator    mIterator;
   gfxTextRun::Range       mOriginalRange;
   gfxFloat                mXOffset;
 };
 
 SelectionIterator::SelectionIterator(SelectionDetails** aSelectionDetails,
                                      gfxTextRun::Range aRange,
                                      PropertyProvider& aProvider,
@@ -5971,23 +5965,23 @@ bool SelectionIterator::GetNextSegment(g
     *aHyphenWidth = mProvider.GetHyphenWidth();
   }
   *aSelectionType = selectionType;
   *aStyle = style;
   return true;
 }
 
 static void
-AddHyphenToMetrics(nsTextFrame* aTextFrame, gfxTextRun* aBaseTextRun,
+AddHyphenToMetrics(nsTextFrame* aTextFrame, const gfxTextRun* aBaseTextRun,
                    gfxTextRun::Metrics* aMetrics,
                    gfxFont::BoundingBoxType aBoundingBoxType,
                    DrawTarget* aDrawTarget)
 {
   // Fix up metrics to include hyphen
-  UniquePtr<gfxTextRun> hyphenTextRun =
+  RefPtr<gfxTextRun> hyphenTextRun =
     GetHyphenTextRun(aBaseTextRun, aDrawTarget, aTextFrame);
   if (!hyphenTextRun) {
     return;
   }
 
   gfxTextRun::Metrics hyphenMetrics =
     hyphenTextRun->MeasureText(aBoundingBoxType, aDrawTarget);
   if (aTextFrame->GetWritingMode().IsLineInverted()) {
@@ -6507,17 +6501,17 @@ nsTextFrame::MeasureCharClippedText(nsco
   Range range = ComputeTransformedRange(provider);
   uint32_t startOffset = range.start;
   uint32_t maxLength = range.Length();
   return MeasureCharClippedText(provider, aVisIStartEdge, aVisIEndEdge,
                                 &startOffset, &maxLength,
                                 aSnappedStartEdge, aSnappedEndEdge);
 }
 
-static uint32_t GetClusterLength(gfxTextRun* aTextRun,
+static uint32_t GetClusterLength(const gfxTextRun* aTextRun,
                                  uint32_t    aStartOffset,
                                  uint32_t    aMaxLength,
                                  bool        aIsRTL)
 {
   uint32_t clusterLength = aIsRTL ? 0 : 1;
   while (clusterLength < aMaxLength) {
     if (aTextRun->IsClusterStart(aStartOffset + clusterLength)) {
       if (aIsRTL) {
@@ -6799,17 +6793,17 @@ nsTextFrame::PaintText(const PaintTextPa
   params.clipEdges = &clipEdges;
   params.drawSoftHyphen = (GetStateBits() & TEXT_HYPHEN_BREAK) != 0;
   params.contextPaint = aParams.contextPaint;
   params.callbacks = aParams.callbacks;
   DrawText(range, textBaselinePt, params);
 }
 
 static void
-DrawTextRun(gfxTextRun* aTextRun,
+DrawTextRun(const gfxTextRun* aTextRun,
             const gfxPoint& aTextBaselinePt,
             gfxTextRun::Range aRange,
             const nsTextFrame::DrawTextRunParams& aParams)
 {
   gfxTextRun::DrawParams params(aParams.context);
   params.provider = aParams.provider;
   params.advanceWidth = aParams.advanceWidth;
   params.contextPaint = aParams.contextPaint;
@@ -6846,17 +6840,17 @@ nsTextFrame::DrawTextRun(Range aRange, c
                          const DrawTextRunParams& aParams)
 {
   MOZ_ASSERT(aParams.advanceWidth, "Must provide advanceWidth");
   ::DrawTextRun(mTextRun, aTextBaselinePt, aRange, aParams);
 
   if (aParams.drawSoftHyphen) {
     // Don't use ctx as the context, because we need a reference context here,
     // ctx may be transformed.
-    UniquePtr<gfxTextRun> hyphenTextRun =
+    RefPtr<gfxTextRun> hyphenTextRun =
       GetHyphenTextRun(mTextRun, nullptr, this);
     if (hyphenTextRun) {
       // For right-to-left text runs, the soft-hyphen is positioned at the left
       // of the text, minus its own width
       gfxFloat hyphenBaselineX = aTextBaselinePt.x +
         mTextRun->GetDirection() * (*aParams.advanceWidth) -
         (mTextRun->IsRightToLeft() ? hyphenTextRun->GetAdvanceWidth() : 0);
       DrawTextRunParams params = aParams;
@@ -7077,17 +7071,17 @@ nsTextFrame::IsVisibleInSelection(nsISel
   return found;
 }
 
 /**
  * Compute the longest prefix of text whose width is <= aWidth. Return
  * the length of the prefix. Also returns the width of the prefix in aFitWidth.
  */
 static uint32_t
-CountCharsFit(gfxTextRun* aTextRun, gfxTextRun::Range aRange,
+CountCharsFit(const gfxTextRun* aTextRun, gfxTextRun::Range aRange,
               gfxFloat aWidth, PropertyProvider* aProvider,
               gfxFloat* aFitWidth)
 {
   uint32_t last = 0;
   gfxFloat width = 0;
   for (uint32_t i = 1; i <= aRange.Length(); ++i) {
     if (i == aRange.Length() || aTextRun->IsClusterStart(aRange.start + i)) {
       gfxTextRun::Range range(aRange.start + last, aRange.start + i);
@@ -7630,17 +7624,17 @@ private:
   nsTextFrame::TrimmedOffsets mTrimmed;
   nsTArray<bool>      mWordBreaks;
   bool                        mHaveWordBreak;
 };
 
 static bool
 IsAcceptableCaretPosition(const gfxSkipCharsIterator& aIter,
                           bool aRespectClusters,
-                          gfxTextRun* aTextRun,
+                          const gfxTextRun* aTextRun,
                           nsIFrame* aFrame)
 {
   if (aIter.IsOriginalCharSkipped())
     return false;
   uint32_t index = aIter.GetSkippedOffset();
   if (aRespectClusters && !aTextRun->IsClusterStart(index))
     return false;
   if (index > 0) {
@@ -7784,17 +7778,17 @@ ClusterIterator::GetAfterOffset()
   return mCharIndex + (mDirection > 0 ? 1 : 0);
 }
 
 bool
 ClusterIterator::NextCluster()
 {
   if (!mDirection)
     return false;
-  gfxTextRun* textRun = mTextFrame->GetTextRun(nsTextFrame::eInflated);
+  const gfxTextRun* textRun = mTextFrame->GetTextRun(nsTextFrame::eInflated);
 
   mHaveWordBreak = false;
   while (true) {
     bool keepGoing = false;
     if (mDirection > 0) {
       if (mIterator.GetOriginalOffset() >= mTrimmed.GetEnd())
         return false;
       keepGoing = mIterator.IsOriginalCharSkipped() ||
@@ -7957,17 +7951,17 @@ nsTextFrame::GetOffsets(int32_t &start, 
 {
   start = GetContentOffset();
   end = GetContentEnd();
   return NS_OK;
 }
 
 static int32_t
 FindEndOfPunctuationRun(const nsTextFragment* aFrag,
-                        gfxTextRun* aTextRun,
+                        const gfxTextRun* aTextRun,
                         gfxSkipCharsIterator* aIter,
                         int32_t aOffset,
                         int32_t aStart,
                         int32_t aEnd)
 {
   int32_t i;
 
   for (i = aStart; i < aEnd - aOffset; ++i) {
@@ -7992,17 +7986,17 @@ FindEndOfPunctuationRun(const nsTextFrag
  * (and it has a good bit in common with nextBidi)
  * 
  * @param aLength an in/out parameter: on entry contains the maximum length to
  * return, on exit returns length of the first-letter fragment (which may
  * include leading and trailing punctuation, for example)
  */
 static bool
 FindFirstLetterRange(const nsTextFragment* aFrag,
-                     gfxTextRun* aTextRun,
+                     const gfxTextRun* aTextRun,
                      int32_t aOffset, const gfxSkipCharsIterator& aIter,
                      int32_t* aLength)
 {
   int32_t i;
   int32_t length = *aLength;
   int32_t endOffset = aOffset + length;
   gfxSkipCharsIterator iter(aIter);
 
@@ -8296,17 +8290,17 @@ nsTextFrame::AddInlineMinISize(nsRenderi
 
   if (trtype == eInflated && !IsCurrentFontInflation(inflation)) {
     // FIXME: Ideally, if we already have a text run, we'd move it to be
     // the uninflated text run.
     ClearTextRun(nullptr, nsTextFrame::eInflated);
   }
 
   nsTextFrame* f;
-  gfxTextRun* lastTextRun = nullptr;
+  const gfxTextRun* lastTextRun = nullptr;
   // nsContinuingTextFrame does nothing for AddInlineMinISize; all text frames
   // in the flow are handled right here.
   for (f = this; f; f = static_cast<nsTextFrame*>(f->GetNextContinuation())) {
     // f->GetTextRun(nsTextFrame::eNotInflated) could be null if we
     // haven't set up textruns yet for f.  Except in OOM situations,
     // lastTextRun will only be null for the first text frame.
     if (f == this || f->GetTextRun(trtype) != lastTextRun) {
       nsIFrame* lc;
@@ -8442,17 +8436,17 @@ nsTextFrame::AddInlinePrefISize(nsRender
 
   if (trtype == eInflated && !IsCurrentFontInflation(inflation)) {
     // FIXME: Ideally, if we already have a text run, we'd move it to be
     // the uninflated text run.
     ClearTextRun(nullptr, nsTextFrame::eInflated);
   }
 
   nsTextFrame* f;
-  gfxTextRun* lastTextRun = nullptr;
+  const gfxTextRun* lastTextRun = nullptr;
   // nsContinuingTextFrame does nothing for AddInlineMinISize; all text frames
   // in the flow are handled right here.
   for (f = this; f; f = static_cast<nsTextFrame*>(f->GetNextContinuation())) {
     // f->GetTextRun(nsTextFrame::eNotInflated) could be null if we
     // haven't set up textruns yet for f.  Except in OOM situations,
     // lastTextRun will only be null for the first text frame.
     if (f == this || f->GetTextRun(trtype) != lastTextRun) {
       nsIFrame* lc;
@@ -8556,17 +8550,17 @@ nsTextFrame::GetPrefWidthTightBounds(nsR
   // Round it like nsTextFrame::ComputeTightBounds() to ensure consistency.
   *aX = NSToCoordFloor(metrics.mBoundingBox.x);
   *aXMost = NSToCoordCeil(metrics.mBoundingBox.XMost());
 
   return NS_OK;
 }
 
 static bool
-HasSoftHyphenBefore(const nsTextFragment* aFrag, gfxTextRun* aTextRun,
+HasSoftHyphenBefore(const nsTextFragment* aFrag, const gfxTextRun* aTextRun,
                     int32_t aStartOffset, const gfxSkipCharsIterator& aIter)
 {
   if (aIter.GetSkippedOffset() < aTextRun->GetLength() &&
       aTextRun->CanHyphenateBefore(aIter.GetSkippedOffset())) {
     return true;
   }
   if (!(aTextRun->GetFlags() & nsTextFrameUtils::TEXT_HAS_SHY))
     return false;
@@ -9526,17 +9520,17 @@ nsTextFrame::RecomputeOverflow(nsIFrame*
   }
   nsRect &vis = result.VisualOverflow();
   vis.UnionRect(vis, boundingBox);
   UnionAdditionalOverflow(PresContext(), aBlockFrame, provider, &vis, true);
   return result;
 }
 
 static void TransformChars(nsTextFrame* aFrame, const nsStyleText* aStyle,
-                           gfxTextRun* aTextRun, uint32_t aSkippedOffset,
+                           const gfxTextRun* aTextRun, uint32_t aSkippedOffset,
                            const nsTextFragment* aFrag, int32_t aFragOffset,
                            int32_t aFragLen, nsAString& aOut)
 {
   aOut.SetLength(aOut.Length() + aFragLen);
   char16_t* out = aOut.EndWriting() - aFragLen;
   for (int32_t i = 0; i < aFragLen; ++i) {
     char16_t ch = aFrag->CharAt(aFragOffset + i);
     if ((ch == '\n' && !aStyle->NewlineIsSignificant(aFrame)) ||
--- a/layout/generic/nsTextFrame.h
+++ b/layout/generic/nsTextFrame.h
@@ -592,17 +592,17 @@ public:
   void AssignJustificationGaps(const mozilla::JustificationAssignment& aAssign);
   mozilla::JustificationAssignment GetJustificationAssignment() const;
 
   uint32_t CountGraphemeClusters() const;
 
 protected:
   virtual ~nsTextFrame();
 
-  gfxTextRun* mTextRun;
+  RefPtr<gfxTextRun> mTextRun;
   nsIFrame*   mNextContinuation;
   // The key invariant here is that mContentOffset never decreases along
   // a next-continuation chain. And of course mContentOffset is always <= the
   // the text node's content length, and the mContentOffset for the first frame
   // is always 0. Furthermore the text mapped by a frame is determined by
   // GetContentOffset() and GetContentLength()/GetContentEnd(), which get
   // the length from the difference between this frame's offset and the next
   // frame's offset, or the text length if there is no next frame. This means
--- a/layout/generic/nsTextRunTransformations.cpp
+++ b/layout/generic/nsTextRunTransformations.cpp
@@ -27,37 +27,38 @@ using namespace mozilla;
 
 // Greek sigma needs custom handling for the lowercase transform; for details
 // see comments under "case NS_STYLE_TEXT_TRANSFORM_LOWERCASE" within
 // nsCaseTransformTextRunFactory::RebuildTextRun(), and bug 740120.
 #define GREEK_CAPITAL_LETTER_SIGMA             0x03A3
 #define GREEK_SMALL_LETTER_FINAL_SIGMA         0x03C2
 #define GREEK_SMALL_LETTER_SIGMA               0x03C3
 
-UniquePtr<nsTransformedTextRun>
+already_AddRefed<nsTransformedTextRun>
 nsTransformedTextRun::Create(const gfxTextRunFactory::Parameters* aParams,
                              nsTransformingTextRunFactory* aFactory,
                              gfxFontGroup* aFontGroup,
                              const char16_t* aString, uint32_t aLength,
                              const uint32_t aFlags,
                              nsTArray<RefPtr<nsTransformedCharStyle>>&& aStyles,
                              bool aOwnsFactory)
 {
   NS_ASSERTION(!(aFlags & gfxTextRunFactory::TEXT_IS_8BIT),
                "didn't expect text to be marked as 8-bit here");
 
   void *storage = AllocateStorageForTextRun(sizeof(nsTransformedTextRun), aLength);
   if (!storage) {
     return nullptr;
   }
 
-  return UniquePtr<nsTransformedTextRun>(
+  RefPtr<nsTransformedTextRun> result =
     new (storage) nsTransformedTextRun(aParams, aFactory, aFontGroup,
                                        aString, aLength, aFlags,
-                                       Move(aStyles), aOwnsFactory));
+                                       Move(aStyles), aOwnsFactory);
+  return result.forget();
 }
 
 void
 nsTransformedTextRun::SetCapitalization(uint32_t aStart, uint32_t aLength,
                                         bool* aCapitalization)
 {
   if (mCapitalize.IsEmpty()) {
     if (!mCapitalize.AppendElements(GetLength()))
@@ -93,29 +94,29 @@ nsTransformedTextRun::SizeOfExcludingThi
 }
 
 size_t
 nsTransformedTextRun::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
 {
   return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
 }
 
-UniquePtr<nsTransformedTextRun>
+already_AddRefed<nsTransformedTextRun>
 nsTransformingTextRunFactory::MakeTextRun(const char16_t* aString, uint32_t aLength,
                                           const gfxTextRunFactory::Parameters* aParams,
                                           gfxFontGroup* aFontGroup, uint32_t aFlags,
                                           nsTArray<RefPtr<nsTransformedCharStyle>>&& aStyles,
                                           bool aOwnsFactory)
 {
   return nsTransformedTextRun::Create(aParams, this, aFontGroup,
                                       aString, aLength, aFlags, Move(aStyles),
                                       aOwnsFactory);
 }
 
-UniquePtr<nsTransformedTextRun>
+already_AddRefed<nsTransformedTextRun>
 nsTransformingTextRunFactory::MakeTextRun(const uint8_t* aString, uint32_t aLength,
                                           const gfxTextRunFactory::Parameters* aParams,
                                           gfxFontGroup* aFontGroup, uint32_t aFlags,
                                           nsTArray<RefPtr<nsTransformedCharStyle>>&& aStyles,
                                           bool aOwnsFactory)
 {
   // We'll only have a Unicode code path to minimize the amount of code needed
   // for these rarely used features
@@ -630,18 +631,18 @@ nsCaseTransformTextRunFactory::RebuildTe
                                      &canBreakBeforeArray,
                                      &styleArray);
 
   uint32_t flags;
   gfxTextRunFactory::Parameters innerParams =
     GetParametersForInner(aTextRun, &flags, aRefDrawTarget);
   gfxFontGroup* fontGroup = aTextRun->GetFontGroup();
 
-  UniquePtr<nsTransformedTextRun> transformedChild;
-  UniquePtr<gfxTextRun> cachedChild;
+  RefPtr<nsTransformedTextRun> transformedChild;
+  RefPtr<gfxTextRun> cachedChild;
   gfxTextRun* child;
 
   if (mInnerTransformingTextRunFactory) {
     transformedChild = mInnerTransformingTextRunFactory->MakeTextRun(
         convertedString.BeginReading(), convertedString.Length(),
         &innerParams, fontGroup, flags, Move(styleArray), false);
     child = transformedChild.get();
   } else {
--- a/layout/generic/nsTextRunTransformations.h
+++ b/layout/generic/nsTextRunTransformations.h
@@ -41,24 +41,24 @@ private:
   nsTransformedCharStyle& operator=(const nsTransformedCharStyle& aOther) = delete;
 };
 
 class nsTransformingTextRunFactory {
 public:
   virtual ~nsTransformingTextRunFactory() {}
 
   // Default 8-bit path just transforms to Unicode and takes that path
-  mozilla::UniquePtr<nsTransformedTextRun>
+  already_AddRefed<nsTransformedTextRun>
   MakeTextRun(const uint8_t* aString, uint32_t aLength,
               const gfxFontGroup::Parameters* aParams,
               gfxFontGroup* aFontGroup, uint32_t aFlags,
               nsTArray<RefPtr<nsTransformedCharStyle>>&& aStyles,
               bool aOwnsFactory);
 
-  mozilla::UniquePtr<nsTransformedTextRun>
+  already_AddRefed<nsTransformedTextRun>
   MakeTextRun(const char16_t* aString, uint32_t aLength,
               const gfxFontGroup::Parameters* aParams,
               gfxFontGroup* aFontGroup, uint32_t aFlags,
               nsTArray<RefPtr<nsTransformedCharStyle>>&& aStyles,
               bool aOwnsFactory);
 
   virtual void RebuildTextRun(nsTransformedTextRun* aTextRun,
                               mozilla::gfx::DrawTarget* aRefDrawTarget,
@@ -114,17 +114,17 @@ protected:
 
 /**
  * So that we can reshape as necessary, we store enough information
  * to fully rebuild the textrun contents.
  */
 class nsTransformedTextRun final : public gfxTextRun {
 public:
 
-  static mozilla::UniquePtr<nsTransformedTextRun>
+  static already_AddRefed<nsTransformedTextRun>
   Create(const gfxTextRunFactory::Parameters* aParams,
          nsTransformingTextRunFactory* aFactory,
          gfxFontGroup* aFontGroup,
          const char16_t* aString, uint32_t aLength,
          const uint32_t aFlags,
          nsTArray<RefPtr<nsTransformedCharStyle>>&& aStyles,
          bool aOwnsFactory);
 
--- a/layout/mathml/nsMathMLChar.cpp
+++ b/layout/mathml/nsMathMLChar.cpp
@@ -103,17 +103,17 @@ public:
 
   // True if this table contains parts to render this char
   virtual bool HasPartsOf(DrawTarget*   aDrawTarget,
                           int32_t       aAppUnitsPerDevPixel,
                           gfxFontGroup* aFontGroup,
                           char16_t      aChar,
                           bool          aVertical) = 0;
 
-  virtual UniquePtr<gfxTextRun>
+  virtual already_AddRefed<gfxTextRun>
   MakeTextRun(DrawTarget*        aDrawTarget,
               int32_t            aAppUnitsPerDevPixel,
               gfxFontGroup*      aFontGroup,
               const nsGlyphCode& aGlyph) = 0;
 protected:
   nsGlyphTable() : mCharCache(0) {}
   // For speedy re-use, we always cache the last data used in the table.
   // mCharCache is the Unicode point of the last char that was queried in this
@@ -225,17 +225,17 @@ public:
             ElementAt(aDrawTarget, aAppUnitsPerDevPixel, aFontGroup,
                       aChar, aVertical, 1).Exists() ||
             ElementAt(aDrawTarget, aAppUnitsPerDevPixel, aFontGroup,
                       aChar, aVertical, 2).Exists() ||
             ElementAt(aDrawTarget, aAppUnitsPerDevPixel, aFontGroup,
                       aChar, aVertical, 3).Exists());
   }
 
-  virtual UniquePtr<gfxTextRun>
+  virtual already_AddRefed<gfxTextRun>
   MakeTextRun(DrawTarget*        aDrawTarget,
               int32_t            aAppUnitsPerDevPixel,
               gfxFontGroup*      aFontGroup,
               const nsGlyphCode& aGlyph) override;
 private:
 
   // mGlyphCodeFonts[0] is the primary font associated to this table. The
   // others are possible "external" fonts for glyphs not in the primary font
@@ -378,17 +378,17 @@ nsPropertiesTable::ElementAt(DrawTarget*
   nsGlyphCode ch;
   ch.code[0] = mGlyphCache.CharAt(index);
   ch.code[1] = mGlyphCache.CharAt(index + 1);
   ch.font = mGlyphCache.CharAt(index + 2);
   return ch.code[0] == char16_t(0xFFFD) ? kNullGlyph : ch;
 }
 
 /* virtual */
-UniquePtr<gfxTextRun>
+already_AddRefed<gfxTextRun>
 nsPropertiesTable::MakeTextRun(DrawTarget*        aDrawTarget,
                                int32_t            aAppUnitsPerDevPixel,
                                gfxFontGroup*      aFontGroup,
                                const nsGlyphCode& aGlyph)
 {
   NS_ASSERTION(!aGlyph.IsGlyphID(),
                "nsPropertiesTable can only access glyphs by code point");
   return aFontGroup->
@@ -427,17 +427,17 @@ public:
 
   const FontFamilyName&
   FontNameFor(const nsGlyphCode& aGlyphCode) const override {
     NS_ASSERTION(aGlyphCode.IsGlyphID(),
                  "nsOpenTypeTable can only access glyphs by id");
     return mFontFamilyName;
   }
 
-  virtual UniquePtr<gfxTextRun>
+  virtual already_AddRefed<gfxTextRun>
   MakeTextRun(DrawTarget*        aDrawTarget,
               int32_t            aAppUnitsPerDevPixel,
               gfxFontGroup*      aFontGroup,
               const nsGlyphCode& aGlyph) override;
 
   // This returns a new OpenTypeTable instance to give access to OpenType MATH
   // table or nullptr if the font does not have such table. Ownership is passed
   // to the caller.
@@ -468,17 +468,17 @@ private:
 
 void
 nsOpenTypeTable::UpdateCache(DrawTarget*   aDrawTarget,
                              int32_t       aAppUnitsPerDevPixel,
                              gfxFontGroup* aFontGroup,
                              char16_t      aChar)
 {
   if (mCharCache != aChar) {
-    UniquePtr<gfxTextRun> textRun = aFontGroup->
+    RefPtr<gfxTextRun> textRun = aFontGroup->
       MakeTextRun(&aChar, 1, aDrawTarget, aAppUnitsPerDevPixel, 0, nullptr);
     const gfxTextRun::CompressedGlyph& data = textRun->GetCharacterGlyphs()[0];
     if (data.IsSimpleGlyph()) {
       mGlyphID = data.GetSimpleGlyph();
     } else if (data.GetGlyphCount() == 1) {
       mGlyphID = textRun->GetDetailedGlyphs(0)->mGlyphID;
     } else {
       mGlyphID = 0;
@@ -550,29 +550,29 @@ nsOpenTypeTable::HasPartsOf(DrawTarget* 
   if (!mFontEntry->GetMathVariantsParts(mGlyphID, aVertical, parts)) {
     return false;
   }
 
   return parts[0] || parts[1] || parts[2] || parts[3];
 }
 
 /* virtual */
-UniquePtr<gfxTextRun>
+already_AddRefed<gfxTextRun>
 nsOpenTypeTable::MakeTextRun(DrawTarget*        aDrawTarget,
                              int32_t            aAppUnitsPerDevPixel,
                              gfxFontGroup*      aFontGroup,
                              const nsGlyphCode& aGlyph)
 {
   NS_ASSERTION(aGlyph.IsGlyphID(),
                "nsOpenTypeTable can only access glyphs by id");
 
   gfxTextRunFactory::Parameters params = {
     aDrawTarget, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevPixel
   };
-  UniquePtr<gfxTextRun> textRun =
+  RefPtr<gfxTextRun> textRun =
     gfxTextRun::Create(&params, 1, aFontGroup, 0);
   textRun->AddGlyphRun(aFontGroup->GetFirstValidFont(),
                        gfxTextRange::kFontGroup, 0,
                        false, gfxTextRunFactory::TEXT_ORIENT_HORIZONTAL);
                               // We don't care about CSS writing mode here;
                               // math runs are assumed to be horizontal.
   gfxTextRun::DetailedGlyph detailedGlyph;
   detailedGlyph.mGlyphID = aGlyph.glyphID;
@@ -580,17 +580,17 @@ nsOpenTypeTable::MakeTextRun(DrawTarget*
     NSToCoordRound(aAppUnitsPerDevPixel *
                    aFontGroup->GetFirstValidFont()->
                    GetGlyphHAdvance(aDrawTarget, aGlyph.glyphID));
   detailedGlyph.mXOffset = detailedGlyph.mYOffset = 0;
   gfxShapedText::CompressedGlyph g;
   g.SetComplex(true, true, 1);
   textRun->SetGlyphs(0, g, &detailedGlyph);
 
-  return textRun;
+  return textRun.forget();
 }
 
 // -----------------------------------------------------------------------------
 // This is the list of all the applicable glyph tables.
 // We will maintain a single global instance that will only reveal those
 // glyph tables that are associated to fonts currently installed on the
 // user' system. The class is an XPCOM shutdown observer to allow us to
 // free its allocated data at shutdown
@@ -1131,17 +1131,17 @@ StretchEnumContext::TryVariants(nsGlyphT
       gfxFont* mathFont = aFontGroup->get()->GetFirstMathFont();
       // For OpenType MATH fonts, we will rely on the DisplayOperatorMinHeight
       // to select the right size variant. Note that the value is sometimes too
       // small so we use kLargeOpFactor/kIntegralFactor as a minimum value.
       if (mathFont) {
         displayOperatorMinHeight =
           mathFont->GetMathConstant(gfxFontEntry::DisplayOperatorMinHeight,
                                     oneDevPixel);
-        UniquePtr<gfxTextRun> textRun =
+        RefPtr<gfxTextRun> textRun =
           aGlyphTable->MakeTextRun(mDrawTarget, oneDevPixel, *aFontGroup, ch);
         nsBoundingMetrics bm = MeasureTextRun(mDrawTarget, textRun.get());
         float largeopFactor = kLargeOpFactor;
         if (NS_STRETCH_INTEGRAL & mStretchHint) {
           // integrals are drawn taller
           largeopFactor = kIntegralFactor;
         }
         nscoord minHeight = largeopFactor * (bm.ascent + bm.descent);
@@ -1161,17 +1161,17 @@ StretchEnumContext::TryVariants(nsGlyphT
     if (!mChar->SetFontFamily(mPresContext, aGlyphTable, ch, aFamilyList, font,
                               aFontGroup)) {
       // if largeopOnly is set, break now
       if (largeopOnly) break;
       ++size;
       continue;
     }
 
-    UniquePtr<gfxTextRun> textRun =
+    RefPtr<gfxTextRun> textRun =
       aGlyphTable->MakeTextRun(mDrawTarget, oneDevPixel, *aFontGroup, ch);
     nsBoundingMetrics bm = MeasureTextRun(mDrawTarget, textRun.get());
     if (ch.IsGlyphID()) {
       gfxFont* mathFont = aFontGroup->get()->GetFirstMathFont();
       if (mathFont) {
         // MeasureTextRun should have set the advance width to the right
         // bearing for OpenType MATH fonts. We now subtract the italic
         // correction, so that nsMathMLmmultiscripts will place the scripts
@@ -1249,17 +1249,17 @@ nsMathMLChar::StretchEnumContext::TryPar
                                            RefPtr<gfxFontGroup>* aFontGroup,
                                            const FontFamilyList& aFamilyList)
 {
   // Use our stretchy style context now that stretching is in progress
   nsFont font = mChar->mStyleContext->StyleFont()->mFont;
   NormalizeDefaultFont(font, mFontSizeInflation);
 
   // Compute the bounding metrics of all partial glyphs
-  UniquePtr<gfxTextRun> textRun[4];
+  RefPtr<gfxTextRun> textRun[4];
   nsGlyphCode chdata[4];
   nsBoundingMetrics bmdata[4];
   nscoord sizedata[4];
 
   bool isVertical = (mDirection == NS_STRETCH_DIRECTION_VERTICAL);
   nscoord oneDevPixel = mPresContext->AppUnitsPerDevPixel();
   char16_t uchar = mChar->mData[0];
   bool maxWidth = (NS_STRETCH_MAXWIDTH & mStretchHint) != 0;
--- a/layout/mathml/nsMathMLChar.h
+++ b/layout/mathml/nsMathMLChar.h
@@ -201,17 +201,17 @@ protected:
 
 private:
   nsRect             mRect;
   nsStretchDirection mDirection;
   nsBoundingMetrics  mBoundingMetrics;
   nsStyleContext*    mStyleContext;
   // mGlyphs/mBmData are arrays describing the glyphs used to draw the operator.
   // See the drawing methods below.
-  mozilla::UniquePtr<gfxTextRun> mGlyphs[4];
+  RefPtr<gfxTextRun> mGlyphs[4];
   nsBoundingMetrics     mBmData[4];
   // mUnscaledAscent is the actual ascent of the char.
   nscoord            mUnscaledAscent;
   // mScaleX, mScaleY are the factors by which we scale the char.
   float              mScaleX, mScaleY;
 
   // mDraw indicates how we draw the stretchy operator:
   // - DRAW_NORMAL: we render the mData string normally.
--- a/moz.configure
+++ b/moz.configure
@@ -96,18 +96,16 @@ add_old_configure_assignment('MOZ_DEBUG'
 include_when('build/moz.configure/toolchain.configure',
              when='--enable-compile-environment')
 include_when('build/moz.configure/memory.configure',
              when='--enable-compile-environment')
 include_when('build/moz.configure/headers.configure',
              when='--enable-compile-environment')
 include_when('build/moz.configure/warnings.configure',
              when='--enable-compile-environment')
-include_when('build/moz.configure/ffi.configure',
-             when='--enable-compile-environment')
 
 @dependable
 @imports(_from='mozbuild.backend', _import='backends')
 def build_backends_choices():
     return tuple(backends)
 
 
 @deprecated_option('--enable-build-backend', nargs='+',
--- a/netwerk/base/moz.build
+++ b/netwerk/base/moz.build
@@ -97,17 +97,16 @@ XPIDL_SOURCES += [
     'nsIRedirectResultListener.idl',
     'nsIRequest.idl',
     'nsIRequestContext.idl',
     'nsIRequestObserver.idl',
     'nsIRequestObserverProxy.idl',
     'nsIResponseHeadProvider.idl',
     'nsIResumableChannel.idl',
     'nsISecCheckWrapChannel.idl',
-    'nsISecretDecoderRing.idl',
     'nsISecureBrowserUI.idl',
     'nsISecurityEventSink.idl',
     'nsISecurityInfoProvider.idl',
     'nsISensitiveInfoHiddenURI.idl',
     'nsISerializationHelper.idl',
     'nsIServerSocket.idl',
     'nsISimpleStreamListener.idl',
     'nsISocketFilter.idl',
--- a/netwerk/cookie/nsCookieService.cpp
+++ b/netwerk/cookie/nsCookieService.cpp
@@ -4108,51 +4108,52 @@ nsCookieService::GetExpiry(nsCookieAttri
 {
   /* Determine when the cookie should expire. This is done by taking the difference between
    * the server time and the time the server wants the cookie to expire, and adding that
    * difference to the client time. This localizes the client time regardless of whether or
    * not the TZ environment variable was set on the client.
    *
    * Note: We need to consider accounting for network lag here, per RFC.
    */
-  int64_t delta;
-
   // check for max-age attribute first; this overrides expires attribute
   if (!aCookieAttributes.maxage.IsEmpty()) {
     // obtain numeric value of maxageAttribute
     int64_t maxage;
     int32_t numInts = PR_sscanf(aCookieAttributes.maxage.get(), "%lld", &maxage);
 
     // default to session cookie if the conversion failed
     if (numInts != 1) {
       return true;
     }
 
-    delta = maxage;
+    // if this addition overflows, expiryTime will be less than currentTime
+    // and the cookie will be expired - that's okay.
+    aCookieAttributes.expiryTime = aCurrentTime + maxage;
 
   // check for expires attribute
   } else if (!aCookieAttributes.expires.IsEmpty()) {
     PRTime expires;
 
     // parse expiry time
     if (PR_ParseTimeString(aCookieAttributes.expires.get(), true, &expires) != PR_SUCCESS) {
       return true;
     }
 
-    delta = expires / int64_t(PR_USEC_PER_SEC) - aServerTime;
+    // If set-cookie used absolute time to set expiration, and it can't use
+    // client time to set expiration.
+    // Because if current time be set in the future, but the cookie expire
+    // time be set less than current time and more than server time.
+    // The cookie item have to be used to the expired cookie.
+    aCookieAttributes.expiryTime = expires / int64_t(PR_USEC_PER_SEC);
 
   // default to session cookie if no attributes found
   } else {
     return true;
   }
 
-  // if this addition overflows, expiryTime will be less than currentTime
-  // and the cookie will be expired - that's okay.
-  aCookieAttributes.expiryTime = aCurrentTime + delta;
-
   return false;
 }
 
 /******************************************************************************
  * nsCookieService impl:
  * private cookielist management functions
  ******************************************************************************/
 
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -6341,17 +6341,24 @@ nsHttpChannel::OnStopRequest(nsIRequest 
         js::ProfileEntry::Category::NETWORK);
 
     LOG(("nsHttpChannel::OnStopRequest [this=%p request=%p status=%x]\n",
         this, request, status));
 
     MOZ_ASSERT(NS_IsMainThread(),
                "OnStopRequest should only be called from the main thread");
 
-    mUploadStream = nullptr;
+    if (!mAuthRetryPending) {
+        // We must not release the upload stream (that may contain POST data)
+        // before we finish any authentication loops happing during lifetime
+        // of this very channel.  Otherwise, we may loose the upload data when
+        // authenticating to e.g. an NTLM authenticated site.
+        LOG(("  dropping upload stream"));
+        mUploadStream = nullptr;
+    }
 
     if (NS_FAILED(status)) {
         ProcessSecurityReport(status);
     }
 
     // If this load failed because of a security error, it may be because we
     // are in a captive portal - trigger an async check to make sure.
     int32_t nsprError = -1 * NS_ERROR_GET_CODE(status);
--- a/netwerk/test/TestCookie.cpp
+++ b/netwerk/test/TestCookie.cpp
@@ -28,16 +28,46 @@ static NS_DEFINE_CID(kPrefServiceCID,   
 static const char kCookiesPermissions[] = "network.cookie.cookieBehavior";
 static const char kCookiesLifetimeEnabled[] = "network.cookie.lifetime.enabled";
 static const char kCookiesLifetimeDays[] = "network.cookie.lifetime.days";
 static const char kCookiesLifetimeCurrentSession[] = "network.cookie.lifetime.behavior";
 static const char kCookiesMaxPerHost[] = "network.cookie.maxPerHost";
 
 static char *sBuffer;
 
+#define OFFSET_ONE_WEEK int64_t(604800) * PR_USEC_PER_SEC
+#define OFFSET_ONE_DAY int64_t(86400) * PR_USEC_PER_SEC
+
+//Set server time or expiry time
+void
+SetTime(PRTime offsetTime,nsAutoCString& serverString,nsAutoCString& cookieString,bool expiry)
+{
+    char timeStringPreset[40];
+    PRTime CurrentTime = PR_Now();
+    PRTime SetCookieTime = CurrentTime + offsetTime;
+    PRTime SetExpiryTime;
+    if (expiry) {
+      SetExpiryTime = SetCookieTime - OFFSET_ONE_DAY;
+    } else {
+      SetExpiryTime = SetCookieTime + OFFSET_ONE_DAY;
+    }
+
+    // Set server time string
+    PRExplodedTime explodedTime;
+    PR_ExplodeTime(SetCookieTime , PR_GMTParameters, &explodedTime);
+    PR_FormatTimeUSEnglish(timeStringPreset, 40, "%c GMT", &explodedTime);
+    serverString.Assign(timeStringPreset);
+
+    // Set cookie string
+    PR_ExplodeTime(SetExpiryTime , PR_GMTParameters, &explodedTime);
+    PR_FormatTimeUSEnglish(timeStringPreset, 40, "%c GMT", &explodedTime);
+    cookieString.Replace(0, strlen("test=expiry; expires=") + strlen(timeStringPreset) + 1, "test=expiry; expires=");
+    cookieString.Append(timeStringPreset);
+}
+
 nsresult
 SetACookie(nsICookieService *aCookieService, const char *aSpec1, const char *aSpec2, const char* aCookieString, const char *aServerTime)
 {
     nsCOMPtr<nsIURI> uri1, uri2;
     NS_NewURI(getter_AddRefs(uri1), aSpec1);
     if (aSpec2)
         NS_NewURI(getter_AddRefs(uri2), aSpec2);
 
@@ -458,18 +488,39 @@ main(int32_t argc, char *argv[])
 
       SetACookie(cookieService, "http://foo.expireme.org/", nullptr, "test=expiry; domain=.expireme.org; max-age=60", nullptr);
       GetACookie(cookieService, "http://expireme.org/", nullptr, getter_Copies(cookie));
       rv[14] = CheckResult(cookie.get(), MUST_EQUAL, "test=expiry");
       SetACookie(cookieService, "http://bar.expireme.org/", nullptr, "test=differentvalue; domain=.expireme.org; max-age=0", nullptr);
       GetACookie(cookieService, "http://expireme.org/", nullptr, getter_Copies(cookie));
       rv[15] = CheckResult(cookie.get(), MUST_BE_NULL);
 
-      allTestsPassed = PrintResult(rv, 16) && allTestsPassed;
+      nsAutoCString ServerTime;
+      nsAutoCString CookieString;
 
+      SetTime(-OFFSET_ONE_WEEK, ServerTime, CookieString, true);
+      SetACookie(cookieService, "http://expireme.org/", nullptr, CookieString.get(), ServerTime.get());
+      GetACookie(cookieService, "http://expireme.org/", nullptr, getter_Copies(cookie));
+      rv[16] = CheckResult(cookie.get(), MUST_BE_NULL);
+      // Set server time earlier than client time for one year + one day, and expirty time earlier than server time for one day.
+      SetTime(-(OFFSET_ONE_DAY + OFFSET_ONE_WEEK), ServerTime, CookieString, false);
+      SetACookie(cookieService, "http://expireme.org/", nullptr, CookieString.get(), ServerTime.get());
+      GetACookie(cookieService, "http://expireme.org/", nullptr, getter_Copies(cookie));
+      rv[17] = CheckResult(cookie.get(), MUST_BE_NULL);
+      // Set server time later than client time for one year, and expiry time later than server time for one day.
+      SetTime(OFFSET_ONE_WEEK, ServerTime, CookieString, false);
+      SetACookie(cookieService, "http://expireme.org/", nullptr, CookieString.get(), ServerTime.get());
+      GetACookie(cookieService, "http://expireme.org/", nullptr, getter_Copies(cookie));
+      rv[18] = CheckResult(cookie.get(), MUST_EQUAL, "test=expiry");
+      // Set server time later than client time for one year + one day, and expiry time earlier than server time for one day.
+      SetTime((OFFSET_ONE_DAY + OFFSET_ONE_WEEK), ServerTime, CookieString, true);
+      SetACookie(cookieService, "http://expireme.org/", nullptr, CookieString.get(), ServerTime.get());
+      GetACookie(cookieService, "http://expireme.org/", nullptr, getter_Copies(cookie));
+      rv[19] = CheckResult(cookie.get(), MUST_EQUAL, "test=expiry");
+      allTestsPassed = PrintResult(rv, 20) && allTestsPassed;
 
       // *** multiple cookie tests
       sBuffer = PR_sprintf_append(sBuffer, "*** Beginning multiple cookie tests...\n");
 
       // test the setting of multiple cookies, and test the order of precedence
       // (a later cookie overwriting an earlier one, in the same header string)
       SetACookie(cookieService, "http://multiple.cookies/", nullptr, "test=multiple; domain=.multiple.cookies \n test=different \n test=same; domain=.multiple.cookies \n newtest=ciao \n newtest=foo; max-age=-6 \n newtest=reincarnated", nullptr);
       GetACookie(cookieService, "http://multiple.cookies/", nullptr, getter_Copies(cookie));
--- a/old-configure.in
+++ b/old-configure.in
@@ -2306,16 +2306,21 @@ if test "$MOZ_SYSTEM_PNG" != 1 -a "$CPU_
     esac
 fi
 
 fi # SKIP_LIBRARY_CHECKS
 
 AC_SUBST(MOZ_PNG_ARM_NEON)
 
 dnl ========================================================
+dnl system libffi Support
+dnl ========================================================
+MOZ_CONFIG_FFI()
+
+dnl ========================================================
 dnl =
 dnl = Application
 dnl =
 dnl ========================================================
 
 MOZ_ARG_HEADER(Application)
 
 ENABLE_SYSTEM_EXTENSION_DIRS=1
@@ -2357,16 +2362,17 @@ MOZ_TOOLKIT_SEARCH=1
 MOZ_UI_LOCALE=en-US
 MOZ_UNIVERSALCHARDET=1
 MOZ_URL_CLASSIFIER=
 MOZ_XUL=1
 MOZ_ZIPWRITER=1
 MOZ_NO_SMART_CARDS=
 NECKO_COOKIES=1
 NECKO_PROTOCOLS_DEFAULT="about data file ftp http res viewsource websocket wyciwyg device"
+BUILD_CTYPES=1
 MOZ_USE_NATIVE_POPUP_WINDOWS=
 MOZ_EXCLUDE_HYPHENATION_DICTIONARIES=
 MOZ_INSTALL_TRACKING=
 ACCESSIBILITY=1
 MOZ_TIME_MANAGER=
 MOZ_SIMPLEPUSH=
 MOZ_PAY=
 MOZ_AUDIO_CHANNEL_MANAGER=
@@ -5576,16 +5582,28 @@ dnl
 if test "$OS_TARGET" != Android -a x"$MOZ_WIDGET_TOOLKIT" != x"gonk"; then
     AC_DEFINE(ENABLE_MARIONETTE)
 fi
 AC_SUBST(ENABLE_MARIONETTE)
 if test "$ENABLE_MARIONETTE"; then
     AC_DEFINE(ENABLE_MARIONETTE)
 fi
 
+dnl
+dnl Build jsctypes on the platforms we can, unless it's explicitly disabled.
+dnl
+MOZ_ARG_DISABLE_BOOL(ctypes,
+[  --disable-ctypes        Disable js-ctypes],
+    BUILD_CTYPES=,
+    BUILD_CTYPES=1)
+AC_SUBST(BUILD_CTYPES)
+if test "$BUILD_CTYPES"; then
+    AC_DEFINE(BUILD_CTYPES)
+fi
+
 dnl ========================================================
 if test "$MOZ_DEBUG" -o "$MOZ_DMD"; then
     MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS=
 fi
 
 dnl ========================================================
 dnl =
 dnl = Maintainer debug option (no --enable equivalent)
@@ -6252,16 +6270,20 @@ ac_configure_args="$_SUBDIR_CONFIG_ARGS"
 # --with-system-nspr will have been converted into the relevant $NSPR_CFLAGS
 # and $NSPR_LIBS.
 ac_configure_args="`echo $ac_configure_args | sed -e 's/--with-system-nspr\S* *//'`"
 
 if test "$_INTL_API" = no; then
     ac_configure_args="$ac_configure_args --without-intl-api"
 fi
 
+if test "$BUILD_CTYPES"; then
+    # Build js-ctypes on the platforms we can.
+    ac_configure_args="$ac_configure_args --enable-ctypes"
+fi
 if test -n "$NSPR_CFLAGS" -o -n "$NSPR_LIBS"; then
     ac_configure_args="$ac_configure_args --with-nspr-cflags='$NSPR_CFLAGS'"
     ac_configure_args="$ac_configure_args --with-nspr-libs='$NSPR_LIBS'"
 fi
 ac_configure_args="$ac_configure_args --prefix=$dist"
 if test -n "$ZLIB_IN_MOZGLUE"; then
    MOZ_ZLIB_LIBS=
 fi
rename from security/manager/ssl/nsSDR.cpp
rename to security/manager/ssl/SecretDecoderRing.cpp
--- a/security/manager/ssl/nsSDR.cpp
+++ b/security/manager/ssl/SecretDecoderRing.cpp
@@ -1,219 +1,162 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "stdlib.h"
-#include "plstr.h"
-#include "plbase64.h"
+#include "SecretDecoderRing.h"
 
+#include "ScopedNSSTypes.h"
 #include "mozilla/Base64.h"
 #include "mozilla/Casting.h"
 #include "mozilla/Services.h"
-#include "nsMemory.h"
-#include "nsString.h"
 #include "nsCOMPtr.h"
-#include "nsThreadUtils.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIObserverService.h"
 #include "nsIServiceManager.h"
 #include "nsITokenPasswordDialogs.h"
-
-#include "nsISecretDecoderRing.h"
-#include "nsCRT.h"
-#include "nsSDR.h"
 #include "nsNSSComponent.h"
 #include "nsNSSHelper.h"
-#include "nsNSSShutDown.h"
-#include "ScopedNSSTypes.h"
-
 #include "pk11func.h"
 #include "pk11sdr.h" // For PK11SDR_Encrypt, PK11SDR_Decrypt
-
 #include "ssl.h" // For SSL_ClearSessionCache
 
 using namespace mozilla;
 
-// Standard ISupports implementation
 // NOTE: Should these be the thread-safe versions?
-NS_IMPL_ISUPPORTS(nsSecretDecoderRing, nsISecretDecoderRing, nsISecretDecoderRingConfig)
+NS_IMPL_ISUPPORTS(SecretDecoderRing, nsISecretDecoderRing)
 
-// nsSecretDecoderRing constructor
-nsSecretDecoderRing::nsSecretDecoderRing()
+SecretDecoderRing::SecretDecoderRing()
 {
-  // initialize superclass
 }
 
-// nsSecretDecoderRing destructor
-nsSecretDecoderRing::~nsSecretDecoderRing()
+SecretDecoderRing::~SecretDecoderRing()
 {
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown()) {
     return;
   }
 
   shutdown(calledFromObject);
 }
 
-NS_IMETHODIMP
-nsSecretDecoderRing::Encrypt(unsigned char* data, uint32_t dataLen,
-                             unsigned char** result, uint32_t* _retval)
+nsresult
+SecretDecoderRing::Encrypt(const nsACString& data, /*out*/ nsACString& result)
 {
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
-
   UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
   if (!slot) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   /* Make sure token is initialized. */
+  nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
   nsresult rv = setPassword(slot.get(), ctx, locker);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   /* Force authentication */
   if (PK11_Authenticate(slot.get(), true, ctx) != SECSuccess) {
     return NS_ERROR_FAILURE;
   }
 
   /* Use default key id */
   SECItem keyid;
   keyid.data = nullptr;
   keyid.len = 0;
   SECItem request;
-  request.data = data;
-  request.len = dataLen;
-  SECItem reply;
-  reply.data = nullptr;
-  reply.len = 0;
+  request.data = BitwiseCast<unsigned char*, const char*>(data.BeginReading());
+  request.len = data.Length();
+  ScopedAutoSECItem reply;
   if (PK11SDR_Encrypt(&keyid, &request, &reply, ctx) != SECSuccess) {
     return NS_ERROR_FAILURE;
   }
 
-  *result = reply.data;
-  *_retval = reply.len;
-
+  result.Assign(BitwiseCast<char*, unsigned char*>(reply.data), reply.len);
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsSecretDecoderRing::Decrypt(unsigned char* data, uint32_t dataLen,
-                             unsigned char** result, uint32_t* _retval)
+nsresult
+SecretDecoderRing::Decrypt(const nsACString& data, /*out*/ nsACString& result)
 {
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
-
-  *result = nullptr;
-  *_retval = 0;
-
   /* Find token with SDR key */
   UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
   if (!slot) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   /* Force authentication */
+  nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
   if (PK11_Authenticate(slot.get(), true, ctx) != SECSuccess) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   SECItem request;
-  request.data = data;
-  request.len = dataLen;
-  SECItem reply;
-  reply.data = nullptr;
-  reply.len = 0;
+  request.data = BitwiseCast<unsigned char*, const char*>(data.BeginReading());
+  request.len = data.Length();
+  ScopedAutoSECItem reply;
   if (PK11SDR_Decrypt(&request, &reply, ctx) != SECSuccess) {
     return NS_ERROR_FAILURE;
   }
 
-  *result = reply.data;
-  *_retval = reply.len;
+  result.Assign(BitwiseCast<char*, unsigned char*>(reply.data), reply.len);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SecretDecoderRing::EncryptString(const nsACString& text,
+                         /*out*/ nsACString& encryptedBase64Text)
+{
+  nsAutoCString encryptedText;
+  nsresult rv = Encrypt(text, encryptedText);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  rv = Base64Encode(encryptedText, encryptedBase64Text);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsSecretDecoderRing::EncryptString(const char* text, char** _retval)
+SecretDecoderRing::DecryptString(const nsACString& encryptedBase64Text,
+                         /*out*/ nsACString& decryptedText)
 {
-  nsresult rv = NS_OK;
-  unsigned char *encrypted = 0;
-  uint32_t eLen = 0;
-
-  if (!text || !_retval) {
-    rv = NS_ERROR_INVALID_POINTER;
-    goto loser;
+  nsAutoCString encryptedText;
+  nsresult rv = Base64Decode(encryptedBase64Text, encryptedText);
+  if (NS_FAILED(rv)) {
+    return rv;
   }
 
-  rv = Encrypt((unsigned char *)text, strlen(text), &encrypted, &eLen);
-  if (rv != NS_OK) { goto loser; }
-
-  rv = Base64Encode(BitwiseCast<const char*>(encrypted), eLen, _retval);
+  rv = Decrypt(encryptedText, decryptedText);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
 
-loser:
-  if (encrypted) PORT_Free(encrypted);
-
-  return rv;
+  return NS_OK;
 }
 
 NS_IMETHODIMP
-nsSecretDecoderRing::DecryptString(const char* crypt, char** _retval)
-{
-  nsresult rv = NS_OK;
-  char *r = 0;
-  unsigned char *decoded = 0;
-  uint32_t decodedLen = 0;
-  unsigned char *decrypted = 0;
-  uint32_t decryptedLen = 0;
-
-  if (!crypt || !_retval) {
-    rv = NS_ERROR_INVALID_POINTER;
-    goto loser;
-  }
-
-  rv = Base64Decode(crypt, strlen(crypt), BitwiseCast<char**>(&decoded),
-                    &decodedLen);
-  if (NS_FAILED(rv)) goto loser;
-
-  rv = Decrypt(decoded, decodedLen, &decrypted, &decryptedLen);
-  if (rv != NS_OK) goto loser;
-
-  // Convert to NUL-terminated string
-  r = (char *)moz_xmalloc(decryptedLen+1);
-  if (!r) { rv = NS_ERROR_OUT_OF_MEMORY; goto loser; }
-
-  memcpy(r, decrypted, decryptedLen);
-  r[decryptedLen] = 0;
-
-  *_retval = r;
-  r = 0;
-
-loser:
-  if (decrypted) PORT_Free(decrypted);
-  if (decoded) free(decoded);
-
-  return rv;
-}
-
-NS_IMETHODIMP
-nsSecretDecoderRing::ChangePassword()
+SecretDecoderRing::ChangePassword()
 {
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
   if (!slot) {
@@ -231,17 +174,17 @@ nsSecretDecoderRing::ChangePassword()
   }
 
   nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
   bool canceled; // Ignored
   return dialogs->SetPassword(ctx, tokenName.get(), &canceled);
 }
 
 NS_IMETHODIMP
-nsSecretDecoderRing::Logout()
+SecretDecoderRing::Logout()
 {
   static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
 
   nsresult rv;
   nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
   if (NS_FAILED(rv))
     return rv;
 
@@ -254,17 +197,17 @@ nsSecretDecoderRing::Logout()
     PK11_LogoutAll();
     SSL_ClearSessionCache();
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsSecretDecoderRing::LogoutAndTeardown()
+SecretDecoderRing::LogoutAndTeardown()
 {
   static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
 
   nsresult rv;
   nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
   if (NS_FAILED(rv))
     return rv;
 
@@ -284,15 +227,8 @@ nsSecretDecoderRing::LogoutAndTeardown()
   // sure that all connections that should be stopped, are stopped. See
   // bug 517584.
   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   if (os)
     os->NotifyObservers(nullptr, "net:prune-dead-connections", nullptr);
 
   return rv;
 }
-
-NS_IMETHODIMP
-nsSecretDecoderRing::SetWindow(nsISupports*)
-{
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
rename from security/manager/ssl/nsSDR.h
rename to security/manager/ssl/SecretDecoderRing.h
--- a/security/manager/ssl/nsSDR.h
+++ b/security/manager/ssl/SecretDecoderRing.h
@@ -1,52 +1,39 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef _NSSDR_H_
-#define _NSSDR_H_
+#ifndef SecretDecoderRing_h
+#define SecretDecoderRing_h
 
 #include "nsISecretDecoderRing.h"
 #include "nsNSSShutDown.h"
+#include "nsString.h"
 
-/**
- * NS_SDR_CONTRACTID - contract id for SDR services.
- *   Implements nsISecretDecoderRing.
- *   Should eventually implement an interface to set window
- *   context and other information. (nsISecretDecoderRingConfig)
- *
- * NOTE: This definition should move to base code.  It
- *   is conditionally defined here until it is moved.
- *   Delete this after defining in the new location.
- */
-#ifndef NS_SDR_CONTRACTID
-#define NS_SDR_CONTRACTID "@mozilla.org/security/sdr;1"
-#endif
+#define NS_SECRETDECODERRING_CONTRACTID "@mozilla.org/security/sdr;1"
 
-// ===============================================
-// nsSecretDecoderRing - implementation of nsISecretDecoderRing
-// ===============================================
-
-#define NS_SDR_CID \
+#define NS_SECRETDECODERRING_CID \
   { 0x0c4f1ddc, 0x1dd2, 0x11b2, { 0x9d, 0x95, 0xf2, 0xfd, 0xf1, 0x13, 0x04, 0x4b } }
 
-class nsSecretDecoderRing : public nsISecretDecoderRing
-                          , public nsISecretDecoderRingConfig
-                          , public nsNSSShutDownObject
+class SecretDecoderRing : public nsISecretDecoderRing
+                        , public nsNSSShutDownObject
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSISECRETDECODERRING
-  NS_DECL_NSISECRETDECODERRINGCONFIG
 
-  nsSecretDecoderRing();
+  SecretDecoderRing();
 
   // Nothing to release.
   virtual void virtualDestroyNSSReference() override {}
 
 protected:
-  virtual ~nsSecretDecoderRing();
+  virtual ~SecretDecoderRing();
+
+private:
+  nsresult Encrypt(const nsACString& data, /*out*/ nsACString& result);
+  nsresult Decrypt(const nsACString& data, /*out*/ nsACString& result);
 };
 
-#endif /* _NSSDR_H_ */
+#endif // SecretDecoderRing_h
--- a/security/manager/ssl/moz.build
+++ b/security/manager/ssl/moz.build
@@ -27,16 +27,17 @@ XPIDL_SOURCES += [
     'nsINSSVersion.idl',
     'nsIPK11Token.idl',
     'nsIPK11TokenDB.idl',
     'nsIPKCS11.idl',
     'nsIPKCS11Module.idl',
     'nsIPKCS11ModuleDB.idl',
     'nsIPKCS11Slot.idl',
     'nsIProtectedAuthThread.idl',
+    'nsISecretDecoderRing.idl',
     'nsISecurityUITelemetry.idl',
     'nsISiteSecurityService.idl',
     'nsISSLStatus.idl',
     'nsISSLStatusProvider.idl',
     'nsITokenDialogs.idl',
     'nsITokenPasswordDialogs.idl',
     'nsIU2FToken.idl',
     'nsIUserCertPicker.idl',
@@ -118,28 +119,28 @@ UNIFIED_SOURCES += [
     'nsNSSVersion.cpp',
     'nsNTLMAuthModule.cpp',
     'nsPK11TokenDB.cpp',
     'nsPKCS11Slot.cpp',
     'nsPKCS12Blob.cpp',
     'nsProtectedAuthThread.cpp',
     'nsPSMBackgroundThread.cpp',
     'nsRandomGenerator.cpp',
-    'nsSDR.cpp',
     'nsSecureBrowserUIImpl.cpp',
     'nsSecurityHeaderParser.cpp',
     'NSSErrorsService.cpp',
     'nsSiteSecurityService.cpp',
     'nsSSLSocketProvider.cpp',
     'nsSSLStatus.cpp',
     'nsTLSSocketProvider.cpp',
     'PSMContentListener.cpp',
     'PSMRunnable.cpp',
     'PublicKeyPinningService.cpp',
     'RootCertificateTelemetryUtils.cpp',
+    'SecretDecoderRing.cpp',
     'SharedSSLState.cpp',
     'SSLServerCertVerification.cpp',
     'TransportSecurityInfo.cpp',
     'WeakCryptoOverride.cpp',
 ]
 
 IPDL_SOURCES += [
     'PPSMContentDownloader.ipdl',
--- a/security/manager/ssl/nsCertOverrideService.cpp
+++ b/security/manager/ssl/nsCertOverrideService.cpp
@@ -10,22 +10,23 @@
 #include "ScopedNSSTypes.h"
 #include "SharedSSLState.h"
 #include "mozilla/Telemetry.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsCRT.h"
 #include "nsILineInputStream.h"
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
+#include "nsIOutputStream.h"
+#include "nsISafeOutputStream.h"
 #include "nsIX509Cert.h"
 #include "nsNSSCertHelper.h"
 #include "nsNSSCertificate.h"
 #include "nsNSSComponent.h"
 #include "nsNetUtil.h"
-#include "nsISafeOutputStream.h"
 #include "nsPromiseFlatString.h"
 #include "nsStreamUtils.h"
 #include "nsStringBuffer.h"
 #include "nsThreadUtils.h"
 #include "ssl.h" // For SSL_ClearSessionCache
 
 using namespace mozilla;
 using namespace mozilla::psm;
rename from netwerk/base/nsISecretDecoderRing.idl
rename to security/manager/ssl/nsISecretDecoderRing.idl
--- a/netwerk/base/nsISecretDecoderRing.idl
+++ b/security/manager/ssl/nsISecretDecoderRing.idl
@@ -1,49 +1,32 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
-/**
- * Buffer type - for storing 8-bit octet values.
- */
-[ptr] native buffer(unsigned char);
-
 [scriptable, uuid(0EC80360-075C-11d4-9FD4-00C04F1B83D8)]
 interface nsISecretDecoderRing: nsISupports {
-
   /**
-   * Encrypt a buffer - callable only from C++.
+   * Encrypt to Base64 output.
    *
-   * @return The length of the data in the output buffer.
+   * @param text The text to encrypt.
+   * @return The encrypted text, encoded as Base64.
    */
-  [noscript] unsigned long encrypt(in buffer data, in unsigned long dataLen,
-                                   out buffer result);
+  ACString encryptString(in AUTF8String text);
 
   /**
-   * Decrypt a buffer - callable only from C++.
+   * Decrypt Base64 input.
    *
-   * @return The length of the data in the output buffer.
+   * @param encryptedBase64Text Encrypted input text, encoded as Base64.
+   * @return The decoded text.
    */
-  [noscript] unsigned long decrypt(in buffer data, in unsigned long dataLen,
-                                   out buffer result);
-
-  /**
-   * Encrypt nul-terminated string to BASE64 output.
-   */
-  string encryptString(in string text);
-
-  /**
-   * Decrypt BASE64 input to nul-terminated string output.  There is
-   * no check for embedded nul values in the decrypted output.
-   */
-  string decryptString(in string crypt);
+  AUTF8String decryptString(in ACString encryptedBase64Text);
 
   /**
    * Prompt the user to change the password on the SDR key.
    */
   void changePassword();
 
   /**
    * Logout of the security device that protects the SDR key.
@@ -51,18 +34,8 @@ interface nsISecretDecoderRing: nsISuppo
   void logout();
 
   /**
    * Logout of the security device that protects the SDR key and tear
    * down authenticated objects.
    */
   void logoutAndTeardown();
 };
-
-/**
- * Configuration interface for the Secret Decoder Ring
- *  - this interface allows setting the window that will be
- *    used as parent for dialog windows (such as password prompts)
- */
-[scriptable, uuid(01D8C0F0-0CCC-11d4-9FDD-000064657374)]
-interface nsISecretDecoderRingConfig: nsISupports {
-  void setWindow(in nsISupports w);
-};
--- a/security/manager/ssl/nsNSSModule.cpp
+++ b/security/manager/ssl/nsNSSModule.cpp
@@ -1,48 +1,48 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "CertBlocklist.h"
 #include "ContentSignatureVerifier.h"
+#include "NSSErrorsService.h"
+#include "PSMContentListener.h"
+#include "SecretDecoderRing.h"
+#include "TransportSecurityInfo.h"
+#include "WeakCryptoOverride.h"
+#include "mozilla/ModuleUtils.h"
+#include "nsCURILoader.h"
 #include "nsCertOverrideService.h"
 #include "nsCertPicker.h"
 #include "nsCrypto.h"
 #include "nsCryptoHash.h"
-#include "nsCURILoader.h"
+#include "nsDOMCID.h" // For the NS_CRYPTO_CONTRACTID define
 #include "nsDataSignatureVerifier.h"
-#include "nsDOMCID.h" //For the NS_CRYPTO_CONTRACTID define
 #include "nsICategoryManager.h"
+#include "nsKeyModule.h"
 #include "nsKeygenHandler.h"
-#include "nsKeyModule.h"
-#include "mozilla/ModuleUtils.h"
-#include "nsNetCID.h"
 #include "nsNSSCertificate.h"
 #include "nsNSSCertificateDB.h"
 #include "nsNSSCertificateFakeTransport.h"
 #include "nsNSSComponent.h"
-#include "NSSErrorsService.h"
 #include "nsNSSU2FToken.h"
 #include "nsNSSVersion.h"
 #include "nsNTLMAuthModule.h"
+#include "nsNetCID.h"
 #include "nsPK11TokenDB.h"
 #include "nsPKCS11Slot.h"
-#include "PSMContentListener.h"
 #include "nsRandomGenerator.h"
-#include "nsSDR.h"
+#include "nsSSLSocketProvider.h"
+#include "nsSSLStatus.h"
 #include "nsSecureBrowserUIImpl.h"
 #include "nsSiteSecurityService.h"
-#include "nsSSLSocketProvider.h"
-#include "nsSSLStatus.h"
 #include "nsTLSSocketProvider.h"
-#include "TransportSecurityInfo.h"
-#include "WeakCryptoOverride.h"
 #include "nsXULAppAPI.h"
 
 #ifdef MOZ_XUL
 #include "nsCertTree.h"
 #endif
 
 #define NS_IS_PROCESS_DEFAULT                                                 \
     (GeckoProcessType_Default == XRE_GetProcessType())
@@ -181,17 +181,17 @@ namespace {
 // Use the special factory constructor for everything this module implements,
 // because all code could potentially require the NSS library.
 // Our factory constructor takes an additional boolean parameter.
 // Only for the nsNSSComponent, set this to true.
 // All other classes must have this set to false.
 
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsSSLSocketProvider)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsTLSSocketProvider)
-NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsSecretDecoderRing)
+NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, SecretDecoderRing)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsPK11TokenDB)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsPKCS11ModuleDB)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(PSMContentListener, init)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR_BYPROCESS(nssEnsureOnChromeOnly,
                                              nsNSSCertificate,
                                              nsNSSCertificateFakeTransport)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsNSSCertificateDB)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR_BYPROCESS(nssEnsureOnChromeOnly,
@@ -221,17 +221,17 @@ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsCe
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsSecureBrowserUIImpl)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(CertBlocklist, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsSiteSecurityService, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(WeakCryptoOverride)
 
 NS_DEFINE_NAMED_CID(NS_NSSCOMPONENT_CID);
 NS_DEFINE_NAMED_CID(NS_SSLSOCKETPROVIDER_CID);
 NS_DEFINE_NAMED_CID(NS_STARTTLSSOCKETPROVIDER_CID);
-NS_DEFINE_NAMED_CID(NS_SDR_CID);
+NS_DEFINE_NAMED_CID(NS_SECRETDECODERRING_CID);
 NS_DEFINE_NAMED_CID(NS_PK11TOKENDB_CID);
 NS_DEFINE_NAMED_CID(NS_PKCS11MODULEDB_CID);
 NS_DEFINE_NAMED_CID(NS_PSMCONTENTLISTEN_CID);
 NS_DEFINE_NAMED_CID(NS_X509CERT_CID);
 NS_DEFINE_NAMED_CID(NS_X509CERTDB_CID);
 NS_DEFINE_NAMED_CID(NS_X509CERTLIST_CID);
 NS_DEFINE_NAMED_CID(NS_FORMPROCESSOR_CID);
 #ifdef MOZ_XUL
@@ -257,17 +257,17 @@ NS_DEFINE_NAMED_CID(NS_SECURE_BROWSER_UI
 NS_DEFINE_NAMED_CID(NS_SITE_SECURITY_SERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_CERT_BLOCKLIST_CID);
 NS_DEFINE_NAMED_CID(NS_WEAKCRYPTOOVERRIDE_CID);
 
 static const mozilla::Module::CIDEntry kNSSCIDs[] = {
   { &kNS_NSSCOMPONENT_CID, false, nullptr, nsNSSComponentConstructor },
   { &kNS_SSLSOCKETPROVIDER_CID, false, nullptr, nsSSLSocketProviderConstructor },
   { &kNS_STARTTLSSOCKETPROVIDER_CID, false, nullptr, nsTLSSocketProviderConstructor },
-  { &kNS_SDR_CID, false, nullptr, nsSecretDecoderRingConstructor },
+  { &kNS_SECRETDECODERRING_CID, false, nullptr, SecretDecoderRingConstructor },
   { &kNS_PK11TOKENDB_CID, false, nullptr, nsPK11TokenDBConstructor },
   { &kNS_PKCS11MODULEDB_CID, false, nullptr, nsPKCS11ModuleDBConstructor },
   { &kNS_PSMCONTENTLISTEN_CID, false, nullptr, PSMContentListenerConstructor },
   { &kNS_X509CERT_CID, false, nullptr, nsNSSCertificateConstructor },
   { &kNS_X509CERTDB_CID, false, nullptr, nsNSSCertificateDBConstructor },
   { &kNS_X509CERTLIST_CID, false, nullptr, nsNSSCertListConstructor },
   { &kNS_FORMPROCESSOR_CID, false, nullptr, nsKeygenFormProcessor::Create },
 #ifdef MOZ_XUL
@@ -297,17 +297,17 @@ static const mozilla::Module::CIDEntry k
 };
 
 static const mozilla::Module::ContractIDEntry kNSSContracts[] = {
   { PSM_COMPONENT_CONTRACTID, &kNS_NSSCOMPONENT_CID },
   { NS_NSS_ERRORS_SERVICE_CONTRACTID, &kNS_NSSERRORSSERVICE_CID },
   { NS_NSSVERSION_CONTRACTID, &kNS_NSSVERSION_CID },
   { NS_SSLSOCKETPROVIDER_CONTRACTID, &kNS_SSLSOCKETPROVIDER_CID },
   { NS_STARTTLSSOCKETPROVIDER_CONTRACTID, &kNS_STARTTLSSOCKETPROVIDER_CID },
-  { NS_SDR_CONTRACTID, &kNS_SDR_CID },
+  { NS_SECRETDECODERRING_CONTRACTID, &kNS_SECRETDECODERRING_CID },
   { NS_PK11TOKENDB_CONTRACTID, &kNS_PK11TOKENDB_CID },
   { NS_PKCS11MODULEDB_CONTRACTID, &kNS_PKCS11MODULEDB_CID },
   { NS_PSMCONTENTLISTEN_CONTRACTID, &kNS_PSMCONTENTLISTEN_CID },
   { NS_X509CERTDB_CONTRACTID, &kNS_X509CERTDB_CID },
   { NS_X509CERTLIST_CONTRACTID, &kNS_X509CERTLIST_CID },
   { NS_FORMPROCESSOR_CONTRACTID, &kNS_FORMPROCESSOR_CID },
 #ifdef MOZ_XUL
   { NS_CERTTREE_CONTRACTID, &kNS_CERTTREE_CID },
--- a/taskcluster/ci/legacy/tasks/builds/android_x86.yml
+++ b/taskcluster/ci/legacy/tasks/builds/android_x86.yml
@@ -50,16 +50,16 @@ task:
       - staging
     treeherder:
       machine:
         # see https://github.com/mozilla/treeherder/blob/master/ui/js/values.js
         platform: android-4-2-x86
       groupSymbol: tc
       groupName: Submitted by taskcluster
       symbol: B
-      tier: 2
+      tier: 1
     # Rather then enforcing particular conventions we require that all build
     # tasks provide the "build" extra field to specify where the build and tests
     # files are located.
     locations:
       build: 'public/build/target.apk'
       mozharness: 'public/build/mozharness.zip'
       test_packages: 'public/build/target.test_packages.json'
--- a/taskcluster/taskgraph/transforms/tests/all_kinds.py
+++ b/taskcluster/taskgraph/transforms/tests/all_kinds.py
@@ -33,17 +33,18 @@ def set_worker_implementation(config, te
 def set_tier(config, tests):
     """Set the tier based on policy for all test descriptions that do not
     specify a tier otherwise."""
     for test in tests: