merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 07 Jul 2017 10:35:44 +0200
changeset 367763 a0b5515b13ebfd3220a06fd1cd31f98a9557f031
parent 367740 78ff4c023b6aea67ac8e6aa3085d73319bba11d9 (current diff)
parent 367762 7d409fc933909679a08f2e10c8eb387001ea228d (diff)
child 367764 173533a310060d0a52d6f6c3b3bee998ab9199d2
child 367772 96a10b07cab1a7c5dc0e87be367621ef83a6a56b
child 367824 c226c2704957ae33564ea07ae0a8e074eb5aa3c5
push id32143
push usercbook@mozilla.com
push dateFri, 07 Jul 2017 08:35:56 +0000
treeherdermozilla-central@a0b5515b13eb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone56.0a1
first release with
nightly linux32
a0b5515b13eb / 56.0a1 / 20170707100138 / files
nightly linux64
a0b5515b13eb / 56.0a1 / 20170707100138 / files
nightly mac
a0b5515b13eb / 56.0a1 / 20170707100152 / files
nightly win32
a0b5515b13eb / 56.0a1 / 20170707030206 / files
nightly win64
a0b5515b13eb / 56.0a1 / 20170707030206 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
dom/base/Link.h
--- a/accessible/windows/msaa/Compatibility.cpp
+++ b/accessible/windows/msaa/Compatibility.cpp
@@ -2,17 +2,19 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "Compatibility.h"
 
 #include "mozilla/WindowsVersion.h"
+#if defined(MOZ_CRASHREPORTER)
 #include "nsExceptionHandler.h"
+#endif // defined(MOZ_CRASHREPORTER)
 #include "nsUnicharUtils.h"
 #include "nsWindowsDllInterceptor.h"
 #include "nsWinUtils.h"
 #include "Statistics.h"
 
 #include "mozilla/Preferences.h"
 
 #include <shlobj.h>
@@ -348,21 +350,23 @@ UseIAccessibleProxyStub()
     return false;
   }
 
   // Otherwise we try the proxy/stub
   if (IsIAccessiblePSRegistered()) {
     return true;
   }
 
+#if defined(MOZ_CRASHREPORTER)
   // If we reach this point then something is seriously wrong with the
   // IAccessible configuration in the computer's registry. Let's annotate this
   // so that we can easily determine this condition during crash analysis.
   CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IAccessibleConfig"),
                                      NS_LITERAL_CSTRING("NoSystemTypeLibOrPS"));
