Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 25 May 2017 16:34:16 -0400
changeset 360695 55e5723b1e62190a38c00927eda796e2ad14778f
parent 360694 bdf2d1918fb0b434ab8782d003afbe3f9639cd45 (current diff)
parent 360579 6037b902d6523133f8761dc3ad42cb9022fdd290 (diff)
child 360696 348e3ebeb9acdb940953249d3cc965755a740913
child 360735 238f6a0d7a1ea68411b40f360e666e7b23f18e29
push id90713
push userryanvm@gmail.com
push dateThu, 25 May 2017 20:36:06 +0000
treeherdermozilla-inbound@348e3ebeb9ac [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to m-c. a=merge
--- a/.eslintignore
+++ b/.eslintignore
@@ -32,17 +32,16 @@ netwerk/**
 nsprpub/**
 other-licenses/**
 parser/**
 probes/**
 python/**
 rdf/**
 servo/**
 startupcache/**
-testing/**
 tools/update-packaging/**
 uriloader/**
 view/**
 widget/**
 xpcom/**
 xpfe/**
 xulrunner/**
 
@@ -290,16 +289,29 @@ security/manager/ssl/security-prefs.js
 security/nss/**
 
 # services/ exclusions
 
 # Uses `#filter substitution`
 services/sync/modules/constants.js
 services/sync/services-sync.js
 
+# testing/ exclusions
+testing/firefox-ui/**
+testing/marionette/**
+testing/mochitest/**
+testing/modules/**
+testing/mozbase/**
+testing/profiles/**
+testing/specialpowers/**
+testing/talos/**
+testing/web-platform/**
+testing/xpcshell/moz-http2/**
+testing/xpcshell/node-http2/**
+
 # Third party services
 services/common/kinto-http-client.js
 services/common/kinto-offline-client.js
 services/sync/tps/extensions/mozmill
 
 # toolkit/ exclusions
 
 # Not part of the default build
--- a/browser/components/about/AboutRedirector.cpp
+++ b/browser/components/about/AboutRedirector.cpp
@@ -127,18 +127,16 @@ GetAboutModuleName(nsIURI *aURI)
 }
 
 NS_IMETHODIMP
 AboutRedirector::NewChannel(nsIURI* aURI,
                             nsILoadInfo* aLoadInfo,
                             nsIChannel** result)
 {
   NS_ENSURE_ARG_POINTER(aURI);
-  NS_ENSURE_ARG_POINTER(aLoadInfo);
-
   NS_ASSERTION(result, "must not be null");
 
   nsAutoCString path = GetAboutModuleName(aURI);
 
   nsresult rv;
   nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -169,32 +167,36 @@ AboutRedirector::NewChannel(nsIURI* aURI
       }
 
       nsCOMPtr<nsIChannel> tempChannel;
       nsCOMPtr<nsIURI> tempURI;
       rv = NS_NewURI(getter_AddRefs(tempURI), url);
       NS_ENSURE_SUCCESS(rv, rv);
 
       // If tempURI links to an external URI (i.e. something other than
-      // chrome:// or resource://) then set the result principal URI on the
-      // load info which forces the channel prncipal to reflect the displayed
+      // chrome:// or resource://) then set the LOAD_REPLACE flag on the
+      // channel which forces the channel owner to reflect the displayed
       // URL rather then being the systemPrincipal.
       bool isUIResource = false;
       rv = NS_URIChainHasFlags(tempURI, nsIProtocolHandler::URI_IS_UI_RESOURCE,
                                &isUIResource);
       NS_ENSURE_SUCCESS(rv, rv);
 
+      nsLoadFlags loadFlags = isUIResource
+                    ? static_cast<nsLoadFlags>(nsIChannel::LOAD_NORMAL)
+                    : static_cast<nsLoadFlags>(nsIChannel::LOAD_REPLACE);
+
       rv = NS_NewChannelInternal(getter_AddRefs(tempChannel),
                                  tempURI,
-                                 aLoadInfo);
+                                 aLoadInfo,
+                                 nullptr, // aLoadGroup
+                                 nullptr, // aCallbacks
+                                 loadFlags);
       NS_ENSURE_SUCCESS(rv, rv);
 
-      if (!isUIResource) {
-        aLoadInfo->SetResultPrincipalURI(tempURI);
-      }
       tempChannel->SetOriginalURI(aURI);
 
       NS_ADDREF(*result = tempChannel);
       return rv;
     }
   }
 
   return NS_ERROR_ILLEGAL_VALUE;
--- a/browser/themes/linux/searchbar.css
+++ b/browser/themes/linux/searchbar.css
@@ -334,11 +334,11 @@ menuitem[cmd="cmd_clearhistory"][disable
 
 .search-setting-button:hover,
 .search-setting-button[selected] {
   background-color: var(--arrowpanel-dimmed-further);
 }
 
 .search-setting-button-compact > .button-box > .button-icon {
   list-style-image: url("chrome://browser/skin/gear.svg");
-  filter: url(chrome://global/skin/filters.svg#fill);
+  -moz-context-properties: fill;
   fill: currentColor;
 }
--- a/browser/themes/osx/searchbar.css
+++ b/browser/themes/osx/searchbar.css
@@ -317,11 +317,11 @@
 
 .search-setting-button:hover,
 .search-setting-button[selected] {
   background-color: var(--arrowpanel-dimmed-further);
 }
 
 .search-setting-button-compact > .button-box > .button-icon {
   list-style-image: url("chrome://browser/skin/gear.svg");
-  filter: url(chrome://global/skin/filters.svg#fill);
+  -moz-context-properties: fill;
   fill: currentColor;
 }
--- a/browser/themes/shared/search/gear.svg
+++ b/browser/themes/shared/search/gear.svg
@@ -1,7 +1,6 @@
-<?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" width="16" height="16" viewBox="0 0 32 32">
-  <path id="glyphShape-gear" d="M28,16c0-1.7,0.9-3.1,2-3.3c-0.4-1.5-0.9-2.9-1.7-4.2c-0.9,0.7-2.6,0.3-3.8-0.9c-1.2-1.2-1.6-2.8-0.9-3.8 c-1.3-0.8-2.7-1.4-4.2-1.7c-0.2,1.1-1.6,2-3.3,2S13,3.1,12.8,2c-1.5,0.4-2.9,0.9-4.2,1.7c0.7,0.9,0.3,2.6-0.9,3.8 c-1.4,1.1-3,1.5-4,0.9C2.9,9.7,2.4,11.2,2,12.7c1.1,0.2,2,1.6,2,3.3s-0.9,3.1-2,3.3c0.4,1.5,0.9,2.9,1.7,4.2 c0.9-0.7,2.6-0.3,3.8,0.9c1.2,1.2,1.6,2.8,0.9,3.8c1.3,0.8,2.7,1.4,4.2,1.7c0.2-1.1,1.6-2,3.3-2s3.1,0.9,3.3,2 c1.5-0.4,2.9-0.9,4.2-1.7c-0.7-0.9-0.3-2.6,0.9-3.8c1.3-1.2,2.8-1.6,3.8-0.9c0.8-1.3,1.4-2.7,1.7-4.2C28.9,19.1,28,17.7,28,16z M16,24c-4.4,0-8-3.6-8-8s3.6-8,8-8s8,3.6,8,8S20.4,24,16,24z"/>
+  <path fill="context-fill" d="M28,16c0-1.7,0.9-3.1,2-3.3c-0.4-1.5-0.9-2.9-1.7-4.2c-0.9,0.7-2.6,0.3-3.8-0.9c-1.2-1.2-1.6-2.8-0.9-3.8 c-1.3-0.8-2.7-1.4-4.2-1.7c-0.2,1.1-1.6,2-3.3,2S13,3.1,12.8,2c-1.5,0.4-2.9,0.9-4.2,1.7c0.7,0.9,0.3,2.6-0.9,3.8 c-1.4,1.1-3,1.5-4,0.9C2.9,9.7,2.4,11.2,2,12.7c1.1,0.2,2,1.6,2,3.3s-0.9,3.1-2,3.3c0.4,1.5,0.9,2.9,1.7,4.2 c0.9-0.7,2.6-0.3,3.8,0.9c1.2,1.2,1.6,2.8,0.9,3.8c1.3,0.8,2.7,1.4,4.2,1.7c0.2-1.1,1.6-2,3.3-2s3.1,0.9,3.3,2 c1.5-0.4,2.9-0.9,4.2-1.7c-0.7-0.9-0.3-2.6,0.9-3.8c1.3-1.2,2.8-1.6,3.8-0.9c0.8-1.3,1.4-2.7,1.7-4.2C28.9,19.1,28,17.7,28,16z M16,24c-4.4,0-8-3.6-8-8s3.6-8,8-8s8,3.6,8,8S20.4,24,16,24z"/>
 </svg>
--- a/browser/themes/windows/searchbar.css
+++ b/browser/themes/windows/searchbar.css
@@ -330,11 +330,11 @@
 
 .search-setting-button:hover,
 .search-setting-button[selected] {
   background-color: var(--arrowpanel-dimmed-further);
 }
 
 .search-setting-button-compact > .button-box > .button-icon {
   list-style-image: url("chrome://browser/skin/gear.svg");
-  filter: url(chrome://global/skin/filters.svg#fill);
+  -moz-context-properties: fill;
   fill: currentColor;
 }
--- a/chrome/nsChromeProtocolHandler.cpp
+++ b/chrome/nsChromeProtocolHandler.cpp
@@ -100,18 +100,16 @@ nsChromeProtocolHandler::NewURI(const ns
 NS_IMETHODIMP
 nsChromeProtocolHandler::NewChannel2(nsIURI* aURI,
                                      nsILoadInfo* aLoadInfo,
                                      nsIChannel** aResult)
 {
     nsresult rv;
 
     NS_ENSURE_ARG_POINTER(aURI);
-    NS_ENSURE_ARG_POINTER(aLoadInfo);
-
     NS_PRECONDITION(aResult, "Null out param");
 
 #ifdef DEBUG
     // Check that the uri we got is already canonified
     nsresult debug_rv;
     nsCOMPtr<nsIURI> debugClone;
     debug_rv = aURI->Clone(getter_AddRefs(debugClone));
     if (NS_SUCCEEDED(debug_rv)) {
@@ -142,22 +140,16 @@ nsChromeProtocolHandler::NewChannel2(nsI
     if (NS_FAILED(rv)) {
 #ifdef DEBUG
         printf("Couldn't convert chrome URL: %s\n",
                aURI->GetSpecOrDefault().get());
 #endif
         return rv;
     }
 
-    // We don't want to allow the inner protocol handler modify the result principal URI
-    // since we want either |aURI| or anything pre-set by upper layers to prevail.
-    nsCOMPtr<nsIURI> savedResultPrincipalURI;
-    rv = aLoadInfo->GetResultPrincipalURI(getter_AddRefs(savedResultPrincipalURI));
-    NS_ENSURE_SUCCESS(rv, rv);
-
     rv = NS_NewChannelInternal(getter_AddRefs(result),
                                resolvedURI,
                                aLoadInfo);
     NS_ENSURE_SUCCESS(rv, rv);
 
 #ifdef DEBUG
     nsCOMPtr<nsIFileChannel> fileChan(do_QueryInterface(result));
     if (fileChan) {
@@ -171,18 +163,19 @@ nsChromeProtocolHandler::NewChannel2(nsI
             file->GetNativePath(path);
             printf("Chrome file doesn't exist: %s\n", path.get());
         }
     }
 #endif
 
     // Make sure that the channel remembers where it was
     // originally loaded from.
-    rv = aLoadInfo->SetResultPrincipalURI(savedResultPrincipalURI);
-    NS_ENSURE_SUCCESS(rv, rv);
+    nsLoadFlags loadFlags = 0;
+    result->GetLoadFlags(&loadFlags);
+    result->SetLoadFlags(loadFlags & ~nsIChannel::LOAD_REPLACE);
     rv = result->SetOriginalURI(aURI);
     if (NS_FAILED(rv)) return rv;
 
     // Get a system principal for content files and set the owner
     // property of the result
     nsCOMPtr<nsIURL> url = do_QueryInterface(aURI);
     nsAutoCString path;
     rv = url->GetPath(path);
--- a/devtools/client/framework/about-devtools-toolbox.js
+++ b/devtools/client/framework/about-devtools-toolbox.js
@@ -21,17 +21,16 @@ AboutURL.prototype = {
   classID: components.ID("11342911-3135-45a8-8d71-737a2b0ad469"),
   contractID: "@mozilla.org/network/protocol/about;1?what=devtools-toolbox",
 
   QueryInterface: XPCOMUtils.generateQI([nsIAboutModule]),
 
   newChannel: function (aURI, aLoadInfo) {
     let chan = Services.io.newChannelFromURIWithLoadInfo(this.uri, aLoadInfo);
     chan.owner = Services.scriptSecurityManager.getSystemPrincipal();
-    chan.originalURI = aURI;
     return chan;
   },
 
   getURIFlags: function (aURI) {
     return nsIAboutModule.ALLOW_SCRIPT || nsIAboutModule.ENABLE_INDEXED_DB;
   }
 };
 
--- a/docshell/base/nsAboutRedirector.cpp
+++ b/docshell/base/nsAboutRedirector.cpp
@@ -153,52 +153,54 @@ static const RedirEntry kRedirMap[] = {
 static const int kRedirTotal = mozilla::ArrayLength(kRedirMap);
 
 NS_IMETHODIMP
 nsAboutRedirector::NewChannel(nsIURI* aURI,
                               nsILoadInfo* aLoadInfo,
                               nsIChannel** aResult)
 {
   NS_ENSURE_ARG_POINTER(aURI);
-  NS_ENSURE_ARG_POINTER(aLoadInfo);
   NS_ASSERTION(aResult, "must not be null");
 
   nsAutoCString path;
   nsresult rv = NS_GetAboutModuleName(aURI, path);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   for (int i = 0; i < kRedirTotal; i++) {
     if (!strcmp(path.get(), kRedirMap[i].id)) {
       nsCOMPtr<nsIChannel> tempChannel;
       nsCOMPtr<nsIURI> tempURI;
       rv = NS_NewURI(getter_AddRefs(tempURI), kRedirMap[i].url);
       NS_ENSURE_SUCCESS(rv, rv);
 
-      rv = NS_NewChannelInternal(getter_AddRefs(tempChannel),
-                                 tempURI,
-                                 aLoadInfo);
-      NS_ENSURE_SUCCESS(rv, rv);
-
       // If tempURI links to an external URI (i.e. something other than
-      // chrome:// or resource://) then set result principal URI on the
-      // load info which forces the channel principal to reflect the displayed
+      // chrome:// or resource://) then set the LOAD_REPLACE flag on the
+      // channel which forces the channel owner to reflect the displayed
       // URL rather then being the systemPrincipal.
       bool isUIResource = false;
       rv = NS_URIChainHasFlags(tempURI, nsIProtocolHandler::URI_IS_UI_RESOURCE,
                                &isUIResource);
       NS_ENSURE_SUCCESS(rv, rv);
 
       bool isAboutBlank = NS_IsAboutBlank(tempURI);
 
-      if (!isUIResource && !isAboutBlank) {
-        aLoadInfo->SetResultPrincipalURI(tempURI);
-      }
+      nsLoadFlags loadFlags = isUIResource || isAboutBlank
+                    ? static_cast<nsLoadFlags>(nsIChannel::LOAD_NORMAL)
+                    : static_cast<nsLoadFlags>(nsIChannel::LOAD_REPLACE);
+
+      rv = NS_NewChannelInternal(getter_AddRefs(tempChannel),
+                                 tempURI,
+                                 aLoadInfo,
+                                 nullptr, // aLoadGroup
+                                 nullptr, // aCallbacks
+                                 loadFlags);
+      NS_ENSURE_SUCCESS(rv, rv);
 
       tempChannel->SetOriginalURI(aURI);
 
       tempChannel.forget(aResult);
       return rv;
     }
   }
 
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -11163,22 +11163,16 @@ nsDocShell::DoURILoad(nsIURI* aURI,
   // Make sure to give the caller a channel if we managed to create one
   // This is important for correct error page/session history interaction
   if (aRequest) {
     NS_ADDREF(*aRequest = channel);
   }
 
   if (aOriginalURI) {
     channel->SetOriginalURI(aOriginalURI);
-    // The LOAD_REPLACE flag and its handling here will be removed as part
-    // of bug 1319110.  For now preserve its restoration here to not break
-    // any code expecting it being set specially on redirected channels.
-    // If the flag has originally been set to change result of
-    // NS_GetFinalChannelURI it won't have any effect and also won't cause
-    // any harm.
     if (aLoadReplace) {
       uint32_t loadFlags;
       channel->GetLoadFlags(&loadFlags);
       NS_ENSURE_SUCCESS(rv, rv);
       channel->SetLoadFlags(loadFlags | nsIChannel::LOAD_REPLACE);
     }
   } else {
     channel->SetOriginalURI(aURI);
--- a/dom/canvas/ImageBitmap.cpp
+++ b/dom/canvas/ImageBitmap.cpp
@@ -1,16 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/ImageBitmap.h"
-
+#include "mozilla/CheckedInt.h"
 #include "mozilla/dom/ImageBitmapBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/StructuredCloneTags.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/WorkerRunnable.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Swizzle.h"
 #include "ImageBitmapColorUtils.h"
@@ -170,20 +170,24 @@ CropAndCopyDataSourceSurface(DataSourceS
         NS_WARN_IF(!dstMap.IsMapped())) {
       return nullptr;
     }
 
     uint8_t* srcBufferPtr = srcMap.GetData() + surfPortion.y * srcMap.GetStride()
                                              + surfPortion.x * bytesPerPixel;
     uint8_t* dstBufferPtr = dstMap.GetData() + dest.y * dstMap.GetStride()
                                              + dest.x * bytesPerPixel;
-    const uint32_t copiedBytesPerRaw = surfPortion.width * bytesPerPixel;
+    CheckedInt<uint32_t> copiedBytesPerRaw =
+      CheckedInt<uint32_t>(surfPortion.width) * bytesPerPixel;
+    if (!copiedBytesPerRaw.isValid()) {
+      return nullptr;
+    }
 
     for (int i = 0; i < surfPortion.height; ++i) {
-      memcpy(dstBufferPtr, srcBufferPtr, copiedBytesPerRaw);
+      memcpy(dstBufferPtr, srcBufferPtr, copiedBytesPerRaw.value());
       srcBufferPtr += srcMap.GetStride();
       dstBufferPtr += dstMap.GetStride();
     }
   }
 
   return dstDataSurface.forget();
 }
 
--- a/dom/presentation/ipc/PresentationIPCService.cpp
+++ b/dom/presentation/ipc/PresentationIPCService.cpp
@@ -30,17 +30,17 @@ PresentationChild* sPresentationChild;
 
 NS_IMPL_ISUPPORTS(PresentationIPCService,
                   nsIPresentationService,
                   nsIPresentationAvailabilityListener)
 
 PresentationIPCService::PresentationIPCService()
 {
   ContentChild* contentChild = ContentChild::GetSingleton();
-  if (NS_WARN_IF(!contentChild)) {
+  if (NS_WARN_IF(!contentChild || contentChild->IsShuttingDown())) {
     return;
   }
   sPresentationChild = new PresentationChild(this);
   Unused <<
     NS_WARN_IF(!contentChild->SendPPresentationConstructor(sPresentationChild));
 }
 
 /* virtual */
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -1590,16 +1590,22 @@ PRThreadFromThread(nsIThread* aThread)
 
   PRThread* result;
   MOZ_ALWAYS_SUCCEEDS(aThread->GetPRThread(&result));
   MOZ_ASSERT(result);
 
   return result;
 }
 
