Merge mozilla-inbound to mozilla-central. a=merge
authorDaniel Varga <dvarga@mozilla.com>
Sat, 11 May 2019 00:48:13 +0300
changeset 473448 2abcefb31ba7b2a1c573edc5695b772826c6a078
parent 473435 362df4629f8f1fd5ed0eece429b77499a77bb955 (current diff)
parent 473447 e96752781d2d3961d797cec48c5357c4ec293a23 (diff)
child 473479 5972961441c097e5b59dd9348715d31b459226c7
child 473510 ab4794c0be03d757f9304c01569d6711dd3884bb
push id35997
push userdvarga@mozilla.com
push dateFri, 10 May 2019 21:49:31 +0000
treeherdermozilla-central@2abcefb31ba7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone68.0a1
first release with
nightly linux32
2abcefb31ba7 / 68.0a1 / 20190510214931 / files
nightly linux64
2abcefb31ba7 / 68.0a1 / 20190510214931 / files
nightly mac
2abcefb31ba7 / 68.0a1 / 20190510214931 / files
nightly win32
2abcefb31ba7 / 68.0a1 / 20190510214931 / files
nightly win64
2abcefb31ba7 / 68.0a1 / 20190510214931 / 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
js/src/jsapi.cpp
modules/libpref/init/all.js
--- 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>