+#endif // defined(MOZ_CRASHREPORTER)
   return false;
 }
 
 #endif // !defined(HAVE_64BIT_BUILD)
 
 uint16_t
 Compatibility::GetActCtxResourceId()
 {
--- a/browser/components/preferences/in-content-new/main.js
+++ b/browser/components/preferences/in-content-new/main.js
@@ -1,15 +1,16 @@
 /* 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 preferences.js */
 /* import-globals-from ../../../../toolkit/mozapps/preferences/fontbuilder.js */
 
+Components.utils.import("resource://gre/modules/Services.jsm");
 Components.utils.import("resource://gre/modules/Downloads.jsm");
 Components.utils.import("resource://gre/modules/FileUtils.jsm");
 Components.utils.import("resource:///modules/ShellService.jsm");
 Components.utils.import("resource:///modules/TransientPrefs.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "OS",
                                   "resource://gre/modules/osfile.jsm");
 
@@ -51,17 +52,35 @@ var gMainPane = {
     if (AppConstants.HAVE_SHELL_SERVICE) {
       this.updateSetDefaultBrowser();
       if (AppConstants.platform == "win") {
         // In Windows 8 we launch the control panel since it's the only
         // way to get all file type association prefs. So we don't know
         // when the user will select the default.  We refresh here periodically
         // in case the default changes. On other Windows OS's defaults can also
         // be set while the prefs are open.
-        window.setInterval(this.updateSetDefaultBrowser.bind(this), 1000);
+        let win = Services.wm.getMostRecentWindow("navigator:browser");
+
+        let pollForDefaultBrowser = () => {
+          let uri = win.gBrowser.currentURI.spec;
+
+          if ((uri == "about:preferences" || uri == "about:preferences#general") &&
+              document.visibilityState == "visible") {
+            this.updateSetDefaultBrowser();
+          }
+
+          // approximately a "requestIdleInterval"
+          window.setTimeout(() => {
+            window.requestIdleCallback(pollForDefaultBrowser);
+          }, 1000);
+        };
+
+        window.setTimeout(() => {
+          window.requestIdleCallback(pollForDefaultBrowser);
+        }, 1000);
       }
     }
 
     gEngineView = new EngineView(new EngineStore());
     document.getElementById("engineList").view = gEngineView;
     this.buildDefaultEngineDropDown();
 
     this.buildContentProcessCountMenuList();
--- a/browser/components/shell/moz.build
+++ b/browser/components/shell/moz.build
@@ -1,14 +1,19 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+# For BinaryPath::GetLong for Windows
+LOCAL_INCLUDES += [
+    '/xpcom/build'
+]
+
 XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
 BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
 
 JAR_MANIFESTS += ['jar.mn']
 
 XPIDL_SOURCES += [
     'nsIShellService.idl',
 ]
--- a/browser/components/shell/nsWindowsShellService.cpp
+++ b/browser/components/shell/nsWindowsShellService.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsWindowsShellService.h"
 
+#include "BinaryPath.h"
 #include "city.h"
 #include "imgIContainer.h"
 #include "imgIRequest.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/RefPtr.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMHTMLImageElement.h"
 #include "nsIImageLoadingContent.h"
@@ -223,23 +224,20 @@ nsWindowsShellService::IsDefaultBrowser(
                                 nullptr,
                                 CLSCTX_INPROC,
                                 IID_IApplicationAssociationRegistration,
                                 getter_AddRefs(pAAR));
   if (FAILED(hr)) {
     return NS_OK;
   }
 
-  wchar_t exePath[MAX_BUF] = L"";
-  if (!::GetModuleFileNameW(0, exePath, MAX_BUF)) {
-    return NS_OK;
-  }
-  // Convert the path to a long path since GetModuleFileNameW returns the path
-  // that was used to launch Firefox which is not necessarily a long path.
-  if (!::GetLongPathNameW(exePath, exePath, MAX_BUF)) {
+  wchar_t exePath[MAXPATHLEN] = L"";
+  nsresult rv = BinaryPath::GetLong(exePath);
+
+  if (NS_FAILED(rv)) {
     return NS_OK;
   }
 
   *aIsDefaultBrowser = IsPathDefaultForClass(pAAR, exePath, L"http");
   if (*aIsDefaultBrowser && aForAllTypes) {
     *aIsDefaultBrowser = IsPathDefaultForClass(pAAR, exePath, L".html");
   }
   return NS_OK;
--- a/dom/base/EventSource.cpp
+++ b/dom/base/EventSource.cpp
@@ -741,80 +741,35 @@ EventSourceImpl::ParseSegment(const char
     }
     if (result == kInputEmpty) {
       return;
     }
     src = src.From(read);
   }
 }
 
-class DataAvailableRunnable final : public Runnable
-{
-  private:
-    RefPtr<EventSourceImpl> mEventSourceImpl;
-    UniquePtr<char[]> mData;
-    uint32_t mLength;
-  public:
-    DataAvailableRunnable(EventSourceImpl* aEventSourceImpl,
-                          UniquePtr<char[]> aData,
-                          uint32_t aLength)
-      : Runnable("dom::DataAvailableRunnable")
-      , mEventSourceImpl(aEventSourceImpl)
-      , mData(Move(aData))
-      , mLength(aLength)
-    {
-    }
-
-    NS_IMETHOD Run() override
-    {
-      mEventSourceImpl->ParseSegment(mData.get(), mLength);
-      return NS_OK;
-    }
-};
-
 NS_IMETHODIMP
 EventSourceImpl::OnDataAvailable(nsIRequest* aRequest,
                                  nsISupports* aContext,
                                  nsIInputStream* aInputStream,
                                  uint64_t aOffset,
                                  uint32_t aCount)
 {
-  // Although we try to retarget OnDataAvailable to target thread, it may fail
-  // and fallback to main thread.
+  AssertIsOnTargetThread();
   NS_ENSURE_ARG_POINTER(aInputStream);
   if (IsClosed()) {
     return NS_ERROR_ABORT;
   }
 
   nsresult rv = CheckHealthOfRequestCallback(aRequest);
   NS_ENSURE_SUCCESS(rv, rv);
 
   uint32_t totalRead;
-  if (IsTargetThread()) {
-    rv = aInputStream->ReadSegments(EventSourceImpl::StreamReaderFunc, this,
+  return aInputStream->ReadSegments(EventSourceImpl::StreamReaderFunc, this,
                                     aCount, &totalRead);
-  } else {
-    // This could be happened when fail to retarget to target thread and
-    // fallback to the main thread.
-    AssertIsOnMainThread();
-    auto data = MakeUniqueFallible<char[]>(aCount);
-    if (!data) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-    rv = aInputStream->Read(data.get(), aCount, &totalRead);
-    NS_ENSURE_SUCCESS(rv, rv);
-    MOZ_ASSERT(totalRead <= aCount, "EventSource read more than available!!");
-
-    nsCOMPtr<nsIRunnable> dataAvailable =
-      new DataAvailableRunnable(this, Move(data), totalRead);
-
-    MOZ_ASSERT(mWorkerPrivate);
-    rv = Dispatch(dataAvailable.forget(), NS_DISPATCH_NORMAL);
-  }
-  return rv;
 }
 
 NS_IMETHODIMP
 EventSourceImpl::OnStopRequest(nsIRequest* aRequest,
                                nsISupports* aContext,
                                nsresult aStatusCode)
 {
   AssertIsOnMainThread();
--- a/dom/html/HTMLFormElement.cpp
+++ b/dom/html/HTMLFormElement.cpp
@@ -95,32 +95,32 @@ bool HTMLFormElement::gFirstFormSubmitte
 bool HTMLFormElement::gPasswordManagerInitialized = false;
 
 HTMLFormElement::HTMLFormElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
   : nsGenericHTMLElement(aNodeInfo),
     mControls(new HTMLFormControlsCollection(this)),
     mSelectedRadioButtons(2),
     mRequiredRadioButtonCounts(2),
     mValueMissingRadioGroups(2),
-    mGeneratingSubmit(false),
-    mGeneratingReset(false),
-    mIsSubmitting(false),
-    mDeferSubmission(false),
-    mNotifiedObservers(false),
-    mNotifiedObserversResult(false),
-    mSubmitPopupState(openAbused),
-    mSubmitInitiatedFromUserInput(false),
     mPendingSubmission(nullptr),
     mSubmittingRequest(nullptr),
     mDefaultSubmitElement(nullptr),
     mFirstSubmitInElements(nullptr),
     mFirstSubmitNotInElements(nullptr),
     mImageNameLookupTable(FORM_CONTROL_LIST_HASHTABLE_LENGTH),
     mPastNameLookupTable(FORM_CONTROL_LIST_HASHTABLE_LENGTH),
+    mSubmitPopupState(openAbused),
     mInvalidElementsCount(0),
+    mGeneratingSubmit(false),
+    mGeneratingReset(false),
+    mIsSubmitting(false),
+    mDeferSubmission(false),
+    mNotifiedObservers(false),
+    mNotifiedObserversResult(false),
+    mSubmitInitiatedFromUserInput(false),
     mEverTriedInvalidSubmit(false)
 {
   // We start out valid.
   AddStatesSilently(NS_EVENT_STATE_VALID);
 }
 
 HTMLFormElement::~HTMLFormElement()
 {
--- a/dom/html/HTMLFormElement.h
+++ b/dom/html/HTMLFormElement.h
@@ -555,32 +555,16 @@ protected:
   /** The list of controls (form.elements as well as stuff not in elements) */
   RefPtr<HTMLFormControlsCollection> mControls;
   /** The currently selected radio button of each group */
   nsRefPtrHashtable<nsStringHashKey, HTMLInputElement> mSelectedRadioButtons;
   /** The number of required radio button of each group */
   nsDataHashtable<nsStringCaseInsensitiveHashKey,uint32_t> mRequiredRadioButtonCounts;
   /** The value missing state of each group */
   nsDataHashtable<nsStringCaseInsensitiveHashKey,bool> mValueMissingRadioGroups;
-  /** Whether we are currently processing a submit event or not */
-  bool mGeneratingSubmit;
-  /** Whether we are currently processing a reset event or not */
-  bool mGeneratingReset;
-  /** Whether we are submitting currently */
-  bool mIsSubmitting;
-  /** Whether the submission is to be deferred in case a script triggers it */
-  bool mDeferSubmission;
-  /** Whether we notified NS_FORMSUBMIT_SUBJECT listeners already */
-  bool mNotifiedObservers;
-  /** If we notified the listeners early, what was the result? */
-  bool mNotifiedObserversResult;
-  /** Keep track of what the popup state was when the submit was initiated */
-  PopupControlState mSubmitPopupState;
-  /** Keep track of whether a submission was user-initiated or not */
-  bool mSubmitInitiatedFromUserInput;
 
   /** The pending submission object */
   nsAutoPtr<HTMLFormSubmission> mPendingSubmission;
   /** The request currently being submitted */
   nsCOMPtr<nsIRequest> mSubmittingRequest;
   /** The web progress object we are currently listening to */
   nsWeakPtr mWebProgress;
 
@@ -607,23 +591,40 @@ protected:
 
   nsInterfaceHashtable<nsStringHashKey,nsISupports> mImageNameLookupTable;
 
   // A map from names to elements that were gotten by those names from this
   // form in that past.  See "past names map" in the HTML5 specification.
 
   nsInterfaceHashtable<nsStringHashKey,nsISupports> mPastNameLookupTable;
 
+  /** Keep track of what the popup state was when the submit was initiated */
+  PopupControlState mSubmitPopupState;
+
   /**
    * Number of invalid and candidate for constraint validation elements in the
    * form the last time UpdateValidity has been called.
    * @note Should only be used by UpdateValidity() and GetValidity()!
    */
   int32_t mInvalidElementsCount;
 
+  /** Whether we are currently processing a submit event or not */
+  bool mGeneratingSubmit;
+  /** Whether we are currently processing a reset event or not */
+  bool mGeneratingReset;
+  /** Whether we are submitting currently */
+  bool mIsSubmitting;
+  /** Whether the submission is to be deferred in case a script triggers it */
+  bool mDeferSubmission;
+  /** Whether we notified NS_FORMSUBMIT_SUBJECT listeners already */
+  bool mNotifiedObservers;
+  /** If we notified the listeners early, what was the result? */
+  bool mNotifiedObserversResult;
+  /** Keep track of whether a submission was user-initiated or not */
+  bool mSubmitInitiatedFromUserInput;
   /**
    * Whether the submission of this form has been ever prevented because of
    * being invalid.
    */
   bool mEverTriedInvalidSubmit;
 
 protected:
   /** Detection of first form to notify observers */
--- a/gfx/2d/DrawEventRecorder.h
+++ b/gfx/2d/DrawEventRecorder.h
@@ -6,21 +6,17 @@
 #ifndef MOZILLA_GFX_DRAWEVENTRECORDER_H_
 #define MOZILLA_GFX_DRAWEVENTRECORDER_H_
 
 #include "2D.h"
 #include "RecordedEvent.h"
 #include <ostream>
 #include <fstream>
 
-#if defined(_MSC_VER)
 #include <unordered_set>
-#else
-#include <set>
-#endif
 
 namespace mozilla {
 namespace gfx {
 
 class PathRecording;
 
 class DrawEventRecorderPrivate : public DrawEventRecorder
 {
@@ -82,32 +78,20 @@ public:
 
   bool HasStoredFontData(const uint64_t aFontDataKey) {
     return mStoredFontData.find(aFontDataKey) != mStoredFontData.end();
   }
 
 protected:
   virtual void Flush() = 0;
 
-#if defined(_MSC_VER)
-  typedef std::unordered_set<const void*> ObjectSet;
-  typedef std::unordered_set<uint64_t> Uint64Set;
-  typedef std::unordered_set<ScaledFont*> FontSet;
-  typedef std::unordered_set<SourceSurface*> SurfaceSet;
-#else
-  typedef std::set<const void*> ObjectSet;
-  typedef std::set<uint64_t> Uint64Set;
-  typedef std::set<ScaledFont*> FontSet;
-  typedef std::set<SourceSurface*> SurfaceSet;
-#endif
-
-  ObjectSet mStoredObjects;
-  Uint64Set mStoredFontData;
-  FontSet mStoredFonts;
-  SurfaceSet mStoredSurfaces;
+  std::unordered_set<const void*> mStoredObjects;
+  std::unordered_set<uint64_t> mStoredFontData;
+  std::unordered_set<ScaledFont*> mStoredFonts;
+  std::unordered_set<SourceSurface*> mStoredSurfaces;
 };
 
 class DrawEventRecorderFile : public DrawEventRecorderPrivate
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorderFile, override)
   explicit DrawEventRecorderFile(const char *aFilename);
   ~DrawEventRecorderFile();
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -180,16 +180,22 @@ struct PreparedData
 };
 
 // ContainerPrepare is shared between RefLayer and ContainerLayer
 template<class ContainerT> void
 ContainerPrepare(ContainerT* aContainer,
                  LayerManagerComposite* aManager,
                  const RenderTargetIntRect& aClipRect)
 {
+  // We can end up calling prepare multiple times if we duplicated
+  // layers due to preserve-3d plane splitting. The results
+  // should be identical, so we only need to do it once.
+  if (aContainer->mPrepared) {
+    return;
+  }
   aContainer->mPrepared = MakeUnique<PreparedData>();
   aContainer->mPrepared->mNeedsSurfaceCopy = false;
 
   const ContainerLayerComposite::SortMode sortMode =
     aManager->GetCompositor()->SupportsLayerGeometry()
     ? ContainerLayerComposite::SortMode::WITH_GEOMETRY
     : ContainerLayerComposite::SortMode::WITHOUT_GEOMETRY;
 
@@ -580,17 +586,16 @@ ContainerRender(ContainerT* aContainer,
       surface = CreateTemporaryTargetAndCopyFromBackground(aContainer, aManager);
       RenderIntermediate(aContainer, aManager,
                          aClipRect, surface);
     } else {
       surface = aContainer->mPrepared->mTmpTarget;
     }
 
     if (!surface) {
-      aContainer->mPrepared = nullptr;
       return;
     }
 
     gfx::Rect visibleRect(aContainer->GetLocalVisibleRegion().ToUnknownRegion().GetBounds());
 
     RefPtr<Compositor> compositor = aManager->GetCompositor();
 #ifdef MOZ_DUMP_PAINTING
     if (gfxEnv::DumpCompositorTextures()) {
@@ -683,16 +688,20 @@ ContainerLayerComposite::GetFirstChildCo
    }
   return static_cast<LayerComposite*>(mFirstChild->AsHostLayer());
 }
 
 void
 ContainerLayerComposite::Cleanup()
 {
   mPrepared = nullptr;
+
+  for (Layer* l = GetFirstChild(); l; l = l->GetNextSibling()) {
+    static_cast<LayerComposite*>(l->AsHostLayer())->Cleanup();
+  }
 }
 
 void
 ContainerLayerComposite::RenderLayer(const gfx::IntRect& aClipRect,
                                      const Maybe<gfx::Polygon>& aGeometry)
 {
   ContainerRender(this, mCompositeManager, aClipRect, aGeometry);
 }
@@ -751,16 +760,26 @@ RefLayerComposite::RenderLayer(const gfx
 
 void
 RefLayerComposite::Prepare(const RenderTargetIntRect& aClipRect)
 {
   ContainerPrepare(this, mCompositeManager, aClipRect);
 }
 
 void
+RefLayerComposite::Cleanup()
+{
+  mPrepared = nullptr;
+
+  for (Layer* l = GetFirstChild(); l; l = l->GetNextSibling()) {
+    static_cast<LayerComposite*>(l->AsHostLayer())->Cleanup();
+  }
+}
+
+void
 RefLayerComposite::CleanupResources()
 {
   mLastIntermediateSurface = nullptr;
   mPrepared = nullptr;
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/composite/ContainerLayerComposite.h
+++ b/gfx/layers/composite/ContainerLayerComposite.h
@@ -178,16 +178,18 @@ public:
 
   virtual void Prepare(const RenderTargetIntRect& aClipRect) override;
 
   virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
   {
     DefaultComputeEffectiveTransforms(aTransformToSurface);
   }
 
+  virtual void Cleanup() override;
+
   virtual void CleanupResources() override;
 
   virtual HostLayer* AsHostLayer() override { return this; }
 
   // ref layers don't use a compositable
   CompositableHost* GetCompositableHost() override { return nullptr; }
 
   virtual const char* Name() const override { return "RefLayerComposite"; }
--- a/gfx/layers/d3d11/TextureD3D11.cpp
+++ b/gfx/layers/d3d11/TextureD3D11.cpp
@@ -927,17 +927,19 @@ DXGITextureHostD3D11::EnsureTextureSourc
   }
 
   if (!mTexture && !OpenSharedHandle()) {
     DeviceManagerDx::Get()->ForceDeviceReset(ForcedDeviceResetReason::OPENSHAREDHANDLE);
     return false;
   }
 
   if (mProvider) {
-    MOZ_RELEASE_ASSERT(mProvider->IsValid());
+    if (!mProvider->IsValid()) {
+      return false;
+    }
     mTextureSource = new DataTextureSourceD3D11(mFormat, mProvider, mTexture);
   } else {
     mTextureSource = new DataTextureSourceD3D11(mDevice, mFormat, mTexture);
   }
   return true;
 }
 
 void
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -1423,16 +1423,22 @@ bool
 CompositorBridgeParent::InitializeAdvancedLayers(const nsTArray<LayersBackend>& aBackendHints,
                                                  TextureFactoryIdentifier* aOutIdentifier)
 {
 #ifdef XP_WIN
   if (!mOptions.UseAdvancedLayers()) {
     return false;
   }
 
+  // Currently LayerManagerMLGPU hardcodes a D3D11 device, so we reject using
+  // AL if LAYERS_D3D11 isn't in the backend hints.
+  if (!aBackendHints.Contains(LayersBackend::LAYERS_D3D11)) {
+    return false;
+  }
+
   RefPtr<LayerManagerMLGPU> manager = new LayerManagerMLGPU(mWidget);
   if (!manager->Initialize()) {
     return false;
   }
 
   if (aOutIdentifier) {
     *aOutIdentifier = manager->GetTextureFactoryIdentifier();
   }
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -454,17 +454,17 @@ gfxWindowsPlatform::HandleDeviceReset()
 
 void
 gfxWindowsPlatform::UpdateBackendPrefs()
 {
   uint32_t canvasMask = BackendTypeBit(BackendType::CAIRO) |
                         BackendTypeBit(BackendType::SKIA);
   uint32_t contentMask = BackendTypeBit(BackendType::CAIRO) |
                          BackendTypeBit(BackendType::SKIA);
-  BackendType defaultBackend = BackendType::CAIRO;
+  BackendType defaultBackend = BackendType::SKIA;
   if (gfxConfig::IsEnabled(Feature::DIRECT2D) && Factory::GetD2D1Device()) {
     contentMask |= BackendTypeBit(BackendType::DIRECT2D1_1);
     canvasMask |= BackendTypeBit(BackendType::DIRECT2D1_1);
     defaultBackend = BackendType::DIRECT2D1_1;
   }
   InitBackendPrefs(canvasMask, defaultBackend, contentMask, defaultBackend);
 }
 
@@ -501,18 +501,18 @@ mozilla::gfx::BackendType
 gfxWindowsPlatform::GetContentBackendFor(mozilla::layers::LayersBackend aLayers)
 {
   mozilla::gfx::BackendType defaultBackend = gfxPlatform::GetDefaultContentBackend();
   if (aLayers == LayersBackend::LAYERS_D3D11) {
     return defaultBackend;
   }
 
   if (defaultBackend == BackendType::DIRECT2D1_1) {
-    // We can't have D2D without D3D11 layers, so fallback to Cairo.
-    return BackendType::CAIRO;
+    // We can't have D2D without D3D11 layers, so fallback to Skia.
+    return BackendType::SKIA;
   }
 
   // Otherwise we have some non-accelerated backend and that's ok.
   return defaultBackend;
 }
 
 gfxPlatformFontList*
 gfxWindowsPlatform::CreatePlatformFontList()
@@ -1382,37 +1382,49 @@ gfxWindowsPlatform::InitializeD3D11Confi
   }
 
   // Check if the user really, really wants WARP.
   if (gfxPrefs::LayersD3D11ForceWARP()) {
     // Force D3D11 on even if we disabled it.
     d3d11.UserForceEnable("User force-enabled WARP");
   }
 
-  // Only enable Advanced Layers if D3D11 succeeded.
-  if (d3d11.IsEnabled()) {
-    FeatureState& al = gfxConfig::GetFeature(Feature::ADVANCED_LAYERS);
+  InitializeAdvancedLayersConfig();
+}
 
-    al.SetDefaultFromPref(
-      gfxPrefs::GetAdvancedLayersEnabledDoNotUseDirectlyPrefName(),
-      true /* aIsEnablePref */,
-      gfxPrefs::GetAdvancedLayersEnabledDoNotUseDirectlyPrefDefault());
+/* static */ void
+gfxWindowsPlatform::InitializeAdvancedLayersConfig()
+{
+  // Only enable Advanced Layers if D3D11 succeeded.
+  if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
+    return;
+  }
 
-    // Windows 7 has an extra pref since it uses totally different buffer paths
-    // that haven't been performance tested yet.
-    if (al.IsEnabled() && !IsWin8OrLater()) {
-      if (gfxPrefs::AdvancedLayersEnableOnWindows7()) {
-        al.UserEnable("Enabled for Windows 7 via user-preference");
-      } else {
-        al.Disable(FeatureStatus::Disabled,
-                   "Advanced Layers is disabled on Windows 7 by default",
-                   NS_LITERAL_CSTRING("FEATURE_FAILURE_DISABLED_ON_WIN7"));
-      }
+  FeatureState& al = gfxConfig::GetFeature(Feature::ADVANCED_LAYERS);
+  al.SetDefaultFromPref(
+    gfxPrefs::GetAdvancedLayersEnabledDoNotUseDirectlyPrefName(),
+    true /* aIsEnablePref */,
+    gfxPrefs::GetAdvancedLayersEnabledDoNotUseDirectlyPrefDefault());
+
+  // Windows 7 has an extra pref since it uses totally different buffer paths
+  // that haven't been performance tested yet.
+  if (al.IsEnabled() && !IsWin8OrLater()) {
+    if (gfxPrefs::AdvancedLayersEnableOnWindows7()) {
+      al.UserEnable("Enabled for Windows 7 via user-preference");
+    } else {
+      al.Disable(FeatureStatus::Disabled,
+                 "Advanced Layers is disabled on Windows 7 by default",
+                 NS_LITERAL_CSTRING("FEATURE_FAILURE_DISABLED_ON_WIN7"));
     }
   }
+
+  nsCString message, failureId;
+  if (!IsGfxInfoStatusOkay(nsIGfxInfo::FEATURE_ADVANCED_LAYERS, &message, failureId)) {
+    al.Disable(FeatureStatus::Blacklisted, message.get(), failureId);
+  }
 }
 
 /* static */ void
 gfxWindowsPlatform::RecordContentDeviceFailure(TelemetryDeviceCode aDevice)
 {
   // If the parent process fails to acquire a device, we record this
   // normally as part of the environment. The exceptional case we're
   // looking for here is when the parent process successfully acquires
--- a/gfx/thebes/gfxWindowsPlatform.h
+++ b/gfx/thebes/gfxWindowsPlatform.h
@@ -253,16 +253,17 @@ private:
     void DisableD2D(mozilla::gfx::FeatureStatus aStatus, const char* aMessage,
                     const nsACString& aFailureId);
 
     void InitializeConfig();
     void InitializeD3D9Config();
     void InitializeD3D11Config();
     void InitializeD2DConfig();
     void InitializeDirectDrawConfig();
+    void InitializeAdvancedLayersConfig();
 
     RefPtr<IDWriteFactory> mDWriteFactory;
     RefPtr<IDWriteRenderingParams> mRenderingParams[TEXT_RENDERING_COUNT];
     DWRITE_MEASURING_MODE mMeasuringMode;
 
     RefPtr<mozilla::layers::ReadbackManagerD3D11> mD3D11ReadbackManager;
 
     nsTArray<D3D_FEATURE_LEVEL> mFeatureLevels;
--- a/ipc/mscom/EnsureMTA.h
+++ b/ipc/mscom/EnsureMTA.h
@@ -64,17 +64,21 @@ public:
 
     // In this case we need to run aClosure on a background thread in the MTA
     nsCOMPtr<nsIThread> thread = GetMTAThread();
     MOZ_ASSERT(thread);
     if (!thread) {
       return;
     }
 
-    static nsAutoHandle event(::CreateEventW(nullptr, FALSE, FALSE, nullptr));
+    // Note that we might reenter the EnsureMTA constructor while we wait on
+    // this event due to APC dispatch, therefore we need a unique event object
+    // for each entry. If perf becomes an issue then we will want to maintain
+    // an array of events where the Nth event is unique to the Nth reentry.
+    nsAutoHandle event(::CreateEventW(nullptr, FALSE, FALSE, nullptr));
     if (!event) {
       return;
     }
 
     HANDLE eventHandle = event.get();
 
     auto eventSetter = [&aClosure, eventHandle]() -> void {
       aClosure();
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -683,17 +683,18 @@ class GCRuntime
     void setMarkStackLimit(size_t limit, AutoLockGC& lock);
 
     MOZ_MUST_USE bool setParameter(JSGCParamKey key, uint32_t value, AutoLockGC& lock);
     uint32_t getParameter(JSGCParamKey key, const AutoLockGC& lock);
 
     MOZ_MUST_USE bool triggerGC(JS::gcreason::Reason reason);
     void maybeAllocTriggerZoneGC(Zone* zone, const AutoLockGC& lock);
     // The return value indicates if we were able to do the GC.
-    bool triggerZoneGC(Zone* zone, JS::gcreason::Reason reason);
+    bool triggerZoneGC(Zone* zone, JS::gcreason::Reason reason,
+                       size_t usedBytes, size_t thresholdBytes);
     void maybeGC(Zone* zone);
     // The return value indicates whether a major GC was performed.
     bool gcIfRequested();
     void gc(JSGCInvocationKind gckind, JS::gcreason::Reason reason);
     void startGC(JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64_t millis = 0);
     void gcSlice(JS::gcreason::Reason reason, int64_t millis = 0);
     void finishGC(JS::gcreason::Reason reason);
     void abortGC();
@@ -791,17 +792,20 @@ class GCRuntime
     bool isShrinkingGC() const { return invocationKind == GC_SHRINK; }
 
     static bool initializeSweepActions();
 
     void setGrayRootsTracer(JSTraceDataOp traceOp, void* data);
     MOZ_MUST_USE bool addBlackRootsTracer(JSTraceDataOp traceOp, void* data);
     void removeBlackRootsTracer(JSTraceDataOp traceOp, void* data);
 
-    bool triggerGCForTooMuchMalloc() { return triggerGC(JS::gcreason::TOO_MUCH_MALLOC); }
+    bool triggerGCForTooMuchMalloc() {
+        stats().recordTrigger(mallocCounter.bytes(), mallocCounter.maxBytes());
+        return triggerGC(JS::gcreason::TOO_MUCH_MALLOC);
+    }
     int32_t getMallocBytes() const { return mallocCounter.bytes(); }
     size_t maxMallocBytesAllocated() const { return mallocCounter.maxBytes(); }
     bool isTooMuchMalloc() const { return mallocCounter.isTooMuchMalloc(); }
     void resetMallocBytes() { mallocCounter.reset(); }
     void setMaxMallocBytes(size_t value);
     void updateMallocCounter(JS::Zone* zone, size_t nbytes);
 
     void setGCCallback(JSGCCallback callback, void* data);
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -612,16 +612,21 @@ Statistics::formatJsonSliceDescription(u
 
     json.property("slice", i);
     json.property("pause", slice.duration(), JSONPrinter::MILLISECONDS);
     json.property("when", when, JSONPrinter::MILLISECONDS);
     json.property("reason", ExplainReason(slice.reason));
     json.property("initial_state", gc::StateName(slice.initialState));
     json.property("final_state", gc::StateName(slice.finalState));
     json.property("budget", budgetDescription);
+    json.property("major_gc_number", startingMajorGCNumber);
+    if (thresholdTriggered) {
+        json.floatProperty("trigger_amount", triggerAmount, 0);
+        json.floatProperty("trigger_threshold", triggerThreshold, 0);
+    }
     json.property("page_faults", int64_t(slice.endFaults - slice.startFaults));
     json.property("start_timestamp", slice.start - originTime, JSONPrinter::SECONDS);
     json.property("end_timestamp", slice.end - originTime, JSONPrinter::SECONDS);
 }
 
 void
 Statistics::formatJsonPhaseTimes(const PhaseTimeTable& phaseTimes, JSONPrinter& json) const
 {
@@ -632,16 +637,19 @@ Statistics::formatJsonPhaseTimes(const P
     }
 }
 
 Statistics::Statistics(JSRuntime* rt)
   : runtime(rt),
     fp(nullptr),
     nonincrementalReason_(gc::AbortReason::None),
     preBytes(0),
+    thresholdTriggered(false),
+    triggerAmount(0.0),
+    triggerThreshold(0.0),
     maxPauseInInterval(0),
     sliceCallback(nullptr),
     nurseryCollectionCallback(nullptr),
     aborted(false),
     enableProfiling_(false),
     sliceCount_(0)
 {
     for (auto& count : counts)
@@ -886,16 +894,17 @@ Statistics::endGC()
         runtime->addTelemetry(JS_TELEMETRY_GC_MMU_50, mmu50 * 100);
     }
 
     if (fp)
         printStats();
 
     // Clear the OOM flag.
     aborted = false;
+    thresholdTriggered = false;
 }
 
 void
 Statistics::beginNurseryCollection(JS::gcreason::Reason reason)
 {
     count(STAT_MINOR_GC);
     startingMinorGCNumber = runtime->gc.minorGCCount();
     if (nurseryCollectionCallback) {
--- a/js/src/gc/Statistics.h
+++ b/js/src/gc/Statistics.h
@@ -182,16 +182,22 @@ struct Statistics
     void count(Stat s) {
         counts[s]++;
     }
 
     uint32_t getCount(Stat s) const {
         return uint32_t(counts[s]);
     }
 
+    void recordTrigger(double amount, double threshold) {
+        triggerAmount = amount;
+        triggerThreshold = threshold;
+        thresholdTriggered = true;
+    }
+
     void beginNurseryCollection(JS::gcreason::Reason reason);
     void endNurseryCollection(JS::gcreason::Reason reason);
 
     TimeStamp beginSCC();
     void endSCC(unsigned scc, TimeStamp start);
 
     UniqueChars formatCompactSliceMessage() const;
     UniqueChars formatCompactSummaryMessage() const;
@@ -291,16 +297,22 @@ struct Statistics
     /* 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;
 
+    /* If the GC was triggered by exceeding some threshold, record the
+     * threshold and the value that exceeded it. */
+    bool thresholdTriggered;
+    double triggerAmount;
+    double triggerThreshold;
+
     /* 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. */
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -402,20 +402,21 @@ struct Zone : public JS::shadow::Zone,
 
     JS::WeakCache<TypeDescrObjectSet>& typeDescrObjects() { return typeDescrObjects_.ref(); }
 
     bool addTypeDescrObject(JSContext* cx, HandleObject obj);
 
     bool triggerGCForTooMuchMalloc() {
         JSRuntime* rt = runtimeFromAnyThread();
 
-        if (CurrentThreadCanAccessRuntime(rt))
-            return rt->gc.triggerZoneGC(this, JS::gcreason::TOO_MUCH_MALLOC);
-        else
-            return false;
+        if (CurrentThreadCanAccessRuntime(rt)) {
+            return rt->gc.triggerZoneGC(this, JS::gcreason::TOO_MUCH_MALLOC,
+                                        gcMallocCounter.bytes(), gcMallocCounter.maxBytes());
+        }
+        return false;
     }
 
     void resetGCMallocBytes() { gcMallocCounter.reset(); }
     void setGCMaxMallocBytes(size_t value) { gcMallocCounter.setMax(value); }
     void updateMallocCounter(size_t nbytes) { gcMallocCounter.update(this, nbytes); }
     size_t GCMaxMallocBytes() const { return gcMallocCounter.maxBytes(); }
     size_t GCMallocBytes() const { return gcMallocCounter.bytes(); }
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -3025,17 +3025,17 @@ GCRuntime::maybeAllocTriggerZoneGC(Zone*
         return;
     }
 
     if (usedBytes >= thresholdBytes) {
         /*
          * The threshold has been surpassed, immediately trigger a GC,
          * which will be done non-incrementally.
          */
-        triggerZoneGC(zone, JS::gcreason::ALLOC_TRIGGER);
+        triggerZoneGC(zone, JS::gcreason::ALLOC_TRIGGER, usedBytes, thresholdBytes);
     } else {
         bool wouldInterruptCollection;
         size_t igcThresholdBytes;
         double zoneAllocThresholdFactor;
 
         wouldInterruptCollection = isIncrementalGCInProgress() &&
             !zone->isCollecting();
         zoneAllocThresholdFactor = wouldInterruptCollection ?
@@ -3051,28 +3051,28 @@ GCRuntime::maybeAllocTriggerZoneGC(Zone*
             else
                 zone->gcDelayBytes -= ArenaSize;
 
             if (!zone->gcDelayBytes) {
                 // Start or continue an in progress incremental GC. We do this
                 // to try to avoid performing non-incremental GCs on zones
                 // which allocate a lot of data, even when incremental slices
                 // can't be triggered via scheduling in the event loop.
-                triggerZoneGC(zone, JS::gcreason::ALLOC_TRIGGER);
+                triggerZoneGC(zone, JS::gcreason::ALLOC_TRIGGER, usedBytes, igcThresholdBytes);
 
                 // Delay the next slice until a certain amount of allocation
                 // has been performed.
                 zone->gcDelayBytes = tunables.zoneAllocDelayBytes();
             }
         }
     }
 }
 
 bool
-GCRuntime::triggerZoneGC(Zone* zone, JS::gcreason::Reason reason)
+GCRuntime::triggerZoneGC(Zone* zone, JS::gcreason::Reason reason, size_t used, size_t threshold)
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
 
     /* GC is already running. */
     if (JS::CurrentThreadIsHeapCollecting())
         return false;
 
 #ifdef JS_GC_ZEAL
@@ -3085,20 +3085,22 @@ GCRuntime::triggerZoneGC(Zone* zone, JS:
     if (zone->isAtomsZone()) {
         /* We can't do a zone GC of the atoms compartment. */
         if (TlsContext.get()->keepAtoms || rt->hasHelperThreadZones()) {
             /* Skip GC and retrigger later, since atoms zone won't be collected
              * if keepAtoms is true. */
             fullGCForAtomsRequested_ = true;
             return false;
         }
+        stats().recordTrigger(used, threshold);
         MOZ_RELEASE_ASSERT(triggerGC(reason));
         return true;
     }
 
+    stats().recordTrigger(used, threshold);
     PrepareZoneForGC(zone);
     requestMajorGC(reason);
     return true;
 }
 
 void
 GCRuntime::maybeGC(Zone* zone)
 {
@@ -3110,21 +3112,22 @@ GCRuntime::maybeGC(Zone* zone)
         gc(GC_NORMAL, JS::gcreason::DEBUG_GC);
         return;
     }
 #endif
 
     if (gcIfRequested())
         return;
 
-    if (zone->usage.gcBytes() > 1024 * 1024 &&
-        zone->usage.gcBytes() >= zone->threshold.allocTrigger(schedulingState.inHighFrequencyGCMode()) &&
-        !isIncrementalGCInProgress() &&
-        !isBackgroundSweeping())
+    double threshold = zone->threshold.allocTrigger(schedulingState.inHighFrequencyGCMode());
+    double usedBytes = zone->usage.gcBytes();
+    if (usedBytes > 1024 * 1024 && usedBytes >= threshold &&
+        !isIncrementalGCInProgress() && !isBackgroundSweeping())
     {
+        stats().recordTrigger(usedBytes, threshold);
         PrepareZoneForGC(zone);
         startGC(GC_NORMAL, JS::gcreason::EAGER_ALLOC_TRIGGER);
     }
 }
 
 // Do all possible decommit immediately from the current thread without
 // releasing the GC lock or allocating any memory.
 void
--- a/memory/mozjemalloc/mozjemalloc.cpp
+++ b/memory/mozjemalloc/mozjemalloc.cpp
@@ -790,16 +790,23 @@ struct arena_s {
 	 *   --------+------+
 	 *       35  | 1024 |
 	 *       36  | 2048 |
 	 *   --------+------+
 	 */
 	arena_bin_t		bins[1]; /* Dynamically sized. */
 };
 
+enum ChunkType {
+  UNKNOWN_CHUNK,
+  ARENA_CHUNK,    // used to back arena runs created by arena_run_alloc
+  HUGE_CHUNK,     // used to back huge allocations (e.g. huge_malloc)
+  RECYCLED_CHUNK, // chunk has been stored for future use by chunk_recycle
+};
+
 /******************************************************************************/
 /*
  * Data.
  */
 
 /*
  * When MALLOC_STATIC_SIZES is defined most of the parameters
  * controlling the malloc behavior are defined as compile-time constants
@@ -1022,17 +1029,17 @@ static size_t	opt_chunk_2pow = CHUNK_2PO
 #endif
 
 /******************************************************************************/
 /*
  * Begin forward declarations.
  */
 
 static void	*chunk_alloc(size_t size, size_t alignment, bool base, bool zero);
-static void	chunk_dealloc(void *chunk, size_t size);
+static void	chunk_dealloc(void *chunk, size_t size, enum ChunkType type);
 static arena_t	*arenas_extend();
 static void	*huge_malloc(size_t size, bool zero);
 static void	*huge_palloc(size_t size, size_t alignment, bool zero);
 static void	*huge_ralloc(void *ptr, size_t size, size_t oldsize);
 static void	huge_dalloc(void *ptr);
 #ifdef MOZ_MEMORY_WINDOWS
 extern "C"
 #else
@@ -2016,17 +2023,17 @@ chunk_recycle(extent_tree_t *chunks_szad
 			 * base_node_alloc() can cause a new base chunk to be
 			 * allocated.  Drop chunks_mtx in order to avoid
 			 * deadlock, and if node allocation fails, deallocate
 			 * the result before returning an error.
 			 */
 			malloc_mutex_unlock(&chunks_mtx);
 			node = base_node_alloc();
 			if (!node) {
-				chunk_dealloc(ret, size);
+				chunk_dealloc(ret, size, RECYCLED_CHUNK);
 				return nullptr;
 			}
 			malloc_mutex_lock(&chunks_mtx);
 		}
 		node->addr = (void *)((uintptr_t)(ret) + size);
 		node->size = trailsize;
 		node->zeroed = zeroed;
 		extent_tree_szad_insert(chunks_szad, node);
@@ -2093,34 +2100,40 @@ chunk_alloc(size_t size, size_t alignmen
 	}
 
 	/* All strategies for allocation failed. */
 	ret = nullptr;
 RETURN:
 
 	if (ret && base == false) {
 		if (malloc_rtree_set(chunk_rtree, (uintptr_t)ret, ret)) {
-			chunk_dealloc(ret, size);
+			chunk_dealloc(ret, size, UNKNOWN_CHUNK);
 			return nullptr;
 		}
 	}
 
 	MOZ_ASSERT(CHUNK_ADDR2BASE(ret) == ret);
 	return (ret);
 }
 
 static void
 chunk_record(extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, void *chunk,
-    size_t size)
+    size_t size, enum ChunkType type)
 {
 	bool unzeroed;
 	extent_node_t *xnode, *node, *prev, *xprev, key;
 
 	unzeroed = pages_purge(chunk, size);
 
+	/* If purge doesn't zero the chunk, only record arena chunks or
+	 * previously recycled chunks. */
+	if (unzeroed && type != ARENA_CHUNK && type != RECYCLED_CHUNK) {
+		return;
+	}
+
 	/*
 	 * Allocate a node before acquiring chunks_mtx even though it might not
 	 * be needed, because base_node_alloc() may cause a new base chunk to
 	 * be allocated, which could cause deadlock if chunks_mtx were already
 	 * held.
 	 */
 	xnode = base_node_alloc();
 	/* Use xprev to implement conditional deferred deallocation of prev. */
@@ -2204,28 +2217,28 @@ chunk_dalloc_mmap(void *chunk, size_t si
 
 	pages_unmap(chunk, size);
 	return false;
 }
 
 #undef CAN_RECYCLE
 
 static void
-chunk_dealloc(void *chunk, size_t size)
+chunk_dealloc(void *chunk, size_t size, enum ChunkType type)
 {
 
 	MOZ_ASSERT(chunk);
 	MOZ_ASSERT(CHUNK_ADDR2BASE(chunk) == chunk);
 	MOZ_ASSERT(size != 0);
 	MOZ_ASSERT((size & chunksize_mask) == 0);
 
 	malloc_rtree_set(chunk_rtree, (uintptr_t)chunk, nullptr);
 
 	if (chunk_dalloc_mmap(chunk, size))
-		chunk_record(&chunks_szad_mmap, &chunks_ad_mmap, chunk, size);
+		chunk_record(&chunks_szad_mmap, &chunks_ad_mmap, chunk, size, type);
 }
 
 /*
  * End chunk management functions.
  */
 /******************************************************************************/
 /*
  * Begin arena.
@@ -2699,17 +2712,17 @@ arena_chunk_dealloc(arena_t *arena, aren
 			arena->stats.committed -= arena->spare->ndirty;
 		}
 
 #ifdef MALLOC_DOUBLE_PURGE
 		/* This is safe to do even if arena->spare is not in the list. */
 		LinkedList_Remove(&arena->spare->chunks_madvised_elem);
 #endif
 
-		chunk_dealloc((void *)arena->spare, chunksize);
+		chunk_dealloc((void *)arena->spare, chunksize, ARENA_CHUNK);
 		arena->stats.mapped -= chunksize;
 		arena->stats.committed -= arena_chunk_header_npages;
 	}
 
 	/*
 	 * Remove run from runs_avail, so that the arena does not use it.
 	 * Dirty page flushing only uses the chunks_dirty tree, so leaving this
 	 * chunk in the chunks_* trees is sufficient for that purpose.
@@ -4217,17 +4230,17 @@ huge_dalloc(void *ptr)
 
 	huge_ndalloc++;
 	huge_allocated -= node->size;
 	huge_mapped -= CHUNK_CEILING(node->size);
 
 	malloc_mutex_unlock(&huge_mtx);
 
 	/* Unmap chunk. */
-	chunk_dealloc(node->addr, CHUNK_CEILING(node->size));
+	chunk_dealloc(node->addr, CHUNK_CEILING(node->size), HUGE_CHUNK);
 
 	base_node_dealloc(node);
 }
 
 /*
  * FreeBSD's pthreads implementation calls malloc(3), so the malloc
  * implementation has to take pains to avoid infinite recursion during
  * initialization.
--- a/testing/talos/talos/test.py
+++ b/testing/talos/talos/test.py
@@ -159,17 +159,17 @@ class sessionrestore(TsBase):
 
     1. Set up Firefox to restore from a given sessionstore.js file.
     2. Launch Firefox.
     3. Measure the delta between firstPaint and sessionRestored.
     """
     extensions = \
         '${talos}/startup_test/sessionrestore/addon/sessionrestore-signed.xpi'
     cycles = 10
-    timeout = 1000000
+    timeout = 900
     gecko_profile_startup = True
     gecko_profile_entries = 10000000
     profile_path = '${talos}/startup_test/sessionrestore/profile'
     shutdown = False
     reinstall = ['sessionstore.js', 'sessionCheckpoints.json']
     # Restore the session. We have to provide a URL, otherwise Talos
     # asks for a manifest URL.
     url = 'about:home'
--- a/toolkit/mozapps/extensions/internal/XPIInstall.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIInstall.jsm
@@ -66,28 +66,27 @@ XPCOMUtils.defineLazyServiceGetter(this,
                                    Ci.nsIRDFService);
 
 
 XPCOMUtils.defineLazyModuleGetter(this, "XPIInternal",
                                   "resource://gre/modules/addons/XPIProvider.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "XPIProvider",
                                   "resource://gre/modules/addons/XPIProvider.jsm");
 
-/* globals AddonInternal, BOOTSTRAP_REASONS, KEY_APP_SYSTEM_ADDONS, KEY_APP_SYSTEM_DEFAULTS, KEY_APP_TEMPORARY, TEMPORARY_ADDON_SUFFIX, TOOLKIT_ID, XPIDatabase, XPIStates, applyBlocklistChanges, getExternalType, isTheme, isUsableAddon, isWebExtension, recordAddonTelemetry */
+/* globals AddonInternal, BOOTSTRAP_REASONS, KEY_APP_SYSTEM_ADDONS, KEY_APP_SYSTEM_DEFAULTS, KEY_APP_TEMPORARY, TEMPORARY_ADDON_SUFFIX, TOOLKIT_ID, XPIDatabase, XPIStates, getExternalType, isTheme, isUsableAddon, isWebExtension, recordAddonTelemetry */
 const XPI_INTERNAL_SYMBOLS = [
   "AddonInternal",
   "BOOTSTRAP_REASONS",
   "KEY_APP_SYSTEM_ADDONS",
   "KEY_APP_SYSTEM_DEFAULTS",
   "KEY_APP_TEMPORARY",
   "TEMPORARY_ADDON_SUFFIX",
   "TOOLKIT_ID",
   "XPIDatabase",
   "XPIStates",
-  "applyBlocklistChanges",
   "getExternalType",
   "isTheme",
   "isUsableAddon",
   "isWebExtension",
   "recordAddonTelemetry",
 ];
 
 for (let name of XPI_INTERNAL_SYMBOLS) {
@@ -850,16 +849,17 @@ var loadManifestFromDir = async function
     addon = await loadFromRDF(uri);
   }
 
   addon._sourceBundle = aDir.clone();
   addon._installLocation = aInstallLocation;
   addon.size = getFileSize(aDir);
   addon.signedState = await verifyDirSignedState(aDir, addon)
     .then(({signedState}) => signedState);
+  addon.updateBlocklistState();
   addon.appDisabled = !isUsableAddon(addon);
 
   defineSyncGUID(addon);
 
   return addon;
 };
 
 /**
@@ -935,16 +935,17 @@ var loadManifestFromZipReader = async fu
       if (!gIDTest.test(addon.id)) {
         throw new Error(`Webextension is signed with an invalid id (${addon.id})`);
       }
     }
     if (!addon.id && aInstallLocation.name == KEY_APP_TEMPORARY) {
       addon.id = generateTemporaryInstallID(aZipReader.file);
     }
   }
+  addon.updateBlocklistState();
   addon.appDisabled = !isUsableAddon(addon);
 
   defineSyncGUID(addon);
 
   return addon;
 };
 
 /**
@@ -2132,18 +2133,17 @@ this.LocalAddonInstall = class extends A
       return;
     }
 
     let addon = await new Promise(resolve => {
       XPIDatabase.getVisibleAddonForID(this.addon.id, resolve);
     });
 
     this.existingAddon = addon;
-    if (addon)
-      applyBlocklistChanges(addon, this.addon);
+    this.addon.updateBlocklistState({oldAddon: this.existingAddon});
     this.addon.updateDate = Date.now();
     this.addon.installDate = addon ? addon.installDate : this.addon.updateDate;
 
     if (!this.addon.isCompatible) {
       this.state = AddonManager.STATE_CHECKING;
 
       await new Promise(resolve => {
         new UpdateChecker(this.addon, {
@@ -2538,20 +2538,20 @@ this.DownloadAddonInstall = class extend
         this.existingAddon = aAddon;
 
       this.state = AddonManager.STATE_DOWNLOADED;
       this.addon.updateDate = Date.now();
 
       if (this.existingAddon) {
         this.addon.existingAddonID = this.existingAddon.id;
         this.addon.installDate = this.existingAddon.installDate;
-        applyBlocklistChanges(this.existingAddon, this.addon);
       } else {
         this.addon.installDate = this.addon.updateDate;
       }
+      this.addon.updateBlocklistState({oldAddon: this.existingAddon});
 
       if (AddonManagerPrivate.callInstallListeners("onDownloadEnded",
                                                    this.listeners,
                                                    this.wrapper)) {
         // If a listener changed our state then do not proceed with the install
         if (this.state != AddonManager.STATE_DOWNLOADED)
           return;
 
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -93,16 +93,17 @@ XPCOMUtils.defineLazyServiceGetter(this,
 
 Cu.importGlobalProperties(["URL"]);
 
 const nsIFile = Components.Constructor("@mozilla.org/file/local;1", "nsIFile",
                                        "initWithPath");
 
 const PREF_DB_SCHEMA                  = "extensions.databaseSchema";
 const PREF_XPI_STATE                  = "extensions.xpiState";
+const PREF_BLOCKLIST_ITEM_URL         = "extensions.blocklist.itemURL";
 const PREF_BOOTSTRAP_ADDONS           = "extensions.bootstrappedAddons";
 const PREF_PENDING_OPERATIONS         = "extensions.pendingOperations";
 const PREF_SKIN_SWITCHPENDING         = "extensions.dss.switchPending";
 const PREF_SKIN_TO_SELECT             = "extensions.lastSelectedSkin";
 const PREF_GENERAL_SKINS_SELECTEDSKIN = "general.skins.selectedSkin";
 const PREF_EM_EXTENSION_FORMAT        = "extensions.";
 const PREF_EM_ENABLED_SCOPES          = "extensions.enabledScopes";
 const PREF_EM_STARTUP_SCAN_SCOPES     = "extensions.startupScanScopes";
@@ -184,17 +185,17 @@ const STARTUP_MTIME_SCOPES = [KEY_APP_GL
 
 const NOTIFICATION_FLUSH_PERMISSIONS  = "flush-pending-permissions";
 const XPI_PERMISSION                  = "install";
 
 const TOOLKIT_ID                      = "toolkit@mozilla.org";
 
 const XPI_SIGNATURE_CHECK_PERIOD      = 24 * 60 * 60;
 
-XPCOMUtils.defineConstant(this, "DB_SCHEMA", 21);
+XPCOMUtils.defineConstant(this, "DB_SCHEMA", 22);
 
 XPCOMUtils.defineLazyPreferenceGetter(this, "ALLOW_NON_MPC", PREF_ALLOW_NON_MPC);
 
 const NOTIFICATION_TOOLBOX_CONNECTION_CHANGE      = "toolbox-connection-change";
 
 // Properties that exist in the install manifest
 const PROP_LOCALE_SINGLE = ["name", "description", "creator", "homepageURL"];
 const PROP_LOCALE_MULTI  = ["developers", "translators", "contributors"];
@@ -328,17 +329,16 @@ function loadLazyObjects() {
     BOOTSTRAP_REASONS,
     DB_SCHEMA,
     AddonInternal,
     XPIProvider,
     XPIStates,
     syncLoadManifestFromFile,
     isUsableAddon,
     recordAddonTelemetry,
-    applyBlocklistChanges,
     flushChromeCaches,
     descriptorToPath,
   });
 
   Services.scriptloader.loadSubScript(uri, scope);
 
   for (let name of LAZY_OBJECTS) {
     delete gGlobalScope[name];
@@ -755,67 +755,16 @@ SafeInstallOperation.prototype = {
     }
 
     while (this._createdDirs.length > 0)
       recursiveRemove(this._createdDirs.pop());
   }
 };
 
 /**
- * Sets the userDisabled and softDisabled properties of an add-on based on what
- * values those properties had for a previous instance of the add-on. The
- * previous instance may be a previous install or in the case of an application
- * version change the same add-on.
- *
- * NOTE: this may modify aNewAddon in place; callers should save the database if
- * necessary
- *
- * @param  aOldAddon
- *         The previous instance of the add-on
- * @param  aNewAddon
- *         The new instance of the add-on
- * @param  aAppVersion
- *         The optional application version to use when checking the blocklist
- *         or undefined to use the current application
- * @param  aPlatformVersion
- *         The optional platform version to use when checking the blocklist or
- *         undefined to use the current platform
- */
-function applyBlocklistChanges(aOldAddon, aNewAddon, aOldAppVersion,
-                               aOldPlatformVersion) {
-  // Copy the properties by default
-  aNewAddon.userDisabled = aOldAddon.userDisabled;
-  aNewAddon.softDisabled = aOldAddon.softDisabled;
-
-  let oldBlocklistState = Blocklist.getAddonBlocklistState(aOldAddon.wrapper,
-                                                           aOldAppVersion,
-                                                           aOldPlatformVersion);
-  let newBlocklistState = Blocklist.getAddonBlocklistState(aNewAddon.wrapper);
-
-  // If the blocklist state hasn't changed then the properties don't need to
-  // change
-  if (newBlocklistState == oldBlocklistState)
-    return;
-
-  if (newBlocklistState == Blocklist.STATE_SOFTBLOCKED) {
-    if (aNewAddon.type != "theme") {
-      // The add-on has become softblocked, set softDisabled if it isn't already
-      // userDisabled
-      aNewAddon.softDisabled = !aNewAddon.userDisabled;
-    } else {
-      // Themes just get userDisabled to switch back to the default theme
-      aNewAddon.userDisabled = true;
-    }
-  } else {
-    // If the new add-on is not softblocked then it cannot be softDisabled
-    aNewAddon.softDisabled = false;
-  }
-}
-
-/**
  * Evaluates whether an add-on is allowed to run in safe mode.
  *
  * @param  aAddon
  *         The add-on to check
  * @return true if the add-on should run in safe mode
  */
 function canRunInSafeMode(aAddon) {
   // Even though the updated system add-ons aren't generally run in safe mode we
@@ -4544,17 +4493,17 @@ this.XPIProvider = {
         AddonManagerPrivate.callAddonListeners("onEnabling", wrapper,
                                                needsRestart);
       }
 
       if (!needsRestart) {
         XPIDatabase.updateAddonActive(aAddon, !isDisabled);
 
         if (isDisabled) {
-          if (aAddon.bootstrap) {
+          if (aAddon.bootstrap && this.activeAddons.has(aAddon.id)) {
             this.callBootstrapMethod(aAddon, aAddon._sourceBundle, "shutdown",
                                      BOOTSTRAP_REASONS.ADDON_DISABLE);
             this.unloadBootstrapScope(aAddon.id);
           }
           AddonManagerPrivate.callAddonListeners("onDisabled", wrapper);
         } else {
           if (aAddon.bootstrap) {
             this.callBootstrapMethod(aAddon, aAddon._sourceBundle, "startup",
@@ -4814,16 +4763,18 @@ function AddonInternal() {
 AddonInternal.prototype = {
   _selectedLocale: null,
   _hasResourceCache: null,
   active: false,
   visible: false,
   userDisabled: false,
   appDisabled: false,
   softDisabled: false,
+  blocklistState: Ci.nsIBlocklistService.STATE_NOT_BLOCKED,
+  blocklistURL: null,
   sourceURI: null,
   releaseNotesURI: null,
   foreignInstall: false,
   seen: true,
   skinnable: false,
 
   /**
    * @property {Array<string>} dependencies
@@ -4983,32 +4934,73 @@ AddonInternal.prototype = {
       if (targetApp.id == Services.appinfo.ID)
         return targetApp;
       if (targetApp.id == TOOLKIT_ID)
         app = targetApp;
     }
     return app;
   },
 
-  get blocklistState() {
-    let staticItem = findMatchingStaticBlocklistItem(this);
-    if (staticItem)
-      return staticItem.level;
-
-    return Blocklist.getAddonBlocklistState(this.wrapper);
-  },
-
-  get blocklistURL() {
+  findBlocklistEntry() {
     let staticItem = findMatchingStaticBlocklistItem(this);
     if (staticItem) {
-      let url = Services.urlFormatter.formatURLPref("extensions.blocklist.itemURL");
-      return url.replace(/%blockID%/g, staticItem.blockID);
-    }
-
-    return Blocklist.getAddonBlocklistURL(this.wrapper);
+      let url = Services.urlFormatter.formatURLPref(PREF_BLOCKLIST_ITEM_URL);
+      return {
+        state: staticItem.level,
+        url: url.replace(/%blockID%/g, staticItem.blockID)
+      };
+    }
+
+    return Blocklist.getAddonBlocklistEntry(this.wrapper);
+  },
+
+  updateBlocklistState(options = {}) {
+    let {applySoftBlock = true, oldAddon = null, updateDatabase = true} = options;
+
+    if (oldAddon) {
+      this.userDisabled = oldAddon.userDisabled;
+      this.softDisabled = oldAddon.softDisabled;
+      this.blocklistState = oldAddon.blocklistState;
+    }
+    let oldState = this.blocklistState;
+
+    let entry = this.findBlocklistEntry();
+    let newState = entry ? entry.state : Blocklist.STATE_NOT_BLOCKED;
+
+    this.blocklistState = newState;
+    this.blocklistURL = entry && entry.url;
+
+    let userDisabled, softDisabled;
+    // After a blocklist update, the blocklist service manually applies
+    // new soft blocks after displaying a UI, in which cases we need to
+    // skip updating it here.
+    if (applySoftBlock && oldState != newState) {
+      if (newState == Blocklist.STATE_SOFTBLOCKED) {
+        if (this.type == "theme") {
+          userDisabled = true;
+        } else {
+          softDisabled = !this.userDisabled;
+        }
+      } else {
+        softDisabled = false;
+      }
+    }
+
+    if (this.inDatabase && updateDatabase) {
+      XPIProvider.updateAddonDisabledState(this, userDisabled, softDisabled);
+      XPIDatabase.saveChanges();
+    } else {
+      this.appDisabled = !isUsableAddon(this);
+      if (userDisabled !== undefined) {
+        this.userDisabled = userDisabled;
+      }
+      if (softDisabled !== undefined) {
+        this.softDisabled = softDisabled;
+      }
+    }
   },
 
   applyCompatibilityUpdate(aUpdate, aSyncCompatibility) {
     for (let targetApp of this.targetApplications) {
       for (let updateTarget of aUpdate.targetApplications) {
         if (targetApp.id == updateTarget.id && (aSyncCompatibility ||
             Services.vc.compare(targetApp.maxVersion, updateTarget.maxVersion) < 0)) {
           targetApp.minVersion = updateTarget.minVersion;
@@ -5418,16 +5410,20 @@ AddonWrapper.prototype = {
     let addon = addonFor(this);
     if (!addon.active)
       return false;
     if (!Services.appinfo.inSafeMode)
       return true;
     return addon.bootstrap && canRunInSafeMode(addon);
   },
 
+  updateBlocklistState(applySoftBlock = true) {
+    addonFor(this).updateBlocklistState({applySoftBlock});
+  },
+
   get userDisabled() {
     let addon = addonFor(this);
     return addon.softDisabled || addon.userDisabled;
   },
   set userDisabled(val) {
     let addon = addonFor(this);
     if (val == this.userDisabled) {
       return val;
@@ -6840,17 +6836,16 @@ this.XPIInternal = {
   AddonInternal,
   BOOTSTRAP_REASONS,
   KEY_APP_SYSTEM_ADDONS,
   KEY_APP_SYSTEM_DEFAULTS,
   KEY_APP_TEMPORARY,
   TEMPORARY_ADDON_SUFFIX,
   TOOLKIT_ID,
   XPIStates,
-  applyBlocklistChanges,
   getExternalType,
   isTheme,
   isUsableAddon,
   isWebExtension,
   recordAddonTelemetry,
 
   get XPIDatabase() { return gGlobalScope.XPIDatabase; },
 };
--- a/toolkit/mozapps/extensions/internal/XPIProviderUtils.js
+++ b/toolkit/mozapps/extensions/internal/XPIProviderUtils.js
@@ -2,17 +2,17 @@
  * 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";
 
 // These are injected from XPIProvider.jsm
 /* globals ADDON_SIGNING, SIGNED_TYPES, BOOTSTRAP_REASONS, DB_SCHEMA,
           AddonInternal, XPIProvider, XPIStates, syncLoadManifestFromFile,
-          isUsableAddon, recordAddonTelemetry, applyBlocklistChanges,
+          isUsableAddon, recordAddonTelemetry,
           flushChromeCaches, descriptorToPath */
 
 var Cc = Components.classes;
 var Ci = Components.interfaces;
 var Cr = Components.results;
 var Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
@@ -71,17 +71,18 @@ const PROP_JSON_FIELDS = ["id", "syncGUI
                           "defaultLocale", "visible", "active", "userDisabled",
                           "appDisabled", "pendingUninstall", "installDate",
                           "updateDate", "applyBackgroundUpdates", "bootstrap", "path",
                           "skinnable", "size", "sourceURI", "releaseNotesURI",
                           "softDisabled", "foreignInstall", "hasBinaryComponents",
                           "strictCompatibility", "locales", "targetApplications",
                           "targetPlatforms", "multiprocessCompatible", "signedState",
                           "seen", "dependencies", "hasEmbeddedWebExtension", "mpcOptedOut",
-                          "userPermissions", "icons", "iconURL", "icon64URL"];
+                          "userPermissions", "icons", "iconURL", "icon64URL",
+                          "blocklistState", "blocklistURL"];
 
 // Time to wait before async save of XPI JSON database, in milliseconds
 const ASYNC_SAVE_DELAY_MS = 20;
 
 /**
  * Asynchronously fill in the _repositoryAddon field for one addon
  */
 function getRepositoryAddon(aAddon, aCallback) {
@@ -1319,23 +1320,24 @@ this.XPIDatabaseReconcile = {
   updateMetadata(aInstallLocation, aOldAddon, aAddonState, aNewAddon) {
     logger.debug("Add-on " + aOldAddon.id + " modified in " + aInstallLocation.name);
 
     try {
       // If there isn't an updated install manifest for this add-on then load it.
       if (!aNewAddon) {
         let file = new nsIFile(aAddonState.path);
         aNewAddon = syncLoadManifestFromFile(file, aInstallLocation);
-        applyBlocklistChanges(aOldAddon, aNewAddon);
 
         // Carry over any pendingUninstall state to add-ons modified directly
         // in the profile. This is important when the attempt to remove the
         // add-on in processPendingFileChanges failed and caused an mtime
         // change to the add-ons files.
         aNewAddon.pendingUninstall = aOldAddon.pendingUninstall;
+
+        aNewAddon.updateBlocklistState({oldAddon: aOldAddon});
       }
 
       // The ID in the manifest that was loaded must match the ID of the old
       // add-on.
       if (aNewAddon.id != aOldAddon.id)
         throw new Error("Incorrect id in install manifest for existing add-on " + aOldAddon.id);
     } catch (e) {
       logger.warn("updateMetadata: Add-on " + aOldAddon.id + " is invalid", e);
@@ -1425,19 +1427,17 @@ this.XPIDatabaseReconcile = {
       let remove = ["syncGUID", "foreignInstall", "visible", "active",
                     "userDisabled", "applyBackgroundUpdates", "sourceURI",
                     "releaseNotesURI", "targetApplications"];
 
       let props = PROP_JSON_FIELDS.filter(a => !remove.includes(a));
       copyProperties(manifest, props, aOldAddon);
     }
 
-    // This updates the addon's JSON cached data in place
-    applyBlocklistChanges(aOldAddon, aOldAddon, aOldAppVersion,
-                          aOldPlatformVersion);
+    aOldAddon.updateBlocklistState({updateDatabase: false});
     aOldAddon.appDisabled = !isUsableAddon(aOldAddon);
 
     return aOldAddon;
   },
 
   /**
    * Compares the add-ons that are currently installed to those that were
    * known to be installed when the application last ran and applies any
--- a/toolkit/mozapps/extensions/nsBlocklistService.js
+++ b/toolkit/mozapps/extensions/nsBlocklistService.js
@@ -375,16 +375,72 @@ Blocklist.prototype = {
   getAddonBlocklistState(addon, appVersion, toolkitVersion) {
     if (!this._isBlocklistLoaded())
       this._loadBlocklist();
     return this._getAddonBlocklistState(addon, this._addonEntries,
                                         appVersion, toolkitVersion);
   },
 
   /**
+   * Returns a matching blocklist entry for the given add-on, if one
+   * exists.
+   *
+   * @param   id
+   *          The ID of the item to get the blocklist state for.
+   * @param   version
+   *          The version of the item to get the blocklist state for.
+   * @param   addonEntries
+   *          The add-on blocklist entries to compare against.
+   * @param   appVersion
+   *          The application version to compare to, will use the current
+   *          version if null.
+   * @param   toolkitVersion
+   *          The toolkit version to compare to, will use the current version if
+   *          null.
+   * @returns A blocklist entry for this item, with `state` and `url`
+   *          properties indicating the block state and URL, if there is
+   *          a matching blocklist entry, or null otherwise.
+   */
+  _getAddonBlocklistEntry(addon, addonEntries, appVersion, toolkitVersion) {
+    if (!gBlocklistEnabled)
+      return null;
+
+    // Not all applications implement nsIXULAppInfo (e.g. xpcshell doesn't).
+    if (!appVersion && !gApp.version)
+      return null;
+
+    if (!appVersion)
+      appVersion = gApp.version;
+    if (!toolkitVersion)
+      toolkitVersion = gApp.platformVersion;
+
+    var blItem = this._findMatchingAddonEntry(addonEntries, addon);
+    if (!blItem)
+      return null;
+
+    for (let currentblItem of blItem.versions) {
+      if (currentblItem.includesItem(addon.version, appVersion, toolkitVersion)) {
+        return {
+          state: (currentblItem.severity >= gBlocklistLevel ?
+                  Ci.nsIBlocklistService.STATE_BLOCKED : Ci.nsIBlocklistService.STATE_SOFTBLOCKED),
+          url: blItem.blockID && this._createBlocklistURL(blItem.blockID),
+        };
+      }
+    }
+    return null;
+  },
+
+  getAddonBlocklistEntry(addon, appVersion, toolkitVersion) {
+    if (!this._isBlocklistLoaded())
+      this._loadBlocklist();
+    return this._getAddonBlocklistEntry(addon, this._addonEntries,
+                                        appVersion, toolkitVersion);
+  },
+
+  /**
    * Private version of getAddonBlocklistState that allows the caller to pass in
    * the add-on blocklist entries to compare against.
    *
    * @param   id
    *          The ID of the item to get the blocklist state for.
    * @param   version
    *          The version of the item to get the blocklist state for.
    * @param   addonEntries
@@ -394,37 +450,19 @@ Blocklist.prototype = {
    *          version if null.
    * @param   toolkitVersion
    *          The toolkit version to compare to, will use the current version if
    *          null.
    * @returns The blocklist state for the item, one of the STATE constants as
    *          defined in nsIBlocklistService.
    */
   _getAddonBlocklistState(addon, addonEntries, appVersion, toolkitVersion) {
-    if (!gBlocklistEnabled)
-      return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
-
-    // Not all applications implement nsIXULAppInfo (e.g. xpcshell doesn't).
-    if (!appVersion && !gApp.version)
-      return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
-
-    if (!appVersion)
-      appVersion = gApp.version;
-    if (!toolkitVersion)
-      toolkitVersion = gApp.platformVersion;
-
-    var blItem = this._findMatchingAddonEntry(addonEntries, addon);
-    if (!blItem)
-      return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
-
-    for (let currentblItem of blItem.versions) {
-      if (currentblItem.includesItem(addon.version, appVersion, toolkitVersion))
-        return currentblItem.severity >= gBlocklistLevel ? Ci.nsIBlocklistService.STATE_BLOCKED :
-                                                       Ci.nsIBlocklistService.STATE_SOFTBLOCKED;
-    }
+    let entry = this._getAddonBlocklistEntry(addon, addonEntries, appVersion, toolkitVersion);
+    if (entry)
+      return entry.state;
     return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
   },
 
   /**
    * Returns the set of prefs of the add-on stored in the blocklist file
    * (probably to revert them on disabling).
    * @param addon
    *        The add-on whose to-be-reset prefs are to be found.
@@ -470,27 +508,21 @@ Blocklist.prototype = {
          return entry;
        }
      }
      return null;
   },
 
   /* See nsIBlocklistService */
   getAddonBlocklistURL(addon, appVersion, toolkitVersion) {
-    if (!gBlocklistEnabled)
-      return "";
-
     if (!this._isBlocklistLoaded())
       this._loadBlocklist();
 
-    let blItem = this._findMatchingAddonEntry(this._addonEntries, addon);
-    if (!blItem || !blItem.blockID)
-      return null;
-
-    return this._createBlocklistURL(blItem.blockID);
+    let entry = this._getAddonBlocklistEntry(addon, this._addonEntries);
+    return entry && entry.url;
   },
 
   _createBlocklistURL(id) {
     let url = Services.urlFormatter.formatURLPref(PREF_BLOCKLIST_ITEM_URL);
     url = url.replace(/%blockID%/g, id);
 
     return url;
   },
@@ -1302,33 +1334,38 @@ Blocklist.prototype = {
     // A helper function that reverts the prefs passed to default values.
     function resetPrefs(prefs) {
       for (let pref of prefs)
         gPref.clearUserPref(pref);
     }
     const types = ["extension", "theme", "locale", "dictionary", "service"];
     AddonManager.getAddonsByTypes(types, addons => {
       for (let addon of addons) {
-        let oldState = Ci.nsIBlocklistService.STATE_NOTBLOCKED;
-        if (oldAddonEntries)
+        let oldState = addon.blocklistState;
+        if (addon.updateBlocklistState) {
+          addon.updateBlocklistState(false);
+        } else if (oldAddonEntries) {
           oldState = this._getAddonBlocklistState(addon, oldAddonEntries);
-        let state = this.getAddonBlocklistState(addon);
+        } else {
+          oldState = Ci.nsIBlocklistService.STATE_NOTBLOCKED;
+        }
+        let state = addon.blocklistState;
 
         LOG("Blocklist state for " + addon.id + " changed from " +
             oldState + " to " + state);
 
         // We don't want to re-warn about add-ons
         if (state == oldState)
           continue;
 
         if (state === Ci.nsIBlocklistService.STATE_BLOCKED) {
           // It's a hard block. We must reset certain preferences.
           let prefs = this._getAddonPrefs(addon);
           resetPrefs(prefs);
-         }
+        }
 
         // Ensure that softDisabled is false if the add-on is not soft blocked
         if (state != Ci.nsIBlocklistService.STATE_SOFTBLOCKED)
           addon.softDisabled = false;
 
         // Don't warn about add-ons becoming unblocked.
         if (state == Ci.nsIBlocklistService.STATE_NOT_BLOCKED)
           continue;
--- a/toolkit/mozapps/extensions/test/xpcshell/test_bug335238.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug335238.js
@@ -83,16 +83,27 @@ var BlocklistService = {
   getAddonBlocklistState(aAddon, aAppVersion, aToolkitVersion) {
     if (aAddon.id == "bug335238_3@tests.mozilla.org")
       return Ci.nsIBlocklistService.STATE_SOFTBLOCKED;
     if (aAddon.id == "bug335238_4@tests.mozilla.org")
       return Ci.nsIBlocklistService.STATE_BLOCKED;
     return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
   },
 
+  getAddonBlocklistEntry(aAddon, aAppVersion, aToolkitVersion) {
+    let state = this.getAddonBlocklistState(aAddon, aAppVersion, aToolkitVersion);
+    if (state != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) {
+      return {
+        state,
+        url: "http://example.com/",
+      };
+    }
+    return null;
+  },
+
   getPluginBlocklistState(aPlugin, aVersion, aAppVersion, aToolkitVersion) {
     return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
   },
 
   isAddonBlocklisted(aAddon, aAppVersion, aToolkitVersion) {
     return this.getAddonBlocklistState(aAddon, aAppVersion, aToolkitVersion) ==
            Ci.nsIBlocklistService.STATE_BLOCKED;
   },
--- a/toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js
+++ b/toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js
@@ -2642,31 +2642,33 @@ function waitForHelperExit() {
  *          When null the updater.ini is not created otherwise this parameter
  *          is passed to createUpdaterINI.
  * @param   aPostUpdateExeRelPathPrefix
  *          When aPostUpdateAsync null this value is ignored otherwise it is
  *          passed to createUpdaterINI.
  */
 function setupUpdaterTest(aMarFile, aPostUpdateAsync,
                           aPostUpdateExeRelPathPrefix = "") {
+  debugDump("start - updater test setup");
   let updatesPatchDir = getUpdatesPatchDir();
   if (!updatesPatchDir.exists()) {
     updatesPatchDir.create(Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY);
   }
   // Copy the mar that will be applied
   let mar = getTestDirFile(aMarFile);
   mar.copyToFollowingLinks(updatesPatchDir, FILE_UPDATE_MAR);
 
   let helperBin = getTestDirFile(FILE_HELPER_BIN);
   helperBin.permissions = PERMS_DIRECTORY;
   let afterApplyBinDir = getApplyDirFile(DIR_RESOURCES, true);
   helperBin.copyToFollowingLinks(afterApplyBinDir, gCallbackBinFile);
   helperBin.copyToFollowingLinks(afterApplyBinDir, gPostUpdateBinFile);
 
   gTestFiles.forEach(function SUT_TF_FE(aTestFile) {
+    debugDump("start - setup test file: " + aTestFile.fileName);
     if (aTestFile.originalFile || aTestFile.originalContents) {
       let testDir = getApplyDirFile(aTestFile.relPathDir, true);
       if (!testDir.exists()) {
         testDir.create(Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY);
       }
 
       let testFile;
       if (aTestFile.originalFile) {
@@ -2685,21 +2687,23 @@ function setupUpdaterTest(aMarFile, aPos
         testFile.permissions = aTestFile.originalPerms;
         // Store the actual permissions on the file for reference later after
         // setting the permissions.
         if (!aTestFile.comparePerms) {
           aTestFile.comparePerms = testFile.permissions;
         }
       }
     }
+    debugDump("finish - setup test file: " + aTestFile.fileName);
   });
 
   // Add the test directory that will be updated for a successful update or left
   // in the initial state for a failed update.
   gTestDirs.forEach(function SUT_TD_FE(aTestDir) {
+    debugDump("start - setup test directory: " + aTestDir.relPathDir);
     let testDir = getApplyDirFile(aTestDir.relPathDir, true);
     if (!testDir.exists()) {
       testDir.create(Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY);
     }
 
     if (aTestDir.files) {
       aTestDir.files.forEach(function SUT_TD_F_FE(aTestFile) {
         let testFile = getApplyDirFile(aTestDir.relPathDir + aTestFile, true);
@@ -2721,24 +2725,26 @@ function setupUpdaterTest(aMarFile, aPos
             let testFile = getApplyDirFile(aTestDir.relPathDir + aSubDir + aTestFile, true);
             if (!testFile.exists()) {
               testFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE);
             }
           });
         }
       });
     }
+    debugDump("finish - setup test directory: " + aTestDir.relPathDir);
   });
 
   setupActiveUpdate();
 
   if (aPostUpdateAsync !== null) {
     createUpdaterINI(aPostUpdateAsync, aPostUpdateExeRelPathPrefix);
   }
 
+  debugDump("finish - updater test setup");
   setupAppFilesAsync();
 }
 
 /**
  * Helper function for updater binary tests that creates the update-settings.ini
  * file.
  */
 function createUpdateSettingsINI() {
--- a/widget/nsIGfxInfo.idl
+++ b/widget/nsIGfxInfo.idl
@@ -122,17 +122,17 @@ interface nsIGfxInfo : nsISupports
   const long FEATURE_DX_INTEROP2 = 19;
   /* Whether the GPU process is supported, starting in 52. */
   const long FEATURE_GPU_PROCESS = 20;
   /* Whether the WebGL2 is supported, starting in 54 */
   const long FEATURE_WEBGL2 = 21;
   /* Whether Advanced Layers is supported, starting in 56 */
   const long FEATURE_ADVANCED_LAYERS = 22;
   /* the maximum feature value. */
-  const long FEATURE_MAX_VALUE = FEATURE_WEBGL2;
+  const long FEATURE_MAX_VALUE = FEATURE_ADVANCED_LAYERS;
 
   /*
    * A set of return values from GetFeatureStatus
    */
 
   /* The driver is safe to the best of our knowledge */
   const long FEATURE_STATUS_OK = 1;
   /* We don't know the status of the feature yet. The analysis probably hasn't finished yet. */
--- a/widget/windows/GfxInfo.cpp
+++ b/widget/windows/GfxInfo.cpp
@@ -1313,16 +1313,26 @@ GfxInfo::GetGfxDriverInfo()
     ////////////////////////////////////
     // FEATURE_DX_INTEROP2
 
     // All AMD.
     APPEND_TO_DRIVER_BLOCKLIST2(OperatingSystem::Windows,
       (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorAMD), GfxDriverInfo::allDevices,
       nsIGfxInfo::FEATURE_DX_INTEROP2, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
       DRIVER_LESS_THAN, GfxDriverInfo::allDriverVersions, "DX_INTEROP2_AMD_CRASH");
+
+    ////////////////////////////////////
+    // FEATURE_ADVANCED_LAYERS
+
+    // bug 1377866
+    APPEND_TO_DRIVER_BLOCKLIST2(OperatingSystem::Windows,
+      (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorIntel),
+      (GfxDeviceFamily*) GfxDriverInfo::GetDeviceFamily(IntelHDGraphicsToSandyBridge),
+      nsIGfxInfo::FEATURE_ADVANCED_LAYERS, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
+      DRIVER_LESS_THAN, GfxDriverInfo::allDriverVersions, "FEATURE_FAILURE_BUG_1377866");
   }
   return *mDriverInfo;
 }
 
 nsresult
 GfxInfo::GetFeatureStatusImpl(int32_t aFeature,
                               int32_t *aStatus,
                               nsAString & aSuggestedDriverVersion,
--- a/xpcom/build/BinaryPath.h
+++ b/xpcom/build/BinaryPath.h
@@ -15,16 +15,18 @@
 #elif defined(XP_UNIX)
 #include <sys/stat.h>
 #include <string.h>
 #endif
 #include "mozilla/UniquePtr.h"
 #include "mozilla/UniquePtrExtensions.h"
 
 #ifdef MOZILLA_INTERNAL_API
+#include "nsCOMPtr.h"
+#include "nsIFile.h"
 #include "nsString.h"
 #endif
 
 namespace mozilla {
 
 class BinaryPath
 {
 public:
@@ -36,23 +38,61 @@ public:
     if (NS_FAILED(rv)) {
       return rv;
     }
     WideCharToMultiByte(CP_UTF8, 0, wide_path, -1,
                         aResult, MAXPATHLEN, nullptr, nullptr);
     return NS_OK;
   }
 
+  static nsresult GetLong(wchar_t aResult[MAXPATHLEN])
+  {
+    static bool cached = false;
+    static wchar_t exeLongPath[MAXPATHLEN] = L"";
+
+    if (!cached) {
+      nsresult rv = GetW(nullptr, exeLongPath);
+
+      if (NS_FAILED(rv)) {
+        return rv;
+      }
+
+      if (!::GetLongPathNameW(exeLongPath, exeLongPath, MAXPATHLEN)) {
+        return NS_ERROR_FAILURE;
+      }
+
+      cached = true;
+    }
+
+    if (wcscpy_s(aResult, MAXPATHLEN, exeLongPath)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    return NS_OK;
+  }
+
 private:
   static nsresult GetW(const char* argv0, wchar_t aResult[MAXPATHLEN])
   {
-    if (::GetModuleFileNameW(0, aResult, MAXPATHLEN)) {
-      return NS_OK;
+    static bool cached = false;
+    static wchar_t moduleFileName[MAXPATHLEN] = L"";
+
+    if (!cached) {
+      if (!::GetModuleFileNameW(0, moduleFileName, MAXPATHLEN)) {
+        return NS_ERROR_FAILURE;
+      }
+
+      cached = true;
     }
-    return NS_ERROR_FAILURE;
+
+    if (wcscpy_s(aResult, MAXPATHLEN, moduleFileName)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    return NS_OK;
   }
 
 #elif defined(XP_MACOSX)
   static nsresult Get(const char* argv0, char aResult[MAXPATHLEN])
   {
     // Works even if we're not bundled.
     CFBundleRef appBundle = CFBundleGetMainBundle();
     if (!appBundle) {
--- a/xpcom/system/nsIBlocklistService.idl
+++ b/xpcom/system/nsIBlocklistService.idl
@@ -88,16 +88,36 @@ interface nsIBlocklistService : nsISuppo
    *          The addon item whose url is required.
    * @returns The URL of the description page.
    */
   AString getAddonBlocklistURL(in jsval addon,
                               [optional] in AString appVersion,
                               [optional] in AString toolkitVersion);
 
   /**
+   * Returns the blocklist entry, as an object with `state` and `url`
+   * properties, if a blocklist entry for the add-on exists, or null
+   * othereise.
+
+   * @param   addon
+   *          The addon object to match.
+   * @param   appVersion
+   *          The version of the application we are checking in the blocklist.
+   *          If this parameter is null, the version of the running application
+   *          is used.
+   * @param   toolkitVersion
+   *          The version of the toolkit we are checking in the blocklist.
+   *          If this parameter is null, the version of the running toolkit
+   *          is used.
+   */
+  jsval getAddonBlocklistEntry(in jsval addon,
+                               [optional] in AString appVersion,
+                               [optional] in AString toolkitVersion);
+
+  /**
    * Determine the blocklist web page of a plugin.
    * @param   plugin
    *          The blocked plugin that we are determining the web page for.
    * @returns The URL of the description page.
    */
   AString getPluginBlocklistURL(in nsIPluginTag plugin);
 
   /**