+class SimpleWorkerHolder final : public WorkerHolder
+{
+public:
+  virtual bool Notify(Status aStatus) { return true; }
+};
+
 } /* anonymous namespace */
 
 NS_IMPL_ISUPPORTS_INHERITED0(MainThreadReleaseRunnable, Runnable)
 
 NS_IMPL_ISUPPORTS_INHERITED0(TopLevelWorkerFinishedRunnable, Runnable)
 
 namespace {
 
@@ -4574,21 +4580,31 @@ WorkerPrivate::Constructor(const GlobalO
 already_AddRefed<WorkerPrivate>
 WorkerPrivate::Constructor(JSContext* aCx,
                            const nsAString& aScriptURL,
                            bool aIsChromeWorker, WorkerType aWorkerType,
                            const nsAString& aWorkerName,
                            const nsACString& aServiceWorkerScope,
                            WorkerLoadInfo* aLoadInfo, ErrorResult& aRv)
 {
+  // If this is a sub-worker, we need to keep the parent worker alive until this
+  // one is registered.
+  UniquePtr<SimpleWorkerHolder> holder;
+
   WorkerPrivate* parent = NS_IsMainThread() ?
                           nullptr :
                           GetCurrentThreadWorkerPrivate();
   if (parent) {
     parent->AssertIsOnWorkerThread();
+
+    holder.reset(new SimpleWorkerHolder());
+    if (!holder->HoldWorker(parent, Canceling)) {
+      aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+      return nullptr;
+    }
   } else {
     AssertIsOnMainThread();
   }
 
   Maybe<WorkerLoadInfo> stackLoadInfo;
   if (!aLoadInfo) {
     stackLoadInfo.emplace();
 
--- a/gfx/skia/skia/src/core/SkScan_AAAPath.cpp
+++ b/gfx/skia/skia/src/core/SkScan_AAAPath.cpp
@@ -17,16 +17,18 @@
 #include "SkRasterClip.h"
 #include "SkRegion.h"
 #include "SkScan.h"
 #include "SkScanPriv.h"
 #include "SkTSort.h"
 #include "SkTemplates.h"
 #include "SkUtils.h"
 
+#include "mozilla/Assertions.h"
+
 ///////////////////////////////////////////////////////////////////////////////
 
 /*
 
 The following is a high-level overview of our analytic anti-aliasing
 algorithm. We consider a path as a collection of line segments, as
 quadratic/cubic curves are converted to small line segments. Without loss of
 generality, let's assume that the draw region is [0, W] x [0, H].
@@ -1003,42 +1005,51 @@ static inline void aaa_walk_convex_edges
         AdditiveBlitter* blitter, int start_y, int stop_y, SkFixed leftBound, SkFixed riteBound,
         bool isUsingMask) {
     validate_sort((SkAnalyticEdge*)prevHead->fNext);
 
     SkAnalyticEdge* leftE = (SkAnalyticEdge*) prevHead->fNext;
     SkAnalyticEdge* riteE = (SkAnalyticEdge*) leftE->fNext;
     SkAnalyticEdge* currE = (SkAnalyticEdge*) riteE->fNext;
 
+    bool currENullInit = !currE, currEChanged = false;
+
     SkFixed y = SkTMax(leftE->fUpperY, riteE->fUpperY);
 
     #ifdef SK_DEBUG
     int frac_y_cnt = 0;
     int total_y_cnt = 0;
     #endif
 
     for (;;) {
         // We have to check fLowerY first because some edges might be alone (e.g., there's only
         // a left edge but no right edge in a given y scan line) due to precision limit.
         while (leftE->fLowerY <= y) { // Due to smooth jump, we may pass multiple short edges
             if (update_edge(leftE, y)) {
+                if (!currE) {
+                    MOZ_RELEASE_ASSERT((!currENullInit || currEChanged) || (stop_y < SkFixedFloorToInt(SK_MaxS32)), "Please help us! Use crash reporter to report me and comment what site you were viewing when this happened.");
+                    MOZ_RELEASE_ASSERT(!currENullInit || currEChanged, "Please help us! Use crash reporter to report me and comment what site you were viewing when this happened.");
+                    MOZ_RELEASE_ASSERT(stop_y < SkFixedFloorToInt(SK_MaxS32), "Please help us! Use crash reporter to report me and comment what site you were viewing when this happened.");
+                }
                 if (SkFixedFloorToInt(currE->fUpperY) >= stop_y) {
                     goto END_WALK;
                 }
                 leftE = currE;
                 currE = (SkAnalyticEdge*)currE->fNext;
+                currEChanged = true;
             }
         }
         while (riteE->fLowerY <= y) { // Due to smooth jump, we may pass multiple short edges
             if (update_edge(riteE, y)) {
                 if (SkFixedFloorToInt(currE->fUpperY) >= stop_y) {
                     goto END_WALK;
                 }
                 riteE = currE;
                 currE = (SkAnalyticEdge*)currE->fNext;
+                currEChanged = true;
             }
         }
 
         SkASSERT(leftE);
         SkASSERT(riteE);
 
         // check our bottom clip
         if (SkFixedFloorToInt(y) >= stop_y) {
--- a/image/imgRequestProxy.cpp
+++ b/image/imgRequestProxy.cpp
@@ -612,17 +612,17 @@ static imgRequestProxy* NewProxy(imgRequ
 {
   return new imgRequestProxy();
 }
 
 imgRequestProxy* NewStaticProxy(imgRequestProxy* aThis)
 {
   nsCOMPtr<nsIPrincipal> currentPrincipal;
   aThis->GetImagePrincipal(getter_AddRefs(currentPrincipal));
-  RefPtr<image::Image> image = aThis->GetImage();
+  RefPtr<mozilla::image::Image> image = aThis->GetImage();
   return new imgRequestProxyStatic(image, currentPrincipal);
 }
 
 NS_IMETHODIMP
 imgRequestProxy::Clone(imgINotificationObserver* aObserver,
                        imgIRequest** aClone)
 {
   nsresult result;
--- a/ipc/glue/BackgroundUtils.cpp
+++ b/ipc/glue/BackgroundUtils.cpp
@@ -16,17 +16,16 @@
 #include "nsIURI.h"
 #include "nsNetUtil.h"
 #include "mozilla/LoadInfo.h"
 #include "ContentPrincipal.h"
 #include "NullPrincipal.h"
 #include "nsContentUtils.h"
 #include "nsString.h"
 #include "nsTArray.h"
-#include "URIUtils.h"
 
 namespace mozilla {
 namespace net {
 class OptionalLoadInfoArgs;
 }
 
 using mozilla::BasePrincipal;
 using namespace mozilla::net;
@@ -300,23 +299,16 @@ LoadInfoToLoadInfoArgs(nsILoadInfo *aLoa
         getter_AddRefs(sandboxedLoadingPrincipal));
     NS_ENSURE_SUCCESS(rv, rv);
     rv = PrincipalToPrincipalInfo(sandboxedLoadingPrincipal,
                                   &sandboxedLoadingPrincipalInfoTemp);
     NS_ENSURE_SUCCESS(rv, rv);
     sandboxedLoadingPrincipalInfo = sandboxedLoadingPrincipalInfoTemp;
   }
 
-  OptionalURIParams optionalResultPrincipalURI = mozilla::void_t();
-  nsCOMPtr<nsIURI> resultPrincipalURI;
-  Unused << aLoadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
-  if (resultPrincipalURI) {
-    SerializeURI(resultPrincipalURI, optionalResultPrincipalURI);
-  }
-
   nsTArray<PrincipalInfo> redirectChainIncludingInternalRedirects;
   for (const nsCOMPtr<nsIPrincipal>& principal : aLoadInfo->RedirectChainIncludingInternalRedirects()) {
     rv = PrincipalToPrincipalInfo(principal, redirectChainIncludingInternalRedirects.AppendElement());
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   nsTArray<PrincipalInfo> redirectChain;
   for (const nsCOMPtr<nsIPrincipal>& principal : aLoadInfo->RedirectChain()) {
@@ -325,17 +317,16 @@ LoadInfoToLoadInfoArgs(nsILoadInfo *aLoa
   }
 
   *aOptionalLoadInfoArgs =
     LoadInfoArgs(
       loadingPrincipalInfo,
       triggeringPrincipalInfo,
       principalToInheritInfo,
       sandboxedLoadingPrincipalInfo,
-      optionalResultPrincipalURI,
       aLoadInfo->GetSecurityFlags(),
       aLoadInfo->InternalContentPolicyType(),
       static_cast<uint32_t>(aLoadInfo->GetTainting()),
       aLoadInfo->GetUpgradeInsecureRequests(),
       aLoadInfo->GetVerifySignedContent(),
       aLoadInfo->GetEnforceSRI(),
       aLoadInfo->GetForceInheritPrincipalDropped(),
       aLoadInfo->GetInnerWindowID(),
@@ -389,22 +380,16 @@ LoadInfoArgsToLoadInfo(const OptionalLoa
 
   nsCOMPtr<nsIPrincipal> sandboxedLoadingPrincipal;
   if (loadInfoArgs.sandboxedLoadingPrincipalInfo().type() != OptionalPrincipalInfo::Tvoid_t) {
     sandboxedLoadingPrincipal =
       PrincipalInfoToPrincipal(loadInfoArgs.sandboxedLoadingPrincipalInfo(), &rv);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  nsCOMPtr<nsIURI> resultPrincipalURI;
-  if (loadInfoArgs.resultPrincipalURI().type() != OptionalURIParams::Tvoid_t) {
-    resultPrincipalURI = DeserializeURI(loadInfoArgs.resultPrincipalURI());
-    NS_ENSURE_TRUE(resultPrincipalURI, NS_ERROR_UNEXPECTED);
-  }
-
   nsTArray<nsCOMPtr<nsIPrincipal>> redirectChainIncludingInternalRedirects;
   for (const PrincipalInfo& principalInfo : loadInfoArgs.redirectChainIncludingInternalRedirects()) {
     nsCOMPtr<nsIPrincipal> redirectedPrincipal =
       PrincipalInfoToPrincipal(principalInfo, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
     redirectChainIncludingInternalRedirects.AppendElement(redirectedPrincipal.forget());
   }
 
@@ -416,17 +401,16 @@ LoadInfoArgsToLoadInfo(const OptionalLoa
     redirectChain.AppendElement(redirectedPrincipal.forget());
   }
 
   nsCOMPtr<nsILoadInfo> loadInfo =
     new mozilla::LoadInfo(loadingPrincipal,
                           triggeringPrincipal,
                           principalToInherit,
                           sandboxedLoadingPrincipal,
-                          resultPrincipalURI,
                           loadInfoArgs.securityFlags(),
                           loadInfoArgs.contentPolicyType(),
                           static_cast<LoadTainting>(loadInfoArgs.tainting()),
                           loadInfoArgs.upgradeInsecureRequests(),
                           loadInfoArgs.verifySignedContent(),
                           loadInfoArgs.enforceSRI(),
                           loadInfoArgs.forceInheritPrincipalDropped(),
                           loadInfoArgs.innerWindowID(),
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -3732,17 +3732,21 @@ SetGCCallback(JSContext* cx, unsigned ar
     } else if (strcmp(action.ptr(), "majorGC") == 0) {
         if (!JS_GetProperty(cx, opts, "depth", &v))
             return false;
         int32_t depth = 1;
         if (!v.isUndefined()) {
             if (!ToInt32(cx, v, &depth))
                 return false;
         }
-        if (depth > int32_t(gcstats::Statistics::MAX_NESTING - 4)) {
+        if (depth < 0) {
+            JS_ReportErrorASCII(cx, "Nesting depth cannot be negative");
+            return false;
+        }
+        if (depth + gcstats::MAX_PHASE_NESTING > gcstats::Statistics::MAX_SUSPENDED_PHASES) {
             JS_ReportErrorASCII(cx, "Nesting depth too large, would overflow");
             return false;
         }
 
         auto info = js_new<gcCallback::MajorGC>();
         if (!info) {
             ReportOutOfMemory(cx);
             return false;
--- a/js/src/gc/GenerateStatsPhases.py
+++ b/js/src/gc/GenerateStatsPhases.py
@@ -65,16 +65,18 @@ MarkRootsPhaseKind = PhaseKind("MARK_ROO
     PhaseKind("BUFFER_GRAY_ROOTS", "Buffer Gray Roots", 49),
     PhaseKind("MARK_CCWS", "Mark Cross Compartment Wrappers", 50),
     PhaseKind("MARK_STACK", "Mark C and JS stacks", 51),
     PhaseKind("MARK_RUNTIME_DATA", "Mark Runtime-wide Data", 52),
     PhaseKind("MARK_EMBEDDING", "Mark Embedding", 53),
     PhaseKind("MARK_COMPARTMENTS", "Mark Compartments", 54),
 ])
 
+JoinParallelTasksPhaseKind = PhaseKind("JOIN_PARALLEL_TASKS", "Join Parallel Tasks", 67)
+
 PhaseKindGraphRoots = [
     PhaseKind("MUTATOR", "Mutator Running", 0),
     PhaseKind("GC_BEGIN", "Begin Callback", 1),
     PhaseKind("WAIT_BACKGROUND_THREAD", "Wait Background Thread", 2),
     PhaseKind("MARK_DISCARD_CODE", "Mark Discard Code", 3),
     PhaseKind("RELAZIFY_FUNCTIONS", "Relazify Functions", 4),
     PhaseKind("PURGE", "Purge", 5),
     PhaseKind("MARK", "Mark", 6, [
@@ -110,32 +112,35 @@ PhaseKindGraphRoots = [
             PhaseKind("SWEEP_UNIQUEIDS", "Sweep Unique IDs", 64),
             PhaseKind("SWEEP_JIT_DATA", "Sweep JIT Data", 65),
             PhaseKind("SWEEP_WEAK_CACHES", "Sweep Weak Caches", 66),
             PhaseKind("SWEEP_MISC", "Sweep Miscellaneous", 29),
             PhaseKind("SWEEP_TYPES", "Sweep type information", 30, [
                 PhaseKind("SWEEP_TYPES_BEGIN", "Sweep type tables and compilations", 31),
                 PhaseKind("SWEEP_TYPES_END", "Free type arena", 32),
             ]),
+            JoinParallelTasksPhaseKind
         ]),
         PhaseKind("SWEEP_OBJECT", "Sweep Object", 33),
         PhaseKind("SWEEP_STRING", "Sweep String", 34),
         PhaseKind("SWEEP_SCRIPT", "Sweep Script", 35),
         PhaseKind("SWEEP_SCOPE", "Sweep Scope", 59),
         PhaseKind("SWEEP_REGEXP_SHARED", "Sweep RegExpShared", 61),
         PhaseKind("SWEEP_SHAPE", "Sweep Shape", 36),
         PhaseKind("SWEEP_JITCODE", "Sweep JIT code", 37),
         PhaseKind("FINALIZE_END", "Finalize End Callback", 38),
-        PhaseKind("DESTROY", "Deallocate", 39)
+        PhaseKind("DESTROY", "Deallocate", 39),
+        JoinParallelTasksPhaseKind
         ]),
     PhaseKind("COMPACT", "Compact", 40, [
         PhaseKind("COMPACT_MOVE", "Compact Move", 41),
         PhaseKind("COMPACT_UPDATE", "Compact Update", 42, [
             MarkRootsPhaseKind,
             PhaseKind("COMPACT_UPDATE_CELLS", "Compact Update Cells", 43),
+            JoinParallelTasksPhaseKind
         ]),
     ]),
     PhaseKind("GC_END", "End Callback", 44),
     PhaseKind("MINOR_GC", "All Minor GCs", 45, [
         MarkRootsPhaseKind,
     ]),
     PhaseKind("EVICT_NURSERY", "Minor GCs to Evict Nursery", 46, [
         MarkRootsPhaseKind,
@@ -221,16 +226,20 @@ AllPhases, PhasesForPhaseKind = expandPh
 for phaseKind in AllPhaseKinds:
     phases = PhasesForPhaseKind[phaseKind]
     if len(phases) == 1:
         phases[0].name = "%s" % phaseKind.name
     else:
         for index, phase in enumerate(phases):
             phase.name = "%s_%d" % (phaseKind.name, index + 1)
 
+# Find the maximum phase nesting.
+
+MaxPhaseNesting = max(phase.depth for phase in AllPhases) + 1
+
 # Generate code.
 
 def writeList(out, items):
     if items:
         out.write(",\n".join("  " + item for item in items) + "\n")
 
 def writeEnumClass(out, name, type, items, extraItems):
     items = [ "FIRST" ] + items + [ "LIMIT" ] + extraItems
@@ -257,16 +266,22 @@ def generateHeader(out):
     #
     phaseNames = map(lambda phase: phase.name, AllPhases)
     extraPhases = [
         "NONE = LIMIT",
         "EXPLICIT_SUSPENSION = LIMIT",
         "IMPLICIT_SUSPENSION"
     ]
     writeEnumClass(out, "Phase", "uint8_t", phaseNames, extraPhases)
+    out.write("\n")
+
+    #
+    # Generate MAX_PHASE_NESTING constant.
+    #
+    out.write("static const size_t MAX_PHASE_NESTING = %d;\n" % MaxPhaseNesting)
 
 def generateCpp(out):
     #
     # Generate the PhaseKindInfo table.
     #
     out.write("static const PhaseKindTable phaseKinds = {\n")
     for phaseKind in AllPhaseKinds:
         phase = PhasesForPhaseKind[phaseKind][0]
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -119,30 +119,30 @@ using PhaseKindTable = EnumeratedArray<P
 #include "gc/StatsPhasesGenerated.cpp"
 
 static double
 t(TimeDuration duration)
 {
     return duration.ToMilliseconds();
 }
 
-Phase
+inline Phase
 Statistics::currentPhase() const
 {
-    return phaseNestingDepth ? phaseNesting[phaseNestingDepth - 1] : Phase::NONE;
+    return phaseStack.empty() ? Phase::NONE : phaseStack.back();
 }
 
 PhaseKind
 Statistics::currentPhaseKind() const
 {
     // Public API to get the current phase kind, suppressing the synthetic
     // PhaseKind::MUTATOR phase.
 
     Phase phase = currentPhase();
-    MOZ_ASSERT_IF(phase == Phase::MUTATOR, phaseNestingDepth == 1);
+    MOZ_ASSERT_IF(phase == Phase::MUTATOR, phaseStack.length() == 1);
     if (phase == Phase::NONE || phase == Phase::MUTATOR)
         return PhaseKind::NONE;
 
     return phases[phase].phaseKind;
 }
 
 Phase
 Statistics::lookupChildPhase(PhaseKind phaseKind) const
@@ -633,26 +633,28 @@ Statistics::formatJsonPhaseTimes(const P
 }
 
 Statistics::Statistics(JSRuntime* rt)
   : runtime(rt),
     fp(nullptr),
     nonincrementalReason_(gc::AbortReason::None),
     preBytes(0),
     maxPauseInInterval(0),
-    phaseNestingDepth(0),
-    suspended(0),
     sliceCallback(nullptr),
     nurseryCollectionCallback(nullptr),
     aborted(false),
     enableProfiling_(false),
     sliceCount_(0)
 {
     for (auto& count : counts)
         count = 0;
+    PodZero(&totalTimes_);
+
+    MOZ_ALWAYS_TRUE(phaseStack.reserve(MAX_PHASE_NESTING));
+    MOZ_ALWAYS_TRUE(suspendedPhases.reserve(MAX_SUSPENDED_PHASES));
 
     const char* env = getenv("MOZ_GCTIMER");
     if (env) {
         if (strcmp(env, "none") == 0) {
             fp = nullptr;
         } else if (strcmp(env, "stdout") == 0) {
             fp = stdout;
         } else if (strcmp(env, "stderr") == 0) {
@@ -669,18 +671,16 @@ Statistics::Statistics(JSRuntime* rt)
         if (0 == strcmp(env, "help")) {
             fprintf(stderr, "JS_GC_PROFILE=N\n"
                     "\tReport major GC's taking more than N milliseconds.\n");
             exit(0);
         }
         enableProfiling_ = true;
         profileThreshold_ = TimeDuration::FromMilliseconds(atoi(env));
     }
-
-    PodZero(&totalTimes_);
 }
 
 Statistics::~Statistics()
 {
     if (fp && fp != stdout && fp != stderr)
         fclose(fp);
 }
 
@@ -946,37 +946,41 @@ Statistics::beginSlice(const ZoneGCStats
         (*sliceCallback)(cx, JS::GC_SLICE_BEGIN, desc);
     }
 }
 
 void
 Statistics::endSlice()
 {
     if (!aborted) {
-        slices_.back().end = TimeStamp::Now();
-        slices_.back().endFaults = GetPageFaultCount();
-        slices_.back().finalState = runtime->gc.state();
+        auto& slice = slices_.back();
+        slice.end = TimeStamp::Now();
+        slice.endFaults = GetPageFaultCount();
+        slice.finalState = runtime->gc.state();
 
-        TimeDuration sliceTime = slices_.back().end - slices_.back().start;
+        TimeDuration sliceTime = slice.end - slice.start;
         runtime->addTelemetry(JS_TELEMETRY_GC_SLICE_MS, t(sliceTime));
-        runtime->addTelemetry(JS_TELEMETRY_GC_RESET, slices_.back().wasReset());
-        if (slices_.back().wasReset())
-            runtime->addTelemetry(JS_TELEMETRY_GC_RESET_REASON, uint32_t(slices_.back().resetReason));
+        runtime->addTelemetry(JS_TELEMETRY_GC_RESET, slice.wasReset());
+        if (slice.wasReset())
+            runtime->addTelemetry(JS_TELEMETRY_GC_RESET_REASON, uint32_t(slice.resetReason));
 
-        if (slices_.back().budget.isTimeBudget()) {
-            int64_t budget_ms = slices_.back().budget.timeBudget.budget;
+        if (slice.budget.isTimeBudget()) {
+            int64_t budget_ms = slice.budget.timeBudget.budget;
             runtime->addTelemetry(JS_TELEMETRY_GC_BUDGET_MS, budget_ms);
             if (budget_ms == runtime->gc.defaultSliceBudget())
                 runtime->addTelemetry(JS_TELEMETRY_GC_ANIMATION_MS, t(sliceTime));
 
             // Record any phase that goes more than 2x over its budget.
             if (sliceTime.ToMilliseconds() > 2 * budget_ms) {
-                PhaseKind longest = LongestPhaseSelfTime(slices_.back().phaseTimes);
-                uint8_t bucket = phaseKinds[longest].telemetryBucket;
-                runtime->addTelemetry(JS_TELEMETRY_GC_SLOW_PHASE, bucket);
+                reportLongestPhase(slice.phaseTimes, JS_TELEMETRY_GC_SLOW_PHASE);
+                // If we spend a significant length of time waiting for parallel
+                // tasks then report the longest task.
+                TimeDuration joinTime = SumPhase(PhaseKind::JOIN_PARALLEL_TASKS, slice.phaseTimes);
+                if (joinTime.ToMilliseconds() > budget_ms)
+                    reportLongestPhase(slice.parallelTimes, JS_TELEMETRY_GC_SLOW_TASK);
             }
         }
 
         sliceCount_++;
     }
 
     bool last = !runtime->gc.isIncrementalGCInProgress();
     if (last)
@@ -1007,124 +1011,129 @@ Statistics::endSlice()
         auto mutatorTime = phaseTimes[Phase::MUTATOR];
         PodZero(&phaseStartTimes);
         PodZero(&phaseTimes);
         phaseStartTimes[Phase::MUTATOR] = mutatorStartTime;
         phaseTimes[Phase::MUTATOR] = mutatorTime;
     }
 }
 
+void
+Statistics::reportLongestPhase(const PhaseTimeTable& times, int telemetryId)
+{
+    PhaseKind longest = LongestPhaseSelfTime(times);
+    if (longest == PhaseKind::NONE)
+        return;
+
+    uint8_t bucket = phaseKinds[longest].telemetryBucket;
+    runtime->addTelemetry(telemetryId, bucket);
+}
+
 bool
 Statistics::startTimingMutator()
 {
-    if (phaseNestingDepth != 0) {
+    if (phaseStack.length() != 0) {
         // Should only be called from outside of GC.
-        MOZ_ASSERT(phaseNestingDepth == 1);
-        MOZ_ASSERT(phaseNesting[0] == Phase::MUTATOR);
+        MOZ_ASSERT(phaseStack.length() == 1);
+        MOZ_ASSERT(phaseStack[0] == Phase::MUTATOR);
         return false;
     }
 
-    MOZ_ASSERT(suspended == 0);
+    MOZ_ASSERT(suspendedPhases.empty());
 
     timedGCTime = 0;
     phaseStartTimes[Phase::MUTATOR] = TimeStamp();
     phaseTimes[Phase::MUTATOR] = 0;
     timedGCStart = TimeStamp();
 
     beginPhase(PhaseKind::MUTATOR);
     return true;
 }
 
 bool
 Statistics::stopTimingMutator(double& mutator_ms, double& gc_ms)
 {
     // This should only be called from outside of GC, while timing the mutator.
-    if (phaseNestingDepth != 1 || phaseNesting[0] != Phase::MUTATOR)
+    if (phaseStack.length() != 1 || phaseStack[0] != Phase::MUTATOR)
         return false;
 
     endPhase(PhaseKind::MUTATOR);
     mutator_ms = t(phaseTimes[Phase::MUTATOR]);
     gc_ms = t(timedGCTime);
 
     return true;
 }
 
 void
 Statistics::suspendPhases(PhaseKind suspension)
 {
     MOZ_ASSERT(suspension == PhaseKind::EXPLICIT_SUSPENSION ||
                suspension == PhaseKind::IMPLICIT_SUSPENSION);
-    while (phaseNestingDepth) {
-        MOZ_ASSERT(suspended < mozilla::ArrayLength(suspendedPhases));
-        Phase parent = phaseNesting[phaseNestingDepth - 1];
-        suspendedPhases[suspended++] = parent;
+    while (!phaseStack.empty()) {
+        MOZ_ASSERT(suspendedPhases.length() < MAX_SUSPENDED_PHASES);
+        Phase parent = phaseStack.back();
+        suspendedPhases.infallibleAppend(parent);
         recordPhaseEnd(parent);
     }
-    suspendedPhases[suspended++] = lookupChildPhase(suspension);
+    suspendedPhases.infallibleAppend(lookupChildPhase(suspension));
 }
 
 void
 Statistics::resumePhases()
 {
-    suspended--;
-#ifdef DEBUG
-    Phase popped = suspendedPhases[suspended];
-    MOZ_ASSERT(popped == Phase::EXPLICIT_SUSPENSION ||
-               popped == Phase::IMPLICIT_SUSPENSION);
-#endif
+    MOZ_ASSERT(suspendedPhases.back() == Phase::EXPLICIT_SUSPENSION ||
+               suspendedPhases.back() == Phase::IMPLICIT_SUSPENSION);
+    suspendedPhases.popBack();
 
-    while (suspended &&
-           suspendedPhases[suspended - 1] != Phase::EXPLICIT_SUSPENSION &&
-           suspendedPhases[suspended - 1] != Phase::IMPLICIT_SUSPENSION)
+    while (!suspendedPhases.empty() &&
+           suspendedPhases.back() != Phase::EXPLICIT_SUSPENSION &&
+           suspendedPhases.back() != Phase::IMPLICIT_SUSPENSION)
     {
-        Phase resumePhase = suspendedPhases[--suspended];
+        Phase resumePhase = suspendedPhases.popCopy();
         if (resumePhase == Phase::MUTATOR)
             timedGCTime += TimeStamp::Now() - timedGCStart;
         recordPhaseBegin(resumePhase);
     }
 }
 
 void
 Statistics::beginPhase(PhaseKind phaseKind)
 {
     // No longer timing these phases. We should never see these.
     MOZ_ASSERT(phaseKind != PhaseKind::GC_BEGIN && phaseKind != PhaseKind::GC_END);
 
     // PhaseKind::MUTATOR is suspended while performing GC.
-    if (currentPhase() == Phase::MUTATOR) {
+    if (currentPhase() == Phase::MUTATOR)
         suspendPhases(PhaseKind::IMPLICIT_SUSPENSION);
-    }
 
     recordPhaseBegin(lookupChildPhase(phaseKind));
 }
 
 void
 Statistics::recordPhaseBegin(Phase phase)
 {
     // Guard against any other re-entry.
     MOZ_ASSERT(!phaseStartTimes[phase]);
 
-    MOZ_ASSERT(phaseNestingDepth < MAX_NESTING);
+    MOZ_ASSERT(phaseStack.length() < MAX_PHASE_NESTING);
     MOZ_ASSERT(phases[phase].parent == currentPhase());
 
-    phaseNesting[phaseNestingDepth] = phase;
-    phaseNestingDepth++;
-
+    phaseStack.infallibleAppend(phase);
     phaseStartTimes[phase] = TimeStamp::Now();
 }
 
 void
 Statistics::recordPhaseEnd(Phase phase)
 {
     TimeStamp now = TimeStamp::Now();
 
     if (phase == Phase::MUTATOR)
         timedGCStart = now;
 
-    phaseNestingDepth--;
+    phaseStack.popBack();
 
     TimeDuration t = now - phaseStartTimes[phase];
     if (!slices_.empty())
         slices_.back().phaseTimes[phase] += t;
     phaseTimes[phase] += t;
     phaseStartTimes[phase] = TimeStamp();
 }
 
@@ -1134,29 +1143,45 @@ Statistics::endPhase(PhaseKind phaseKind
     Phase phase = currentPhase();
     MOZ_ASSERT(phase != Phase::NONE);
     MOZ_ASSERT(phases[phase].phaseKind == phaseKind);
 
     recordPhaseEnd(phase);
 
     // When emptying the stack, we may need to return to timing the mutator
     // (PhaseKind::MUTATOR).
-    if (phaseNestingDepth == 0 &&
-        suspended > 0 &&
-        suspendedPhases[suspended - 1] == Phase::IMPLICIT_SUSPENSION)
+    if (phaseStack.empty() &&
+        !suspendedPhases.empty() &&
+        suspendedPhases.back() == Phase::IMPLICIT_SUSPENSION)
     {
         resumePhases();
     }
 }
 
 void
+Statistics::recordParallelPhase(PhaseKind phaseKind, TimeDuration duration)
+{
+    Phase phase = lookupChildPhase(phaseKind);
+
+    // Record the duration for all phases in the tree up to the root. This is
+    // not strictly necessary but makes the invariant that parent phase times
+    // include their children apply to both phaseTimes and parallelTimes.
+    while (phase != Phase::NONE) {
+        if (!slices_.empty())
+            slices_.back().parallelTimes[phase] += duration;
+        parallelTimes[phase] += duration;
+        phase = phases[phase].parent;
+    }
+}
+
+void
 Statistics::endParallelPhase(PhaseKind phaseKind, const GCParallelTask* task)
 {
     Phase phase = lookupChildPhase(phaseKind);
-    phaseNestingDepth--;
+    phaseStack.popBack();
 
     if (!slices_.empty())
         slices_.back().phaseTimes[phase] += task->duration();
     phaseTimes[phase] += task->duration();
     phaseStartTimes[phase] = TimeStamp();
 }
 
 TimeStamp
--- a/js/src/gc/Statistics.h
+++ b/js/src/gc/Statistics.h
@@ -127,16 +127,17 @@ struct Statistics
     ~Statistics();
 
     Statistics(const Statistics&) = delete;
     Statistics& operator=(const Statistics&) = delete;
 
     void beginPhase(PhaseKind phaseKind);
     void endPhase(PhaseKind phaseKind);
     void endParallelPhase(PhaseKind phaseKind, const GCParallelTask* task);
+    void recordParallelPhase(PhaseKind phaseKind, TimeDuration duration);
 
     // Occasionally, we may be in the middle of something that is tracked by
     // this class, and we need to do something unusual (eg evict the nursery)
     // that doesn't normally nest within the current phase. Suspend the
     // currently tracked phase stack, at which time the caller is free to do
     // other tracked operations.
     //
     // This also happens internally with the PhaseKind::MUTATOR "phase". While in
@@ -200,17 +201,17 @@ struct Statistics
     JS::GCNurseryCollectionCallback setNurseryCollectionCallback(
         JS::GCNurseryCollectionCallback callback);
 
     TimeDuration clearMaxGCPauseAccumulator();
     TimeDuration getMaxGCPauseSinceClear();
 
     PhaseKind currentPhaseKind() const;
 
-    static const size_t MAX_NESTING = 20;
+    static const size_t MAX_SUSPENDED_PHASES = MAX_PHASE_NESTING * 3;
 
     struct SliceData {
         SliceData(SliceBudget budget, JS::gcreason::Reason reason,
                   TimeStamp start, size_t startFaults, gc::State initialState)
           : budget(budget), reason(reason),
             initialState(initialState),
             finalState(gc::State::NotActive),
             resetReason(gc::AbortReason::None),
@@ -220,16 +221,17 @@ struct Statistics
 
         SliceBudget budget;
         JS::gcreason::Reason reason;
         gc::State initialState, finalState;
         gc::AbortReason resetReason;
         TimeStamp start, end;
         size_t startFaults, endFaults;
         PhaseTimeTable phaseTimes;
+        PhaseTimeTable parallelTimes;
 
         TimeDuration duration() const { return end - start; }
         bool wasReset() const { return resetReason != gc::AbortReason::None; }
     };
 
     typedef Vector<SliceData, 8, SystemAllocPolicy> SliceDataVector;
 
     const SliceDataVector& slices() const { return slices_; }
@@ -279,16 +281,17 @@ struct Statistics
     EnumeratedArray<Phase, Phase::LIMIT, TimeStamp> phaseStartTimes;
 
     /* Bookkeeping for GC timings when timingMutator is true */
     TimeStamp timedGCStart;
     TimeDuration timedGCTime;
 
     /* Total time in a given phase for this GC. */
     PhaseTimeTable phaseTimes;
+    PhaseTimeTable parallelTimes;
 
     /* Number of events of this type for this GC. */
     EnumeratedArray<Stat,
                     STAT_LIMIT,
                     mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire>> counts;
 
     /* Allocated space before the GC started. */
     size_t preBytes;
@@ -296,28 +299,26 @@ struct Statistics
     /* GC numbers as of the beginning of the collection. */
     uint64_t startingMinorGCNumber;
     uint64_t startingMajorGCNumber;
 
     /* Records the maximum GC pause in an API-controlled interval (in us). */
     mutable TimeDuration maxPauseInInterval;
 
     /* Phases that are currently on stack. */
-    Array<Phase, MAX_NESTING> phaseNesting;
-    size_t phaseNestingDepth;
+    Vector<Phase, MAX_PHASE_NESTING, SystemAllocPolicy> phaseStack;
 
     /*
      * Certain phases can interrupt the phase stack, eg callback phases. When
      * this happens, we move the suspended phases over to a sepearate list,
      * terminated by a dummy PhaseKind::SUSPENSION phase (so that we can nest
      * suspensions by suspending multiple stacks with a PhaseKind::SUSPENSION in
      * between).
      */
-    Array<Phase, MAX_NESTING * 3> suspendedPhases;
-    size_t suspended;
+    Vector<Phase, MAX_SUSPENDED_PHASES, SystemAllocPolicy> suspendedPhases;
 
     /* Sweep times for SCCs of compartments. */
     Vector<TimeDuration, 0, SystemAllocPolicy> sccTimes;
 
     JS::GCSliceCallback sliceCallback;
     JS::GCNurseryCollectionCallback nurseryCollectionCallback;
 
     /*
@@ -353,16 +354,18 @@ FOR_EACH_GC_PROFILE_TIME(DEFINE_TIME_KEY
 
     void recordPhaseBegin(Phase phase);
     void recordPhaseEnd(Phase phase);
 
     void gcDuration(TimeDuration* total, TimeDuration* maxPause) const;
     void sccDurations(TimeDuration* total, TimeDuration* maxPause) const;
     void printStats();
 
+    void reportLongestPhase(const PhaseTimeTable& times, int telemetryId);
+
     UniqueChars formatCompactSlicePhaseTimes(const PhaseTimeTable& phaseTimes) const;
 
     UniqueChars formatDetailedDescription() const;
     UniqueChars formatDetailedSliceDescription(unsigned i, const SliceData& slice) const;
     UniqueChars formatDetailedPhaseTimes(const PhaseTimeTable& phaseTimes) const;
     UniqueChars formatDetailedTotals() const;
 
     void formatJsonDescription(uint64_t timestamp, JSONPrinter&) const;
@@ -387,47 +390,34 @@ struct MOZ_RAII AutoGCSlice
     ~AutoGCSlice() { stats.endSlice(); }
 
     Statistics& stats;
 };
 
 struct MOZ_RAII AutoPhase
 {
     AutoPhase(Statistics& stats, PhaseKind phaseKind)
-      : stats(stats), task(nullptr), phaseKind(phaseKind), enabled(true)
+      : stats(stats), phaseKind(phaseKind), enabled(true)
     {
         stats.beginPhase(phaseKind);
     }
 
     AutoPhase(Statistics& stats, bool condition, PhaseKind phaseKind)
-      : stats(stats), task(nullptr), phaseKind(phaseKind), enabled(condition)
-    {
-        if (enabled)
-            stats.beginPhase(phaseKind);
-    }
-
-    AutoPhase(Statistics& stats, const GCParallelTask& task, PhaseKind phaseKind)
-      : stats(stats), task(&task), phaseKind(phaseKind), enabled(true)
+      : stats(stats), phaseKind(phaseKind), enabled(condition)
     {
         if (enabled)
             stats.beginPhase(phaseKind);
     }
 
     ~AutoPhase() {
-        if (enabled) {
-            // Bug 1309651 - we only record active thread time (including time
-            // spent waiting to join with helper threads), but should start
-            // recording total work on helper threads sometime by calling
-            // endParallelPhase here if task is nonnull.
+        if (enabled)
             stats.endPhase(phaseKind);
-        }
     }
 
     Statistics& stats;
-    const GCParallelTask* task;
     PhaseKind phaseKind;
     bool enabled;
 };
 
 struct MOZ_RAII AutoSCC
 {
     AutoSCC(Statistics& stats, unsigned scc)
       : stats(stats), scc(scc)
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -33,17 +33,16 @@ JS::Zone::Zone(JSRuntime* rt, ZoneGroup*
     types(this),
     gcWeakMapList_(group),
     compartments_(),
     gcGrayRoots_(group),
     gcWeakRefs_(group),
     weakCaches_(group),
     gcWeakKeys_(group, SystemAllocPolicy(), rt->randomHashCodeScrambler()),
     gcSweepGroupEdges_(group),
-    hasDeadProxies_(group),
     typeDescrObjects_(group, this, SystemAllocPolicy()),
     markedAtoms_(group),
     atomCache_(group),
     externalStringCache_(group),
     usage(&rt->gc.usage),
     threshold(),
     gcDelayBytes(0),
     propertyTree_(group, this),
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -368,26 +368,19 @@ struct Zone : public JS::shadow::Zone,
 
   private:
     // A set of edges from this zone to other zones.
     //
     // This is used during GC while calculating sweep groups to record edges
     // that can't be determined by examining this zone by itself.
     js::ZoneGroupData<ZoneSet> gcSweepGroupEdges_;
 
-    // Zones with dead proxies require an extra scan through the wrapper map,
-    // so track whether any dead proxies are known to exist.
-    js::ZoneGroupData<bool> hasDeadProxies_;
-
   public:
     ZoneSet& gcSweepGroupEdges() { return gcSweepGroupEdges_.ref(); }
 
-    bool hasDeadProxies() { return hasDeadProxies_; }
-    void setHasDeadProxies(bool b) { hasDeadProxies_ = b; }
-
     // Keep track of all TypeDescr and related objects in this compartment.
     // This is used by the GC to trace them all first when compacting, since the
     // TypedObject trace hook may access these objects.
     //
     // There are no barriers here - the set contains only tenured objects so no
     // post-barrier is required, and these are weak references so no pre-barrier
     // is required.
     using TypeDescrObjectSet = js::GCHashSet<JSObject*,
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1357022.js
@@ -0,0 +1,6 @@
+const root3 = newGlobal();
+function test(constructor) {
+    if (!nukeCCW(root3.load)) {}
+}
+test(Map);
+test(Set);
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -766,18 +766,16 @@ struct JSCompartment
     bool getTemplateLiteralObject(JSContext* cx, js::HandleObject rawStrings,
                                   js::MutableHandleObject templateObj);
 
     // Per above, but an entry must already exist in the template registry.
     JSObject* getExistingTemplateLiteralObject(JSObject* rawStrings);
 
     void findOutgoingEdges(js::gc::ZoneComponentFinder& finder);
 
-    MOZ_MUST_USE bool findDeadProxyZoneEdges(bool* foundAny);
-
     js::DtoaCache dtoaCache;
     js::NewProxyCache newProxyCache;
 
     // Random number generator for Math.random().
     mozilla::Maybe<mozilla::non_crypto::XorShift128PlusRNG> randomNumberGenerator;
 
     // Initialize randomNumberGenerator if needed.
     void ensureRandomNumberGenerator();
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -124,16 +124,17 @@ enum {
     JS_TELEMETRY_GC_MAX_PAUSE_MS,
     JS_TELEMETRY_GC_MARK_MS,
     JS_TELEMETRY_GC_SWEEP_MS,
     JS_TELEMETRY_GC_COMPACT_MS,
     JS_TELEMETRY_GC_MARK_ROOTS_MS,
     JS_TELEMETRY_GC_MARK_GRAY_MS,
     JS_TELEMETRY_GC_SLICE_MS,
     JS_TELEMETRY_GC_SLOW_PHASE,
+    JS_TELEMETRY_GC_SLOW_TASK,
     JS_TELEMETRY_GC_MMU_50,
     JS_TELEMETRY_GC_RESET,
     JS_TELEMETRY_GC_RESET_REASON,
     JS_TELEMETRY_GC_INCREMENTAL_DISABLED,
     JS_TELEMETRY_GC_NON_INCREMENTAL,
     JS_TELEMETRY_GC_NON_INCREMENTAL_REASON,
     JS_TELEMETRY_GC_SCC_SWEEP_TOTAL_MS,
     JS_TELEMETRY_GC_SCC_SWEEP_MAX_PAUSE_MS,
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -4477,41 +4477,16 @@ JSCompartment::findOutgoingEdges(ZoneCom
         if (key.is<JSObject*>()) {
             TenuredCell& other = key.as<JSObject*>()->asTenured();
             needsEdge = !other.isMarked(BLACK) || other.isMarked(GRAY);
         }
         key.applyToWrapped(AddOutgoingEdgeFunctor(needsEdge, finder));
     }
 }
 
-bool
-JSCompartment::findDeadProxyZoneEdges(bool* foundAny)
-{
-    // As an optimization, return whether any dead proxy objects are found in
-    // this compartment so that if a zone has none, its cross compartment
-    // wrappers do not need to be scanned.
-    *foundAny = false;
-    for (js::WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
-        Value value = e.front().value().get();
-        if (value.isObject()) {
-            if (IsDeadProxyObject(&value.toObject())) {
-                *foundAny = true;
-                CrossCompartmentKey& key = e.front().mutableKey();
-                Zone* wrappedZone = key.as<JSObject*>()->zone();
-                if (!wrappedZone->isGCMarking())
-                    continue;
-                if (!wrappedZone->gcSweepGroupEdges().put(zone()))
-                    return false;
-            }
-        }
-    }
-
-    return true;
-}
-
 void
 Zone::findOutgoingEdges(ZoneComponentFinder& finder)
 {
     /*
      * Any compartment may have a pointer to an atom in the atoms
      * compartment, and these aren't in the cross compartment map.
      */
     JSRuntime* rt = runtimeFromActiveCooperatingThread();
@@ -4543,30 +4518,16 @@ GCRuntime::findInterZoneEdges()
      * group.
      */
 
     for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
         if (!WeakMapBase::findInterZoneEdges(zone))
             return false;
     }
 
-    for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
-        if (zone->hasDeadProxies()) {
-            bool foundInZone = false;
-            for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) {
-                bool foundInCompartment = false;
-                if (!comp->findDeadProxyZoneEdges(&foundInCompartment))
-                    return false;
-                foundInZone = foundInZone || foundInCompartment;
-            }
-            if (!foundInZone)
-                zone->setHasDeadProxies(false);
-        }
-    }
-
     return true;
 }
 
 void
 GCRuntime::groupZonesForSweeping(JS::gcreason::Reason reason, AutoLockForExclusiveAccess& lock)
 {
 #ifdef DEBUG
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next())
@@ -4854,18 +4815,16 @@ ResetGrayList(JSCompartment* comp)
 void
 js::NotifyGCNukeWrapper(JSObject* obj)
 {
     /*
      * References to target of wrapper are being removed, we no longer have to
      * remember to mark it.
      */
     RemoveFromGrayList(obj);
-
-    obj->zone()->setHasDeadProxies(true);
 }
 
 enum {
     JS_GC_SWAP_OBJECT_A_REMOVED = 1 << 0,
     JS_GC_SWAP_OBJECT_B_REMOVED = 1 << 1
 };
 
 unsigned
