Merge mozilla-central to autoland. a=merge on a CLOSED TREE
authorDaniel Varga <dvarga@mozilla.com>
Sat, 11 May 2019 00:51:51 +0300
changeset 535363 5972961441c097e5b59dd9348715d31b459226c7
parent 535362 7e08cf4753a79d765acc9721c65104c229a3f5fd (current diff)
parent 535332 2abcefb31ba7b2a1c573edc5695b772826c6a078 (diff)
child 535364 2ae93b4399fbeb63cb3ba4884e569b56baff1916
push id2082
push userffxbld-merge
push dateMon, 01 Jul 2019 08:34:18 +0000
treeherdermozilla-release@2fb19d0466d2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone68.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 mozilla-central to autoland. a=merge on a CLOSED TREE
--- a/gfx/ipc/GPUParent.cpp
+++ b/gfx/ipc/GPUParent.cpp
@@ -47,16 +47,18 @@
 #include "VRGPUChild.h"
 #include "VRManager.h"
 #include "VRManagerParent.h"
 #include "VsyncBridgeParent.h"
 #include "cairo.h"
 #include "skia/include/core/SkGraphics.h"
 #if defined(XP_WIN)
 #  include "mozilla/gfx/DeviceManagerDx.h"
+#  include "mozilla/widget/WinCompositorWindowThread.h"
+#  include "mozilla/WindowsVersion.h"
 #  include <process.h>
 #  include <dwrite.h>
 #endif
 #ifdef MOZ_WIDGET_GTK
 #  include <gtk/gtk.h>
 #  include "skia/include/ports/SkTypeface_cairo.h"
 #endif
 #ifdef MOZ_GECKO_PROFILER
@@ -247,16 +249,25 @@ mozilla::ipc::IPCResult GPUParent::RecvI
   }
 #endif
 
   // Make sure to do this *after* we update gfxVars above.
   if (gfxVars::UseWebRender()) {
     wr::RenderThread::Start();
     image::ImageMemoryReporter::InitForWebRender();
   }
+#ifdef XP_WIN
+  else {
+    if (gfxPrefs::Direct3D11UseDoubleBuffering() && IsWin10OrLater()) {
+      // This is needed to avoid freezing the window on a device crash on double
+      // buffering, see bug 1549674.
+      widget::WinCompositorWindowThread::Start();
+    }
+  }
+#endif
 
   VRManager::ManagerInit();
   // Send a message to the UI process that we're done.
   GPUDeviceData data;
   RecvGetDeviceStatus(&data);
   Unused << SendInitComplete(data);
 
   Telemetry::AccumulateTimeDelta(Telemetry::GPU_PROCESS_INITIALIZATION_TIME_MS,
@@ -496,16 +507,21 @@ void GPUParent::ActorDestroy(ActorDestro
   }
   VideoDecoderManagerParent::ShutdownVideoBridge();
   CompositorThreadHolder::Shutdown();
   // There is a case that RenderThread exists when gfxVars::UseWebRender() is
   // false. This could happen when WebRender was fallbacked to compositor.
   if (wr::RenderThread::Get()) {
     wr::RenderThread::ShutDown();
   }
+#ifdef XP_WIN
+  else if (gfxPrefs::Direct3D11UseDoubleBuffering() && IsWin10OrLater()) {
+    widget::WinCompositorWindowThread::ShutDown();
+  }
+#endif
 
   image::ImageMemoryReporter::ShutdownForWebRender();
 
   // Shut down the default GL context provider.
   gl::GLContextProvider::Shutdown();
 
 #if defined(XP_WIN)
   // The above shutdown calls operate on the available context providers on
--- a/gfx/layers/d3d11/CompositorD3D11.cpp
+++ b/gfx/layers/d3d11/CompositorD3D11.cpp
@@ -1413,16 +1413,18 @@ void CompositorD3D11::PrepareViewport(co
 
   memcpy(&mVSConstants.projection, &aProjection._11,
          sizeof(mVSConstants.projection));
 }
 
 void CompositorD3D11::EnsureSize() { mSize = mWidget->GetClientSize(); }
 
 bool CompositorD3D11::VerifyBufferSize() {
+  mWidget->AsWindows()->UpdateCompositorWndSizeIfNecessary();
+
   DXGI_SWAP_CHAIN_DESC swapDesc;
   HRESULT hr;
 
   hr = mSwapChain->GetDesc(&swapDesc);
   if (FAILED(hr)) {
     gfxCriticalError() << "Failed to get the description " << hexa(hr) << ", "
                        << mSize << ", " << (int)mVerifyBuffersFailed;
     HandleError(hr);
--- a/gfx/layers/d3d11/MLGDeviceD3D11.cpp
+++ b/gfx/layers/d3d11/MLGDeviceD3D11.cpp
@@ -229,25 +229,28 @@ bool MLGSwapChainD3D11::Initialize(Compo
     dxgiDevice->GetAdapter(getter_AddRefs(adapter));
 
     adapter->GetParent(IID_PPV_ARGS(dxgiFactory.StartAssignment()));
   }
 
   RefPtr<IDXGIFactory2> dxgiFactory2;
   if (gfxPrefs::Direct3D11UseDoubleBuffering() &&
       SUCCEEDED(dxgiFactory->QueryInterface(dxgiFactory2.StartAssignment())) &&
-      dxgiFactory2 && IsWin10OrLater()) {
+      dxgiFactory2 && IsWin10OrLater() && XRE_IsGPUProcess()) {
     // DXGI_SCALING_NONE is not available on Windows 7 with the Platform Update:
     // This looks awful for things like the awesome bar and browser window
     // resizing, so we don't use a flip buffer chain here. (Note when using
     // EFFECT_SEQUENTIAL Windows doesn't stretch the surface when resizing).
     //
     // We choose not to run this on platforms earlier than Windows 10 because
     // it appears sometimes this breaks our ability to test ASAP compositing,
     // which breaks Talos.
+    //
+    // When the GPU process is disabled we don't have a compositor window which
+    // can lead to issues with Window re-use so we don't use this.
     DXGI_SWAP_CHAIN_DESC1 desc;
     ::ZeroMemory(&desc, sizeof(desc));
     desc.Width = 0;
     desc.Height = 0;
     desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
     desc.SampleDesc.Count = 1;
     desc.SampleDesc.Quality = 0;
     desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
@@ -370,16 +373,18 @@ void MLGSwapChainD3D11::UpdateBackBuffer
   }
 
   // The back and front buffers are now in sync.
   mBackBufferInvalid = mFrontBufferInvalid;
   MOZ_ASSERT(!mBackBufferInvalid.IsEmpty());
 }
 
 bool MLGSwapChainD3D11::ResizeBuffers(const IntSize& aSize) {
+  mWidget->AsWindows()->UpdateCompositorWndSizeIfNecessary();
+
   // We have to clear all references to the old backbuffer before resizing.
   mRT = nullptr;
 
   // Clear the size before re-allocating. If allocation fails we want to try
   // again, because we had to sacrifice our original backbuffer to try
   // resizing.
   mSize = IntSize(0, 0);
 
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -75,16 +75,17 @@
 #include "nsISupportsImpl.h"  // for MOZ_COUNT_CTOR, etc
 #include "nsIWidget.h"        // for nsIWidget
 #include "nsTArray.h"         // for nsTArray
 #include "nsThreadUtils.h"    // for NS_IsMainThread
 #include "nsXULAppAPI.h"      // for XRE_GetIOMessageLoop
 #ifdef XP_WIN
 #  include "mozilla/layers/CompositorD3D11.h"
 #  include "mozilla/widget/WinCompositorWidget.h"
+#  include "mozilla/WindowsVersion.h"
 #endif
 #include "GeckoProfiler.h"
 #include "mozilla/ipc/ProtocolTypes.h"
 #include "mozilla/Unused.h"
 #include "mozilla/Hal.h"
 #include "mozilla/HalTypes.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/Telemetry.h"
@@ -1545,16 +1546,25 @@ RefPtr<Compositor> CompositorBridgeParen
 
   return nullptr;
 }
 
 PLayerTransactionParent* CompositorBridgeParent::AllocPLayerTransactionParent(
     const nsTArray<LayersBackend>& aBackendHints, const LayersId& aId) {
   MOZ_ASSERT(!aId.IsValid());
 
+#ifdef XP_WIN
+  // This is needed to avoid freezing the window on a device crash on double
+  // buffering, see bug 1549674.
+  if (gfxPrefs::Direct3D11UseDoubleBuffering() && IsWin10OrLater() &&
+      XRE_IsGPUProcess()) {
+    mWidget->AsWindows()->EnsureCompositorWindow();
+  }
+#endif
+
   InitializeLayerManager(aBackendHints);
 
   if (!mLayerManager) {
     NS_WARNING("Failed to initialise Compositor");
     LayerTransactionParent* p = new LayerTransactionParent(
         /* aManager */ nullptr, this, /* aAnimStorage */ nullptr,
         mRootLayerTreeID, mVsyncRate);
     p->AddIPDLReference();
--- a/gfx/src/gfxTelemetry.cpp
+++ b/gfx/src/gfxTelemetry.cpp
@@ -9,20 +9,46 @@ namespace mozilla {
 namespace gfx {
 
 const char* FeatureStatusToString(FeatureStatus aStatus) {
   switch (aStatus) {
     case FeatureStatus::Unused:
       return "unused";
     case FeatureStatus::Unavailable:
       return "unavailable";
+    case FeatureStatus::UnavailableInSafeMode:
+      return "unavailable-in-safe-mode";
+    case FeatureStatus::UnavailableNoGpuProcess:
+      return "unavailable-no-gpu-process";
+    case FeatureStatus::UnavailableNoHwCompositing:
+      return "unavailable-no-hw-compositing";
+    case FeatureStatus::UnavailableNotBuilt:
+      return "unavailable-not-built";
+    case FeatureStatus::UnavailableNoAngle:
+      return "unavailable-no-angle";
     case FeatureStatus::CrashedInHandler:
       return "crashed";
     case FeatureStatus::Blocked:
       return "blocked";
+    case FeatureStatus::BlockedDeviceUnknown:
+      return "blocked-device-unknown";
+    case FeatureStatus::BlockedDeviceTooOld:
+      return "blocked-device-too-old";
+    case FeatureStatus::BlockedVendorUnsupported:
+      return "blocked-vendor-unsupported";
+    case FeatureStatus::BlockedHasBattery:
+      return "blocked-has-battery";
+    case FeatureStatus::BlockedScreenTooLarge:
+      return "blocked-screen-too-large";
+    case FeatureStatus::BlockedScreenUnknown:
+      return "blocked-screen-unknown";
+    case FeatureStatus::BlockedNoGfxInfo:
+      return "blocked-no-gfx-info";
+    case FeatureStatus::BlockedOverride:
+      return "blocked-override";
     case FeatureStatus::Blacklisted:
       return "blacklisted";
     case FeatureStatus::OptIn:
       return "opt-in";
     case FeatureStatus::Failed:
       return "failed";
     case FeatureStatus::Disabled:
       return "disabled";
--- a/gfx/src/gfxTelemetry.h
+++ b/gfx/src/gfxTelemetry.h
@@ -14,24 +14,37 @@ namespace gfx {
 // (and if not, why).
 enum class FeatureStatus {
   // This feature has not been requested.
   Unused,
 
   // This feature is unavailable due to Safe Mode, not being included with
   // the operating system, or a dependent feature being disabled.
   Unavailable,
+  UnavailableInSafeMode,
+  UnavailableNoGpuProcess,
+  UnavailableNoHwCompositing,
+  UnavailableNotBuilt,
+  UnavailableNoAngle,
 
   // This feature crashed immediately when we tried to initialize it, but we
   // were able to recover via SEH (or something similar).
   CrashedInHandler,
 
   // This feature was blocked for reasons outside the blacklist, such as a
   // runtime test failing.
   Blocked,
+  BlockedDeviceUnknown,
+  BlockedDeviceTooOld,
+  BlockedVendorUnsupported,
+  BlockedHasBattery,
+  BlockedScreenTooLarge,
+  BlockedScreenUnknown,
+  BlockedNoGfxInfo,
+  BlockedOverride,
 
   // This feature has been blocked by the graphics blacklist.
   Blacklisted,
 
   // This feature is disabled by default, and so activation isn't attempted
   // unless something explicitly enables it.
   OptIn,
 
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -2531,40 +2531,40 @@ static FeatureState& WebRenderHardwareQu
     const IntSize& aScreenSize, bool aHasBattery, nsCString& aOutFailureId) {
   FeatureState& featureWebRenderQualified =
       gfxConfig::GetFeature(Feature::WEBRENDER_QUALIFIED);
   featureWebRenderQualified.EnableByDefault();
 
   if (Preferences::HasUserValue(WR_ROLLOUT_HW_QUALIFIED_OVERRIDE)) {
     if (!Preferences::GetBool(WR_ROLLOUT_HW_QUALIFIED_OVERRIDE)) {
       featureWebRenderQualified.Disable(
-          FeatureStatus::Blocked, "HW qualification pref override",
+          FeatureStatus::BlockedOverride, "HW qualification pref override",
           NS_LITERAL_CSTRING("FEATURE_FAILURE_WR_QUALIFICATION_OVERRIDE"));
     }
     return featureWebRenderQualified;
   }
 
   nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
   int32_t status;
   if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBRENDER,
                                              aOutFailureId, &status))) {
     if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
-      featureWebRenderQualified.Disable(FeatureStatus::Blocked,
+      featureWebRenderQualified.Disable(FeatureStatus::Blacklisted,
                                         "No qualified hardware", aOutFailureId);
     } else {
       nsAutoString adapterVendorID;
       gfxInfo->GetAdapterVendorID(adapterVendorID);
 
       nsAutoString adapterDeviceID;
       gfxInfo->GetAdapterDeviceID(adapterDeviceID);
       nsresult valid;
       int32_t deviceID = adapterDeviceID.ToInteger(&valid, 16);
       if (valid != NS_OK) {
         featureWebRenderQualified.Disable(
-            FeatureStatus::Blocked, "Bad device id",
+            FeatureStatus::BlockedDeviceUnknown, "Bad device id",
             NS_LITERAL_CSTRING("FEATURE_FAILURE_BAD_DEVICE_ID"));
       } else {
 #ifdef NIGHTLY_BUILD
         // For Intel devices, if we have a battery, ignore it if the screen is
         // small enough. Note that we always check for a battery with NVIDIA
         // because we do not have a limited/curated set of devices to support
         // WebRender on.
         const int32_t kMaxPixelsBattery = 1920 * 1200;  // WUXGA
@@ -2575,25 +2575,25 @@ static FeatureState& WebRenderHardwareQu
           disableForBattery = false;
         }
 #else
         bool disableForBattery = aHasBattery;
 #endif
 
         if (disableForBattery) {
           featureWebRenderQualified.Disable(
-              FeatureStatus::Blocked, "Has battery",
+              FeatureStatus::BlockedHasBattery, "Has battery",
               NS_LITERAL_CSTRING("FEATURE_FAILURE_WR_HAS_BATTERY"));
         } else if (adapterVendorID == u"0x10de") {
           if (deviceID < 0x6c0) {
             // 0x6c0 is the lowest Fermi device id. Unfortunately some Tesla
             // devices that don't support D3D 10.1 have higher deviceIDs. They
             // will be included, but blocked by ANGLE.
             featureWebRenderQualified.Disable(
-                FeatureStatus::Blocked, "Device too old",
+                FeatureStatus::BlockedDeviceTooOld, "Device too old",
                 NS_LITERAL_CSTRING("FEATURE_FAILURE_DEVICE_TOO_OLD"));
           }
 #ifdef EARLY_BETA_OR_EARLIER
         } else if (adapterVendorID == u"0x1002") {  // AMD
           // AMD deviceIDs are not very well ordered. This
           // condition is based off the information in gpu-db
           if ((deviceID >= 0x6600 && deviceID < 0x66b0) ||
               (deviceID >= 0x6700 && deviceID < 0x6720) ||
@@ -2601,17 +2601,17 @@ static FeatureState& WebRenderHardwareQu
               (deviceID >= 0x6860 && deviceID < 0x6880) ||
               (deviceID >= 0x6900 && deviceID < 0x6a00) ||
               (deviceID == 0x7300) ||
               (deviceID >= 0x9830 && deviceID < 0x9870) ||
               (deviceID >= 0x9900 && deviceID < 0x9a00)) {
             // we have a desktop CAYMAN, SI, CIK, VI, or GFX9 device
           } else {
             featureWebRenderQualified.Disable(
-                FeatureStatus::Blocked, "Device too old",
+                FeatureStatus::BlockedDeviceTooOld, "Device too old",
                 NS_LITERAL_CSTRING("FEATURE_FAILURE_DEVICE_TOO_OLD"));
           }
 #endif
 #ifdef NIGHTLY_BUILD
         } else if (adapterVendorID == u"0x8086") {  // Intel
           const uint16_t supportedDevices[] = {
               // skylake gt2+
               0x1912,
@@ -2678,46 +2678,46 @@ static FeatureState& WebRenderHardwareQu
           bool supported = false;
           for (uint16_t id : supportedDevices) {
             if (deviceID == id) {
               supported = true;
             }
           }
           if (!supported) {
             featureWebRenderQualified.Disable(
-                FeatureStatus::Blocked, "Device too old",
+                FeatureStatus::BlockedDeviceTooOld, "Device too old",
                 NS_LITERAL_CSTRING("FEATURE_FAILURE_DEVICE_TOO_OLD"));
           }
 #  ifdef MOZ_WIDGET_GTK
           else {
             // Performance is not great on 4k screens with WebRender + Linux.
             // Disable it for now if it is too large.
             const int32_t kMaxPixelsLinux = 3440 * 1440;  // UWQHD
             if (screenPixels > kMaxPixelsLinux) {
               featureWebRenderQualified.Disable(
-                  FeatureStatus::Blocked, "Screen size too large",
+                  FeatureStatus::BlockedScreenTooLarge, "Screen size too large",
                   NS_LITERAL_CSTRING("FEATURE_FAILURE_SCREEN_SIZE_TOO_LARGE"));
             } else if (screenPixels <= 0) {
               featureWebRenderQualified.Disable(
-                  FeatureStatus::Blocked, "Screen size unknown",
+                  FeatureStatus::BlockedScreenUnknown, "Screen size unknown",
                   NS_LITERAL_CSTRING("FEATURE_FAILURE_SCREEN_SIZE_UNKNOWN"));
             }
           }
 #  endif  // MOZ_WIDGET_GTK
 #endif    // NIGHTLY_BUILD
         } else {
           featureWebRenderQualified.Disable(
-              FeatureStatus::Blocked, "Unsupported vendor",
+              FeatureStatus::BlockedVendorUnsupported, "Unsupported vendor",
               NS_LITERAL_CSTRING("FEATURE_FAILURE_UNSUPPORTED_VENDOR"));
         }
       }
     }
   } else {
     featureWebRenderQualified.Disable(
-        FeatureStatus::Blocked, "gfxInfo is broken",
+        FeatureStatus::BlockedNoGfxInfo, "gfxInfo is broken",
         NS_LITERAL_CSTRING("FEATURE_FAILURE_WR_NO_GFX_INFO"));
   }
   return featureWebRenderQualified;
 }
 
 void gfxPlatform::InitWebRenderConfig() {
   bool prefEnabled = WebRenderPrefEnabled();
   bool envvarEnabled = WebRenderEnvvarEnabled();
@@ -2778,46 +2778,47 @@ void gfxPlatform::InitWebRenderConfig() 
     featureWebRender.UserDisable(
         "User force-disabled WR",
         NS_LITERAL_CSTRING("FEATURE_FAILURE_USER_FORCE_DISABLED"));
   }
 
   // HW_COMPOSITING being disabled implies interfacing with the GPU might break
   if (!gfxConfig::IsEnabled(Feature::HW_COMPOSITING)) {
     featureWebRender.ForceDisable(
-        FeatureStatus::Unavailable, "Hardware compositing is disabled",
+        FeatureStatus::UnavailableNoHwCompositing,
+        "Hardware compositing is disabled",
         NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBRENDER_NEED_HWCOMP"));
   }
 
   // WebRender relies on the GPU process when on Windows
 #ifdef XP_WIN
   if (!gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
     featureWebRender.ForceDisable(
-        FeatureStatus::Unavailable, "GPU Process is disabled",
+        FeatureStatus::UnavailableNoGpuProcess, "GPU Process is disabled",
         NS_LITERAL_CSTRING("FEATURE_FAILURE_GPU_PROCESS_DISABLED"));
   }
 #endif
 
   if (InSafeMode()) {
     featureWebRender.ForceDisable(
-        FeatureStatus::Unavailable, "Safe-mode is enabled",
+        FeatureStatus::UnavailableInSafeMode, "Safe-mode is enabled",
         NS_LITERAL_CSTRING("FEATURE_FAILURE_SAFE_MODE"));
   }
 
 #ifndef MOZ_BUILD_WEBRENDER
   featureWebRender.ForceDisable(
-      FeatureStatus::Unavailable, "Build doesn't include WebRender",
+      FeatureStatus::UnavailableNotBuilt, "Build doesn't include WebRender",
       NS_LITERAL_CSTRING("FEATURE_FAILURE_NO_WEBRENDER"));
 #endif
 
 #ifdef XP_WIN
   if (Preferences::GetBool("gfx.webrender.force-angle", false)) {
     if (!gfxConfig::IsEnabled(Feature::D3D11_HW_ANGLE)) {
       featureWebRender.ForceDisable(
-          FeatureStatus::Unavailable, "ANGLE is disabled",
+          FeatureStatus::UnavailableNoAngle, "ANGLE is disabled",
           NS_LITERAL_CSTRING("FEATURE_FAILURE_ANGLE_DISABLED"));
     } else {
       gfxVars::SetUseWebRenderANGLE(gfxConfig::IsEnabled(Feature::WEBRENDER));
     }
   }
 #endif
 
   if (Preferences::GetBool("gfx.webrender.program-binary-disk", false)) {
@@ -3162,16 +3163,20 @@ class FrameStatsComparator {
   // Reverse the condition here since we want the array sorted largest to
   // smallest.
   bool LessThan(const FrameStats& aA, const FrameStats& aB) const {
     return aA.contentFrameTime() > aB.contentFrameTime();
   }
 };
 
 void gfxPlatform::NotifyFrameStats(nsTArray<FrameStats>&& aFrameStats) {
+  if (!gfxPrefs::LoggingSlowFramesEnabled()) {
+    return;
+  }
+
   FrameStatsComparator comp;
   for (FrameStats& f : aFrameStats) {
     mFrameStats.InsertElementSorted(f, comp);
   }
   if (mFrameStats.Length() > 10) {
     mFrameStats.SetLength(10);
   }
 }
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -502,16 +502,17 @@ class gfxPrefs final {
   DECL_GFX_PREF(Live, "gfx.layerscope.port",                   LayerScopePort, int32_t, 23456);
   // Note that        "gfx.logging.level" is defined in Logging.h.
   DECL_GFX_PREF(Live, "gfx.logging.level",                     GfxLoggingLevel, int32_t, mozilla::gfx::LOG_DEFAULT);
   DECL_GFX_PREF(Once, "gfx.logging.crash.length",              GfxLoggingCrashLength, uint32_t, 16);
   DECL_GFX_PREF(Live, "gfx.logging.painted-pixel-count.enabled",GfxLoggingPaintedPixelCountEnabled, bool, false);
   // The maximums here are quite conservative, we can tighten them if problems show up.
   DECL_GFX_PREF(Once, "gfx.logging.texture-usage.enabled",     GfxLoggingTextureUsageEnabled, bool, false);
   DECL_GFX_PREF(Once, "gfx.logging.peak-texture-usage.enabled",GfxLoggingPeakTextureUsageEnabled, bool, false);
+  DECL_GFX_PREF(Once, "gfx.logging.slow-frames.enabled",       LoggingSlowFramesEnabled, bool, false);
   // Use gfxPlatform::MaxAllocSize instead of the pref directly
   DECL_GFX_PREF(Once, "gfx.max-alloc-size",                    MaxAllocSizeDoNotUseDirectly, int32_t, (int32_t)500000000);
   // Use gfxPlatform::MaxTextureSize instead of the pref directly
   DECL_GFX_PREF(Once, "gfx.max-texture-size",                  MaxTextureSizeDoNotUseDirectly, int32_t, (int32_t)32767);
   DECL_GFX_PREF(Live, "gfx.partialpresent.force",              PartialPresent, int32_t, 0);
   DECL_GFX_PREF(Live, "gfx.perf-warnings.enabled",             PerfWarnings, bool, false);
   DECL_GFX_PREF(Live, "gfx.testing.device-reset",              DeviceResetForTesting, int32_t, 0);
   DECL_GFX_PREF(Live, "gfx.testing.device-fail",               DeviceFailForTesting, bool, false);
new file mode 100644
--- /dev/null
+++ b/js/src/gc/FreeOp-inl.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef gc_FreeOp_inl_h
+#define gc_FreeOp_inl_h
+
+#include "gc/FreeOp.h"
+
+#include "gc/Zone.h"
+
+namespace js {
+
+inline void FreeOp::free_(gc::Cell* cell, void* p, size_t nbytes,
+                          MemoryUse use) {
+  if (p) {
+    RemoveCellMemory(cell, nbytes, use);
+    js_free(p);
+  }
+}
+
+inline void FreeOp::freeLater(gc::Cell* cell, void* p, size_t nbytes,
+                              MemoryUse use) {
+  if (p) {
+    RemoveCellMemory(cell, nbytes, use);
+    freeLater(p);
+  }
+}
+
+inline void FreeOp::freeLater(void* p) {
+  // Default FreeOps are not constructed on the stack, and will hold onto the
+  // pointers to free indefinitely.
+  MOZ_ASSERT(!isDefaultFreeOp());
+
+  // It's not safe to free this allocation immediately, so we must crash if we
+  // can't append to the list.
+  AutoEnterOOMUnsafeRegion oomUnsafe;
+  if (!freeLaterList.append(p)) {
+    oomUnsafe.crash("FreeOp::freeLater");
+  }
+}
+
+} // namespace js
+
+#endif // gc_FreeOp_inl_h
--- a/js/src/gc/FreeOp.h
+++ b/js/src/gc/FreeOp.h
@@ -14,16 +14,18 @@
 #include "js/MemoryFunctions.h"       // JSFreeOp
 #include "js/Utility.h"               // AutoEnterOOMUnsafeRegion, js_free
 #include "js/Vector.h"                // js::Vector
 
 struct JSRuntime;
 
 namespace js {
 
+enum class MemoryUse : uint8_t;
+
 /*
  * A FreeOp can do one thing: free memory. For convenience, it has delete_
  * convenience methods that also call destructors.
  *
  * FreeOp is passed to finalizers and other sweep-phase hooks so that we do not
  * need to pass a JSContext to those hooks.
  */
 class FreeOp : public JSFreeOp {
@@ -44,39 +46,62 @@ class FreeOp : public JSFreeOp {
     // runtime_ being null doesn't always mean we are off thread.
     return !runtime_;
   }
 
   bool isDefaultFreeOp() const { return isDefault; }
 
   void free_(void* p) { js_free(p); }
 
-  void freeLater(void* p) {
-    // FreeOps other than the defaultFreeOp() are constructed on the stack,
-    // and won't hold onto the pointers to free indefinitely.
-    MOZ_ASSERT(!isDefaultFreeOp());
+  // Free memory that was associated with a GC thing using js::AddCellMemory.
+  void free_(gc::Cell* cell, void* p, size_t nbytes, MemoryUse use);
 
-    AutoEnterOOMUnsafeRegion oomUnsafe;
-    if (!freeLaterList.append(p)) {
-      oomUnsafe.crash("FreeOp::freeLater");
-    }
-  }
+  // Queue an allocation to be freed when the FreeOp is destroyed.
+  //
+  // This should not be called on the default FreeOps returned by
+  // JSRuntime/JSContext::defaultFreeOp() since these are not destroyed until
+  // the runtime itself is destroyed.
+  //
+  // This is used to ensure that copy-on-write object elements are not freed
+  // until all objects that refer to them have been finalized.
+  void freeLater(void* p);
+
+  // Free memory that was associated with a GC thing using js::AddCellMemory.
+  void freeLater(gc::Cell* cell, void* p, size_t nbytes, MemoryUse use);
 
   bool appendJitPoisonRange(const jit::JitPoisonRange& range) {
     // FreeOps other than the defaultFreeOp() are constructed on the stack,
     // and won't hold onto the pointers to free indefinitely.
     MOZ_ASSERT(!isDefaultFreeOp());
 
     return jitPoisonRanges.append(range);
   }
 
   template <class T>
   void delete_(T* p) {
     if (p) {
       p->~T();
       free_(p);
     }
   }
+
+  // Delete a C++ object that was associated with a GC thing using
+  // js::AddCellMemory. The size is determined by the type T.
+  template <class T>
+  void delete_(gc::Cell* cell, T* p, MemoryUse use) {
+    delete_(cell, p, sizeof(T), use);
+  }
+
+  // Delete a C++ object that was associated with a GC thing using
+  // js::AddCellMemory. The size of the allocation is passed in to allow for
+  // allocations with trailing data after the object.
+  template <class T>
+  void delete_(gc::Cell* cell, T* p, size_t nbytes, MemoryUse use) {
+    if (p) {
+      p->~T();
+      free_(cell, p, nbytes, use);
+    }
+  }
 };
 
 }  // namespace js
 
 #endif  // gc_FreeOp_h
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -3131,16 +3131,21 @@ size_t js::TenuringTracer::moveStringToT
 
   if (!src->isInline() && src->isLinear()) {
     if (src->isUndepended() || !src->hasBase()) {
       void* chars = src->asLinear().nonInlineCharsRaw();
       nursery().removeMallocedBuffer(chars);
     }
   }
 
+  if (dst->isFlat() && !dst->isInline()) {
+    AddCellMemory(dst, dst->asFlat().allocSize(),
+                  MemoryUse::StringContents);
+  }
+
   return size;
 }
 
 /*** IsMarked / IsAboutToBeFinalized ****************************************/
 
 template <typename T>
 static inline void CheckIsMarkedThing(T* thingp) {
 #define IS_SAME_TYPE_OR(name, type, _, _1) mozilla::IsSame<type*, T>::value ||
--- a/js/src/gc/Scheduling.h
+++ b/js/src/gc/Scheduling.h
@@ -308,18 +308,29 @@
 #define gc_Scheduling_h
 
 #include "mozilla/Atomics.h"
 
 #include "js/HashTable.h"
 
 namespace js {
 
-// This will eventually have internal reasons too.
-using JS::MemoryUse;
+#define JS_FOR_EACH_INTERNAL_MEMORY_USE(_)      \
+  _(ArrayBufferContents)                        \
+  _(StringContents)
+
+#define JS_FOR_EACH_MEMORY_USE(_)               \
+  JS_FOR_EACH_PUBLIC_MEMORY_USE(_)              \
+  JS_FOR_EACH_INTERNAL_MEMORY_USE(_)
+
+enum class MemoryUse : uint8_t {
+#define DEFINE_MEMORY_USE(Name) Name,
+  JS_FOR_EACH_MEMORY_USE(DEFINE_MEMORY_USE)
+#undef DEFINE_MEMORY_USE
+};
 
 namespace gc {
 
 struct Cell;
 
 enum TriggerKind { NoTrigger = 0, IncrementalTrigger, NonIncrementalTrigger };
 
 /*
@@ -657,23 +668,27 @@ class MemoryTracker {
  public:
 #ifdef DEBUG
   MemoryTracker();
   ~MemoryTracker();
   void fixupAfterMovingGC();
 #endif
 
   void addMemory(Cell* cell, size_t nbytes, MemoryUse use) {
+    MOZ_ASSERT(cell);
+    MOZ_ASSERT(nbytes);
 #ifdef DEBUG
     trackMemory(cell, nbytes, use);
 #endif
     MOZ_ASSERT(bytes_ + nbytes >= bytes_);
     bytes_ += nbytes;
   }
   void removeMemory(Cell* cell, size_t nbytes, MemoryUse use) {
+    MOZ_ASSERT(cell);
+    MOZ_ASSERT(nbytes);
 #ifdef DEBUG
     untrackMemory(cell, nbytes, use);
 #endif
     MOZ_ASSERT(bytes_ >= nbytes);
     bytes_ -= nbytes;
   }
 
   size_t bytes() const { return bytes_; }
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -521,72 +521,84 @@ void MemoryTracker::adopt(MemoryTracker&
     }
   }
   other.map.clear();
 #endif
 }
 
 #ifdef DEBUG
 
+static const char* MemoryUseName(MemoryUse use) {
+  switch (use) {
+#define DEFINE_CASE(Name) \
+    case MemoryUse::Name: return #Name;
+JS_FOR_EACH_MEMORY_USE(DEFINE_CASE)
+#undef DEFINE_CASE
+  }
+
+  MOZ_CRASH("Unknown memory use");
+}
+
 MemoryTracker::MemoryTracker() : mutex(mutexid::MemoryTracker) {}
 
 MemoryTracker::~MemoryTracker() {
   if (!TlsContext.get()->runtime()->gc.shutdownCollectedEverything()) {
     // Memory leak, suppress crashes.
     return;
   }
 
   if (map.empty()) {
     MOZ_ASSERT(bytes() == 0);
     return;
   }
 
   fprintf(stderr, "Missing calls to JS::RemoveAssociatedMemory:\n");
   for (auto r = map.all(); !r.empty(); r.popFront()) {
-    fprintf(stderr, "  %p 0x%zx %u\n", r.front().key().cell,
-            r.front().value(), unsigned(r.front().key().use));
+    fprintf(stderr, "  %p 0x%zx %s\n", r.front().key().cell,
+            r.front().value(),
+            MemoryUseName(r.front().key().use));
   }
 
   MOZ_CRASH();
 }
 
 void MemoryTracker::trackMemory(Cell* cell, size_t nbytes, MemoryUse use) {
   MOZ_ASSERT(cell->isTenured());
 
   LockGuard<Mutex> lock(mutex);
 
   Key key{cell, use};
   AutoEnterOOMUnsafeRegion oomUnsafe;
   auto ptr = map.lookupForAdd(key);
   if (ptr) {
-    MOZ_CRASH_UNSAFE_PRINTF("Association already present: %p 0x%zx %u", cell,
-                            nbytes, unsigned(use));
+    MOZ_CRASH_UNSAFE_PRINTF("Association already present: %p 0x%zx %s", cell,
+                            nbytes, MemoryUseName(use));
   }
 
   if (!map.add(ptr, key, nbytes)) {
     oomUnsafe.crash("MemoryTracker::noteExternalAlloc");
   }
 }
 
 void MemoryTracker::untrackMemory(Cell* cell, size_t nbytes, MemoryUse use) {
   MOZ_ASSERT(cell->isTenured());
 
   LockGuard<Mutex> lock(mutex);
 
   Key key{cell, use};
   auto ptr = map.lookup(key);
   if (!ptr) {
-    MOZ_CRASH_UNSAFE_PRINTF("Association not found: %p 0x%x %u", cell,
-                            unsigned(nbytes), unsigned(use));
+    MOZ_CRASH_UNSAFE_PRINTF("Association not found: %p 0x%x %s", cell,
+                            unsigned(nbytes), MemoryUseName(use));
   }
   if (ptr->value() != nbytes) {
     MOZ_CRASH_UNSAFE_PRINTF(
-        "Association for %p %u has different size: "
+        "Association for %p %s has different size: "
         "expected 0x%zx but got 0x%zx",
-        cell, unsigned(use), ptr->value(), nbytes);
+        cell, MemoryUseName(use), ptr->value(), nbytes);
   }
   map.remove(ptr);
 }
 
 void MemoryTracker::fixupAfterMovingGC() {
   // Update the table after we move GC things. We don't use MovableCellHasher
   // because that would create a difference between debug and release builds.
   for (Map::Enum e(map); !e.empty(); e.popFront()) {
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -448,17 +448,17 @@ class Zone : public JS::shadow::Zone,
   js::ZoneData<JS::WeakCache<TypeDescrObjectSet>> typeDescrObjects_;
 
   // Malloc counter to measure memory pressure for GC scheduling. This counter
   // is used for allocations where the size of the allocation is not known on
   // free. Currently this is used for all internal malloc allocations.
   js::gc::MemoryCounter gcMallocCounter;
 
   // Malloc counter used for allocations where size information is
-  // available. Currently this is used for external allocations only.
+  // available. Used for some internal and all tracked external allocations.
   js::gc::MemoryTracker gcMallocSize;
 
   // Counter of JIT code executable memory for GC scheduling. Also imprecise,
   // since wasm can generate code that outlives a zone.
   js::gc::MemoryCounter jitCodeCounter;
 
   void updateMemoryCounter(js::gc::MemoryCounter& counter, size_t nbytes) {
     JSRuntime* rt = runtimeFromAnyThread();
@@ -750,11 +750,42 @@ class ZoneAllocPolicy {
   }
   void reportAllocOverflow() const {}
 
   MOZ_MUST_USE bool checkSimulatedOOM() const {
     return !js::oom::ShouldFailWithOOM();
   }
 };
 
+// Convenience functions for memory accounting on the zone.
+
+// Associate malloc memory with a GC thing. This call must be matched by a
+// following call to RemoveCellMemory with the same size and use. The total
+// amount of malloc memory associated with a zone is used to trigger GC.
+inline void AddCellMemory(gc::TenuredCell* cell, size_t nbytes,
+                          MemoryUse use) {
+  if (nbytes) {
+    cell->zone()->addCellMemory(cell, nbytes, use);
+  }
+}
+inline void AddCellMemory(gc::Cell* cell, size_t nbytes, MemoryUse use) {
+  if (cell->isTenured()) {
+    AddCellMemory(&cell->asTenured(), nbytes, use);
+  }
+}
+
+// Remove association between malloc memory and a GC thing. This call must
+// follow a call to AddCellMemory with the same size and use.
+inline void RemoveCellMemory(gc::TenuredCell* cell, size_t nbytes,
+                             MemoryUse use) {
+  if (nbytes) {
+    cell->zoneFromAnyThread()->removeCellMemory(cell, nbytes, use);
+  }
+}
+inline void RemoveCellMemory(gc::Cell* cell, size_t nbytes, MemoryUse use) {
+  if (cell->isTenured()) {
+    RemoveCellMemory(&cell->asTenured(), nbytes, use);
+  }
+}
+
 }  // namespace js
 
 #endif  // gc_Zone_h
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1157,28 +1157,28 @@ JS_PUBLIC_API void JS::AddAssociatedMemo
                                            JS::MemoryUse use) {
   MOZ_ASSERT(obj);
   if (!nbytes) {
     return;
   }
 
   Zone* zone = obj->zone();
   zone->updateMallocCounter(nbytes);
-  zone->addCellMemory(obj, nbytes, use);
+  zone->addCellMemory(obj, nbytes, js::MemoryUse(use));
   zone->runtimeFromMainThread()->gc.maybeAllocTriggerZoneGC(zone);
 }
 
 JS_PUBLIC_API void JS::RemoveAssociatedMemory(JSObject* obj, size_t nbytes,
                                               JS::MemoryUse use) {
   MOZ_ASSERT(obj);
   if (!nbytes) {
     return;
   }
 
-  obj->zoneFromAnyThread()->removeCellMemory(obj, nbytes, use);
+  obj->zoneFromAnyThread()->removeCellMemory(obj, nbytes, js::MemoryUse(use));
 }
 
 #undef JS_AddRoot
 
 JS_PUBLIC_API bool JS_AddExtraGCRootsTracer(JSContext* cx,
                                             JSTraceDataOp traceOp, void* data) {
   return cx->runtime()->gc.addBlackRootsTracer(traceOp, data);
 }
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -29,17 +29,16 @@
 #include "jsfriendapi.h"
 #include "jsnum.h"
 #include "jstypes.h"
 #include "jsutil.h"
 
 #include "builtin/Array.h"
 #include "builtin/DataViewObject.h"
 #include "gc/Barrier.h"
-#include "gc/FreeOp.h"
 #include "gc/Memory.h"
 #include "js/ArrayBuffer.h"
 #include "js/Conversions.h"
 #include "js/MemoryMetrics.h"
 #include "js/PropertySpec.h"
 #include "js/SharedArrayBuffer.h"
 #include "js/Wrapper.h"
 #include "util/Windows.h"
@@ -47,16 +46,17 @@
 #include "vm/Interpreter.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/SharedArrayObject.h"
 #include "vm/WrapperObject.h"
 #include "wasm/WasmSignalHandlers.h"
 #include "wasm/WasmTypes.h"
 
+#include "gc/FreeOp-inl.h"
 #include "gc/Marking-inl.h"
 #include "gc/Nursery-inl.h"
 #include "vm/JSAtom-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/Shape-inl.h"
 
 using JS::ToInt32;
 
@@ -939,30 +939,34 @@ ArrayBufferObject::FreeInfo* ArrayBuffer
 }
 
 void ArrayBufferObject::releaseData(FreeOp* fop) {
   switch (bufferKind()) {
     case INLINE_DATA:
       // Inline data doesn't require releasing.
       break;
     case MALLOCED:
-      fop->free_(dataPointer());
+      fop->free_(this, dataPointer(), byteLength(),
+                 MemoryUse::ArrayBufferContents);
       break;
     case NO_DATA:
       // There's nothing to release if there's no data.
       MOZ_ASSERT(dataPointer() == nullptr);
       break;
     case USER_OWNED:
       // User-owned data is released by, well, the user.
       break;
     case MAPPED:
       gc::DeallocateMappedContent(dataPointer(), byteLength());
+      RemoveCellMemory(this, associatedBytes(),
+                       MemoryUse::ArrayBufferContents);
       break;
     case WASM:
       WasmArrayRawBuffer::Release(dataPointer());
+      RemoveCellMemory(this, byteLength(), MemoryUse::ArrayBufferContents);
       break;
     case EXTERNAL:
       if (freeInfo()->freeFunc) {
         // The analyzer can't know for sure whether the embedder-supplied
         // free function will GC. We give the analyzer a hint here.
         // (Doing a GC in the free function is considered a programmer
         // error.)
         JS::AutoSuppressGCAnalysis nogc;
@@ -985,16 +989,26 @@ void ArrayBufferObject::setDataPointer(B
     info->freeUserData = contents.freeUserData();
   }
 }
 
 uint32_t ArrayBufferObject::byteLength() const {
   return getFixedSlot(BYTE_LENGTH_SLOT).toInt32();
 }
 
+inline size_t ArrayBufferObject::associatedBytes() const {
+  if (bufferKind() == MALLOCED) {
+    return byteLength();
+  } else if (bufferKind() == MAPPED) {
+    return JS_ROUNDUP(byteLength(), js::gc::SystemPageSize());
+  } else {
+    MOZ_CRASH("Unexpected buffer kind");
+  }
+}
+
 void ArrayBufferObject::setByteLength(uint32_t length) {
   MOZ_ASSERT(length <= INT32_MAX);
   setFixedSlot(BYTE_LENGTH_SLOT, Int32Value(length));
 }
 
 size_t ArrayBufferObject::wasmMappedSize() const {
   if (isWasm()) {
     return contents().wasmBuffer()->mappedSize();
@@ -1066,20 +1080,24 @@ bool ArrayBufferObject::wasmGrowToSizeIn
 
   // Extract the grown contents from |oldBuf|.
   BufferContents oldContents = oldBuf->contents();
 
   // Overwrite |oldBuf|'s data pointer *without* releasing old data.
   oldBuf->setDataPointer(BufferContents::createNoData());
 
   // Detach |oldBuf| now that doing so won't release |oldContents|.
+  RemoveCellMemory(oldBuf, oldBuf->byteLength(),
+                   MemoryUse::ArrayBufferContents);
   ArrayBufferObject::detach(cx, oldBuf);
 
   // Set |newBuf|'s contents to |oldBuf|'s original contents.
   newBuf->initialize(newSize, oldContents);
+  AddCellMemory(newBuf, newSize, MemoryUse::ArrayBufferContents);
+
   return true;
 }
 
 #ifndef WASM_HUGE_MEMORY
 /* static */
 bool ArrayBufferObject::wasmMovingGrowToSize(
     uint32_t newSize, HandleArrayBufferObject oldBuf,
     MutableHandleArrayBufferObject newBuf, JSContext* cx) {
@@ -1101,16 +1119,19 @@ bool ArrayBufferObject::wasmMovingGrowTo
     return false;
   }
 
   WasmArrayRawBuffer* newRawBuf =
       WasmArrayRawBuffer::Allocate(newSize, Nothing());
   if (!newRawBuf) {
     return false;
   }
+
+  AddCellMemory(newBuf, newSize, MemoryUse::ArrayBufferContents);
+
   BufferContents contents =
       BufferContents::createWasm(newRawBuf->dataPointer());
   newBuf->initialize(newSize, contents);
 
   memcpy(newBuf->dataPointer(), oldBuf->dataPointer(), oldBuf->byteLength());
   ArrayBufferObject::detach(cx, oldBuf);
   return true;
 }
@@ -1170,41 +1191,36 @@ ArrayBufferObject* ArrayBufferObject::cr
     return nullptr;
   }
 
   // Some |contents| kinds need to store extra data in the ArrayBuffer beyond a
   // data pointer.  If needed for the particular kind, add extra fixed slots to
   // the ArrayBuffer for use as raw storage to store such information.
   size_t reservedSlots = JSCLASS_RESERVED_SLOTS(&class_);
 
+  size_t nAllocated = 0;
   size_t nslots = reservedSlots;
   if (contents.kind() == USER_OWNED) {
     // No accounting to do in this case.
   } else if (contents.kind() == EXTERNAL) {
     // Store the FreeInfo in the inline data slots so that we
     // don't use up slots for it in non-refcounted array buffers.
     size_t freeInfoSlots = JS_HOWMANY(sizeof(FreeInfo), sizeof(Value));
     MOZ_ASSERT(reservedSlots + freeInfoSlots <= NativeObject::MAX_FIXED_SLOTS,
                "FreeInfo must fit in inline slots");
     nslots += freeInfoSlots;
   } else {
     // The ABO is taking ownership, so account the bytes against the zone.
-    size_t nAllocated = nbytes;
+    nAllocated = nbytes;
     if (contents.kind() == MAPPED) {
       nAllocated = JS_ROUNDUP(nbytes, js::gc::SystemPageSize());
     } else {
       MOZ_ASSERT(contents.kind() == MALLOCED,
                  "should have handled all possible callers' kinds");
     }
-
-    // "mapped" bytes are fed into a "malloc" counter because (bug 1037358) this
-    // counter constitutes an input to the "when do we GC?" subsystem.  Arguably
-    // it deserves renaming to something that doesn't narrowly cabin it to just
-    // "malloc" stuff, if we're going to use it this way.
-    cx->updateMallocCounter(nAllocated);
   }
 
   MOZ_ASSERT(!(class_.flags & JSCLASS_HAS_PRIVATE));
   gc::AllocKind allocKind = gc::GetGCObjectKind(nslots);
 
   AutoSetNewObjectMetadata metadata(cx);
   Rooted<ArrayBufferObject*> buffer(
       cx, NewObjectWithClassProto<ArrayBufferObject>(cx, nullptr, allocKind,
@@ -1214,16 +1230,20 @@ ArrayBufferObject* ArrayBufferObject::cr
   }
 
   MOZ_ASSERT(!gc::IsInsideNursery(buffer),
              "ArrayBufferObject has a finalizer that must be called to not "
              "leak in some cases, so it can't be nursery-allocated");
 
   buffer->initialize(nbytes, contents);
 
+  if (contents.kind() == MAPPED || contents.kind() == MALLOCED) {
+    AddCellMemory(buffer, nAllocated, MemoryUse::ArrayBufferContents);
+  }
+
   return buffer;
 }
 
 ArrayBufferObject* ArrayBufferObject::createZeroed(
     JSContext* cx, uint32_t nbytes, HandleObject proto /* = nullptr */) {
   // 24.1.1.1, step 3 (Inlined 6.2.6.1 CreateByteDataBlock, step 2).
   if (!CheckArrayBufferTooLarge(cx, nbytes)) {
     return nullptr;
@@ -1262,16 +1282,17 @@ ArrayBufferObject* ArrayBufferObject::cr
   }
 
   MOZ_ASSERT(!gc::IsInsideNursery(buffer),
              "ArrayBufferObject has a finalizer that must be called to not "
              "leak in some cases, so it can't be nursery-allocated");
 
   if (data) {
     buffer->initialize(nbytes, BufferContents::createMalloced(data));
+    AddCellMemory(buffer, nbytes, MemoryUse::ArrayBufferContents);
   } else {
     void* inlineData = buffer->initializeToInlineData(nbytes);
     memset(inlineData, 0, nbytes);
   }
 
   return buffer;
 }
 
@@ -1297,30 +1318,33 @@ ArrayBufferObject* ArrayBufferObject::cr
 
   buffer->setByteLength(initialSize);
   buffer->setFlags(0);
   buffer->setFirstView(nullptr);
 
   auto contents = BufferContents::createWasm(rawBuffer->dataPointer());
   buffer->setDataPointer(contents);
 
-  cx->updateMallocCounter(initialSize);
+  AddCellMemory(buffer, initialSize, MemoryUse::ArrayBufferContents);
 
   return buffer;
 }
 
 /* static */ uint8_t* ArrayBufferObject::stealMallocedContents(
     JSContext* cx, Handle<ArrayBufferObject*> buffer) {
   CheckStealPreconditions(buffer, cx);
 
   switch (buffer->bufferKind()) {
     case MALLOCED: {
       uint8_t* stolenData = buffer->dataPointer();
       MOZ_ASSERT(stolenData);
 
+      RemoveCellMemory(buffer, buffer->byteLength(),
+                       MemoryUse::ArrayBufferContents);
+
       // Overwrite the old data pointer *without* releasing the contents
       // being stolen.
       buffer->setDataPointer(BufferContents::createNoData());
 
       // Detach |buffer| now that doing so won't free |stolenData|.
       ArrayBufferObject::detach(cx, buffer);
       return stolenData;
     }
@@ -1377,16 +1401,19 @@ ArrayBufferObject::extractStructuredClon
       ArrayBufferObject::detach(cx, buffer);
       return BufferContents::createMalloced(copiedData);
     }
 
     case MALLOCED:
     case MAPPED: {
       MOZ_ASSERT(contents);
 
+      RemoveCellMemory(buffer, buffer->associatedBytes(),
+                       MemoryUse::ArrayBufferContents);
+
       // Overwrite the old data pointer *without* releasing old data.
       buffer->setDataPointer(BufferContents::createNoData());
 
       // Detach |buffer| now that doing so won't release |oldContents|.
       ArrayBufferObject::detach(cx, buffer);
       return contents;
     }
 
--- a/js/src/vm/ArrayBufferObject.h
+++ b/js/src/vm/ArrayBufferObject.h
@@ -434,16 +434,18 @@ class ArrayBufferObject : public ArrayBu
   static size_t offsetOfDataSlot() { return getFixedSlotOffset(DATA_SLOT); }
 
   void setHasTypedObjectViews() { setFlags(flags() | TYPED_OBJECT_VIEWS); }
 
  protected:
   void setDataPointer(BufferContents contents);
   void setByteLength(uint32_t length);
 
+  size_t associatedBytes() const;
+
   uint32_t flags() const;
   void setFlags(uint32_t flags);
 
   bool hasTypedObjectViews() const { return flags() & TYPED_OBJECT_VIEWS; }
 
   void setIsDetached() { setFlags(flags() | DETACHED); }
   void setIsPreparedForAsmJS() {
     MOZ_ASSERT(!isWasm());
--- a/js/src/vm/JSObject-inl.h
+++ b/js/src/vm/JSObject-inl.h
@@ -4,22 +4,22 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef vm_JSObject_inl_h
 #define vm_JSObject_inl_h
 
 #include "vm/JSObject.h"
 
-#include "gc/FreeOp.h"
 #include "vm/ArrayObject.h"
 #include "vm/EnvironmentObject.h"
 #include "vm/JSFunction.h"
 #include "vm/Probes.h"
 
+#include "gc/FreeOp-inl.h"
 #include "gc/Marking-inl.h"
 #include "gc/ObjectKind-inl.h"
 #include "vm/ObjectOperations-inl.h"  // js::MaybeHasInterestingSymbolProperty
 #include "vm/Realm-inl.h"
 
 inline void JSObject::finalize(js::FreeOp* fop) {
   js::probes::FinalizeObject(this);
 
--- a/js/src/vm/StringType-inl.h
+++ b/js/src/vm/StringType-inl.h
@@ -8,23 +8,23 @@
 #define vm_StringType_inl_h
 
 #include "vm/StringType.h"
 
 #include "mozilla/PodOperations.h"
 #include "mozilla/Range.h"
 
 #include "gc/Allocator.h"
-#include "gc/FreeOp.h"
 #include "gc/Marking.h"
 #include "gc/StoreBuffer.h"
 #include "js/UniquePtr.h"
 #include "vm/JSContext.h"
 #include "vm/Realm.h"
 
+#include "gc/FreeOp-inl.h"
 #include "gc/StoreBuffer-inl.h"
 
 namespace js {
 
 // Allocate a thin inline string if possible, and a fat inline string if not.
 template <AllowGC allowGC, typename CharT>
 static MOZ_ALWAYS_INLINE JSInlineString* AllocateInlineString(JSContext* cx,
                                                               size_t len,
@@ -288,16 +288,20 @@ MOZ_ALWAYS_INLINE JSFlatString* JSFlatSt
     // uninitialized memory.
     if (!cx->runtime()->gc.nursery().registerMallocedBuffer(chars.get())) {
       str->init(static_cast<JS::Latin1Char*>(nullptr), 0);
       if (allowGC) {
         ReportOutOfMemory(cx);
       }
       return nullptr;
     }
+  } else {
+    // This can happen off the main thread for the atoms zone.
+    cx->zone()->addCellMemory(str, (length + 1) * sizeof(CharT),
+                              js::MemoryUse::StringContents);
   }
 
   str->init(chars.release(), length);
   return str;
 }
 
 inline js::PropertyName* JSFlatString::toPropertyName(JSContext* cx) {
 #ifdef DEBUG
@@ -380,17 +384,22 @@ MOZ_ALWAYS_INLINE JSExternalString* JSEx
   if (!validateLength(cx, length)) {
     return nullptr;
   }
   JSExternalString* str = js::Allocate<JSExternalString>(cx);
   if (!str) {
     return nullptr;
   }
   str->init(chars, length, fin);
-  cx->updateMallocCounter((length + 1) * sizeof(char16_t));
+  size_t nbytes = (length + 1) * sizeof(char16_t);
+  cx->updateMallocCounter(nbytes);
+
+  MOZ_ASSERT(str->isTenured());
+  js::AddCellMemory(str, nbytes, js::MemoryUse::StringContents);
+
   return str;
 }
 
 inline JSLinearString* js::StaticStrings::getUnitStringForElement(
     JSContext* cx, JSString* str, size_t index) {
   MOZ_ASSERT(index < str->length());
 
   char16_t c;
@@ -415,50 +424,52 @@ MOZ_ALWAYS_INLINE void JSString::finaliz
   }
 }
 
 inline void JSFlatString::finalize(js::FreeOp* fop) {
   MOZ_ASSERT(getAllocKind() != js::gc::AllocKind::FAT_INLINE_STRING);
   MOZ_ASSERT(getAllocKind() != js::gc::AllocKind::FAT_INLINE_ATOM);
 
   if (!isInline()) {
-    fop->free_(nonInlineCharsRaw());
+    fop->free_(this, nonInlineCharsRaw(), allocSize(),
+               js::MemoryUse::StringContents);
   }
 }
 
+inline size_t JSFlatString::allocSize() const {
+  MOZ_ASSERT(!isInline());
+
+  size_t charSize = hasLatin1Chars() ? sizeof(JS::Latin1Char)
+                                     : sizeof(char16_t);
+  size_t count = isExtensible() ? asExtensible().capacity() : length();
+  return (count + 1) * charSize;
+}
+
 inline void JSFatInlineString::finalize(js::FreeOp* fop) {
   MOZ_ASSERT(getAllocKind() == js::gc::AllocKind::FAT_INLINE_STRING);
   MOZ_ASSERT(isInline());
 
   // Nothing to do.
 }
 
-inline void JSAtom::finalize(js::FreeOp* fop) {
-  MOZ_ASSERT(JSString::isAtom());
-  MOZ_ASSERT(JSString::isFlat());
-  MOZ_ASSERT(getAllocKind() == js::gc::AllocKind::ATOM);
-
-  if (!isInline()) {
-    fop->free_(nonInlineCharsRaw());
-  }
-}
-
 inline void js::FatInlineAtom::finalize(js::FreeOp* fop) {
   MOZ_ASSERT(JSString::isAtom());
   MOZ_ASSERT(getAllocKind() == js::gc::AllocKind::FAT_INLINE_ATOM);
 
   // Nothing to do.
 }
 
 inline void JSExternalString::finalize(js::FreeOp* fop) {
   if (!JSString::isExternal()) {
     // This started out as an external string, but was turned into a
     // non-external string by JSExternalString::ensureFlat.
-    MOZ_ASSERT(isFlat());
-    fop->free_(nonInlineCharsRaw());
+    asFlat().finalize(fop);
     return;
   }
 
+  size_t nbytes = (length() + 1) * sizeof(char16_t);
+  js::RemoveCellMemory(this, nbytes, js::MemoryUse::StringContents);
+
   const JSStringFinalizer* fin = externalFinalizer();
   fin->finalize(fin, const_cast<char16_t*>(rawTwoByteChars()));
 }
 
 #endif /* vm_StringType_inl_h */
--- a/js/src/vm/StringType.cpp
+++ b/js/src/vm/StringType.cpp
@@ -568,16 +568,24 @@ JSFlatString* JSRope::flattenInternal(JS
       }
       if (b == WithIncrementalBarrier) {
         JSString::writeBarrierPre(str->d.s.u2.left);
         JSString::writeBarrierPre(str->d.s.u3.right);
       }
       str->setNonInlineChars(wholeChars);
       uint32_t left_len = left.length();
       pos = wholeChars + left_len;
+
+      // Remove memory association for left node we're about to make into a
+      // dependent string.
+      if (left.isTenured()) {
+        RemoveCellMemory(&left, left.allocSize(),
+                         MemoryUse::StringContents);
+      }
+
       if (IsSame<CharT, char16_t>::value) {
         left.setLengthAndFlags(left_len, DEPENDENT_FLAGS);
       } else {
         left.setLengthAndFlags(left_len, DEPENDENT_FLAGS | LATIN1_CHARS_BIT);
       }
       left.d.s.u3.base = (JSLinearString*)this; /* will be true on exit */
       Nursery& nursery = runtimeFromMainThread()->gc.nursery();
       bool inTenured = !bufferIfNursery;
@@ -650,16 +658,22 @@ finish_node : {
     *pos = '\0';
     if (IsSame<CharT, char16_t>::value) {
       str->setLengthAndFlags(wholeLength, EXTENSIBLE_FLAGS);
     } else {
       str->setLengthAndFlags(wholeLength, EXTENSIBLE_FLAGS | LATIN1_CHARS_BIT);
     }
     str->setNonInlineChars(wholeChars);
     str->d.s.u3.capacity = wholeCapacity;
+
+    if (str->isTenured()) {
+      AddCellMemory(str, str->asFlat().allocSize(),
+                    MemoryUse::StringContents);
+    }
+
     return &this->asFlat();
   }
   uintptr_t flattenData;
   uint32_t len = pos - str->nonInlineCharsRaw<CharT>();
   if (IsSame<CharT, char16_t>::value) {
     flattenData = str->unsetFlattenData(len, DEPENDENT_FLAGS);
   } else {
     flattenData =
@@ -866,16 +880,18 @@ JSFlatString* JSDependentString::undepen
     return nullptr;
   }
 
   if (!isTenured()) {
     if (!cx->runtime()->gc.nursery().registerMallocedBuffer(s.get())) {
       ReportOutOfMemory(cx);
       return nullptr;
     }
+  } else {
+    AddCellMemory(this, (n + 1) * sizeof(CharT), MemoryUse::StringContents);
   }
 
   AutoCheckCannotGC nogc;
   FillAndTerminate(s.get(), nonInlineChars<CharT>(nogc), n);
   setNonInlineChars<CharT>(s.release());
 
   /*
    * Transform *this into an undepended string so 'base' will remain rooted
@@ -1445,32 +1461,29 @@ JSFlatString* JSExternalString::ensureFl
   MOZ_ASSERT(hasTwoByteChars());
 
   size_t n = length();
   auto s = cx->make_pod_array<char16_t>(n + 1, js::StringBufferArena);
   if (!s) {
     return nullptr;
   }
 
-  if (!isTenured()) {
-    if (!cx->runtime()->gc.nursery().registerMallocedBuffer(s.get())) {
-      ReportOutOfMemory(cx);
-      return nullptr;
-    }
-  }
-
   // Copy the chars before finalizing the string.
   {
     AutoCheckCannotGC nogc;
     FillAndTerminate(s.get(), nonInlineChars<char16_t>(nogc), n);
   }
 
   // Release the external chars.
   finalize(cx->runtime()->defaultFreeOp());
 
+  MOZ_ASSERT(isTenured());
+  AddCellMemory(this, (n + 1) * sizeof(char16_t),
+                MemoryUse::StringContents);
+
   // Transform the string into a non-external, flat string. Note that the
   // resulting string will still be in an AllocKind::EXTERNAL_STRING arena,
   // but will no longer be an external string.
   setLengthAndFlags(n, INIT_FLAT_FLAGS);
   setNonInlineChars<char16_t>(s.release());
 
   return &this->asFlat();
 }
@@ -1577,16 +1590,19 @@ static JSFlatString* NewStringDeflated(J
 
   if (JSInlineString::lengthFits<Latin1Char>(n)) {
     return NewInlineStringDeflated<allowGC>(
         cx, mozilla::Range<const char16_t>(s, n));
   }
 
   auto news = cx->make_pod_array<Latin1Char>(n + 1, js::StringBufferArena);
   if (!news) {
+    if (!allowGC) {
+      cx->recoverFromOutOfMemory();
+    }
     return nullptr;
   }
 
   MOZ_ASSERT(CanStoreCharsAsLatin1(s, n));
   FillFromCompatibleAndTerminate(news.get(), s, n);
 
   return JSFlatString::new_<allowGC>(cx, std::move(news), n);
 }
--- a/js/src/vm/StringType.h
+++ b/js/src/vm/StringType.h
@@ -1038,16 +1038,18 @@ class JSFlatString : public JSLinearStri
   /*
    * Once a JSFlatString sub-class has been added to the atom state, this
    * operation changes the string to the JSAtom type, in place.
    */
   MOZ_ALWAYS_INLINE JSAtom* morphAtomizedStringIntoAtom(js::HashNumber hash);
   MOZ_ALWAYS_INLINE JSAtom* morphAtomizedStringIntoPermanentAtom(
       js::HashNumber hash);
 
+  inline size_t allocSize() const;
+
   inline void finalize(js::FreeOp* fop);
 
 #if defined(DEBUG) || defined(JS_JITSPEW)
   void dumpRepresentation(js::GenericPrinter& out, int indent) const;
 #endif
 };
 
 static_assert(sizeof(JSFlatString) == sizeof(JSString),
@@ -1236,18 +1238,16 @@ class JSAtom : public JSFlatString {
   /* Vacuous and therefore unimplemented. */
   bool isAtom() const = delete;
   JSAtom& asAtom() const = delete;
 
  public:
   /* Returns the PropertyName for this.  isIndex() must be false. */
   inline js::PropertyName* asPropertyName();
 
-  inline void finalize(js::FreeOp* fop);
-
   MOZ_ALWAYS_INLINE
   bool isPermanent() const { return JSString::isPermanentAtom(); }
 
   // Transform this atom into a permanent atom. This is only done during
   // initialization of the runtime. Permanent atoms are always pinned.
   MOZ_ALWAYS_INLINE void morphIntoPermanentAtom() {
     MOZ_ASSERT(static_cast<JSString*>(this)->isAtom());
     setFlagBit(PERMANENT_ATOM_FLAGS | PINNED_ATOM_BIT);
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -876,16 +876,17 @@ pref("gfx.text.disable-aa", false);
 
 pref("gfx.work-around-driver-bugs", true);
 
 pref("gfx.draw-color-bars", false);
 
 pref("gfx.logging.painted-pixel-count.enabled", false);
 pref("gfx.logging.texture-usage.enabled", false);
 pref("gfx.logging.peak-texture-usage.enabled", false);
+pref("gfx.logging.slow-frames.enabled", false);
 
 pref("gfx.ycbcr.accurate-conversion", false);
 
 // We expose two prefs: gfx.webrender.all and gfx.webrender.enabled.
 // The first enables WR+additional features, and the second just enables WR.
 // For developer convenience, building with --enable-webrender=true or just
 // --enable-webrender will set gfx.webrender.enabled to true by default.
 //
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-NSS_3_44_BETA2
+NSS_3_44_BETA3
--- a/security/nss/automation/taskcluster/graph/src/extend.js
+++ b/security/nss/automation/taskcluster/graph/src/extend.js
@@ -919,19 +919,23 @@ async function scheduleWindows(name, bas
 
   return queue.submit();
 }
 
 /*****************************************************************************/
 
 function scheduleTests(task_build, task_cert, test_base) {
   test_base = merge(test_base, {kind: "test"});
+  let no_cert_base = merge(test_base, {parent: task_build});
+  let cert_base = merge(test_base, {parent: task_cert});
+  let cert_base_long = merge(cert_base, {maxRunTime: 7200});
 
-  // Schedule tests that do NOT need certificates.
-  let no_cert_base = merge(test_base, {parent: task_build});
+  // Schedule tests that do NOT need certificates. This is defined as
+  // the test itself not needing certs AND not running under the upgradedb
+  // cycle (which itself needs certs). If cycle is not defined, default is all.
   queue.scheduleTask(merge(no_cert_base, {
     name: "Gtests", symbol: "Gtest", tests: "ssl_gtests gtests", cycle: "standard"
   }));
   queue.scheduleTask(merge(no_cert_base, {
     name: "Bogo tests",
     symbol: "Bogo",
     tests: "bogo",
     cycle: "standard",
@@ -942,56 +946,55 @@ function scheduleTests(task_build, task_
     symbol: "Interop",
     tests: "interop",
     cycle: "standard",
     image: LINUX_INTEROP_IMAGE,
   }));
   queue.scheduleTask(merge(no_cert_base, {
     name: "tlsfuzzer tests", symbol: "tlsfuzzer", tests: "tlsfuzzer", cycle: "standard"
   }));
-  queue.scheduleTask(merge(no_cert_base, {
+  queue.scheduleTask(merge(cert_base, {
     name: "Chains tests", symbol: "Chains", tests: "chains"
   }));
-  queue.scheduleTask(merge(no_cert_base, {
+  queue.scheduleTask(merge(cert_base_long, {
     name: "Cipher tests", symbol: "Default", tests: "cipher", group: "Cipher"
   }));
-  queue.scheduleTask(merge(no_cert_base, {
+  queue.scheduleTask(merge(cert_base_long, {
     name: "Cipher tests", symbol: "NoAESNI", tests: "cipher",
     env: {NSS_DISABLE_HW_AES: "1"}, group: "Cipher"
   }));
-  queue.scheduleTask(merge(no_cert_base, {
+  queue.scheduleTask(merge(cert_base_long, {
     name: "Cipher tests", symbol: "NoPCLMUL", tests: "cipher",
     env: {NSS_DISABLE_PCLMUL: "1"}, group: "Cipher"
   }));
-  queue.scheduleTask(merge(no_cert_base, {
+  queue.scheduleTask(merge(cert_base_long, {
     name: "Cipher tests", symbol: "NoAVX", tests: "cipher",
     env: {NSS_DISABLE_AVX: "1"}, group: "Cipher"
   }));
-  queue.scheduleTask(merge(no_cert_base, {
+  queue.scheduleTask(merge(cert_base_long, {
     name: "Cipher tests", symbol: "NoSSSE3|NEON", tests: "cipher",
     env: {
       NSS_DISABLE_ARM_NEON: "1",
       NSS_DISABLE_SSSE3: "1"
     }, group: "Cipher"
   }));
-  queue.scheduleTask(merge(no_cert_base, {
+  queue.scheduleTask(merge(cert_base, {
     name: "EC tests", symbol: "EC", tests: "ec"
   }));
-  queue.scheduleTask(merge(no_cert_base, {
+  queue.scheduleTask(merge(cert_base, {
     name: "Lowhash tests", symbol: "Lowhash", tests: "lowhash"
   }));
-  queue.scheduleTask(merge(no_cert_base, {
+  queue.scheduleTask(merge(cert_base, {
     name: "SDR tests", symbol: "SDR", tests: "sdr"
   }));
-  queue.scheduleTask(merge(no_cert_base, {
+  queue.scheduleTask(merge(cert_base, {
     name: "Policy tests", symbol: "Policy", tests: "policy"
   }));
 
   // Schedule tests that need certificates.
-  let cert_base = merge(test_base, {parent: task_cert});
   queue.scheduleTask(merge(cert_base, {
     name: "CRMF tests", symbol: "CRMF", tests: "crmf"
   }));
   queue.scheduleTask(merge(cert_base, {
     name: "DB tests", symbol: "DB", tests: "dbtests"
   }));
   queue.scheduleTask(merge(cert_base, {
     name: "Merge tests", symbol: "Merge", tests: "merge"
--- a/security/nss/coreconf/config.gypi
+++ b/security/nss/coreconf/config.gypi
@@ -14,33 +14,35 @@
         'python%': '<(python)',
         'host_arch%': '<!(<(python) <(DEPTH)/coreconf/detect_host_arch.py)',
       },
       'python%': '<(python)',
       'host_arch%': '<(host_arch)',
       'conditions': [
         ['OS=="android"', {
           'target_arch%': 'arm',
+        }, 'OS=="ios"', {
+          'target_arch%': 'arm64',
         }, {
           # Default architecture we're building for is the architecture we're
           # building on.
           'target_arch%': '<(host_arch)',
         }],
         ['OS=="linux"', {
           # FIPS-140 LOWHASH
           'freebl_name': 'freeblpriv3',
         }, {
           'freebl_name': 'freebl3',
         }],
         ['OS=="mac"', {
           'use_system_sqlite%': 1,
         },{
           'use_system_sqlite%': 0,
         }],
-        ['OS=="mac" or OS=="win"', {
+        ['OS=="mac" or OS=="ios" or OS=="win"', {
           'cc_use_gnu_ld%': 0,
         }, {
           'cc_use_gnu_ld%': 1,
         }],
         ['OS=="win"', {
           'use_system_zlib%': 0,
           'nspr_libs%': ['libnspr4.lib', 'libplc4.lib', 'libplds4.lib'],
           'zlib_libs%': [],
@@ -49,17 +51,17 @@
           'dll_prefix': '',
           'dll_suffix': 'dll',
         }, {
           'use_system_zlib%': 1,
           'nspr_libs%': ['-lplds4', '-lplc4', '-lnspr4'],
           'zlib_libs%': ['-lz'],
           'dll_prefix': 'lib',
           'conditions': [
-            ['OS=="mac"', {
+            ['OS=="mac" or OS=="ios"', {
               'moz_debug_flags%': '-gdwarf-2 -gfull',
               'dll_suffix': 'dylib',
             }, {
               'moz_debug_flags%': '-gdwarf-2',
               'dll_suffix': 'so',
             }],
           ],
         }],
@@ -150,17 +152,17 @@
           'NSS_NO_INIT_SUPPORT',
         ],
       }],
       [ 'static_libs==1', {
         'variables': {
           'standalone_static_library': '1',
         },
       }],
-      [ 'OS!="android" and OS!="mac" and OS!="win"', {
+      [ 'OS!="android" and OS!="mac" and OS!="ios" and OS!="win"', {
         'libraries': [
           '-lpthread',
         ],
       }],
       [ 'OS=="linux"', {
         'libraries': [
           '-ldl',
           '-lc',
@@ -368,29 +370,29 @@
               'NETBSD',
             ],
           }],
           [ 'OS=="openbsd"', {
             'defines': [
               'OPENBSD',
             ],
           }],
-          ['OS=="mac" or OS=="dragonfly" or OS=="freebsd" or OS=="netbsd" or OS=="openbsd"', {
+          ['OS=="mac" or OS=="ios" or OS=="dragonfly" or OS=="freebsd" or OS=="netbsd" or OS=="openbsd"', {
             'defines': [
               'HAVE_BSD_FLOCK',
             ],
           }],
           [ 'OS!="win"', {
             'defines': [
               'HAVE_STRERROR',
               'XP_UNIX',
               '_REENTRANT',
             ],
           }],
-          [ 'OS!="mac" and OS!="win"', {
+          [ 'OS!="mac" and OS!="ios" and OS!="win"', {
             'cflags': [
               '-fPIC',
               '-pipe',
               '-ffunction-sections',
               '-fdata-sections',
             ],
             'cflags_cc': [
               '-std=c++0x',
@@ -406,17 +408,17 @@
               [ 'target_arch=="x64"', {
                 'cflags': ['-m64'],
                 'ldflags': ['-m64'],
               }],
             ],
           }],
           [ 'use_pprof==1 and OS!="android" and OS!="win"', {
             'conditions': [
-              [ 'OS=="mac"', {
+              [ 'OS=="mac" or OS=="ios"', {
                 'xcode_settings': {
                   'OTHER_LDFLAGS': [ '-lprofiler' ],
                 },
               }, {
                 'ldflags': [ '-lprofiler' ],
               }],
               [ 'OS!="linux"', {
                 'library_dirs': [
@@ -466,33 +468,43 @@
           }],
           [ 'OS=="android" and mozilla_client==0', {
             'defines': [
               'NO_SYSINFO',
               'NO_FORK_CHECK',
               'ANDROID',
             ],
           }],
-          [ 'OS=="mac"', {
+          [ 'OS=="mac" or OS=="ios"', {
             'defines': [
               'DARWIN',
             ],
             'conditions': [
               [ 'target_arch=="ia32"', {
                 'xcode_settings': {
                   'ARCHS': ['i386'],
                 },
               }],
               [ 'target_arch=="x64"', {
                 'xcode_settings': {
                   'ARCHS': ['x86_64'],
                 },
               }],
+              [ 'target_arch=="arm64"', {
+                'xcode_settings': {
+                  'ARCHS': ['arm64'],
+                },
+              }],
             ],
           }],
+          [ 'OS=="ios"', {
+            'xcode_settings': {
+              'IPHONEOS_DEPLOYMENT_TARGET': '<(iphone_deployment_target)',
+            },
+          }],
           [ 'OS=="win"', {
             'defines': [
               '_WINDOWS',
               'WIN95',
               '_CRT_SECURE_NO_WARNINGS',
               '_CRT_NONSTDC_NO_WARNINGS',
             ],
             'cflags': [
@@ -550,17 +562,17 @@
             ],
           }],
         ],
       },
       # Common settings for debug should go here.
       'Debug': {
         'inherit_from': ['Common'],
         'conditions': [
-          [ 'OS!="mac" and OS!="win"', {
+          [ 'OS!="mac" and OS!="ios" and OS!="win"', {
             'cflags': [
               '-g',
               '<(moz_debug_flags)',
             ],
           }]
         ],
         #TODO: DEBUG_$USER
         'defines': ['DEBUG'],
@@ -620,15 +632,15 @@
     },
   },
   'conditions': [
     [ 'cc_use_gnu_ld==1', {
       'variables': {
         'process_map_file': ['/bin/sh', '-c', '/usr/bin/env grep -v ";-" >(mapfile) | sed -e "s,;+,," -e "s; DATA ;;" -e "s,;;,," -e "s,;.*,;," > >@(_outputs)'],
       },
     }],
-    [ 'OS=="mac"', {
+    [ 'OS=="mac" or OS=="ios"', {
       'variables': {
         'process_map_file': ['/bin/sh', '-c', '/usr/bin/grep -v ";+" >(mapfile) | grep -v ";-" | sed -e "s; DATA ;;" -e "s,;;,," -e "s,;.*,," -e "s,^,_," > >@(_outputs)'],
       },
     }],
   ],
 }
--- a/security/nss/coreconf/coreconf.dep
+++ b/security/nss/coreconf/coreconf.dep
@@ -5,8 +5,9 @@
 
 /*
  * A dummy header file that is a dependency for all the object files.
  * Used to force a full recompilation of NSS in Mozilla's Tinderbox
  * depend builds.  See comments in rules.mk.
  */
 
 #error "Do not include this header file."
+
--- a/security/nss/lib/freebl/freebl.gyp
+++ b/security/nss/lib/freebl/freebl.gyp
@@ -102,17 +102,17 @@
       ],
       'conditions': [
         [ 'OS=="linux" or OS=="android" or OS=="dragonfly" or OS=="freebsd" or OS=="netbsd" or OS=="openbsd"', {
           'cflags': [
             '-mpclmul', '-maes'
           ],
         }],
         # macOS build doesn't use cflags.
-        [ 'OS=="mac"', {
+        [ 'OS=="mac" or OS=="ios"', {
           'xcode_settings': {
             'OTHER_CFLAGS': [
               '-mpclmul', '-maes'
             ],
           },
         }]
       ]
     },
--- a/security/nss/lib/sqlite/sqlite3.c
+++ b/security/nss/lib/sqlite/sqlite3.c
@@ -18392,18 +18392,25 @@ static int sqlite3MemInit(void *NotUsed)
     _sqliteZone_ = malloc_default_zone();
   }else{
     /* only 1 core, use our own zone to contention over global locks, 
     ** e.g. we have our own dedicated locks */
     bool success;
     malloc_zone_t* newzone = malloc_create_zone(4096, 0);
     malloc_set_zone_name(newzone, "Sqlite_Heap");
     do{
+      #ifdef DARWIN // On iOS gyp build fails because of -Werror.
+      #pragma clang diagnostic push
+      #pragma clang diagnostic warning "-Wdeprecated-declarations"
+      #endif
       success = OSAtomicCompareAndSwapPtrBarrier(NULL, newzone, 
                                  (void * volatile *)&_sqliteZone_);
+      #ifdef DARWIN
+      #pragma clang diagnostic pop
+      #endif
     }while(!_sqliteZone_);
     if( !success ){
       /* somebody registered a zone first */
       malloc_destroy_zone(newzone);
     }
   }
 #endif
   UNUSED_PARAMETER(NotUsed);
@@ -26952,17 +26959,24 @@ SQLITE_PRIVATE const char *sqlite3Opcode
 #endif /* SQLITE_ENABLE_LOCKING_STYLE */
 
 #if defined(__APPLE__) && ((__MAC_OS_X_VERSION_MIN_REQUIRED > 1050) || \
                            (__IPHONE_OS_VERSION_MIN_REQUIRED > 2000))
 #  if (!defined(TARGET_OS_EMBEDDED) || (TARGET_OS_EMBEDDED==0)) \
        && (!defined(TARGET_IPHONE_SIMULATOR) || (TARGET_IPHONE_SIMULATOR==0))
 #    define HAVE_GETHOSTUUID 1
 #  else
+#    ifdef DARWIN // On iOS gyp build fails because of -Werror.
+#    pragma clang diagnostic push
+#    pragma clang diagnostic warning "-W#warnings"
+#    endif
 #    warning "gethostuuid() is disabled."
+#    ifdef DARWIN
+#    pragma clang diagnostic pop
+#    endif
 #  endif
 #endif
 
 
 #if OS_VXWORKS
 /* # include <sys/ioctl.h> */
 # include <semaphore.h>
 # include <limits.h>