@@ -5070,18 +5029,21 @@ GCRuntime::startTask(GCParallelTask& tas
         gcstats::AutoPhase ap(stats(), phase);
         task.runFromActiveCooperatingThread(rt);
     }
 }
 
 void
 GCRuntime::joinTask(GCParallelTask& task, gcstats::PhaseKind phase, AutoLockHelperThreadState& locked)
 {
-    gcstats::AutoPhase ap(stats(), task, phase);
-    task.joinWithLockHeld(locked);
+    {
+        gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::JOIN_PARALLEL_TASKS);
+        task.joinWithLockHeld(locked);
+    }
+    stats().recordParallelPhase(phase, task.duration());
 }
 
 void
 GCRuntime::sweepDebuggerOnMainThread(FreeOp* fop)
 {
     // Detach unreachable debuggers and global objects from each other.
     // This can modify weakmaps and so must happen before weakmap sweeping.
     Debugger::sweepAll(fop);
@@ -5193,17 +5155,17 @@ class MOZ_RAII js::gc::AutoRunParallelTa
     using Func = void (*)(JSRuntime*);
 
     Func func_;
     gcstats::PhaseKind phase_;
     AutoLockHelperThreadState& lock_;
 
   public:
     AutoRunParallelTask(JSRuntime* rt, Func func, gcstats::PhaseKind phase,
-                       AutoLockHelperThreadState& lock)
+                        AutoLockHelperThreadState& lock)
       : GCParallelTask(rt),
         func_(func),
         phase_(phase),
         lock_(lock)
     {
         runtime()->gc.startTask(*this, phase_, lock_);
     }
 
@@ -5221,16 +5183,18 @@ GCRuntime::beginSweepingSweepGroup()
 {
     /*
      * Begin sweeping the group of zones in currentSweepGroup, performing
      * actions that must be done before yielding to caller.
      */
 
     using namespace gcstats;
 
+    AutoSCC scc(stats(), sweepGroupIndex);
+
     bool sweepingAtoms = false;
     for (GCSweepGroupIter zone(rt); !zone.done(); zone.next()) {
         /* Set the GC state to sweeping. */
         MOZ_ASSERT(zone->isGCMarking());
         zone->setGCState(Zone::Sweep);
 
         /* Purge the ArenaLists before sweeping. */
         zone->arenas.purge();
@@ -5269,17 +5233,16 @@ GCRuntime::beginSweepingSweepGroup()
     {
         AutoLockHelperThreadState lock;
 
         Maybe<AutoRunParallelTask> sweepAtoms;
         if (sweepingAtoms)
             sweepAtoms.emplace(rt, SweepAtoms, PhaseKind::SWEEP_ATOMS, lock);
 
         AutoPhase ap(stats(), PhaseKind::SWEEP_COMPARTMENTS);
-        AutoSCC scc(stats(), sweepGroupIndex);
 
         AutoRunParallelTask sweepCCWrappers(rt, SweepCCWrappers, PhaseKind::SWEEP_CC_WRAPPER, lock);
         AutoRunParallelTask sweepObjectGroups(rt, SweepObjectGroups, PhaseKind::SWEEP_TYPE_OBJECT, lock);
         AutoRunParallelTask sweepRegExps(rt, SweepRegExps, PhaseKind::SWEEP_REGEXP, lock);
         AutoRunParallelTask sweepMisc(rt, SweepMisc, PhaseKind::SWEEP_MISC, lock);
         AutoRunParallelTask sweepCompTasks(rt, SweepCompressionTasks, PhaseKind::SWEEP_COMPRESSION, lock);
         AutoRunParallelTask sweepWeakMaps(rt, SweepWeakMaps, PhaseKind::SWEEP_WEAKMAPS, lock);
         AutoRunParallelTask sweepUniqueIds(rt, SweepUniqueIds, PhaseKind::SWEEP_UNIQUEIDS, lock);
@@ -5296,17 +5259,16 @@ GCRuntime::beginSweepingSweepGroup()
         for (auto& task : sweepCacheTasks)
             joinTask(task, PhaseKind::SWEEP_WEAK_CACHES, lock);
     }
 
     // Queue all GC things in all zones for sweeping, either on the foreground
     // or on the background thread.
 
     for (GCSweepGroupIter zone(rt); !zone.done(); zone.next()) {
-        AutoSCC scc(stats(), sweepGroupIndex);
 
         zone->arenas.queueForForegroundSweep(&fop, ForegroundObjectFinalizePhase);
         for (unsigned i = 0; i < ArrayLength(IncrementalFinalizePhases); ++i)
             zone->arenas.queueForForegroundSweep(&fop, IncrementalFinalizePhases[i]);
 
         for (unsigned i = 0; i < ArrayLength(BackgroundFinalizePhases); ++i)
             zone->arenas.queueForBackgroundSweep(&fop, BackgroundFinalizePhases[i]);
 
--- a/js/src/proxy/CrossCompartmentWrapper.cpp
+++ b/js/src/proxy/CrossCompartmentWrapper.cpp
@@ -487,28 +487,38 @@ const CrossCompartmentWrapper CrossCompa
 
 bool
 js::IsCrossCompartmentWrapper(JSObject* obj)
 {
     return IsWrapper(obj) &&
            !!(Wrapper::wrapperHandler(obj)->flags() & Wrapper::CROSS_COMPARTMENT);
 }
 
-JS_FRIEND_API(void)
-js::NukeCrossCompartmentWrapper(JSContext* cx, JSObject* wrapper)
+static void
+NukeRemovedCrossCompartmentWrapper(JSContext* cx, JSObject* wrapper)
 {
     MOZ_ASSERT(wrapper->is<CrossCompartmentWrapperObject>());
 
     NotifyGCNukeWrapper(wrapper);
 
     wrapper->as<ProxyObject>().nuke();
 
     MOZ_ASSERT(IsDeadProxyObject(wrapper));
 }
 
+JS_FRIEND_API(void)
+js::NukeCrossCompartmentWrapper(JSContext* cx, JSObject* wrapper)
+{
+    JSCompartment* comp = wrapper->compartment();
+    auto ptr = comp->lookupWrapper(Wrapper::wrappedObject(wrapper));
+    if (ptr)
+        comp->removeWrapper(ptr);
+    NukeRemovedCrossCompartmentWrapper(cx, wrapper);
+}
+
 /*
  * NukeChromeCrossCompartmentWrappersForGlobal reaches into chrome and cuts
  * all of the cross-compartment wrappers that point to objects parented to
  * obj's global.  The snag here is that we need to avoid cutting wrappers that
  * point to the window object on page navigation (inner window destruction)
  * and only do that on tab close (outer window destruction).  Thus the
  * option of how to handle the global object.
  */
@@ -556,17 +566,17 @@ js::NukeCrossCompartmentWrappers(JSConte
                 MOZ_LIKELY(!nukeAll) && IsWindowProxy(wrapped))
             {
                 continue;
             }
 
             if (MOZ_UNLIKELY(nukeAll) || targetFilter.match(wrapped->compartment())) {
                 // We found a wrapper to nuke.
                 e.removeFront();
-                NukeCrossCompartmentWrapper(cx, wobj);
+                NukeRemovedCrossCompartmentWrapper(cx, wobj);
             }
         }
     }
 
     return true;
 }
 
 // Given a cross-compartment wrapper |wobj|, update it to point to
--- a/js/src/tests/shell/gcstats.js
+++ b/js/src/tests/shell/gcstats.js
@@ -26,17 +26,17 @@ setGCCallback({
   phases: "both"
 });
 
 gc();
 garbage();
 
 setGCCallback({
   action: "majorGC",
-  depth: 10,
+  depth: 8,
   phases: "begin"
 });
 
 gc();
 garbage();
 
 setGCCallback({
   action: "minorGC",
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -2712,16 +2712,19 @@ AccumulateTelemetryCallback(int id, uint
         Telemetry::Accumulate(Telemetry::GC_MARK_GRAY_MS, sample);
         break;
       case JS_TELEMETRY_GC_SLICE_MS:
         Telemetry::Accumulate(Telemetry::GC_SLICE_MS, sample);
         break;
       case JS_TELEMETRY_GC_SLOW_PHASE:
         Telemetry::Accumulate(Telemetry::GC_SLOW_PHASE, sample);
         break;
+      case JS_TELEMETRY_GC_SLOW_TASK:
+        Telemetry::Accumulate(Telemetry::GC_SLOW_TASK, sample);
+        break;
       case JS_TELEMETRY_GC_MMU_50:
         Telemetry::Accumulate(Telemetry::GC_MMU_50, sample);
         break;
       case JS_TELEMETRY_GC_RESET:
         Telemetry::Accumulate(Telemetry::GC_RESET, sample);
         break;
       case JS_TELEMETRY_GC_RESET_REASON:
         Telemetry::Accumulate(Telemetry::GC_RESET_REASON, sample);
--- a/modules/libjar/nsJARChannel.cpp
+++ b/modules/libjar/nsJARChannel.cpp
@@ -915,18 +915,18 @@ nsJARChannel::OnDownloadComplete(MemoryD
 {
     nsresult rv;
 
     nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
     if (channel) {
         uint32_t loadFlags;
         channel->GetLoadFlags(&loadFlags);
         if (loadFlags & LOAD_REPLACE) {
-            // Update our URI to reflect any redirects that happen during
-            // the HTTP request.
+            mLoadFlags |= LOAD_REPLACE;
+
             if (!mOriginalURI) {
                 SetOriginalURI(mJarURI);
             }
 
             nsCOMPtr<nsIURI> innerURI;
             rv = channel->GetURI(getter_AddRefs(innerURI));
             if (NS_SUCCEEDED(rv)) {
                 nsCOMPtr<nsIJARURI> newURI;
@@ -938,19 +938,16 @@ nsJARChannel::OnDownloadComplete(MemoryD
             }
             if (NS_SUCCEEDED(status)) {
                 status = rv;
             }
         }
     }
 
     if (NS_SUCCEEDED(status) && channel) {
-        // In case the load info object has changed during a redirect,
-        // grab it from the target channel.
-        channel->GetLoadInfo(getter_AddRefs(mLoadInfo));
         // Grab the security info from our base channel
         channel->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
 
         nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
         if (httpChannel) {
             // We only want to run scripts if the server really intended to
             // send us a JAR file.  Check the server-supplied content type for
             // a JAR type.
--- a/netwerk/base/LoadInfo.cpp
+++ b/netwerk/base/LoadInfo.cpp
@@ -264,17 +264,16 @@ LoadInfo::LoadInfo(nsPIDOMWindowOuter* a
 #endif
 }
 
 LoadInfo::LoadInfo(const LoadInfo& rhs)
   : mLoadingPrincipal(rhs.mLoadingPrincipal)
   , mTriggeringPrincipal(rhs.mTriggeringPrincipal)
   , mPrincipalToInherit(rhs.mPrincipalToInherit)
   , mSandboxedLoadingPrincipal(rhs.mSandboxedLoadingPrincipal)
-  , mResultPrincipalURI(rhs.mResultPrincipalURI)
   , mLoadingContext(rhs.mLoadingContext)
   , mSecurityFlags(rhs.mSecurityFlags)
   , mInternalContentPolicyType(rhs.mInternalContentPolicyType)
   , mTainting(rhs.mTainting)
   , mUpgradeInsecureRequests(rhs.mUpgradeInsecureRequests)
   , mVerifySignedContent(rhs.mVerifySignedContent)
   , mEnforceSRI(rhs.mEnforceSRI)
   , mForceInheritPrincipalDropped(rhs.mForceInheritPrincipalDropped)
@@ -296,17 +295,16 @@ LoadInfo::LoadInfo(const LoadInfo& rhs)
   , mMixedContentWouldBlock(rhs.mMixedContentWouldBlock)
 {
 }
 
 LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
                    nsIPrincipal* aTriggeringPrincipal,
                    nsIPrincipal* aPrincipalToInherit,
                    nsIPrincipal* aSandboxedLoadingPrincipal,
-                   nsIURI* aResultPrincipalURI,
                    nsSecurityFlags aSecurityFlags,
                    nsContentPolicyType aContentPolicyType,
                    LoadTainting aTainting,
                    bool aUpgradeInsecureRequests,
                    bool aVerifySignedContent,
                    bool aEnforceSRI,
                    bool aForceInheritPrincipalDropped,
                    uint64_t aInnerWindowID,
@@ -322,17 +320,16 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadin
                    const nsTArray<nsCString>& aCorsUnsafeHeaders,
                    bool aForcePreflight,
                    bool aIsPreflight,
                    bool aForceHSTSPriming,
                    bool aMixedContentWouldBlock)
   : mLoadingPrincipal(aLoadingPrincipal)
   , mTriggeringPrincipal(aTriggeringPrincipal)
   , mPrincipalToInherit(aPrincipalToInherit)
-  , mResultPrincipalURI(aResultPrincipalURI)
   , mSecurityFlags(aSecurityFlags)
   , mInternalContentPolicyType(aContentPolicyType)
   , mTainting(aTainting)
   , mUpgradeInsecureRequests(aUpgradeInsecureRequests)
   , mVerifySignedContent(aVerifySignedContent)
   , mEnforceSRI(aEnforceSRI)
   , mForceInheritPrincipalDropped(aForceInheritPrincipalDropped)
   , mInnerWindowID(aInnerWindowID)
@@ -934,24 +931,10 @@ LoadInfo::MaybeIncreaseTainting(uint32_t
 NS_IMETHODIMP
 LoadInfo::GetIsTopLevelLoad(bool *aResult)
 {
   *aResult = mFrameOuterWindowID ? mFrameOuterWindowID == mOuterWindowID
                                  : mParentOuterWindowID == mOuterWindowID;
   return NS_OK;
 }
 
-NS_IMETHODIMP
-LoadInfo::GetResultPrincipalURI(nsIURI **aURI)
-{
-  NS_IF_ADDREF(*aURI = mResultPrincipalURI);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-LoadInfo::SetResultPrincipalURI(nsIURI *aURI)
-{
-  mResultPrincipalURI = aURI;
-  return NS_OK;
-}
-
 } // namespace net
 } // namespace mozilla
--- a/netwerk/base/LoadInfo.h
+++ b/netwerk/base/LoadInfo.h
@@ -79,17 +79,16 @@ private:
   // private constructor that is only allowed to be called from within
   // HttpChannelParent and FTPChannelParent declared as friends undeneath.
   // In e10s we can not serialize nsINode, hence we store the innerWindowID.
   // Please note that aRedirectChain uses swapElements.
   LoadInfo(nsIPrincipal* aLoadingPrincipal,
            nsIPrincipal* aTriggeringPrincipal,
            nsIPrincipal* aPrincipalToInherit,
            nsIPrincipal* aSandboxedLoadingPrincipal,
-           nsIURI* aResultPrincipalURI,
            nsSecurityFlags aSecurityFlags,
            nsContentPolicyType aContentPolicyType,
            LoadTainting aTainting,
            bool aUpgradeInsecureRequests,
            bool aVerifySignedContent,
            bool aEnforceSRI,
            bool aForceInheritPrincipalDropped,
            uint64_t aInnerWindowID,
@@ -124,17 +123,16 @@ private:
   void SetIncludeCookiesSecFlag();
   friend class mozilla::dom::XMLHttpRequestMainThread;
 
   // if you add a member, please also update the copy constructor
   nsCOMPtr<nsIPrincipal>           mLoadingPrincipal;
   nsCOMPtr<nsIPrincipal>           mTriggeringPrincipal;
   nsCOMPtr<nsIPrincipal>           mPrincipalToInherit;
   nsCOMPtr<nsIPrincipal>           mSandboxedLoadingPrincipal;
-  nsCOMPtr<nsIURI>                 mResultPrincipalURI;
   nsWeakPtr                        mLoadingContext;
   nsSecurityFlags                  mSecurityFlags;
   nsContentPolicyType              mInternalContentPolicyType;
   LoadTainting                     mTainting;
   bool                             mUpgradeInsecureRequests;
   bool                             mVerifySignedContent;
   bool                             mEnforceSRI;
   bool                             mForceInheritPrincipalDropped;
--- a/netwerk/base/nsILoadInfo.idl
+++ b/netwerk/base/nsILoadInfo.idl
@@ -5,17 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 #include "nsIContentPolicy.idl"
 
 interface nsIDOMDocument;
 interface nsINode;
 interface nsIPrincipal;
-interface nsIURI;
 
 %{C++
 #include "nsTArray.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/LoadTainting.h"
 
 class nsCString;
 %}
@@ -737,23 +736,15 @@ interface nsILoadInfo : nsISupports
 
   /**
    * Returns true if this load is for top level document.
    * Note that the load for a sub-frame's document will return false here.
    */
   [infallible] readonly attribute boolean isTopLevelLoad;
 
   /**
-   * If this is non-null, this property represents two things: (1) the
-   * URI to be used for the principal if the channel with this loadinfo
-   * gets a principal based on URI and (2) the URI to use for a document
-   * created from the channel with this loadinfo.
-   */
-  attribute nsIURI resultPrincipalURI;
-
-  /**
    * Returns the null principal of the resulting resource if the SEC_SANDBOXED
    * flag is set.  Otherwise returns null.  This is used by
    * GetChannelResultPrincipal() to ensure that the same null principal object
    * is returned every time.
    */
   [noscript] readonly attribute nsIPrincipal sandboxedLoadingPrincipal;
 };
--- a/netwerk/base/nsNetUtil.cpp
+++ b/netwerk/base/nsNetUtil.cpp
@@ -183,27 +183,21 @@ NS_NewChannelInternal(nsIChannel        
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if (aCallbacks) {
     rv = channel->SetNotificationCallbacks(aCallbacks);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-#ifdef DEBUG
-  nsLoadFlags channelLoadFlags = 0;
-  channel->GetLoadFlags(&channelLoadFlags);
-  // Will be removed when we remove LOAD_REPLACE altogether
-  // This check is trying to catch protocol handlers that still
-  // try to set the LOAD_REPLACE flag.
-  MOZ_DIAGNOSTIC_ASSERT(!(channelLoadFlags & nsIChannel::LOAD_REPLACE));
-#endif
-
   if (aLoadFlags != nsIRequest::LOAD_NORMAL) {
-    rv = channel->SetLoadFlags(aLoadFlags);
+    // Retain the LOAD_REPLACE load flag if set.
+    nsLoadFlags normalLoadFlags = 0;
+    channel->GetLoadFlags(&normalLoadFlags);
+    rv = channel->SetLoadFlags(aLoadFlags | (normalLoadFlags & nsIChannel::LOAD_REPLACE));
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   channel.forget(outChannel);
   return NS_OK;
 }
 
 nsresult
@@ -268,27 +262,21 @@ NS_NewChannelInternal(nsIChannel        
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if (aCallbacks) {
     rv = channel->SetNotificationCallbacks(aCallbacks);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-#ifdef DEBUG
-  nsLoadFlags channelLoadFlags = 0;
-  channel->GetLoadFlags(&channelLoadFlags);
-  // Will be removed when we remove LOAD_REPLACE altogether
-  // This check is trying to catch protocol handlers that still
-  // try to set the LOAD_REPLACE flag.
-  MOZ_DIAGNOSTIC_ASSERT(!(channelLoadFlags & nsIChannel::LOAD_REPLACE));
-#endif
-
   if (aLoadFlags != nsIRequest::LOAD_NORMAL) {
-    rv = channel->SetLoadFlags(aLoadFlags);
+    // Retain the LOAD_REPLACE load flag if set.
+    nsLoadFlags normalLoadFlags = 0;
+    channel->GetLoadFlags(&normalLoadFlags);
+    rv = channel->SetLoadFlags(aLoadFlags | (normalLoadFlags & nsIChannel::LOAD_REPLACE));
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   channel.forget(outChannel);
   return NS_OK;
 }
 
 nsresult /*NS_NewChannelWithNodeAndTriggeringPrincipal */
@@ -1892,25 +1880,22 @@ NS_GetInnermostURI(nsIURI *aURI)
 
     return uri.forget();
 }
 
 nsresult
 NS_GetFinalChannelURI(nsIChannel *channel, nsIURI **uri)
 {
     *uri = nullptr;
+    nsLoadFlags loadFlags = 0;
+    nsresult rv = channel->GetLoadFlags(&loadFlags);
+    NS_ENSURE_SUCCESS(rv, rv);
 
-    nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
-    if (loadInfo) {
-        nsCOMPtr<nsIURI> resultPrincipalURI;
-        loadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
-        if (resultPrincipalURI) {
-            resultPrincipalURI.forget(uri);
-            return NS_OK;
-        }
+    if (loadFlags & nsIChannel::LOAD_REPLACE) {
+        return channel->GetURI(uri);
     }
 
     return channel->GetOriginalURI(uri);
 }
 
 nsresult
 NS_URIChainHasFlags(nsIURI   *uri,
                     uint32_t  flags,
--- a/netwerk/base/nsNetUtil.h
+++ b/netwerk/base/nsNetUtil.h
@@ -776,20 +776,21 @@ nsresult NS_URIChainHasFlags(nsIURI   *u
 
 /**
  * Helper function for getting the innermost URI for a given URI.  The return
  * value could be just the object passed in if it's not a nested URI.
  */
 already_AddRefed<nsIURI> NS_GetInnermostURI(nsIURI *aURI);
 
 /**
- * Get the "final" URI for a channel.  This is either channel's load info
- * resultPrincipalURI, if set, or GetOriginalURI.  In most cases (but not all) load
- * info resultPrincipalURI, if set, corresponds to URI of the channel if it's required
- * to represent the actual principal for the channel.
+ * Get the "final" URI for a channel.  This is either the same as GetURI or
+ * GetOriginalURI, depending on whether this channel has
+ * nsIChanel::LOAD_REPLACE set.  For channels without that flag set, the final
+ * URI is the original URI, while for ones with the flag the final URI is the
+ * channel URI.
  */
 nsresult NS_GetFinalChannelURI(nsIChannel *channel, nsIURI **uri);
 
 // NS_SecurityHashURI must return the same hash value for any two URIs that
 // compare equal according to NS_SecurityCompareURIs.  Unfortunately, in the
 // case of files, it's not clear we can do anything better than returning
 // the schemeHash, so hashing files degenerates to storing them in a list.
 uint32_t NS_SecurityHashURI(nsIURI *aURI);
--- a/netwerk/ipc/NeckoChannelParams.ipdlh
+++ b/netwerk/ipc/NeckoChannelParams.ipdlh
@@ -29,17 +29,16 @@ namespace net {
 //-----------------------------------------------------------------------------
 
 struct LoadInfoArgs
 {
   OptionalPrincipalInfo requestingPrincipalInfo;
   PrincipalInfo         triggeringPrincipalInfo;
   OptionalPrincipalInfo principalToInheritInfo;
   OptionalPrincipalInfo sandboxedLoadingPrincipalInfo;
-  OptionalURIParams     resultPrincipalURI;
   uint32_t              securityFlags;
   uint32_t              contentPolicyType;
   uint32_t              tainting;
   bool                  upgradeInsecureRequests;
   bool                  verifySignedContent;
   bool                  enforceSRI;
   bool                  forceInheritPrincipalDropped;
   uint64_t              innerWindowID;
--- a/netwerk/protocol/file/nsFileChannel.cpp
+++ b/netwerk/protocol/file/nsFileChannel.cpp
@@ -246,74 +246,61 @@ nsFileUploadContentStream::OnCopyComplet
   nsresult status = mCopyEvent->Status();
 
   CloseWithStatus(NS_FAILED(status) ? status : NS_BASE_STREAM_CLOSED);
 }
 
 //-----------------------------------------------------------------------------
 
 nsFileChannel::nsFileChannel(nsIURI *uri) 
-  : mFileURI(uri)
 {
-}
-
-nsresult
-nsFileChannel::Init()
-{
-  NS_ENSURE_STATE(mLoadInfo);
-
-  nsresult rv;
-
-  rv = nsBaseChannel::Init();
-  NS_ENSURE_SUCCESS(rv, rv);
-
   // If we have a link file, we should resolve its target right away.
   // This is to protect against a same origin attack where the same link file
   // can point to different resources right after the first resource is loaded.
   nsCOMPtr<nsIFile> file;
   nsCOMPtr <nsIURI> targetURI;
 #ifdef XP_WIN
   nsAutoString fileTarget;
 #else
   nsAutoCString fileTarget;
 #endif
   nsCOMPtr<nsIFile> resolvedFile;
   bool symLink;
-  nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mFileURI);
-  if (fileURL &&
+  nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(uri);
+  if (fileURL && 
       NS_SUCCEEDED(fileURL->GetFile(getter_AddRefs(file))) &&
-      NS_SUCCEEDED(file->IsSymlink(&symLink)) &&
+      NS_SUCCEEDED(file->IsSymlink(&symLink)) && 
       symLink &&
 #ifdef XP_WIN
       NS_SUCCEEDED(file->GetTarget(fileTarget)) &&
-      NS_SUCCEEDED(NS_NewLocalFile(fileTarget, true,
+      NS_SUCCEEDED(NS_NewLocalFile(fileTarget, PR_TRUE, 
                                    getter_AddRefs(resolvedFile))) &&
 #else
       NS_SUCCEEDED(file->GetNativeTarget(fileTarget)) &&
-      NS_SUCCEEDED(NS_NewNativeLocalFile(fileTarget, true,
+      NS_SUCCEEDED(NS_NewNativeLocalFile(fileTarget, PR_TRUE, 
                                          getter_AddRefs(resolvedFile))) &&
 #endif
-      NS_SUCCEEDED(NS_NewFileURI(getter_AddRefs(targetURI),
-                                 resolvedFile, nullptr))) {
+      NS_SUCCEEDED(NS_NewFileURI(getter_AddRefs(targetURI), 
+                   resolvedFile, nullptr))) {
     // Make an effort to match up the query strings.
-    nsCOMPtr<nsIURL> origURL = do_QueryInterface(mFileURI);
+    nsCOMPtr<nsIURL> origURL = do_QueryInterface(uri);
     nsCOMPtr<nsIURL> targetURL = do_QueryInterface(targetURI);
     nsAutoCString queryString;
     if (origURL && targetURL && NS_SUCCEEDED(origURL->GetQuery(queryString))) {
       targetURL->SetQuery(queryString);
     }
 
     SetURI(targetURI);
-    SetOriginalURI(mFileURI);
-    mLoadInfo->SetResultPrincipalURI(targetURI);
+    SetOriginalURI(uri);
+    nsLoadFlags loadFlags = 0;
+    GetLoadFlags(&loadFlags);
+    SetLoadFlags(loadFlags | nsIChannel::LOAD_REPLACE);
   } else {
-    SetURI(mFileURI);
+    SetURI(uri);
   }
-
-  return NS_OK;
 }
 
 nsFileChannel::~nsFileChannel()
 {
 }
 
 nsresult
 nsFileChannel::MakeFileInputStream(nsIFile *file,
--- a/netwerk/protocol/file/nsFileChannel.h
+++ b/netwerk/protocol/file/nsFileChannel.h
@@ -17,18 +17,16 @@ class nsFileChannel : public nsBaseChann
 {
 public: 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIFILECHANNEL
   NS_DECL_NSIUPLOADCHANNEL
 
   explicit nsFileChannel(nsIURI *uri);
 
-  nsresult Init();
-
 protected:
   ~nsFileChannel();
 
   // Called to construct a blocking file input stream for the given file.  This
   // method also returns a best guess at the content-type for the data stream.
   // NOTE: If the channel has a type hint set, contentType will be left
   // untouched. The caller should not use it in that case.
   MOZ_MUST_USE nsresult MakeFileInputStream(nsIFile *file,
@@ -37,12 +35,11 @@ protected:
 
   virtual MOZ_MUST_USE nsresult OpenContentStream(bool async,
                                                   nsIInputStream **result,
                                                   nsIChannel** channel) override;
 
 private:
   nsCOMPtr<nsIInputStream> mUploadStream;
   int64_t mUploadLength;
-  nsCOMPtr<nsIURI> mFileURI;
 };
 
 #endif // !nsFileChannel_h__
--- a/netwerk/protocol/file/nsFileProtocolHandler.cpp
+++ b/netwerk/protocol/file/nsFileProtocolHandler.cpp
@@ -185,38 +185,34 @@ nsFileProtocolHandler::NewURI(const nsAC
     return CallQueryInterface(url, result);
 }
 
 NS_IMETHODIMP
 nsFileProtocolHandler::NewChannel2(nsIURI* uri,
                                    nsILoadInfo* aLoadInfo,
                                    nsIChannel** result)
 {
-    nsresult rv;
-
     nsFileChannel *chan;
     if (IsNeckoChild()) {
         chan = new mozilla::net::FileChannelChild(uri);
     } else {
         chan = new nsFileChannel(uri);
     }
     if (!chan)
         return NS_ERROR_OUT_OF_MEMORY;
     NS_ADDREF(chan);
 
-    // set the loadInfo on the new channel ; must do this
-    // before calling Init() on it, since it needs the load
-    // info be already set.
-    rv = chan->SetLoadInfo(aLoadInfo);
+    nsresult rv = chan->Init();
     if (NS_FAILED(rv)) {
         NS_RELEASE(chan);
         return rv;
     }
 
-    rv = chan->Init();
+    // set the loadInfo on the new channel
+    rv = chan->SetLoadInfo(aLoadInfo);
     if (NS_FAILED(rv)) {
         NS_RELEASE(chan);
         return rv;
     }
 
     *result = chan;
     return NS_OK;
 }
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -2967,82 +2967,16 @@ void HttpBaseChannel::AssertPrivateBrows
 
   OriginAttributes docShellAttrs;
   loadContext->GetOriginAttributes(docShellAttrs);
   MOZ_ASSERT(mLoadInfo->GetOriginAttributes().mPrivateBrowsingId == docShellAttrs.mPrivateBrowsingId,
              "PrivateBrowsingId values are not the same between LoadInfo and LoadContext.");
 }
 #endif
 
-already_AddRefed<nsILoadInfo>
-HttpBaseChannel::CloneLoadInfoForRedirect(nsIURI * newURI, uint32_t redirectFlags)
-{
-  // make a copy of the loadinfo, append to the redirectchain
-  // this will be set on the newly created channel for the redirect target.
-  if (!mLoadInfo) {
-    return nullptr;
-  }
-
-  nsCOMPtr<nsILoadInfo> newLoadInfo =
-    static_cast<mozilla::LoadInfo*>(mLoadInfo.get())->Clone();
-
-  nsContentPolicyType contentPolicyType = mLoadInfo->GetExternalContentPolicyType();
-  if (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT ||
-      contentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT) {
-    nsCOMPtr<nsIPrincipal> nullPrincipalToInherit = NullPrincipal::Create();
-    newLoadInfo->SetPrincipalToInherit(nullPrincipalToInherit);
-  }
-
-  // re-compute the origin attributes of the loadInfo if it's top-level load.
-  bool isTopLevelDoc =
-    newLoadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_DOCUMENT;
-
-  if (isTopLevelDoc) {
-    nsCOMPtr<nsILoadContext> loadContext;
-    NS_QueryNotificationCallbacks(this, loadContext);
-    OriginAttributes docShellAttrs;
-    if (loadContext) {
-      loadContext->GetOriginAttributes(docShellAttrs);
-    }
-
-    OriginAttributes attrs = newLoadInfo->GetOriginAttributes();
-
-    MOZ_ASSERT(docShellAttrs.mUserContextId == attrs.mUserContextId,
-                "docshell and necko should have the same userContextId attribute.");
-    MOZ_ASSERT(docShellAttrs.mInIsolatedMozBrowser == attrs.mInIsolatedMozBrowser,
-                "docshell and necko should have the same inIsolatedMozBrowser attribute.");
-    MOZ_ASSERT(docShellAttrs.mPrivateBrowsingId == attrs.mPrivateBrowsingId,
-                "docshell and necko should have the same privateBrowsingId attribute.");
-
-    attrs = docShellAttrs;
-    attrs.SetFirstPartyDomain(true, newURI);
-    newLoadInfo->SetOriginAttributes(attrs);
-  }
-
-  // This makes the original URI (the redirect URL) of the target channel become
-  // the resulting principal URI.
-  // After the veto and security checking of this redirect is done, the original
-  // URI of the target channel will be rewriten with the first URI in the redirect
-  // chain (the source URI of this load).  Hance the original URI itself can't be
-  // used for determining the result principal URI.
-  // If the target schema protocol handler wants to express a different URI as
-  // the result principal URI, it's free to rewrite this property.
-  // By not modifying the result principal URI the target protocol handler
-  // expresses that it's OK to use the channel's pre-redirect-veto original URI
-  // (the redirect URL) as the result principal URI.
-  newLoadInfo->SetResultPrincipalURI(newURI);
-
-  bool isInternalRedirect =
-    (redirectFlags & (nsIChannelEventSink::REDIRECT_INTERNAL |
-                      nsIChannelEventSink::REDIRECT_STS_UPGRADE));
-  newLoadInfo->AppendRedirectedPrincipal(GetURIPrincipal(), isInternalRedirect);
-
-  return newLoadInfo.forget();
-}
-
 //-----------------------------------------------------------------------------
 // nsHttpChannel::nsITraceableChannel
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 HttpBaseChannel::SetNewListener(nsIStreamListener *aListener, nsIStreamListener **_retval)
 {
   LOG(("HttpBaseChannel::SetNewListener [this=%p, mListener=%p, newListener=%p]",
@@ -3214,16 +3148,67 @@ HttpBaseChannel::SetupReplacementChannel
   if (mPrivateBrowsingOverriden) {
     nsCOMPtr<nsIPrivateBrowsingChannel> newPBChannel =
       do_QueryInterface(newChannel);
     if (newPBChannel) {
       newPBChannel->SetPrivate(mPrivateBrowsing);
     }
   }
 
+  // make a copy of the loadinfo, append to the redirectchain
+  // and set it on the new channel
+  if (mLoadInfo) {
+    nsCOMPtr<nsILoadInfo> newLoadInfo =
+      static_cast<mozilla::LoadInfo*>(mLoadInfo.get())->Clone();
+
+    nsContentPolicyType contentPolicyType = mLoadInfo->GetExternalContentPolicyType();
+    if (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT ||
+        contentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT) {
+      nsCOMPtr<nsIPrincipal> nullPrincipalToInherit = NullPrincipal::Create();
+      newLoadInfo->SetPrincipalToInherit(nullPrincipalToInherit);
+    }
+
+    // re-compute the origin attributes of the loadInfo if it's top-level load.
+    bool isTopLevelDoc =
+      newLoadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_DOCUMENT;
+
+    if (isTopLevelDoc) {
+      nsCOMPtr<nsILoadContext> loadContext;
+      NS_QueryNotificationCallbacks(this, loadContext);
+      OriginAttributes docShellAttrs;
+      if (loadContext) {
+        loadContext->GetOriginAttributes(docShellAttrs);
+      }
+
+      OriginAttributes attrs = newLoadInfo->GetOriginAttributes();
+
+      MOZ_ASSERT(docShellAttrs.mUserContextId == attrs.mUserContextId,
+                "docshell and necko should have the same userContextId attribute.");
+      MOZ_ASSERT(docShellAttrs.mInIsolatedMozBrowser == attrs.mInIsolatedMozBrowser,
+                "docshell and necko should have the same inIsolatedMozBrowser attribute.");
+      MOZ_ASSERT(docShellAttrs.mPrivateBrowsingId == attrs.mPrivateBrowsingId,
+                 "docshell and necko should have the same privateBrowsingId attribute.");
+
+      attrs = docShellAttrs;
+      attrs.SetFirstPartyDomain(true, newURI);
+      newLoadInfo->SetOriginAttributes(attrs);
+    }
+
+    bool isInternalRedirect =
+      (redirectFlags & (nsIChannelEventSink::REDIRECT_INTERNAL |
+                        nsIChannelEventSink::REDIRECT_STS_UPGRADE));
+    newLoadInfo->AppendRedirectedPrincipal(GetURIPrincipal(), isInternalRedirect);
+    newChannel->SetLoadInfo(newLoadInfo);
+  }
+  else {
+    // the newChannel was created with a dummy loadInfo, we should clear
+    // it in case the original channel does not have a loadInfo
+    newChannel->SetLoadInfo(nullptr);
+  }
+
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(newChannel);
   if (!httpChannel)
     return NS_OK; // no other options to set
 
   // Preserve the CORS preflight information.
   nsCOMPtr<nsIHttpChannelInternal> httpInternal = do_QueryInterface(newChannel);
   if (mRequireCORSPreflight && httpInternal) {
     httpInternal->SetCorsPreflightParameters(mUnsafeHeaders);
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -419,18 +419,16 @@ protected:
   // the new mUploadStream.
   void EnsureUploadStreamIsCloneableComplete(nsresult aStatus);
 
 #ifdef DEBUG
   // Check if mPrivateBrowsingId matches between LoadInfo and LoadContext.
   void AssertPrivateBrowsingId();
 #endif
 
-  already_AddRefed<nsILoadInfo> CloneLoadInfoForRedirect(nsIURI *newURI, uint32_t redirectFlags);
-
   friend class PrivateBrowsingChannel<HttpBaseChannel>;
   friend class InterceptFailedOnStop;
 
 protected:
   // this section is for main-thread-only object
   // all the references need to be proxy released on main thread.
   nsCOMPtr<nsIURI> mURI;
   nsCOMPtr<nsIURI> mOriginalURI;
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -1488,20 +1488,19 @@ HttpChannelChild::SetupRedirect(nsIURI* 
   LOG(("HttpChannelChild::SetupRedirect [this=%p]\n", this));
 
   nsresult rv;
   nsCOMPtr<nsIIOService> ioService;
   rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIChannel> newChannel;
-  nsCOMPtr<nsILoadInfo> redirectLoadInfo = CloneLoadInfoForRedirect(uri, redirectFlags);
   rv = NS_NewChannelInternal(getter_AddRefs(newChannel),
                              uri,
-                             redirectLoadInfo,
+                             mLoadInfo,
                              nullptr, // aLoadGroup
                              nullptr, // aCallbacks
                              nsIRequest::LOAD_NORMAL,
                              ioService);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // We won't get OnStartRequest, set cookies here.
   mResponseHead = new nsHttpResponseHead(*responseHead);
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -2750,26 +2750,24 @@ nsHttpChannel::HandleAsyncAPIRedirect()
 
 nsresult
 nsHttpChannel::StartRedirectChannelToURI(nsIURI *upgradedURI, uint32_t flags)
 {
     nsresult rv = NS_OK;
     LOG(("nsHttpChannel::StartRedirectChannelToURI()\n"));
 
     nsCOMPtr<nsIChannel> newChannel;
-    nsCOMPtr<nsILoadInfo> redirectLoadInfo = CloneLoadInfoForRedirect(upgradedURI,
-                                                                      flags);
 
     nsCOMPtr<nsIIOService> ioService;
     rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = NS_NewChannelInternal(getter_AddRefs(newChannel),
                                upgradedURI,
-                               redirectLoadInfo,
+                               mLoadInfo,
                                nullptr, // aLoadGroup
                                nullptr, // aCallbacks
                                nsIRequest::LOAD_NORMAL,
                                ioService);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = SetupReplacementChannel(upgradedURI, newChannel, true, flags);
     NS_ENSURE_SUCCESS(rv, rv);
@@ -5617,33 +5615,32 @@ nsHttpChannel::ContinueProcessRedirectio
         rv = PromptTempRedirect();
         if (NS_FAILED(rv)) return rv;
     }
 
     nsCOMPtr<nsIIOService> ioService;
     rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
     if (NS_FAILED(rv)) return rv;
 
+    nsCOMPtr<nsIChannel> newChannel;
+    rv = NS_NewChannelInternal(getter_AddRefs(newChannel),
+                               mRedirectURI,
+                               mLoadInfo,
+                               nullptr, // aLoadGroup
+                               nullptr, // aCallbacks
+                               nsIRequest::LOAD_NORMAL,
+                               ioService);
+    NS_ENSURE_SUCCESS(rv, rv);
+
     uint32_t redirectFlags;
     if (nsHttp::IsPermanentRedirect(mRedirectType))
         redirectFlags = nsIChannelEventSink::REDIRECT_PERMANENT;
     else
         redirectFlags = nsIChannelEventSink::REDIRECT_TEMPORARY;
 
-    nsCOMPtr<nsIChannel> newChannel;
-    nsCOMPtr<nsILoadInfo> redirectLoadInfo = CloneLoadInfoForRedirect(mRedirectURI, redirectFlags);
-    rv = NS_NewChannelInternal(getter_AddRefs(newChannel),
-                               mRedirectURI,
-                               redirectLoadInfo,
-                               nullptr, // aLoadGroup
-                               nullptr, // aCallbacks
-                               nsIRequest::LOAD_NORMAL,
-                               ioService);
-    NS_ENSURE_SUCCESS(rv, rv);
-
     rv = SetupReplacementChannel(mRedirectURI, newChannel,
                                  !rewriteToGET, redirectFlags);
     if (NS_FAILED(rv)) return rv;
 
     // verify that this is a legal redirect
     mRedirectChannel = newChannel;
 
     PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirection);
--- a/netwerk/protocol/res/ExtensionProtocolHandler.h
+++ b/netwerk/protocol/res/ExtensionProtocolHandler.h
@@ -28,20 +28,16 @@ public:
 protected:
   ~ExtensionProtocolHandler() {}
 
   MOZ_MUST_USE bool ResolveSpecialCases(const nsACString& aHost,
                                         const nsACString& aPath,
                                         const nsACString& aPathname,
                                         nsACString& aResult) override;
 
-  // |result| is an inout param.  On entry to this function, *result
-  // is expected to be non-null and already addrefed.  This function
-  // may release the object stored in *result on entry and write
-  // a new pointer to an already addrefed channel to *result.
   virtual MOZ_MUST_USE nsresult SubstituteChannel(nsIURI* uri,
                                                   nsILoadInfo* aLoadInfo,
                                                   nsIChannel** result) override;
 };
 
 } // namespace net
 } // namespace mozilla
 
--- a/netwerk/protocol/res/SubstitutingProtocolHandler.cpp
+++ b/netwerk/protocol/res/SubstitutingProtocolHandler.cpp
@@ -241,38 +241,30 @@ SubstitutingProtocolHandler::NewURI(cons
 }
 
 nsresult
 SubstitutingProtocolHandler::NewChannel2(nsIURI* uri,
                                          nsILoadInfo* aLoadInfo,
                                          nsIChannel** result)
 {
   NS_ENSURE_ARG_POINTER(uri);
-  NS_ENSURE_ARG_POINTER(aLoadInfo);
-
   nsAutoCString spec;
   nsresult rv = ResolveURI(uri, spec);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIURI> newURI;
   rv = NS_NewURI(getter_AddRefs(newURI), spec);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // We don't want to allow the inner protocol handler to modify the result
-  // principal URI since we want either |uri| or anything pre-set by upper
-  // layers to prevail.
-  nsCOMPtr<nsIURI> savedResultPrincipalURI;
-  rv = aLoadInfo->GetResultPrincipalURI(getter_AddRefs(savedResultPrincipalURI));
-  NS_ENSURE_SUCCESS(rv, rv);
-
   rv = NS_NewChannelInternal(result, newURI, aLoadInfo);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = aLoadInfo->SetResultPrincipalURI(savedResultPrincipalURI);
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsLoadFlags loadFlags = 0;
+  (*result)->GetLoadFlags(&loadFlags);
+  (*result)->SetLoadFlags(loadFlags & ~nsIChannel::LOAD_REPLACE);
   rv = (*result)->SetOriginalURI(uri);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return SubstituteChannel(uri, aLoadInfo, result);
 }
 
 nsresult
 SubstitutingProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result)
--- a/netwerk/streamconv/converters/nsMultiMixedConv.cpp
+++ b/netwerk/streamconv/converters/nsMultiMixedConv.cpp
@@ -872,20 +872,16 @@ nsMultiMixedConv::SendStart()
     rv = mPartChannel->SetContentType(mContentType);
     if (NS_FAILED(rv)) return rv;
 
     rv = mPartChannel->SetContentLength(mContentLength);
     if (NS_FAILED(rv)) return rv;
 
     mPartChannel->SetContentDisposition(mContentDisposition);
 
-    // Each part of a multipart/replace response can be used
-    // for the top level document.  We must inform upper layers
-    // about this by setting the LOAD_REPLACE flag so that certain
-    // state assertions are evaluated as positive.
     nsLoadFlags loadFlags = 0;
     mPartChannel->GetLoadFlags(&loadFlags);
     loadFlags |= nsIChannel::LOAD_REPLACE;
     mPartChannel->SetLoadFlags(loadFlags);
 
     nsCOMPtr<nsILoadGroup> loadGroup;
     (void)mPartChannel->GetLoadGroup(getter_AddRefs(loadGroup));
 
--- a/netwerk/test/unit/test_file_protocol.js
+++ b/netwerk/test/unit/test_file_protocol.js
@@ -227,16 +227,19 @@ function test_upload_file() {
 
 function test_load_replace() {
   // lnk files should resolve to their targets
   if (mozinfo.os == "win") {
     dump("*** test_load_replace\n");
     file = do_get_file("data/system_root.lnk", false);
     var chan = new_file_channel(file);
 
+    // The LOAD_REPLACE flag should be set
+    do_check_eq(chan.loadFlags & chan.LOAD_REPLACE, chan.LOAD_REPLACE);
+
     // The original URI path should differ from the URI path
     do_check_neq(chan.URI.path, chan.originalURI.path);
 
     // The original URI path should be the same as the lnk file path
     var ios = Cc["@mozilla.org/network/io-service;1"].
               getService(Ci.nsIIOService);
     do_check_eq(chan.originalURI.path, ios.newFileURI(file).path);
   }
--- a/testing/mochitest/runtestsremote.py
+++ b/testing/mochitest/runtestsremote.py
@@ -45,16 +45,17 @@ class MochiRemote(MochitestDesktop):
         self.remoteMozLog = os.path.join(options.remoteTestRoot, "mozlog")
         self._dm.removeDir(self.remoteMozLog)
         self._dm.mkDir(self.remoteMozLog)
         self.remoteChromeTestDir = os.path.join(
             options.remoteTestRoot,
             "chrome")
         self._dm.removeDir(self.remoteChromeTestDir)
         self._dm.mkDir(self.remoteChromeTestDir)
+        self._dm.removeDir(self.remoteProfile)
 
     def cleanup(self, options):
         if self._dm.fileExists(self.remoteLog):
             self._dm.getFile(self.remoteLog, self.localLog)
             self._dm.removeFile(self.remoteLog)
         else:
             self.log.warning(
                 "Unable to retrieve log file (%s) from remote device" %
@@ -324,16 +325,17 @@ def run_test_harness(parser, options):
     productPieces = options.remoteProductName.split('.')
     if (productPieces is not None):
         auto.setProduct(productPieces[0])
     else:
         auto.setProduct(options.remoteProductName)
     auto.setAppName(options.remoteappname)
 
     logParent = os.path.dirname(options.remoteLogFile)
+    dm.removeDir(logParent)
     dm.mkDir(logParent)
     auto.setRemoteLog(options.remoteLogFile)
     auto.setServerInfo(options.webServer, options.httpPort, options.sslPort)
 
     if options.log_mach is None:
         mochitest.printDeviceInfo()
 
     # Add Android version (SDK level) to mozinfo so that manifest entries
--- a/testing/mozharness/scripts/awsy_script.py
+++ b/testing/mozharness/scripts/awsy_script.py
@@ -142,16 +142,19 @@ class AWSY(TestingMixin, MercurialScript
         cmd.append("--log-raw=-")
         cmd.append("--log-errorsummary=%s" % error_summary_file)
         cmd.append("--binary=%s" % self.binary_path)
         cmd.append("--profile=%s" % (os.path.join(dirs['abs_work_dir'], 'profile')))
         if not self.config['e10s']:
             cmd.append('--disable-e10s')
         cmd.append('--gecko-log=%s' % os.path.join(dirs["abs_blob_upload_dir"],
                                                    'gecko.log'))
+        # TestingMixin._download_and_extract_symbols() should set
+        # self.symbols_path
+        cmd.append('--symbols-path=%s' % self.symbols_path)
 
         test_file = os.path.join(self.awsy_libdir, 'test_memory_usage.py')
         cmd.append(test_file)
 
         env['MOZ_UPLOAD_DIR'] = dirs['abs_blob_upload_dir']
         if not os.path.isdir(env['MOZ_UPLOAD_DIR']):
             self.mkdir_p(env['MOZ_UPLOAD_DIR'])
         if self.query_minidump_stackwalk():
--- a/testing/xpcshell/dbg-actors.js
+++ b/testing/xpcshell/dbg-actors.js
@@ -1,49 +1,49 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-'use strict';
+"use strict";
+
+/* import-globals-from ../../devtools/server/main.js */
 
 const { Promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
 var { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
 const { devtools } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 const { RootActor } = devtools.require("devtools/server/actors/root");
 const { BrowserTabList } = devtools.require("devtools/server/actors/webbrowser");
 
 /**
  * xpcshell-test (XPCST) specific actors.
  *
  */
 
 /**
  * Construct a root actor appropriate for use in a server running xpcshell
  * tests. <snip boilerplate> :)
  */
-function createRootActor(connection)
-{
+function createRootActor(connection) {
   let parameters = {
     tabList: new XPCSTTabList(connection),
     globalActorFactories: DebuggerServer.globalActorFactories,
     onShutdown() {
       // If the user never switches to the "debugger" tab we might get a
       // shutdown before we've attached.
       Services.obs.notifyObservers(null, "xpcshell-test-devtools-shutdown");
     }
   };
   return new RootActor(connection, parameters);
 }
 
 /**
  * A "stub" TabList implementation that provides no tabs.
  */
 
-function XPCSTTabList(connection)
-{
+function XPCSTTabList(connection) {
   BrowserTabList.call(this, connection);
 }
 
 XPCSTTabList.prototype = Object.create(BrowserTabList.prototype);
 
 XPCSTTabList.prototype.constructor = XPCSTTabList;
 
 XPCSTTabList.prototype.getList = function() {
new file mode 100644
--- /dev/null
+++ b/testing/xpcshell/example/unit/.eslintrc.js
@@ -0,0 +1,7 @@
+"use strict";
+
+module.exports = {
+  "extends": [
+    "plugin:mozilla/xpcshell-test"
+  ]
+};
--- a/testing/xpcshell/example/unit/check_profile.js
+++ b/testing/xpcshell/example/unit/check_profile.js
@@ -1,38 +1,36 @@
 /* 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/. */
 
 var {classes: Cc, interfaces: Ci} = Components;
 
-function check_profile_dir(profd)
-{
+function check_profile_dir(profd) {
   Assert.ok(profd.exists());
   Assert.ok(profd.isDirectory());
   let dirSvc = Cc["@mozilla.org/file/directory_service;1"]
                  .getService(Ci.nsIProperties);
   let profd2 = dirSvc.get("ProfD", Ci.nsILocalFile);
   Assert.ok(profd2.exists());
   Assert.ok(profd2.isDirectory());
   // make sure we got the same thing back...
   Assert.ok(profd.equals(profd2));
 }
 
-function check_do_get_profile(fireProfileAfterChange)
-{
+function check_do_get_profile(fireProfileAfterChange) {
   const observedTopics = new Map([
     ["profile-do-change", 0],
     ["profile-after-change", 0],
   ]);
   const expectedTopics = new Map(observedTopics);
 
   const obs = Cc["@mozilla.org/observer-service;1"]
                    .getService(Ci.nsIObserverService);
-  for (let [topic,] of observedTopics) {
+  for (let [topic, ] of observedTopics) {
     obs.addObserver(() => {
       let val = observedTopics.get(topic) + 1;
       observedTopics.set(topic, val);
     }, topic);
   }
 
   // Trigger profile creation.
   let profd = do_get_profile();
--- a/testing/xpcshell/example/unit/import_module.jsm
+++ b/testing/xpcshell/example/unit/import_module.jsm
@@ -1,26 +1,28 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+/* globals __URI__ */
+
 // Module used by test_import_module.js
 
 const EXPORTED_SYMBOLS = [ "MODULE_IMPORTED", "MODULE_URI", "SUBMODULE_IMPORTED", "same_scope", "SUBMODULE_IMPORTED_TO_SCOPE" ];
 
 const MODULE_IMPORTED = true;
 const MODULE_URI = __URI__;
 
 // Will import SUBMODULE_IMPORTED into scope.
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 XPCOMUtils.importRelative(this, "import_sub_module.jsm");
 
 // Prepare two scopes that we can import the submodule into.
-var scope1 = { __URI__: __URI__ };
-var scope2 = { __URI__: __URI__ };
+var scope1 = { __URI__ };
+var scope2 = { __URI__ };
 // First one is the regular path.
 XPCOMUtils.importRelative(scope1, "import_sub_module.jsm");
 scope1.test_obj.i++;
 // Second one is with a different path (leads to the same file).
 XPCOMUtils.importRelative(scope2, "duh/../import_sub_module.jsm");
 // test_obj belongs to import_sub_module.jsm and has a mutable field name i, if
 // the two modules are actually the same, then they'll share the same value.
 // We'll leave it up to test_import_module.js to check that this variable is
--- a/testing/xpcshell/example/unit/load_subscript.js
+++ b/testing/xpcshell/example/unit/load_subscript.js
@@ -1,5 +1,6 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+/* globals subscriptLoaded:true */
 subscriptLoaded = true;
--- a/testing/xpcshell/example/unit/location_load.js
+++ b/testing/xpcshell/example/unit/location_load.js
@@ -1,6 +1,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/. */
 
+/* globals __LOCATION__ */
+
 // Gets loaded via test_location.js
 do_check_eq(__LOCATION__.leafName, "location_load.js");
--- a/testing/xpcshell/example/unit/test_check_nsIException.js
+++ b/testing/xpcshell/example/unit/test_check_nsIException.js
@@ -1,11 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+/* import-globals-from ../../head.js */
+
 function run_test() {
   let env = Components.classes["@mozilla.org/process/environment;1"]
                       .getService(Components.interfaces.nsIEnvironment);
-  do_check_throws_nsIException(function () {
+  do_check_throws_nsIException(function() {
     env.QueryInterface(Components.interfaces.nsIFile);
   }, "NS_NOINTERFACE");
 }
 
--- a/testing/xpcshell/example/unit/test_check_nsIException_failing.js
+++ b/testing/xpcshell/example/unit/test_check_nsIException_failing.js
@@ -1,9 +1,11 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+/* import-globals-from ../../head.js */
+
 function run_test() {
-  do_check_throws_nsIException(function () {
+  do_check_throws_nsIException(function() {
     throw Error("I find your relaxed dishabille unpalatable");
   }, "NS_NOINTERFACE");
 }
 
--- a/testing/xpcshell/example/unit/test_do_check_matches.js
+++ b/testing/xpcshell/example/unit/test_do_check_matches.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function run_test() {
-  do_check_matches({x:1}, {x:1});
+  do_check_matches({x: 1}, {x: 1});
 
   // Property order is irrelevant.
-  do_check_matches({x:"foo", y:"bar"}, {y:"bar", x:"foo"});// pass
+  do_check_matches({x: "foo", y: "bar"}, {y: "bar", x: "foo"});// pass
 
   // Patterns nest.
-  do_check_matches({a:1, b:{c:2,d:3}}, {a:1, b:{c:2,d:3}});
+  do_check_matches({a: 1, b: {c: 2, d: 3}}, {a: 1, b: {c: 2, d: 3}});
 
-  do_check_matches([3,4,5], [3,4,5]);
+  do_check_matches([3, 4, 5], [3, 4, 5]);
 }
--- a/testing/xpcshell/example/unit/test_do_check_matches_failing.js
+++ b/testing/xpcshell/example/unit/test_do_check_matches_failing.js
@@ -1,12 +1,12 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function run_test() {
-  do_check_matches({x:1}, {});         // fail: all pattern props required
-  do_check_matches({x:1}, {x:2});      // fail: values must match
-  do_check_matches({x:undefined}, {});
+  do_check_matches({x: 1}, {});         // fail: all pattern props required
+  do_check_matches({x: 1}, {x: 2});      // fail: values must match
+  do_check_matches({x: undefined}, {});
 
   // 'length' property counts, even if non-enumerable.
-  do_check_matches([3,4,5], [3,5,5]);  // fail; value doesn't match
-  do_check_matches([3,4,5], [3,4,5,6]);// fail; length doesn't match
+  do_check_matches([3, 4, 5], [3, 5, 5]);  // fail; value doesn't match
+  do_check_matches([3, 4, 5], [3, 4, 5, 6]);// fail; length doesn't match
 }
--- a/testing/xpcshell/example/unit/test_import_module.js
+++ b/testing/xpcshell/example/unit/test_import_module.js
@@ -1,22 +1,25 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* 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 import_module.jsm */
+/* import-globals-from import_sub_module.jsm */
+
 /**
  * Ensures that tests can import a module in the same folder through:
  * Components.utils.import("resource://test/module.jsm");
  */
 
 function run_test() {
-  do_check_true(typeof(this['MODULE_IMPORTED']) == "undefined");
-  do_check_true(typeof(this['MODULE_URI']) == "undefined");
+  do_check_true(typeof(this["MODULE_IMPORTED"]) == "undefined");
+  do_check_true(typeof(this["MODULE_URI"]) == "undefined");
   let uri = "resource://test/import_module.jsm";
   Components.utils.import(uri);
   do_check_true(MODULE_URI == uri);
   do_check_true(MODULE_IMPORTED);
   do_check_true(SUBMODULE_IMPORTED);
   do_check_true(same_scope);
   do_check_true(SUBMODULE_IMPORTED_TO_SCOPE);
 }
--- a/testing/xpcshell/example/unit/test_load.js
+++ b/testing/xpcshell/example/unit/test_load.js
@@ -8,14 +8,13 @@ var subscriptLoaded = false;
 
 function run_test() {
   load("load_subscript.js");
   do_check_true(subscriptLoaded);
   subscriptLoaded = false;
   try {
     load("file_that_does_not_exist.js");
     subscriptLoaded = true;
-  }
-  catch (ex) {
-    do_check_eq(ex.message.substring(0,16), "cannot open file");
+  } catch (ex) {
+    do_check_eq(ex.message.substring(0, 16), "cannot open file");
   }
   do_check_false(subscriptLoaded, "load() should throw an error");
 }
--- a/testing/xpcshell/example/unit/test_location.js
+++ b/testing/xpcshell/example/unit/test_location.js
@@ -1,11 +1,13 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+/* globals __LOCATION__ */
+
 function run_test() {
   do_check_eq(__LOCATION__.leafName, "test_location.js");
   // also check that __LOCATION__ works via load()
   load("location_load.js");
 }
--- a/testing/xpcshell/example/unit/test_profile.js
+++ b/testing/xpcshell/example/unit/test_profile.js
@@ -1,11 +1,11 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* 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/. */
 
-function run_test()
-{
+function run_test() {
+  /* import-globals-from check_profile.js */
   load("check_profile.js");
   check_do_get_profile(false);
 }
--- a/testing/xpcshell/example/unit/test_profile_afterChange.js
+++ b/testing/xpcshell/example/unit/test_profile_afterChange.js
@@ -1,11 +1,11 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* 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/. */
 
-function run_test()
-{
+function run_test() {
+  /* import-globals-from check_profile.js */
   load("check_profile.js");
   check_do_get_profile(true);
 }
--- a/testing/xpcshell/example/unit/test_sample.js
+++ b/testing/xpcshell/example/unit/test_sample.js
@@ -1,17 +1,17 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* 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/. */
 
-/* This is the most basic testcase.  It makes some trivial assertions, 
- * then sets a timeout, and exits the test harness when that timeout 
- * fires. This is meant to demonstrate that there is a complete event 
+/* This is the most basic testcase.  It makes some trivial assertions,
+ * then sets a timeout, and exits the test harness when that timeout
+ * fires. This is meant to demonstrate that there is a complete event
  * system available to test scripts.
  * Available functions are described at:
  * http://developer.mozilla.org/en/docs/Writing_xpcshell-based_unit_tests
  */
 function run_test() {
   do_check_eq(57, 57)
   do_check_neq(1, 2)
   do_check_true(true);
--- a/testing/xpcshell/head.js
+++ b/testing/xpcshell/head.js
@@ -5,16 +5,30 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*
  * This file contains common code that is loaded before each test file(s).
  * See http://developer.mozilla.org/en/docs/Writing_xpcshell-based_unit_tests
  * for more information.
  */
 
+/* defined by the harness */
+/* globals _HEAD_FILES, _HEAD_JS_PATH, _JSDEBUGGER_PORT, _JSCOV_DIR,
+    _MOZINFO_JS_PATH, _TEST_FILE, _TEST_NAME, _TESTING_MODULES_DIR,
+    _XPCSHELL_PROCESS:true */
+
+/* defined by XPCShellImpl.cpp */
+/* globals load, sendCommand */
+
+/* must be defined by tests using do_await_remote_message/do_send_remote_message */
+/* globals Cc, Ci */
+
+/* may be defined in test files */
+/* globals run_test */
+
 var _quit = false;
 var _passed = true;
 var _tests_pending = 0;
 var _cleanupFunctions = [];
 var _pendingTimers = [];
 var _profileInitialized = false;
 
 // Register the testing-common resource protocol early, to have access to its
@@ -33,23 +47,23 @@ var Assert = new AssertCls(function(err,
   if (err) {
     do_report_result(false, err.message, err.stack);
   } else {
     do_report_result(true, message, stack);
   }
 });
 
 
-var _add_params = function (params) {
+var _add_params = function(params) {
   if (typeof _XPCSHELL_PROCESS != "undefined") {
     params.xpcshell_process = _XPCSHELL_PROCESS;
   }
 };
 
-var _dumpLog = function (raw_msg) {
+var _dumpLog = function(raw_msg) {
   dump("\n" + JSON.stringify(raw_msg) + "\n");
 }
 
 var _LoggerClass = Components.utils.import("resource://testing-common/StructuredLog.jsm", null).StructuredLogger;
 var _testLogger = new _LoggerClass("xpcshell/head.js", _dumpLog, [_add_params]);
 
 // Disable automatic network detection, so tests work correctly when
 // not connected to a network.
@@ -61,18 +75,17 @@ var _testLogger = new _LoggerClass("xpcs
 }
 
 // Determine if we're running on parent or child
 var runningInParent = true;
 try {
   runningInParent = Components.classes["@mozilla.org/xre/runtime;1"].
                     getService(Components.interfaces.nsIXULRuntime).processType
                     == Components.interfaces.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
-}
-catch (e) { }
+} catch (e) { }
 
 // Only if building of places is enabled.
 if (runningInParent &&
     "mozIAsyncHistory" in Components.interfaces) {
   // Ensure places history is enabled for xpcshell-tests as some non-FF
   // apps disable it.
   let prefs = Components.classes["@mozilla.org/preferences-service;1"]
               .getService(Components.interfaces.nsIPrefBranch);
@@ -88,53 +101,51 @@ try {
     // docshells needed to pass them
     prefs.setBoolPref("network.disable.ipc.security", true);
 
     // Disable IPv6 lookups for 'localhost' on windows.
     if ("@mozilla.org/windows-registry-key;1" in Components.classes) {
       prefs.setCharPref("network.dns.ipv4OnlyDomains", "localhost");
     }
   }
-}
-catch (e) { }
+} catch (e) { }
 
 // Configure crash reporting, if possible
 // We rely on the Python harness to set MOZ_CRASHREPORTER,
 // MOZ_CRASHREPORTER_NO_REPORT, and handle checking for minidumps.
 // Note that if we're in a child process, we don't want to init the
 // crashreporter component.
 try {
   if (runningInParent &&
       "@mozilla.org/toolkit/crash-reporter;1" in Components.classes) {
     let crashReporter =
           Components.classes["@mozilla.org/toolkit/crash-reporter;1"]
           .getService(Components.interfaces.nsICrashReporter);
     crashReporter.UpdateCrashEventsDir();
     crashReporter.minidumpPath = do_get_minidumpdir();
   }
-}
-catch (e) { }
+} catch (e) { }
 
 // Configure a console listener so messages sent to it are logged as part
 // of the test.
 try {
   let levelNames = {}
   for (let level of ["debug", "info", "warn", "error"]) {
     levelNames[Components.interfaces.nsIConsoleMessage[level]] = level;
   }
 
   let listener = {
-    QueryInterface : function(iid) {
+    QueryInterface(iid) {
       if (!iid.equals(Components.interfaces.nsISupports) &&
           !iid.equals(Components.interfaces.nsIConsoleListener)) {
         throw Components.results.NS_NOINTERFACE;
       }
       return this;
     },
-    observe : function (msg) {
+    observe(msg) {
       if (typeof do_print === "function")
         do_print("CONSOLE_MESSAGE: (" + levelNames[msg.logLevel] + ") " + msg.toString());
     }
   };
   Components.classes["@mozilla.org/consoleservice;1"]
             .getService(Components.interfaces.nsIConsoleService)
             .registerListener(listener);
 } catch (e) {}
@@ -161,25 +172,25 @@ function _Timer(func, delay) {
   var timer = Components.classes["@mozilla.org/timer;1"]
                         .createInstance(Components.interfaces.nsITimer);
   timer.initWithCallback(this, delay + _timerFuzz, timer.TYPE_ONE_SHOT);
 
   // Keep timer alive until it fires
   _pendingTimers.push(timer);
 }
 _Timer.prototype = {
-  QueryInterface: function(iid) {
+  QueryInterface(iid) {
     if (iid.equals(Components.interfaces.nsITimerCallback) ||
         iid.equals(Components.interfaces.nsISupports))
       return this;
 
     throw Components.results.NS_ERROR_NO_INTERFACE;
   },
 
-  notify: function(timer) {
+  notify(timer) {
     _pendingTimers.splice(_pendingTimers.indexOf(timer), 1);
 
     // The current nsITimer implementation can undershoot, but even if it
     // couldn't, paranoia is probably a virtue here given the potential for
     // random orange on tinderboxen.
     var end = Date.now();
     var elapsed = end - this._start;
     if (elapsed >= this._delay) {
@@ -235,73 +246,70 @@ var _fakeIdleService = {
     return this.registrar =
       Components.manager.QueryInterface(Components.interfaces.nsIComponentRegistrar);
   },
   contractID: "@mozilla.org/widget/idleservice;1",
   get CID() {
     return this.registrar.contractIDToCID(this.contractID);
   },
 
-  activate: function FIS_activate()
-  {
+  activate: function FIS_activate() {
     if (!this.originalFactory) {
       // Save original factory.
       this.originalFactory =
         Components.manager.getClassObject(Components.classes[this.contractID],
                                           Components.interfaces.nsIFactory);
       // Unregister original factory.
       this.registrar.unregisterFactory(this.CID, this.originalFactory);
       // Replace with the mock.
       this.registrar.registerFactory(this.CID, "Fake Idle Service",
                                      this.contractID, this.factory
       );
     }
   },
 
-  deactivate: function FIS_deactivate()
-  {
+  deactivate: function FIS_deactivate() {
     if (this.originalFactory) {
       // Unregister the mock.
       this.registrar.unregisterFactory(this.CID, this.factory);
       // Restore original factory.
       this.registrar.registerFactory(this.CID, "Idle Service",
                                      this.contractID, this.originalFactory);
       delete this.originalFactory;
     }
   },
 
   factory: {
     // nsIFactory
-    createInstance: function (aOuter, aIID)
-    {
+    createInstance(aOuter, aIID) {
       if (aOuter) {
         throw Components.results.NS_ERROR_NO_AGGREGATION;
       }
       return _fakeIdleService.QueryInterface(aIID);
     },
-    lockFactory: function (aLock) {
+    lockFactory(aLock) {
       throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
     },
-    QueryInterface: function(aIID) {
+    QueryInterface(aIID) {
       if (aIID.equals(Components.interfaces.nsIFactory) ||
           aIID.equals(Components.interfaces.nsISupports)) {
         return this;
       }
       throw Components.results.NS_ERROR_NO_INTERFACE;
     }
   },
 
   // nsIIdleService
   get idleTime() {
     return 0;
   },
-  addIdleObserver: function () {},
-  removeIdleObserver: function () {},
+  addIdleObserver() {},
+  removeIdleObserver() {},
 
-  QueryInterface: function(aIID) {
+  QueryInterface(aIID) {
     // Useful for testing purposes, see test_get_idle.js.
     if (aIID.equals(Components.interfaces.nsIFactory)) {
       return this.factory;
     }
     if (aIID.equals(Components.interfaces.nsIIdleService) ||
         aIID.equals(Components.interfaces.nsISupports)) {
       return this;
     }
@@ -443,17 +451,17 @@ function _setupDebuggerServer(breakpoint
   for (let topic of TOPICS) {
     obsSvc.addObserver(observe, topic);
   }
   return DebuggerServer;
 }
 
 function _initDebugging(port) {
   let initialized = false;
-  let DebuggerServer = _setupDebuggerServer(_TEST_FILE, () => {initialized = true;});
+  let DebuggerServer = _setupDebuggerServer(_TEST_FILE, () => { initialized = true; });
 
   do_print("");
   do_print("*******************************************************************");
   do_print("Waiting for the debugger to connect on port " + port)
   do_print("")
   do_print("To connect the debugger, open a Firefox instance, select 'Connect'");
   do_print("from the Developer menu and specify the port as " + port);
   do_print("*******************************************************************");
@@ -500,34 +508,34 @@ function _execute_test() {
   // Override idle service by default.
   // Call do_get_idle() to restore the factory and get the service.
   _fakeIdleService.activate();
 
   _PromiseTestUtils.init();
   _PromiseTestUtils.Assert = Assert;
 
   let coverageCollector = null;
-  if (typeof _JSCOV_DIR === 'string') {
+  if (typeof _JSCOV_DIR === "string") {
     let _CoverageCollector = Components.utils.import("resource://testing-common/CoverageUtils.jsm", {}).CoverageCollector;
     coverageCollector = new _CoverageCollector(_JSCOV_DIR);
   }
 
   // _HEAD_FILES is dynamically defined by <runxpcshelltests.py>.
   _load_files(_HEAD_FILES);
   // _TEST_FILE is dynamically defined by <runxpcshelltests.py>.
   _load_files(_TEST_FILE);
 
   // Tack Assert.jsm methods to the current scope.
   this.Assert = Assert;
   for (let func in Assert) {
     this[func] = Assert[func].bind(Assert);
   }
 
   if (_gTestHasOnly) {
-    _gTests = _gTests.filter(([props,]) => {
+    _gTests = _gTests.filter(([props, ]) => {
       return ("_only" in props) && props._only;
     });
   }
 
   try {
     do_test_pending("MAIN run_test");
     // Check if run_test() is defined. If defined, run it.
     // Else, call run_next_test() directly to invoke tests
@@ -600,18 +608,17 @@ function _execute_test() {
     for (let func of _cleanupFunctions.reverse()) {
       try {
         yield func();
       } catch (ex) {
         reportCleanupError(ex);
       }
     }
     _cleanupFunctions = [];
-  }.bind(this)).catch(reportCleanupError)
-               .then(() => complete = true);
+  }).catch(reportCleanupError).then(() => complete = true);
   let thr = Components.classes["@mozilla.org/thread-manager;1"]
                       .getService().currentThread;
   while (!complete) {
     thr.processNextEvent(true);
   }
 
   // Restore idle service to avoid leaks.
   _fakeIdleService.deactivate();
@@ -661,17 +668,17 @@ function _load_files(aFiles) {
 
   aFiles.forEach(load_file);
 }
 
 function _wrap_with_quotes_if_necessary(val) {
   return typeof val == "string" ? '"' + val + '"' : val;
 }
 
-/************** Functions to be used from the tests **************/
+/* ************* Functions to be used from the tests ************* */
 
 /**
  * Prints a message to the output log.
  */
 function do_print(msg, data) {
   msg = _wrap_with_quotes_if_necessary(msg);
   data = data ? data : null;
   _testLogger.info(msg, data);
@@ -693,37 +700,36 @@ function do_timeout(delay, func) {
 
 function do_execute_soon(callback, aName) {
   let funcName = (aName ? aName : callback.name);
   do_test_pending(funcName);
   var tm = Components.classes["@mozilla.org/thread-manager;1"]
                      .getService(Components.interfaces.nsIThreadManager);
 
   tm.dispatchToMainThread({
-    run: function() {
+    run() {
       try {
         callback();
       } catch (e) {
         // do_check failures are already logged and set _quit to true and throw
         // NS_ERROR_ABORT. If both of those are true it is likely this exception
         // has already been logged so there is no need to log it again. It's
         // possible that this will mask an NS_ERROR_ABORT that happens after a
         // do_check failure though.
         if (!_quit || e != Components.results.NS_ERROR_ABORT) {
           let stack = e.stack ? _format_stack(e.stack) : null;
           _testLogger.testStatus(_TEST_NAME,
                                  funcName,
-                                 'FAIL',
-                                 'PASS',
+                                 "FAIL",
+                                 "PASS",
                                  _exception_message(e),
                                  stack);
           _do_quit();
         }
-      }
-      finally {
+      } finally {
         do_test_finished(funcName);
       }
     }
   });
 }
 
 /**
  * Shows an error message and the current stack and aborts the test.
@@ -860,32 +866,30 @@ function do_report_result(passed, text, 
       _abort_failed_test();
     } else {
       _testLogger.testStatus(_TEST_NAME,
                              name,
                              "PASS",
                              "PASS",
                              message);
     }
+  } else if (todo) {
+    _testLogger.testStatus(_TEST_NAME,
+                           name,
+                           "FAIL",
+                           "FAIL",
+                           message);
   } else {
-    if (todo) {
-      _testLogger.testStatus(_TEST_NAME,
-                             name,
-                             "FAIL",
-                             "FAIL",
-                             message);
-    } else {
-      _testLogger.testStatus(_TEST_NAME,
-                             name,
-                             "FAIL",
-                             "PASS",
-                             message,
-                             _format_stack(stack));
-      _abort_failed_test();
-    }
+    _testLogger.testStatus(_TEST_NAME,
+                           name,
+                           "FAIL",
+                           "PASS",
+                           message,
+                           _format_stack(stack));
+    _abort_failed_test();
   }
 }
 
 function _do_check_eq(left, right, stack, todo) {
   if (!stack)
     stack = Components.stack.caller;
 
   var text = _wrap_with_quotes_if_necessary(left) + " == " +
@@ -925,30 +929,29 @@ function todo_check_false(condition, sta
 
   todo_check_eq(condition, false, stack);
 }
 
 function do_check_null(condition, stack) {
   Assert.equal(condition, null);
 }
 
-function todo_check_null(condition, stack=Components.stack.caller) {
+function todo_check_null(condition, stack = Components.stack.caller) {
   todo_check_eq(condition, null, stack);
 }
 function do_check_matches(pattern, value) {
   Assert.deepEqual(pattern, value);
 }
 
 // Check that |func| throws an nsIException that has
 // |Components.results[resultName]| as the value of its 'result' property.
 function do_check_throws_nsIException(func, resultName,
-                                      stack=Components.stack.caller, todo=false)
-{
+                                      stack = Components.stack.caller, todo = false) {
   let expected = Components.results[resultName];
-  if (typeof expected !== 'number') {
+  if (typeof expected !== "number") {
     do_throw("do_check_throws_nsIException requires a Components.results" +
              " property name, not " + uneval(resultName), stack);
   }
 
   let msg = ("do_check_throws_nsIException: func should throw" +
              " an nsIException whose 'result' is Components.results." +
              resultName);
 
@@ -967,47 +970,46 @@ function do_check_throws_nsIException(fu
 
   // Call this here, not in the 'try' clause, so do_report_result's own
   // throw doesn't get caught by our 'catch' clause.
   do_report_result(false, msg + ", but returned normally", stack, todo);
 }
 
 // Produce a human-readable form of |exception|. This looks up
 // Components.results values, tries toString methods, and so on.
-function legible_exception(exception)
-{
+function legible_exception(exception) {
   switch (typeof exception) {
-    case 'object':
+    case "object":
     if (exception instanceof Components.interfaces.nsIException) {
       return "nsIException instance: " + uneval(exception.toString());
     }
     return exception.toString();
 
-    case 'number':
+    case "number":
     for (let name in Components.results) {
       if (exception === Components.results[name]) {
         return "Components.results." + name;
       }
     }
 
     // Fall through.
     default:
     return uneval(exception);
   }
 }
 
 function do_check_instanceof(value, constructor,
-                             stack=Components.stack.caller, todo=false) {
+                             stack = Components.stack.caller, todo = false) {
   do_report_result(value instanceof constructor,
                    "value should be an instance of " + constructor.name,
                    stack, todo);
 }
 
 function todo_check_instanceof(value, constructor,
-                             stack=Components.stack.caller) {
+                             stack = Components.stack.caller) {
   do_check_instanceof(value, constructor, stack, true);
 }
 
 function do_test_pending(aName) {
   ++_tests_pending;
 
   _testLogger.info("(xpcshell/head.js) | test" +
                    (aName ? " " + aName : "") +
@@ -1042,18 +1044,17 @@ function do_get_file(path, allowNonexist
       // Not using do_throw(): caller will continue.
       _passed = false;
       var stack = Components.stack.caller;
       _testLogger.error("[" + stack.name + " : " + stack.lineNumber + "] " +
                         lf.path + " does not exist");
     }
 
     return lf;
-  }
-  catch (ex) {
+  } catch (ex) {
     do_throw(ex.toString(), Components.stack.caller);
   }
 
   return null;
 }
 
 // do_get_cwd() isn't exactly self-explanatory, so provide a helper
 function do_get_cwd() {
@@ -1085,17 +1086,17 @@ function do_parse_document(aPath, aType)
 
     default:
       do_throw("type: expected application/xhtml+xml, application/xml or text/xml," +
                  " got '" + aType + "'",
                Components.stack.caller);
   }
 
   let file = do_get_file(aPath),
-      ios = Components.classes['@mozilla.org/network/io-service;1']
+      ios = Components.classes["@mozilla.org/network/io-service;1"]
             .getService(Components.interfaces.nsIIOService),
       url = ios.newFileURI(file).spec;
   file = null;
   return new Promise((resolve, reject) => {
     let xhr = new XMLHttpRequest();
     xhr.open("GET", url);
     xhr.responseType = "document";
     xhr.onerror = reject;
@@ -1108,18 +1109,17 @@ function do_parse_document(aPath, aType)
 
 /**
  * Registers a function that will run when the test harness is done running all
  * tests.
  *
  * @param aFunction
  *        The function to be called when the test harness has finished running.
  */
-function do_register_cleanup(aFunction)
-{
+function do_register_cleanup(aFunction) {
   _cleanupFunctions.push(aFunction);
 }
 
 /**
  * Returns the directory for a temp dir, which is created by the
  * test harness. Every test gets its own temp dir.
  *
  * @return nsILocalFile of the temporary directory
@@ -1145,19 +1145,18 @@ function do_get_minidumpdir() {
                       .getService(Components.interfaces.nsIEnvironment);
   // the python harness may set this in the environment for us
   let path = env.get("XPCSHELL_MINIDUMP_DIR");
   if (path) {
     let file = Components.classes["@mozilla.org/file/local;1"]
                          .createInstance(Components.interfaces.nsILocalFile);
     file.initWithPath(path);
     return file;
-  } else {
-    return do_get_tempdir();
   }
+  return do_get_tempdir();
 }
 
 /**
  * Registers a directory with the profile service,
  * and return the directory as an nsILocalFile.
  *
  * @param notifyProfileAfterChange Whether to notify for "profile-after-change".
  * @return nsILocalFile of the profile directory.
@@ -1174,25 +1173,25 @@ function do_get_profile(notifyProfileAft
   let profd = env.get("XPCSHELL_TEST_PROFILE_DIR");
   let file = Components.classes["@mozilla.org/file/local;1"]
                        .createInstance(Components.interfaces.nsILocalFile);
   file.initWithPath(profd);
 
   let dirSvc = Components.classes["@mozilla.org/file/directory_service;1"]
                          .getService(Components.interfaces.nsIProperties);
   let provider = {
-    getFile: function(prop, persistent) {
+    getFile(prop, persistent) {
       persistent.value = true;
       if (prop == "ProfD" || prop == "ProfLD" || prop == "ProfDS" ||
           prop == "ProfLDS" || prop == "TmpD") {
         return file.clone();
       }
       return null;
     },
-    QueryInterface: function(iid) {
+    QueryInterface(iid) {
       if (iid.equals(Components.interfaces.nsIDirectoryServiceProvider) ||
           iid.equals(Components.interfaces.nsISupports)) {
         return this;
       }
       throw Components.results.NS_ERROR_NO_INTERFACE;
     }
   };
   dirSvc.QueryInterface(Components.interfaces.nsIDirectoryService)
@@ -1231,18 +1230,17 @@ function do_get_profile(notifyProfileAft
 /**
  * This function loads head.js (this file) in the child process, so that all
  * functions defined in this file (do_throw, etc) are available to subsequent
  * sendCommand calls.  It also sets various constants used by these functions.
  *
  * (Note that you may use sendCommand without calling this function first;  you
  * simply won't have any of the functions in this file available.)
  */
-function do_load_child_test_harness()
-{
+function do_load_child_test_harness() {
   // Make sure this isn't called from child process
   if (!runningInParent) {
     do_throw("run_test_in_child cannot be called from child!");
   }
 
   // Allow to be called multiple times, but only run once
   if (typeof do_load_child_test_harness.alreadyRun != "undefined")
     return;
@@ -1254,17 +1252,17 @@ function do_load_child_test_harness()
         "const _HEAD_JS_PATH=" + uneval(_HEAD_JS_PATH) + "; "
       + "const _HEAD_FILES=" + uneval(_HEAD_FILES) + "; "
       + "const _MOZINFO_JS_PATH=" + uneval(_MOZINFO_JS_PATH) + "; "
       + "const _TEST_NAME=" + uneval(_TEST_NAME) + "; "
       // We'll need more magic to get the debugger working in the child
       + "const _JSDEBUGGER_PORT=0; "
       + "const _XPCSHELL_PROCESS='child';";
 
-  if (typeof _JSCOV_DIR === 'string') {
+  if (typeof _JSCOV_DIR === "string") {
     command += " const _JSCOV_DIR=" + uneval(_JSCOV_DIR) + ";";
   }
 
   if (_TESTING_MODULES_DIR) {
     command += " const _TESTING_MODULES_DIR=" + uneval(_TESTING_MODULES_DIR) + ";";
   }
 
   command += " load(_HEAD_JS_PATH);";
@@ -1279,22 +1277,21 @@ function do_load_child_test_harness()
  *
  * @param testFile
  *        The name of the script to run.  Path format same as load().
  * @param optionalCallback.
  *        Optional function to be called (in parent) when test on child is
  *        complete.  If provided, the function must call do_test_finished();
  * @return Promise Resolved when the test in the child is complete.
  */
-function run_test_in_child(testFile, optionalCallback)
-{
+function run_test_in_child(testFile, optionalCallback) {
   return new Promise((resolve) => {
     var callback = () => {
       resolve();
-      if (typeof optionalCallback == 'undefined') {
+      if (typeof optionalCallback == "undefined") {
         do_test_finished();
       } else {
         optionalCallback();
       }
     };
 
     do_load_child_test_harness();
 
@@ -1312,21 +1309,20 @@ function run_test_in_child(testFile, opt
  * Execute a given function as soon as a particular cross-process message is received.
  * Must be paired with do_send_remote_message or equivalent ProcessMessageManager calls.
  *
  * @param optionalCallback
  *        Optional callback that is invoked when the message is received.  If provided,
  *        the function must call do_test_finished().
  * @return Promise Promise that is resolved when the message is received.
  */
-function do_await_remote_message(name, optionalCallback)
-{
+function do_await_remote_message(name, optionalCallback) {
   return new Promise((resolve) => {
     var listener = {
-      receiveMessage: function(message) {
+      receiveMessage(message) {
         if (message.name == name) {
           mm.removeMessageListener(name, listener);
           resolve();
           if (optionalCallback) {
             optionalCallback();
           } else {
             do_test_finished();
           }
@@ -1349,20 +1345,20 @@ function do_await_remote_message(name, o
  * Asynchronously send a message to all remote processes. Pairs with do_await_remote_message
  * or equivalent ProcessMessageManager listeners.
  */
 function do_send_remote_message(name) {
   var mm;
   var sender;
   if (runningInParent) {
     mm = Cc["@mozilla.org/parentprocessmessagemanager;1"].getService(Ci.nsIMessageBroadcaster);
-    sender = 'broadcastAsyncMessage';
+    sender = "broadcastAsyncMessage";
   } else {
     mm = Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsISyncMessageSender);
-    sender = 'sendAsyncMessage';
+    sender = "sendAsyncMessage";
   }
   mm[sender](name);
 }
 
 /**
  * Helper function to add the _only property to add_task/add_test function when
  * running it as add_task.only(...).
  *
@@ -1513,31 +1509,29 @@ add_task.skip = _add_skip.bind(undefined
 
 /**
  * Runs the next test function from the list of async tests.
  */
 var _gRunningTest = null;
 var _gTestIndex = 0; // The index of the currently running test.
 var _gTaskRunning = false;
 var _gTestHasOnly = false;
-function run_next_test()
-{
+function run_next_test() {
   if (_gTaskRunning) {
     throw new Error("run_next_test() called from an add_task() test function. " +
                     "run_next_test() should not be called from inside add_task() " +
                     "under any circumstances!");
   }
 
-  function _run_next_test()
-  {
+  function _run_next_test() {
     if (_gTestIndex < _gTests.length) {
       // Check for uncaught rejections as early and often as possible.
       _PromiseTestUtils.assertNoUncaughtRejections();
       let _properties;
-      [_properties, _gRunningTest,] = _gTests[_gTestIndex++];
+      [_properties, _gRunningTest, ] = _gTests[_gTestIndex++];
       if (typeof(_properties.skip_if) == "function" && _properties.skip_if()) {
         let _condition = _properties.skip_if.toSource().replace(/\(\)\s*=>\s*/, "");
         let _message = _gRunningTest.name
           + " skipped because the following conditions were"
           + " met: (" + _condition + ")";
         _testLogger.testStatus(_TEST_NAME,
                                _gRunningTest.name,
                                "SKIP",
--- a/toolkit/components/mozprotocol/mozProtocolHandler.js
+++ b/toolkit/components/mozprotocol/mozProtocolHandler.js
@@ -27,17 +27,17 @@ mozProtocolHandler.prototype = {
       uri.spec = spec;
     }
     return uri;
   },
 
   newChannel2(uri, loadInfo) {
     let realURL = NetUtil.newURI(this.urlToLoad);
     let channel = Services.io.newChannelFromURIWithLoadInfo(realURL, loadInfo)
-    loadInfo.resultPrincipalURI = realURL;
+    channel.loadFlags |= Ci.nsIChannel.LOAD_REPLACE;
     return channel;
   },
 
   newChannel(uri) {
     return this.newChannel2(uri, null);
   },
 
   classID: Components.ID("{47a45e5f-691e-4799-8686-14f8d3fc0f8c}"),
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -1057,17 +1057,26 @@
     "description": "Time spent running a JS GC slice (ms)"
   },
   "GC_SLOW_PHASE": {
     "record_in_processes": ["main", "content"],
     "alert_emails": ["dev-telemetry-gc-alerts@mozilla.org"],
     "expires_in_version": "never",
     "kind": "enumerated",
     "n_values": 75,
-    "description": "The longest phase in any slice that goes over 2x the budget"
+    "description": "The longest phase in any slice that goes over 2x the budget. The phase values are defined in js/src/gc/GenerateStatsPhases.py."
+  },
+  "GC_SLOW_TASK": {
+    "record_in_processes": ["main", "content"],
+    "alert_emails": ["dev-telemetry-gc-alerts@mozilla.org","jcoppeard@mozilla.com"],
+    "expires_in_version": "never",
+    "kind": "enumerated",
+    "n_values": 75,
+    "description": "The longest parallel task in any slice that goes over 2x the budget. The phase values are defined in js/src/gc/GenerateStatsPhases.py.",
+    "bug_numbers": [1309651]
   },
   "GC_MMU_50": {
     "record_in_processes": ["main", "content"],
     "alert_emails": ["dev-telemetry-gc-alerts@mozilla.org"],
     "expires_in_version": "never",
     "kind": "linear",
     "high": 100,
     "n_buckets": 20,
--- a/toolkit/components/telemetry/tests/unit/test_TelemetryControllerShutdown.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryControllerShutdown.js
@@ -37,22 +37,22 @@ add_task(async function test_setup() {
 });
 
 /**
  * Ensures that TelemetryController does not hang processing shutdown
  * phases. Assumes that Telemetry shutdown routines do not take longer than
  * CRASH_TIMEOUT_MS to complete.
  */
 add_task(async function test_sendTelemetryShutsDownWithinReasonableTimeout() {
-  const CRASH_TIMEOUT_MS = 5 * 1000;
+  const CRASH_TIMEOUT_MS = 10 * 1000;
   // Enable testing mode for AsyncShutdown, otherwise some testing-only functionality
   // is not available.
   Services.prefs.setBoolPref("toolkit.asyncshutdown.testing", true);
   // Reducing the max delay for waitiing on phases to complete from 1 minute
-  // (standard) to 10 seconds to avoid blocking the tests in case of misbehavior.
+  // (standard) to 20 seconds to avoid blocking the tests in case of misbehavior.
   Services.prefs.setIntPref("toolkit.asyncshutdown.crash_timeout", CRASH_TIMEOUT_MS);
 
   let httpServer = new HttpServer();
   httpServer.registerPrefixHandler("/", contentHandler);
   httpServer.start(-1);
 
   await TelemetryController.testSetup();
   TelemetrySend.setServer("http://localhost:" + httpServer.identity.primaryPort);
--- a/toolkit/mozapps/update/nsUpdateService.js
+++ b/toolkit/mozapps/update/nsUpdateService.js
@@ -1597,23 +1597,19 @@ UpdateService.prototype = {
    *          The notification name
    * @param   data
    *          Additional data
    */
   observe: function AUS_observe(subject, topic, data) {
     switch (topic) {
       case "post-update-processing":
         if (readStatusFile(getUpdatesDir()) == STATE_SUCCEEDED) {
-          // The active update needs to be copied to the first update in the
-          // updates.xml early during startup to support post update actions
-          // (bug 1301288).
-          let um = Cc["@mozilla.org/updates/update-manager;1"].
-                   getService(Ci.nsIUpdateManager);
-          um.activeUpdate.state = STATE_SUCCEEDED;
-          um.saveUpdates();
+          // After a successful update the post update preference needs to be
+          // set early during startup so applications can perform post update
+          // actions when they are defined in the update's metadata.
           Services.prefs.setBoolPref(PREF_APP_UPDATE_POSTUPDATE, true);
         }
 
         if (Services.appinfo.ID in APPID_TO_TOPIC) {
           // Delay post-update processing to ensure that possible update
           // dialogs are shown in front of the app window, if possible.
           // See bug 311614.
           Services.obs.addObserver(this, APPID_TO_TOPIC[Services.appinfo.ID]);
@@ -2645,33 +2641,30 @@ UpdateManager.prototype = {
    * replace the existing value with the new value.
    * @param   update
    *          The nsIUpdate object to add.
    */
   _addUpdate: function UM__addUpdate(update) {
     if (!update)
       return;
     this._ensureUpdates();
-    if (this._updates) {
-      for (var i = 0; i < this._updates.length; ++i) {
-        // Keep all update entries with a state of STATE_FAILED and replace the
-        // first update entry that has the same application version and build ID
-        // if it exists. This allows the update history to only have one success
-        // entry for an update and entries for all failed updates.
-        if (update.state != STATE_FAILED &&
-            this._updates[i] &&
-            this._updates[i].state != STATE_FAILED &&
-            this._updates[i].appVersion == update.appVersion &&
-            this._updates[i].buildID == update.buildID) {
-          // Replace the existing entry with the new value, updating
-          // all metadata.
-          this._updates[i] = update;
-          return;
-        }
-      }
+    // Only the latest update entry is checked so the the latest successful
+    // step for an update is recorded and all failures are kept. This way
+    // mutliple attempts to update for the same update are kept in the update
+    // history.
+    if (this._updates &&
+        update.state != STATE_FAILED &&
+        this._updates[0] &&
+        this._updates[0].state != STATE_FAILED &&
+        this._updates[0].appVersion == update.appVersion &&
+        this._updates[0].buildID == update.buildID) {
+      // Replace the existing entry with the new value, updating
+      // all metadata.
+      this._updates[0] = update;
+      return;
     }
     // Otherwise add it to the front of the list.
     this._updates.unshift(update);
   },
 
   /**
    * Serializes an array of updates to an XML file
    * @param   updates
@@ -2717,25 +2710,22 @@ UpdateManager.prototype = {
    */
   saveUpdates: function UM_saveUpdates() {
     this._writeUpdatesToXMLFile([this._activeUpdate],
                                 getUpdateFile([FILE_ACTIVE_UPDATE_XML]));
     if (this._activeUpdate)
       this._addUpdate(this._activeUpdate);
 
     this._ensureUpdates();
-    // Don't write updates that have a temporary state to the updates.xml file.
+    // Don't write updates that don't have a state to the updates.xml file.
     if (this._updates) {
       let updates = this._updates.slice();
       for (let i = updates.length - 1; i >= 0; --i) {
         let state = updates[i].state;
-        if (state == STATE_NONE || state == STATE_DOWNLOADING ||
-            state == STATE_APPLIED || state == STATE_APPLIED_SERVICE ||
-            state == STATE_PENDING || state == STATE_PENDING_SERVICE ||
-            state == STATE_PENDING_ELEVATE) {
+        if (state == STATE_NONE) {
           updates.splice(i, 1);
         }
       }
 
       this._writeUpdatesToXMLFile(updates.slice(0, 20),
                                   getUpdateFile([FILE_UPDATES_XML]));
     }
   },
--- a/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateStageOldVersionFailure.js
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateStageOldVersionFailure.js
@@ -55,17 +55,17 @@ function stageUpdateFinished() {
  * Called after the call to runUpdateUsingApp finishes.
  */
 function runUpdateFinished() {
   standardInit();
   Assert.equal(readStatusState(), STATE_NONE,
                "the status file state" + MSG_SHOULD_EQUAL);
   Assert.ok(!gUpdateManager.activeUpdate,
             "the active update should not be defined");
-  Assert.equal(gUpdateManager.updateCount, 1,
+  Assert.equal(gUpdateManager.updateCount, 2,
                "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
   Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_STAGE,
                "the update state" + MSG_SHOULD_EQUAL);
   checkPostUpdateRunningFile(false);
   setTestFilesAndDirsForFailure();
   checkFilesAfterUpdateFailure(getApplyDirFile, IS_MACOSX ? false : true, false);
 
   let updatesDir = getUpdatesPatchDir();
--- a/widget/cocoa/nsDragService.mm
+++ b/widget/cocoa/nsDragService.mm
@@ -607,17 +607,17 @@ nsDragService::IsDataFlavorSupported(con
         }
       }
     }
   }
 
   const NSString* type = nil;
   bool allowFileURL = false;
   if (dataFlavor.EqualsLiteral(kFileMime)) {
-    type = [UTIHelper stringFromPboardType:kMozFileUrlsPboardType];
+    type = [UTIHelper stringFromPboardType:(NSString*)kUTTypeFileURL];
     allowFileURL = true;
   } else if (dataFlavor.EqualsLiteral(kUnicodeMime)) {
     type = [UTIHelper stringFromPboardType:NSPasteboardTypeString];
   } else if (dataFlavor.EqualsLiteral(kHTMLMime)) {
     type = [UTIHelper stringFromPboardType:NSPasteboardTypeHTML];
   } else if (dataFlavor.EqualsLiteral(kURLMime) ||
              dataFlavor.EqualsLiteral(kURLDataMime)) {
     type = [UTIHelper stringFromPboardType:kPublicUrlPboardType];
--- a/widget/windows/TSFTextStore.cpp
+++ b/widget/windows/TSFTextStore.cpp
@@ -1548,72 +1548,104 @@ TSFTextStore::~TSFTextStore()
 bool
 TSFTextStore::Init(nsWindowBase* aWidget,
                    const InputContext& aContext)
 {
   MOZ_LOG(sTextStoreLog, LogLevel::Info,
     ("0x%p TSFTextStore::Init(aWidget=0x%p)",
      this, aWidget));
 
+  if (NS_WARN_IF(!aWidget) || NS_WARN_IF(aWidget->Destroyed())) {
+    MOZ_LOG(sTextStoreLog, LogLevel::Error,
+      ("0x%p   TSFTextStore::Init() FAILED due to being initialized with "
+       "destroyed widget",
+       this));
+    return false;
+  }
+
   TSFStaticSink::GetInstance()->EnsureInitActiveTIPKeyboard();
 
   if (mDocumentMgr) {
     MOZ_LOG(sTextStoreLog, LogLevel::Error,
       ("0x%p   TSFTextStore::Init() FAILED due to already initialized",
        this));
     return false;
   }
 
-  // Create document manager
-  HRESULT hr = sThreadMgr->CreateDocumentMgr(getter_AddRefs(mDocumentMgr));
-  if (FAILED(hr)) {
-    MOZ_LOG(sTextStoreLog, LogLevel::Error,
-      ("0x%p   TSFTextStore::Init() FAILED to create DocumentMgr "
-       "(0x%08X)", this, hr));
-    return false;
-  }
   mWidget = aWidget;
   if (NS_WARN_IF(!mWidget)) {
     MOZ_LOG(sTextStoreLog, LogLevel::Error,
       ("0x%p   TSFTextStore::Init() FAILED "
        "due to aWidget is nullptr ", this));
     return false;
   }
   mDispatcher = mWidget->GetTextEventDispatcher();
   if (NS_WARN_IF(!mDispatcher)) {
     MOZ_LOG(sTextStoreLog, LogLevel::Error,
       ("0x%p   TSFTextStore::Init() FAILED "
        "due to aWidget->GetTextEventDispatcher() failure", this));
     return false;
   }
 
+  SetInputScope(aContext.mHTMLInputType, aContext.mHTMLInputInputmode);
+
+  // Create document manager
+  RefPtr<ITfThreadMgr> threadMgr = sThreadMgr;
+  RefPtr<ITfDocumentMgr> documentMgr;
+  HRESULT hr = threadMgr->CreateDocumentMgr(getter_AddRefs(documentMgr));
+  if (NS_WARN_IF(FAILED(hr))) {
+    MOZ_LOG(sTextStoreLog, LogLevel::Error,
+      ("0x%p   TSFTextStore::Init() FAILED to create ITfDocumentMgr "
+       "(0x%08X)", this, hr));
+    return false;
+  }
+  if (NS_WARN_IF(mDestroyed)) {
+    MOZ_LOG(sTextStoreLog, LogLevel::Error,
+      ("0x%p   TSFTextStore::Init() FAILED to create ITfDocumentMgr due to "
+       "TextStore being destroyed during calling "
+       "ITfThreadMgr::CreateDocumentMgr()", this));
+    return false;
+  }
   // Create context and add it to document manager
-  hr = mDocumentMgr->CreateContext(sClientId, 0,
-                                   static_cast<ITextStoreACP*>(this),
-                                   getter_AddRefs(mContext), &mEditCookie);
-  if (FAILED(hr)) {
+  RefPtr<ITfContext> context;
+  hr = documentMgr->CreateContext(sClientId, 0,
+                                  static_cast<ITextStoreACP*>(this),
+                                  getter_AddRefs(context), &mEditCookie);
+  if (NS_WARN_IF(FAILED(hr))) {
     MOZ_LOG(sTextStoreLog, LogLevel::Error,
       ("0x%p   TSFTextStore::Init() FAILED to create the context "
        "(0x%08X)", this, hr));
-    mDocumentMgr = nullptr;
     return false;
   }
-
-  SetInputScope(aContext.mHTMLInputType, aContext.mHTMLInputInputmode);
-
-  hr = mDocumentMgr->Push(mContext);
-  if (FAILED(hr)) {
+  if (NS_WARN_IF(mDestroyed)) {
+    MOZ_LOG(sTextStoreLog, LogLevel::Error,
+      ("0x%p   TSFTextStore::Init() FAILED to create ITfContext due to "
+       "TextStore being destroyed during calling "
+       "ITfDocumentMgr::CreateContext()", this));
+    return false;
+  }
+
+  hr = documentMgr->Push(context);
+  if (NS_WARN_IF(FAILED(hr))) {
     MOZ_LOG(sTextStoreLog, LogLevel::Error,
       ("0x%p   TSFTextStore::Init() FAILED to push the context (0x%08X)",
        this, hr));
-    // XXX Why don't we use NS_IF_RELEASE() here??
-    mContext = nullptr;
-    mDocumentMgr = nullptr;
     return false;
   }
+  if (NS_WARN_IF(mDestroyed)) {
+    MOZ_LOG(sTextStoreLog, LogLevel::Error,
+      ("0x%p   TSFTextStore::Init() FAILED to create ITfContext due to "
+       "TextStore being destroyed during calling ITfDocumentMgr::Push()",
+       this));
+    documentMgr->Pop(TF_POPF_ALL);
+    return false;
+  }
+
+  mDocumentMgr = documentMgr;
+  mContext = context;
 
   MOZ_LOG(sTextStoreLog, LogLevel::Info,
     ("0x%p   TSFTextStore::Init() succeeded: "
      "mDocumentMgr=0x%p, mContext=0x%p, mEditCookie=0x%08X",
      this, mDocumentMgr.get(), mContext.get(), mEditCookie));
 
   return true;
 }
@@ -1653,17 +1685,18 @@ TSFTextStore::Destroy()
     CommitCompositionInternal(false);
   }
 
   if (mSink) {
     MOZ_LOG(sTextStoreLog, LogLevel::Debug,
       ("0x%p   TSFTextStore::Destroy(), calling "
        "ITextStoreACPSink::OnLayoutChange(TS_LC_DESTROY)...",
        this));
-    mSink->OnLayoutChange(TS_LC_DESTROY, TEXTSTORE_DEFAULT_VIEW);
+    RefPtr<ITextStoreACPSink> sink = mSink;
+    sink->OnLayoutChange(TS_LC_DESTROY, TEXTSTORE_DEFAULT_VIEW);
   }
 
   // If this is called during handling a keydown or keyup message, we should
   // put off to release TSF objects until it completely finishes since
   // MS-IME for Japanese refers some objects without grabbing them.
   if (!mHandlingKeyMessage) {
     ReleaseTSFObjects();
   }
@@ -1677,18 +1710,18 @@ TSFTextStore::ReleaseTSFObjects()
 {
   MOZ_ASSERT(!mHandlingKeyMessage);
 
   MOZ_LOG(sTextStoreLog, LogLevel::Info,
     ("0x%p TSFTextStore::ReleaseTSFObjects()", this));
 
   mContext = nullptr;
   if (mDocumentMgr) {
-    mDocumentMgr->Pop(TF_POPF_ALL);
-    mDocumentMgr = nullptr;
+    RefPtr<ITfDocumentMgr> documentMgr = mDocumentMgr.forget();
+    documentMgr->Pop(TF_POPF_ALL);
   }
   mSink = nullptr;
   mWidget = nullptr;
   mDispatcher = nullptr;
 
   if (!mMouseTrackers.IsEmpty()) {
     MOZ_LOG(sTextStoreLog, LogLevel::Debug,
       ("0x%p   TSFTextStore::ReleaseTSFObjects(), "
@@ -1845,30 +1878,31 @@ TSFTextStore::RequestLock(DWORD dwLockFl
     mLock = dwLockFlags & (~TS_LF_SYNC);
     MOZ_LOG(sTextStoreLog, LogLevel::Info,
       ("0x%p   Locking (%s) >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
        ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",
        this, GetLockFlagNameStr(mLock).get()));
     // Don't release this instance during this lock because this is called by
     // TSF but they don't grab us during this call.
     RefPtr<TSFTextStore> kungFuDeathGrip(this);
-    *phrSession = mSink->OnLockGranted(mLock);
+    RefPtr<ITextStoreACPSink> sink = mSink;
+    *phrSession = sink->OnLockGranted(mLock);
     MOZ_LOG(sTextStoreLog, LogLevel::Info,
       ("0x%p   Unlocked (%s) <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
        "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<",
        this, GetLockFlagNameStr(mLock).get()));
     DidLockGranted();
     while (mLockQueued) {
       mLock = mLockQueued;
       mLockQueued = 0;
       MOZ_LOG(sTextStoreLog, LogLevel::Info,
         ("0x%p   Locking for the request in the queue (%s) >>>>>>>>>>>>>>"
          ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",
          this, GetLockFlagNameStr(mLock).get()));
-      mSink->OnLockGranted(mLock);
+      sink->OnLockGranted(mLock);
       MOZ_LOG(sTextStoreLog, LogLevel::Info,
         ("0x%p   Unlocked (%s) <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
          "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<",
          this, GetLockFlagNameStr(mLock).get()));
       DidLockGranted();
     }
 
     // The document is now completely unlocked.
@@ -2182,16 +2216,18 @@ TSFTextStore::MaybeFlushPendingNotificat
     mContentForTSF.Clear();
     MOZ_LOG(sTextStoreLog, LogLevel::Debug,
       ("0x%p   TSFTextStore::MaybeFlushPendingNotifications(), "
        "mContentForTSF is cleared", this));
   }
 
   // When there is no cached content, we can sync actual contents and TSF/TIP
   // expecting contents.
+  RefPtr<TSFTextStore> kungFuDeathGrip = this;
+  Unused << kungFuDeathGrip;
   if (!mContentForTSF.IsInitialized()) {
     if (mPendingTextChangeData.IsValid()) {
       MOZ_LOG(sTextStoreLog, LogLevel::Info,
         ("0x%p   TSFTextStore::MaybeFlushPendingNotifications(), "
          "calling TSFTextStore::NotifyTSFOfTextChange()...", this));
       NotifyTSFOfTextChange();
     }
     if (mPendingSelectionChangeData.IsValid()) {
@@ -4940,130 +4976,201 @@ TSFTextStore::OnFocusChange(bool aGotFoc
      GetInputContextString(aContext).get(),
      sThreadMgr.get(), sEnabledTextStore.get()));
 
   if (NS_WARN_IF(!IsInTSFMode())) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   RefPtr<ITfDocumentMgr> prevFocusedDocumentMgr;
+  RefPtr<TSFTextStore> oldTextStore = sEnabledTextStore.forget();
 
   // If currently sEnableTextStore has focus, notifies TSF of losing focus.
   if (ThinksHavingFocus()) {
+    RefPtr<ITfThreadMgr> threadMgr = sThreadMgr;
     DebugOnly<HRESULT> hr =
-      sThreadMgr->AssociateFocus(
-        sEnabledTextStore->mWidget->GetWindowHandle(),
+      threadMgr->AssociateFocus(
+        oldTextStore->mWidget->GetWindowHandle(),
         nullptr, getter_AddRefs(prevFocusedDocumentMgr));
     NS_ASSERTION(SUCCEEDED(hr), "Disassociating focus failed");
-    NS_ASSERTION(prevFocusedDocumentMgr == sEnabledTextStore->mDocumentMgr,
+    NS_ASSERTION(prevFocusedDocumentMgr == oldTextStore->mDocumentMgr,
                  "different documentMgr has been associated with the window");
   }
 
   // If there is sEnabledTextStore, we don't use it in the new focused editor.
   // Release it now.
-  if (sEnabledTextStore) {
-    sEnabledTextStore->Destroy();
-    sEnabledTextStore = nullptr;
+  if (oldTextStore) {
+    oldTextStore->Destroy();
+  }
+
+  if (NS_WARN_IF(!sThreadMgr)) {
+    MOZ_LOG(sTextStoreLog, LogLevel::Error,
+      ("  TSFTextStore::OnFocusChange() FAILED, due to "
+       "sThreadMgr being destroyed during calling "
+       "ITfThreadMgr::AssociateFocus()"));
+    return NS_ERROR_FAILURE;
+  }
+  if (NS_WARN_IF(sEnabledTextStore)) {
+    MOZ_LOG(sTextStoreLog, LogLevel::Error,
+      ("  TSFTextStore::OnFocusChange() FAILED, due to "
+       "nested event handling has created another focused TextStore during "
+       "calling ITfThreadMgr::AssociateFocus()"));
+    return NS_ERROR_FAILURE;
   }
 
   // If this is a notification of blur, move focus to the dummy document
   // manager.
   if (!aGotFocus || !aContext.mIMEState.IsEditable()) {
-    HRESULT hr = sThreadMgr->SetFocus(sDisabledDocumentMgr);
+    RefPtr<ITfThreadMgr> threadMgr = sThreadMgr;
+    RefPtr<ITfDocumentMgr> disabledDocumentMgr = sDisabledDocumentMgr;
+    HRESULT hr = threadMgr->SetFocus(disabledDocumentMgr);
     if (NS_WARN_IF(FAILED(hr))) {
       MOZ_LOG(sTextStoreLog, LogLevel::Error,
         ("  TSFTextStore::OnFocusChange() FAILED due to "
          "ITfThreadMgr::SetFocus() failure"));
       return NS_ERROR_FAILURE;
     }
     return NS_OK;
   }
 
   // If an editor is getting focus, create new TextStore and set focus.
   if (NS_WARN_IF(!CreateAndSetFocus(aFocusedWidget, aContext))) {
     MOZ_LOG(sTextStoreLog, LogLevel::Error,
       ("  TSFTextStore::OnFocusChange() FAILED due to "
        "ITfThreadMgr::CreateAndSetFocus() failure"));
-    // If setting focus, we should destroy the TextStore completely because
-    // it causes memory leak.
-    if (sEnabledTextStore) {
-      sEnabledTextStore->Destroy();
-      sEnabledTextStore = nullptr;
-    }
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 // static
+void
+TSFTextStore::EnsureToDestroyAndReleaseEnabledTextStoreIf(
+                RefPtr<TSFTextStore>& aTextStore)
+{
+  aTextStore->Destroy();
+  if (sEnabledTextStore == aTextStore) {
+    sEnabledTextStore = nullptr;
+  }
+  aTextStore = nullptr;
+}
+
+// static
 bool
 TSFTextStore::CreateAndSetFocus(nsWindowBase* aFocusedWidget,
                                 const InputContext& aContext)
 {
   // TSF might do something which causes that we need to access static methods
   // of TSFTextStore.  At that time, sEnabledTextStore may be necessary.
   // So, we should set sEnabledTextStore directly.
   RefPtr<TSFTextStore> textStore = new TSFTextStore();
   sEnabledTextStore = textStore;
   if (NS_WARN_IF(!textStore->Init(aFocusedWidget, aContext))) {
     MOZ_LOG(sTextStoreLog, LogLevel::Error,
       ("  TSFTextStore::CreateAndSetFocus() FAILED due to "
        "TSFTextStore::Init() failure"));
+    EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore);
     return false;
   }
-  if (NS_WARN_IF(!textStore->mDocumentMgr)) {
+  RefPtr<ITfDocumentMgr> newDocMgr = textStore->mDocumentMgr;
+  if (NS_WARN_IF(!newDocMgr)) {
     MOZ_LOG(sTextStoreLog, LogLevel::Error,
       ("  TSFTextStore::CreateAndSetFocus() FAILED due to "
        "invalid TSFTextStore::mDocumentMgr"));
+    EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore);
     return false;
   }
   if (aContext.mIMEState.mEnabled == IMEState::PASSWORD) {
     MarkContextAsKeyboardDisabled(textStore->mContext);
     RefPtr<ITfContext> topContext;
-    textStore->mDocumentMgr->GetTop(getter_AddRefs(topContext));
+    newDocMgr->GetTop(getter_AddRefs(topContext));
     if (topContext && topContext != textStore->mContext) {
       MarkContextAsKeyboardDisabled(topContext);
     }
   }
 
   HRESULT hr;
+  RefPtr<ITfThreadMgr> threadMgr = sThreadMgr;
   {
     // Windows 10's softwware keyboard requires that SetSelection must be
     // always successful into SetFocus.  If returning error, it might crash
     // into TextInputFramework.dll.
     AutoSetTemporarySelection setSelection(textStore->SelectionForTSFRef());
 
-    hr = sThreadMgr->SetFocus(textStore->mDocumentMgr);
+    hr = threadMgr->SetFocus(newDocMgr);
   }
 
   if (NS_WARN_IF(FAILED(hr))) {
     MOZ_LOG(sTextStoreLog, LogLevel::Error,
       ("  TSFTextStore::CreateAndSetFocus() FAILED due to "
        "ITfTheadMgr::SetFocus() failure"));
+    EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore);
     return false;
   }
+  if (NS_WARN_IF(!sThreadMgr)) {
+    MOZ_LOG(sTextStoreLog, LogLevel::Error,
+      ("  TSFTextStore::CreateAndSetFocus() FAILED due to "
+       "sThreadMgr being destroyed during calling "
+       "ITfTheadMgr::SetFocus()"));
+    EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore);
+    return false;
+  }
+  if (NS_WARN_IF(sEnabledTextStore != textStore)) {
+    MOZ_LOG(sTextStoreLog, LogLevel::Error,
+      ("  TSFTextStore::CreateAndSetFocus() FAILED due to "
+       "creating TextStore has lost focus during calling "
+       "ITfThreadMgr::SetFocus()"));
+    EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore);
+    return false;
+  }
+
   // Use AssociateFocus() for ensuring that any native focus event
   // never steal focus from our documentMgr.
   RefPtr<ITfDocumentMgr> prevFocusedDocumentMgr;
-  hr = sThreadMgr->AssociateFocus(aFocusedWidget->GetWindowHandle(),
-                                  textStore->mDocumentMgr,
-                                  getter_AddRefs(prevFocusedDocumentMgr));
+  hr = threadMgr->AssociateFocus(aFocusedWidget->GetWindowHandle(), newDocMgr,
+                                 getter_AddRefs(prevFocusedDocumentMgr));
   if (NS_WARN_IF(FAILED(hr))) {
     MOZ_LOG(sTextStoreLog, LogLevel::Error,
       ("  TSFTextStore::CreateAndSetFocus() FAILED due to "
        "ITfTheadMgr::AssociateFocus() failure"));
+    EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore);
+    return false;
+  }
+  if (NS_WARN_IF(!sThreadMgr)) {
+    MOZ_LOG(sTextStoreLog, LogLevel::Error,
+      ("  TSFTextStore::CreateAndSetFocus() FAILED due to "
+       "sThreadMgr being destroyed during calling "
+       "ITfTheadMgr::AssociateFocus()"));
+    EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore);
+    return false;
+  }
+  if (NS_WARN_IF(sEnabledTextStore != textStore)) {
+    MOZ_LOG(sTextStoreLog, LogLevel::Error,
+      ("  TSFTextStore::CreateAndSetFocus() FAILED due to "
+       "creating TextStore has lost focus during calling "
+       "ITfTheadMgr::AssociateFocus()"));
+    EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore);
     return false;
   }
 
   if (textStore->mSink) {
     MOZ_LOG(sTextStoreLog, LogLevel::Info,
       ("  TSFTextStore::CreateAndSetFocus(), calling "
        "ITextStoreACPSink::OnLayoutChange(TS_LC_CREATE) for 0x%p...",
        textStore.get()));
-    textStore->mSink->OnLayoutChange(TS_LC_CREATE, TEXTSTORE_DEFAULT_VIEW);
+    RefPtr<ITextStoreACPSink> sink = textStore->mSink;
+    sink->OnLayoutChange(TS_LC_CREATE, TEXTSTORE_DEFAULT_VIEW);
+    if (NS_WARN_IF(sEnabledTextStore != textStore)) {
+      MOZ_LOG(sTextStoreLog, LogLevel::Error,
+        ("  TSFTextStore::CreateAndSetFocus() FAILED due to "
+         "creating TextStore has lost focus during calling "
+         "ITextStoreACPSink::OnLayoutChange(TS_LC_CREATE)"));
+      EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore);
+      return false;
+    }
   }
   return true;
 }
 
 // static
 IMENotificationRequests
 TSFTextStore::GetIMENotificationRequests()
 {
@@ -5178,17 +5285,18 @@ TSFTextStore::NotifyTSFOfTextChange()
     static_cast<LONG>(mPendingTextChangeData.mAddedEndOffset);
   mPendingTextChangeData.Clear();
 
   MOZ_LOG(sTextStoreLog, LogLevel::Info,
     ("0x%p   TSFTextStore::NotifyTSFOfTextChange(), calling "
      "ITextStoreACPSink::OnTextChange(0, { acpStart=%ld, acpOldEnd=%ld, "
      "acpNewEnd=%ld })...", this, textChange.acpStart,
      textChange.acpOldEnd, textChange.acpNewEnd));
-  mSink->OnTextChange(0, &textChange);
+  RefPtr<ITextStoreACPSink> sink = mSink;
+  sink->OnTextChange(0, &textChange);
 }
 
 nsresult
 TSFTextStore::OnSelectionChangeInternal(const IMENotification& aIMENotification)
 {
   const SelectionChangeDataBase& selectionChangeData =
     aIMENotification.mSelectionChangeData;
   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
@@ -5257,17 +5365,18 @@ TSFTextStore::NotifyTSFOfSelectionChange
 
   if (!mSink || !(mSinkMask & TS_AS_SEL_CHANGE)) {
     return;
   }
 
   MOZ_LOG(sTextStoreLog, LogLevel::Info,
     ("0x%p   TSFTextStore::NotifyTSFOfSelectionChange(), calling "
      "ITextStoreACPSink::OnSelectionChange()...", this));
-  mSink->OnSelectionChange();
+  RefPtr<ITextStoreACPSink> sink = mSink;
+  sink->OnSelectionChange();
 }
 
 nsresult
 TSFTextStore::OnLayoutChangeInternal()
 {
   if (mDestroyed) {
     // If this instance is already destroyed, we shouldn't notify TSF of any
     // changes.
@@ -5329,17 +5438,18 @@ TSFTextStore::NotifyTSFOfLayoutChange()
   // This method should return true if either way succeeds.
   bool ret = true;
 
   if (mSink) {
     MOZ_LOG(sTextStoreLog, LogLevel::Info,
       ("0x%p   TSFTextStore::NotifyTSFOfLayoutChange(), "
        "calling ITextStoreACPSink::OnLayoutChange()...",
        this));
-    HRESULT hr = mSink->OnLayoutChange(TS_LC_CHANGE, TEXTSTORE_DEFAULT_VIEW);
+    RefPtr<ITextStoreACPSink> sink = mSink;
+    HRESULT hr = sink->OnLayoutChange(TS_LC_CHANGE, TEXTSTORE_DEFAULT_VIEW);
     MOZ_LOG(sTextStoreLog, LogLevel::Info,
       ("0x%p   TSFTextStore::NotifyTSFOfLayoutChange(), "
        "called ITextStoreACPSink::OnLayoutChange()",
        this));
     ret = SUCCEEDED(hr);
   }
 
   // The layout change caused by composition string change should cause
@@ -5689,17 +5799,18 @@ TSFTextStore::CommitCompositionInternal(
       textChange.acpStart = mComposition.mStart;
       textChange.acpOldEnd = endOffset;
       textChange.acpNewEnd = mComposition.mStart;
       MOZ_LOG(sTextStoreLog, LogLevel::Info,
         ("0x%p   TSFTextStore::CommitCompositionInternal(), calling"
          "mSink->OnTextChange(0, { acpStart=%ld, acpOldEnd=%ld, "
          "acpNewEnd=%ld })...", this, textChange.acpStart,
          textChange.acpOldEnd, textChange.acpNewEnd));
-      mSink->OnTextChange(0, &textChange);
+      RefPtr<ITextStoreACPSink> sink = mSink;
+      sink->OnTextChange(0, &textChange);
     }
   }
   // Terminate two contexts, the base context (mContext) and the top
   // if the top context is not the same as the base context
   RefPtr<ITfContext> context = mContext;
   do {
     if (context) {
       RefPtr<ITfContextOwnerCompositionServices> services;
@@ -6098,45 +6209,47 @@ bool
 TSFTextStore::ProcessRawKeyMessage(const MSG& aMsg)
 {
   if (!sKeystrokeMgr) {
     return false; // not in TSF mode
   }
 
   if (aMsg.message == WM_KEYDOWN) {
     BOOL eaten;
-    HRESULT hr = sKeystrokeMgr->TestKeyDown(aMsg.wParam, aMsg.lParam, &eaten);
-    if (FAILED(hr) || !eaten) {
+    RefPtr<ITfKeystrokeMgr> keystrokeMgr = sKeystrokeMgr;
+    HRESULT hr = keystrokeMgr->TestKeyDown(aMsg.wParam, aMsg.lParam, &eaten);
+    if (FAILED(hr) || !sKeystrokeMgr || !eaten) {
       return false;
     }
     RefPtr<TSFTextStore> textStore(sEnabledTextStore);
     if (textStore) {
       textStore->OnStartToHandleKeyMessage();
     }
-    hr = sKeystrokeMgr->KeyDown(aMsg.wParam, aMsg.lParam, &eaten);
+    hr = keystrokeMgr->KeyDown(aMsg.wParam, aMsg.lParam, &eaten);
     if (textStore) {
       textStore->OnEndHandlingKeyMessage();
     }
-    return SUCCEEDED(hr) && eaten;
+    return SUCCEEDED(hr) && (eaten || !sKeystrokeMgr);
   }
   if (aMsg.message == WM_KEYUP) {
     BOOL eaten;
-    HRESULT hr = sKeystrokeMgr->TestKeyUp(aMsg.wParam, aMsg.lParam, &eaten);
-    if (FAILED(hr) || !eaten) {
+    RefPtr<ITfKeystrokeMgr> keystrokeMgr = sKeystrokeMgr;
+    HRESULT hr = keystrokeMgr->TestKeyUp(aMsg.wParam, aMsg.lParam, &eaten);
+    if (FAILED(hr) || !sKeystrokeMgr || !eaten) {
       return false;
     }
     RefPtr<TSFTextStore> textStore(sEnabledTextStore);
     if (textStore) {
       textStore->OnStartToHandleKeyMessage();
     }
-    hr = sKeystrokeMgr->KeyUp(aMsg.wParam, aMsg.lParam, &eaten);
+    hr = keystrokeMgr->KeyUp(aMsg.wParam, aMsg.lParam, &eaten);
     if (textStore) {
       textStore->OnEndHandlingKeyMessage();
     }
-    return SUCCEEDED(hr) && eaten;
+    return SUCCEEDED(hr) && (eaten || !sKeystrokeMgr);
   }
   return false;
 }
 
 // static
 void
 TSFTextStore::ProcessMessage(nsWindowBase* aWindow,
                              UINT aMessage,
@@ -6476,17 +6589,18 @@ TSFTextStore::MouseTracker::UnadviseSink
 bool
 TSFTextStore::MouseTracker::OnMouseButtonEvent(ULONG aEdge,
                                                ULONG aQuadrant,
                                                DWORD aButtonStatus)
 {
   MOZ_ASSERT(IsUsing(), "The caller must check before calling OnMouseEvent()");
 
   BOOL eaten = FALSE;
-  HRESULT hr = mSink->OnMouseEvent(aEdge, aQuadrant, aButtonStatus, &eaten);
+  RefPtr<ITfMouseSink> sink = mSink;
+  HRESULT hr = sink->OnMouseEvent(aEdge, aQuadrant, aButtonStatus, &eaten);
 
   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
     ("0x%p   TSFTextStore::MouseTracker::OnMouseEvent(aEdge=%d, "
      "aQuadrant=%d, aButtonStatus=0x%08X), hr=0x%08X, eaten=%s",
      this, aEdge, aQuadrant, aButtonStatus, hr, GetBoolName(!!eaten)));
 
   return SUCCEEDED(hr) && eaten;
 }
--- a/widget/windows/TSFTextStore.h
+++ b/widget/windows/TSFTextStore.h
@@ -243,16 +243,18 @@ public:
 #endif // #ifdef DEBUG
 
 protected:
   TSFTextStore();
   ~TSFTextStore();
 
   static bool CreateAndSetFocus(nsWindowBase* aFocusedWidget,
                                 const InputContext& aContext);
+  static void EnsureToDestroyAndReleaseEnabledTextStoreIf(
+                RefPtr<TSFTextStore>& aTextStore);
   static void MarkContextAsKeyboardDisabled(ITfContext* aContext);
   static void MarkContextAsEmpty(ITfContext* aContext);
 
   bool     Init(nsWindowBase* aWidget, const InputContext& aContext);
   void     Destroy();
   void     ReleaseTSFObjects();
 
   bool     IsReadLock(DWORD aLock) const
--- a/widget/windows/WinUtils.cpp
+++ b/widget/windows/WinUtils.cpp
@@ -689,17 +689,17 @@ WinUtils::PeekMessage(LPMSG aMsg, HWND a
                       UINT aLastMessage, UINT aOption)
 {
 #ifdef ACCESSIBILITY
   if (NS_IsMainThread() && sAPCPending.exchange(false)) {
     while (sNtTestAlert() != STATUS_SUCCESS) ;
   }
 #endif
 #ifdef NS_ENABLE_TSF
-  ITfMessagePump* msgPump = TSFTextStore::GetMessagePump();
+  RefPtr<ITfMessagePump> msgPump = TSFTextStore::GetMessagePump();
   if (msgPump) {
     BOOL ret = FALSE;
     HRESULT hr = msgPump->PeekMessageW(aMsg, aWnd, aFirstMessage, aLastMessage,
                                        aOption, &ret);
     NS_ENSURE_TRUE(SUCCEEDED(hr), false);
     return ret;
   }
 #endif // #ifdef NS_ENABLE_TSF
@@ -707,17 +707,17 @@ WinUtils::PeekMessage(LPMSG aMsg, HWND a
 }
 
 /* static */
 bool
 WinUtils::GetMessage(LPMSG aMsg, HWND aWnd, UINT aFirstMessage,
                      UINT aLastMessage)
 {
 #ifdef NS_ENABLE_TSF
-  ITfMessagePump* msgPump = TSFTextStore::GetMessagePump();
+  RefPtr<ITfMessagePump> msgPump = TSFTextStore::GetMessagePump();
   if (msgPump) {
     BOOL ret = FALSE;
     HRESULT hr = msgPump->GetMessageW(aMsg, aWnd, aFirstMessage, aLastMessage,
                                       &ret);
     NS_ENSURE_TRUE(SUCCEEDED(hr), false);
     return ret;
   }
 #endif // #ifdef NS_ENABLE_TSF