Merge m-c to f-t
authorPhil Ringnalda <philringnalda@gmail.com>
Mon, 04 Jul 2016 15:07:39 -0700
changeset 303690 162b6fca7d868e8165d0f6ef80e02b5dd73161cb
parent 303689 760b8e6d19e41780e554573b3983aa8647bf3cbd (current diff)
parent 303593 c9a70b64f2faa264296f0cc90d68a2ee2bac6ac5 (diff)
child 303691 e27437d7fe35881b13a2f493427cb0e3ba6aeee0
push id79141
push usercbook@mozilla.com
push dateTue, 05 Jul 2016 14:07:42 +0000
treeherdermozilla-inbound@f08c54971dd1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone50.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 m-c to f-t
layout/base/nsPresShell.cpp
--- a/accessible/base/Logging.cpp
+++ b/accessible/base/Logging.cpp
@@ -643,17 +643,17 @@ logging::TreeInfo(const char* aMsg, uint
 void
 logging::TreeInfo(const char* aMsg, uint32_t aExtraFlags,
                   const char* aMsg1, Accessible* aAcc,
                   const char* aMsg2, nsINode* aNode)
 {
   if (IsEnabledAll(logging::eTree | aExtraFlags)) {
     MsgBegin("TREE", "%s; doc: %p", aMsg, aAcc ? aAcc->Document() : nullptr);
     AccessibleInfo(aMsg1, aAcc);
-    Accessible* acc = aAcc->Document()->GetAccessible(aNode);
+    Accessible* acc = aAcc ? aAcc->Document()->GetAccessible(aNode) : nullptr;
     if (acc) {
       AccessibleInfo(aMsg2, acc);
     }
     else {
       Node(aMsg2, aNode);
     }
     MsgEnd();
   }
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -124,17 +124,17 @@
   <project name="platform/external/tinyxml2" path="external/tinyxml2" revision="ea87469657dd498dd76ea01b35daab9d48503a3c"/>
   <project name="platform/external/tinyxml" path="external/tinyxml" revision="d09b587fe9bd7f1524d434c15941a5483c2d7046"/>
   <project name="platform/external/tremolo" path="external/tremolo" revision="7954026e4cbeeaa4bb7d7e2c82a6556ea34c58ab"/>
   <project name="platform/external/webp" path="external/webp" revision="5df3d5cb644f301e4a0c78b939e411b061a36558"/>
   <project name="platform/external/webrtc" path="external/webrtc" revision="de40077759a01a02a3e21b30ea0755f2d6fc4e09"/>
   <project name="platform/external/yaffs2" path="external/yaffs2" revision="b2aadfdf9482777530efac1fb13a25ff401e78ee"/>
   <project name="platform/external/zlib" path="external/zlib" revision="a9dc8ffc4b43f0ff455d52fc5a889e92794962a4"/>
   <project name="platform/external/zopfli" path="external/zopfli" revision="8b994159cf3fc74a58e42fca72bc6849e6027912"/>
-  <project name="platform/frameworks/native" path="frameworks/native" revision="c39bd4ee1f4b3ef92bf7a45824b77703f40a5fd4"/>
+  <project name="platform_frameworks_native" path="frameworks/native" remote="b2g" revision="77c23f8067bca84476f96d663efaae636817edd5"/>
   <project name="platform/frameworks/opt/emoji" path="frameworks/opt/emoji" revision="2293192ed15b88ebe962fb5377dd197200e6472b"/>
   <project name="platform/hardware/libhardware" path="hardware/libhardware" revision="f5feb2aa2047fbaf13be448fe8d06bff3ccf7b84"/>
   <project name="platform/hardware/libhardware_legacy" path="hardware/libhardware_legacy" revision="8d075b4d5e9e032b18fbc8b5def63827d1b4a30d"/>
   <project name="platform/libcore" path="libcore" revision="bdec7d684c083760bef7bc4ba2429cceccaaf7d0"/>
   <project name="platform/libnativehelper" path="libnativehelper" revision="27bcc086236cedd31c056303e255c6d0ea3d4a50"/>
   <project name="platform/ndk" path="ndk" revision="42e85f81cc6c74af145056ee80b06e520cccb9a7"/>
   <project name="platform_prebuilts_misc" path="prebuilts/misc" remote="b2g" revision="25b96077aeae7bd0e3a5e7c284fb636664337013"/>
   <project name="platform/prebuilts/ndk" path="prebuilts/ndk" revision="1d080491f26dfdfd76d5bbc3e6b40c660e8565af"/>
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -1014,20 +1014,16 @@ menuitem:not([type]):not(.menuitem-toolt
 #notification-popup-box {
   border-radius: 2.5px 0 0 2.5px;
 }
 
 .notification-anchor-icon:-moz-focusring {
   outline: 1px dotted -moz-DialogText;
 }
 
-.indexedDB-icon {
-  list-style-image: url(moz-icon://stock/gtk-dialog-question?size=16);
-}
-
 /* Translation infobar */
 
 %include ../shared/translation/infobar.inc.css
 
 notification[value="translation"] {
   min-height: 40px;
 }
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -13073,23 +13073,16 @@ nsGlobalWindow::ResumeTimeouts(bool aTha
     EnableGamepadUpdates();
 
     // Resume all of the AudioContexts for this window
     for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
       ErrorResult dummy;
       RefPtr<Promise> d = mAudioContexts[i]->Resume(dummy);
     }
 
-    // Thaw or resume all of the workers for this window.
-    if (aThawWorkers) {
-      mozilla::dom::workers::ThawWorkersForWindow(AsInner());
-    } else {
-      mozilla::dom::workers::ResumeWorkersForWindow(AsInner());
-    }
-
     // Restore all of the timeouts, using the stored time remaining
     // (stored in timeout->mTimeRemaining).
 
     TimeStamp now = TimeStamp::Now();
 
 #ifdef DEBUG
     bool _seenDummyTimeout = false;
 #endif
@@ -13124,16 +13117,25 @@ nsGlobalWindow::ResumeTimeouts(bool aTha
       if (NS_FAILED(rv)) {
         t->mTimer = nullptr;
         return rv;
       }
 
       // Add a reference for the new timer's closure.
       t->AddRef();
     }
+
+    // Thaw or resume all of the workers for this window.  We must do this
+    // after timeouts since workers may have queued events that can trigger
+    // a setTimeout().
+    if (aThawWorkers) {
+      mozilla::dom::workers::ThawWorkersForWindow(AsInner());
+    } else {
+      mozilla::dom::workers::ResumeWorkersForWindow(AsInner());
+    }
   }
 
   // Resume our children as well.
   nsCOMPtr<nsIDocShell> docShell = GetDocShell();
   if (docShell) {
     int32_t childCount = 0;
     docShell->GetChildCount(&childCount);
 
--- a/dom/base/nsImageLoadingContent.cpp
+++ b/dom/base/nsImageLoadingContent.cpp
@@ -743,20 +743,25 @@ nsImageLoadingContent::LoadImage(const n
 {
   // First, get a document (needed for security checks and the like)
   nsIDocument* doc = GetOurOwnerDoc();
   if (!doc) {
     // No reason to bother, I think...
     return NS_OK;
   }
 
+  // Second, parse the URI string to get image URI
   nsCOMPtr<nsIURI> imageURI;
   nsresult rv = StringToURI(aNewURI, doc, getter_AddRefs(imageURI));
-  NS_ENSURE_SUCCESS(rv, rv);
-  // XXXbiesi fire onerror if that failed?
+  if (NS_FAILED(rv)) {
+    // Cancel image requests and fire error event per spec
+    CancelImageRequests(aNotify);
+    FireEvent(NS_LITERAL_STRING("error"));
+    return NS_OK;
+  }
 
   bool equal;
 
   if (aNewURI.IsEmpty() &&
       doc->GetDocumentURI() &&
       NS_SUCCEEDED(doc->GetDocumentURI()->EqualsExceptRef(imageURI, &equal)) &&
       equal)  {
 
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -3292,103 +3292,51 @@ SetDocumentAndPageUseCounter(JSContext* 
     win->GetDocument()->SetDocumentAndPageUseCounter(aUseCounter);
   }
 }
 
 namespace {
 
 // This runnable is used to write a deprecation message from a worker to the
 // console running on the main-thread.
-class DeprecationWarningRunnable final : public Runnable
-                                       , public WorkerHolder
+class DeprecationWarningRunnable final : public WorkerProxyToMainThreadRunnable
 {
-  WorkerPrivate* mWorkerPrivate;
   nsIDocument::DeprecatedOperations mOperation;
 
 public:
   DeprecationWarningRunnable(WorkerPrivate* aWorkerPrivate,
                              nsIDocument::DeprecatedOperations aOperation)
-    : mWorkerPrivate(aWorkerPrivate)
+    : WorkerProxyToMainThreadRunnable(aWorkerPrivate)
     , mOperation(aOperation)
   {
     MOZ_ASSERT(aWorkerPrivate);
-  }
-
-  void
-  Dispatch()
-  {
-    if (NS_WARN_IF(!HoldWorker(mWorkerPrivate))) {
-      return;
-    }
-
-    if (NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(this)))) {
-      ReleaseWorker();
-      return;
-    }
-  }
-
-  virtual bool
-  Notify(Status aStatus) override
-  {
-    // We don't care about the notification. We just want to keep the
-    // mWorkerPrivate alive.
-    return true;
+    aWorkerPrivate->AssertIsOnWorkerThread();
   }
 
 private:
-
-  NS_IMETHOD
-  Run() override
+  void
+  RunOnMainThread() override
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     // Walk up to our containing page
     WorkerPrivate* wp = mWorkerPrivate;
     while (wp->GetParent()) {
       wp = wp->GetParent();
     }
 
     nsPIDOMWindowInner* window = wp->GetWindow();
     if (window && window->GetExtantDoc()) {
       window->GetExtantDoc()->WarnOnceAbout(mOperation);
     }
-
-    ReleaseWorkerHolder();
-    return NS_OK;
   }
 
   void
-  ReleaseWorkerHolder()
-  {
-    class ReleaseRunnable final : public MainThreadWorkerRunnable
-    {
-      RefPtr<DeprecationWarningRunnable> mRunnable;
-
-    public:
-      ReleaseRunnable(WorkerPrivate* aWorkerPrivate,
-                      DeprecationWarningRunnable* aRunnable)
-        : MainThreadWorkerRunnable(aWorkerPrivate)
-        , mRunnable(aRunnable)
-      {}
-
-      virtual bool
-      WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
-      {
-        MOZ_ASSERT(aWorkerPrivate);
-        aWorkerPrivate->AssertIsOnWorkerThread();
-
-        mRunnable->ReleaseWorker();
-        return true;
-      }
-    };
-
-    RefPtr<ReleaseRunnable> runnable =
-      new ReleaseRunnable(mWorkerPrivate, this);
-    NS_WARN_IF(!runnable->Dispatch());
-  }
+  RunBackOnWorkerThread() override
+  {}
 };
 
 } // anonymous namespace
 
 void
 DeprecationWarning(JSContext* aCx, JSObject* aObject,
                    nsIDocument::DeprecatedOperations aOperation)
 {
--- a/dom/console/Console.cpp
+++ b/dom/console/Console.cpp
@@ -308,142 +308,68 @@ public:
   {
     JS_ClearPendingException(mCx);
   }
 
 private:
   JSContext* mCx;
 };
 
-class ConsoleRunnable : public Runnable
-                      , public WorkerHolder
+class ConsoleRunnable : public WorkerProxyToMainThreadRunnable
                       , public StructuredCloneHolderBase
 {
 public:
   explicit ConsoleRunnable(Console* aConsole)
-    : mWorkerPrivate(GetCurrentThreadWorkerPrivate())
+    : WorkerProxyToMainThreadRunnable(GetCurrentThreadWorkerPrivate())
     , mConsole(aConsole)
-  {
-    MOZ_ASSERT(mWorkerPrivate);
-  }
+  {}
 
   virtual
   ~ConsoleRunnable()
   {
     // Clear the StructuredCloneHolderBase class.
     Clear();
   }
 
   bool
   Dispatch(JSContext* aCx)
   {
-    if (!DispatchInternal(aCx)) {
-      ReleaseData();
+    mWorkerPrivate->AssertIsOnWorkerThread();
+
+    if (NS_WARN_IF(!PreDispatch(aCx))) {
+      RunBackOnWorkerThread();
+      return false;
+    }
+
+    if (NS_WARN_IF(!WorkerProxyToMainThreadRunnable::Dispatch())) {
+      RunBackOnWorkerThread();
       return false;
     }
 
     return true;
   }
 
-  virtual bool Notify(workers::Status aStatus) override
-  {
-    // We don't care about the notification. We just want to keep the
-    // mWorkerPrivate alive.
-    return true;
-  }
-
-private:
-  NS_IMETHOD
-  Run() override
+protected:
+  void
+  RunOnMainThread() override
   {
     AssertIsOnMainThread();
 
     // Walk up to our containing page
     WorkerPrivate* wp = mWorkerPrivate;
     while (wp->GetParent()) {
       wp = wp->GetParent();
     }
 
     nsPIDOMWindowInner* window = wp->GetWindow();
     if (!window) {
       RunWindowless();
     } else {
       RunWithWindow(window);
     }
-
-    PostDispatch();
-    return NS_OK;
-  }
-
-  bool
-  DispatchInternal(JSContext* aCx)
-  {
-    mWorkerPrivate->AssertIsOnWorkerThread();
-
-    if (NS_WARN_IF(!PreDispatch(aCx))) {
-      return false;
-    }
-
-    if (NS_WARN_IF(!HoldWorker(mWorkerPrivate))) {
-      return false;
-    }
-
-    if (NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(this)))) {
-      return false;
-    }
-
-    return true;
-  }
-
-  void
-  PostDispatch()
-  {
-    class ConsoleReleaseRunnable final : public MainThreadWorkerControlRunnable
-    {
-      RefPtr<ConsoleRunnable> mRunnable;
-
-    public:
-      ConsoleReleaseRunnable(WorkerPrivate* aWorkerPrivate,
-                             ConsoleRunnable* aRunnable)
-        : MainThreadWorkerControlRunnable(aWorkerPrivate)
-        , mRunnable(aRunnable)
-      {
-        MOZ_ASSERT(aRunnable);
-      }
-
-      // If something goes wrong, we still need to release the ConsoleCallData
-      // object. For this reason we have a custom Cancel method.
-      nsresult
-      Cancel() override
-      {
-        WorkerRun(nullptr, mWorkerPrivate);
-        return NS_OK;
-      }
-
-      virtual bool
-      WorkerRun(JSContext* aCx, workers::WorkerPrivate* aWorkerPrivate) override
-      {
-        MOZ_ASSERT(aWorkerPrivate);
-        aWorkerPrivate->AssertIsOnWorkerThread();
-
-        mRunnable->ReleaseData();
-        mRunnable->mConsole = nullptr;
-
-        mRunnable->ReleaseWorker();
-        return true;
-      }
-
-    private:
-      ~ConsoleReleaseRunnable()
-      {}
-    };
-
-    RefPtr<WorkerControlRunnable> runnable =
-      new ConsoleReleaseRunnable(mWorkerPrivate, this);
-    NS_WARN_IF(!runnable->Dispatch());
   }
 
   void
   RunWithWindow(nsPIDOMWindowInner* aWindow)
   {
     AssertIsOnMainThread();
 
     AutoJSAPI jsapi;
@@ -486,17 +412,24 @@ private:
     // don't need a proxy here.
     global = js::UncheckedUnwrap(global);
 
     JSAutoCompartment ac(cx, global);
 
     RunConsole(cx, nullptr, nullptr);
   }
 
-protected:
+  void
+  RunBackOnWorkerThread() override
+  {
+    mWorkerPrivate->AssertIsOnWorkerThread();
+    ReleaseData();
+    mConsole = nullptr;
+  }
+
   // This method is called in the owning thread of the Console object.
   virtual bool
   PreDispatch(JSContext* aCx) = 0;
 
   // This method is called in the main-thread.
   virtual void
   RunConsole(JSContext* aCx, nsPIDOMWindowOuter* aOuterWindow,
              nsPIDOMWindowInner* aInnerWindow) = 0;
@@ -559,18 +492,16 @@ protected:
 
     if (NS_WARN_IF(!JS_WriteString(aWriter, jsString))) {
       return false;
     }
 
     return true;
   }
 
-  WorkerPrivate* mWorkerPrivate;
-
   // This must be released on the worker thread.
   RefPtr<Console> mConsole;
 
   ConsoleStructuredCloneData mClonedData;
 };
 
 // This runnable appends a CallData object into the Console queue running on
 // the main-thread.
--- a/dom/indexedDB/ActorsChild.cpp
+++ b/dom/indexedDB/ActorsChild.cpp
@@ -898,17 +898,16 @@ protected:
   ~WorkerPermissionRequestChildProcessActor()
   {}
 
   virtual bool
   Recv__delete__(const uint32_t& aPermission) override;
 };
 
 class WorkerPermissionChallenge final : public Runnable
-                                      , public WorkerHolder
 {
 public:
   WorkerPermissionChallenge(WorkerPrivate* aWorkerPrivate,
                             BackgroundFactoryRequestChild* aActor,
                             IDBFactory* aFactory,
                             const PrincipalInfo& aPrincipalInfo)
     : mWorkerPrivate(aWorkerPrivate)
     , mActor(aActor)
@@ -916,35 +915,43 @@ public:
     , mPrincipalInfo(aPrincipalInfo)
   {
     MOZ_ASSERT(mWorkerPrivate);
     MOZ_ASSERT(aActor);
     MOZ_ASSERT(aFactory);
     mWorkerPrivate->AssertIsOnWorkerThread();
   }
 
+  bool
+  Dispatch()
+  {
+    mWorkerPrivate->AssertIsOnWorkerThread();
+    if (NS_WARN_IF(!mWorkerPrivate->ModifyBusyCountFromWorker(true))) {
+      return false;
+    }
+
+    if (NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(this)))) {
+      mWorkerPrivate->ModifyBusyCountFromWorker(false);
+      return false;
+    }
+
+    return true;
+  }
+
   NS_IMETHOD
   Run() override
   {
     bool completed = RunInternal();
     if (completed) {
       OperationCompleted();
     }
 
     return NS_OK;
   }
 
-  virtual bool
-  Notify(workers::Status aStatus) override
-  {
-    // We don't care about the notification. We just want to keep the
-    // mWorkerPrivate alive.
-    return true;
-  }
-
   void
   OperationCompleted()
   {
     if (NS_IsMainThread()) {
       RefPtr<WorkerPermissionOperationCompleted> runnable =
         new WorkerPermissionOperationCompleted(mWorkerPrivate, this);
 
       MOZ_ALWAYS_TRUE(runnable->Dispatch());
@@ -958,17 +965,17 @@ public:
 
     RefPtr<IDBFactory> factory;
     mFactory.swap(factory);
 
     mActor->SendPermissionRetry();
     mActor = nullptr;
 
     mWorkerPrivate->AssertIsOnWorkerThread();
-    ReleaseWorker();
+    mWorkerPrivate->ModifyBusyCountFromWorker(false);
   }
 
 private:
   bool
   RunInternal()
   {
     MOZ_ASSERT(NS_IsMainThread());
 
@@ -1409,23 +1416,17 @@ BackgroundFactoryRequestChild::RecvPermi
   if (!NS_IsMainThread()) {
     WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
     MOZ_ASSERT(workerPrivate);
     workerPrivate->AssertIsOnWorkerThread();
 
     RefPtr<WorkerPermissionChallenge> challenge =
       new WorkerPermissionChallenge(workerPrivate, this, mFactory,
                                     aPrincipalInfo);
-
-    if (NS_WARN_IF(!challenge->HoldWorker(workerPrivate))) {
-      return false;
-    }
-
-    MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(challenge));
-    return true;
+    return challenge->Dispatch();
   }
 
   nsresult rv;
   nsCOMPtr<nsIPrincipal> principal =
     mozilla::ipc::PrincipalInfoToPrincipal(aPrincipalInfo, &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return false;
   }
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -837,27 +837,33 @@ AudioContext::OnStateChanged(void* aProm
   if (mAudioContextState == AudioContextState::Closed &&
       aNewState == AudioContextState::Running &&
       !aPromise) {
     return;
   }
 
 #ifndef WIN32 // Bug 1170547
 
-  MOZ_ASSERT((mAudioContextState == AudioContextState::Suspended &&
-              aNewState == AudioContextState::Running)   ||
-             (mAudioContextState == AudioContextState::Running   &&
-              aNewState == AudioContextState::Suspended) ||
-             (mAudioContextState == AudioContextState::Running   &&
-              aNewState == AudioContextState::Closed)    ||
-             (mAudioContextState == AudioContextState::Suspended &&
-              aNewState == AudioContextState::Closed)    ||
-             (mAudioContextState == aNewState),
-             "Invalid AudioContextState transition");
+#ifdef DEBUG
+  if (!((mAudioContextState == AudioContextState::Suspended &&
+       aNewState == AudioContextState::Running)   ||
+      (mAudioContextState == AudioContextState::Running   &&
+       aNewState == AudioContextState::Suspended) ||
+      (mAudioContextState == AudioContextState::Running   &&
+       aNewState == AudioContextState::Closed)    ||
+      (mAudioContextState == AudioContextState::Suspended &&
+       aNewState == AudioContextState::Closed)    ||
+      (mAudioContextState == aNewState))) {
+    fprintf(stderr,
+            "Invalid transition: mAudioContextState: %d -> aNewState %d\n",
+            static_cast<int>(mAudioContextState), static_cast<int>(aNewState));
+    MOZ_ASSERT(false);
+  }
 
+#endif // DEBUG
 #endif // WIN32
 
   MOZ_ASSERT(
     mIsOffline || aPromise || aNewState == AudioContextState::Running,
     "We should have a promise here if this is a real-time AudioContext."
     "Or this is the first time we switch to \"running\".");
 
   if (aPromise) {
--- a/dom/workers/ServiceWorkerContainer.cpp
+++ b/dom/workers/ServiceWorkerContainer.cpp
@@ -64,16 +64,17 @@ ServiceWorkerContainer::ServiceWorkerCon
 ServiceWorkerContainer::~ServiceWorkerContainer()
 {
   RemoveReadyPromise();
 }
 
 void
 ServiceWorkerContainer::DisconnectFromOwner()
 {
+  mControllerWorker = nullptr;
   RemoveReadyPromise();
   DOMEventTargetHelper::DisconnectFromOwner();
 }
 
 void
 ServiceWorkerContainer::ControllerChanged(ErrorResult& aRv)
 {
   mControllerWorker = nullptr;
@@ -216,28 +217,27 @@ ServiceWorkerContainer::Register(const n
   MOZ_ASSERT(ret);
   return ret.forget();
 }
 
 already_AddRefed<workers::ServiceWorker>
 ServiceWorkerContainer::GetController()
 {
   if (!mControllerWorker) {
-    nsresult rv;
     nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
     if (!swm) {
       return nullptr;
     }
 
     // TODO: What should we do here if the ServiceWorker script fails to load?
     //       In theory the DOM ServiceWorker object can exist without the worker
     //       thread running, but it seems our design does not expect that.
     nsCOMPtr<nsISupports> serviceWorker;
-    rv = swm->GetDocumentController(GetOwner(),
-                                    getter_AddRefs(serviceWorker));
+    nsresult rv = swm->GetDocumentController(GetOwner(),
+                                             getter_AddRefs(serviceWorker));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return nullptr;
     }
 
     mControllerWorker =
       static_cast<workers::ServiceWorker*>(serviceWorker.get());
   }
 
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -2323,17 +2323,20 @@ ServiceWorkerManager::GetDocumentRegistr
 /*
  * The .controller is for the registration associated with the document when
  * the document was loaded.
  */
 NS_IMETHODIMP
 ServiceWorkerManager::GetDocumentController(nsPIDOMWindowInner* aWindow,
                                             nsISupports** aServiceWorker)
 {
-  MOZ_ASSERT(aWindow);
+  if (NS_WARN_IF(!aWindow)) {
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
+  }
+
   nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
   if (!doc) {
     return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
   RefPtr<ServiceWorkerRegistrationInfo> registration;
   nsresult rv = GetDocumentRegistration(doc, getter_AddRefs(registration));
   if (NS_WARN_IF(NS_FAILED(rv))) {
--- a/dom/workers/WorkerHolder.cpp
+++ b/dom/workers/WorkerHolder.cpp
@@ -40,14 +40,15 @@ WorkerHolder::ReleaseWorker()
   MOZ_ASSERT(mWorkerPrivate);
   ReleaseWorkerInternal();
 }
 
 void
 WorkerHolder::ReleaseWorkerInternal()
 {
   if (mWorkerPrivate) {
+    mWorkerPrivate->AssertIsOnWorkerThread();
     mWorkerPrivate->RemoveHolder(this);
     mWorkerPrivate = nullptr;
   }
 }
 
 END_WORKERS_NAMESPACE
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -5099,17 +5099,17 @@ WorkerPrivate::ProcessAllControlRunnable
   return result;
 }
 
 void
 WorkerPrivate::ClearMainEventQueue(WorkerRanOrNot aRanOrNot)
 {
   AssertIsOnWorkerThread();
 
-  MOZ_ASSERT(!mSyncLoopStack.Length());
+  MOZ_ASSERT(mSyncLoopStack.IsEmpty());
   MOZ_ASSERT(!mCancelAllPendingRunnables);
   mCancelAllPendingRunnables = true;
 
   if (WorkerNeverRan == aRanOrNot) {
     for (uint32_t count = mPreStartRunnables.Length(), index = 0;
          index < count;
          index++) {
       RefPtr<WorkerRunnable> runnable = mPreStartRunnables[index].forget();
@@ -5497,17 +5497,17 @@ WorkerPrivate::DestroySyncLoop(uint32_t 
 #endif
 
     // This will delete |loopInfo|!
     mSyncLoopStack.RemoveElementAt(aLoopIndex);
   }
 
   MOZ_ALWAYS_SUCCEEDS(aThread->PopEventQueue(nestedEventTarget));
 
-  if (!mSyncLoopStack.Length() && mPendingEventQueueClearing) {
+  if (mSyncLoopStack.IsEmpty() && mPendingEventQueueClearing) {
     ClearMainEventQueue(WorkerRan);
     mPendingEventQueueClearing = false;
   }
 
   return result;
 }
 
 void
@@ -5797,17 +5797,17 @@ WorkerPrivate::NotifyInternal(JSContext*
   NotifyHolders(aCx, aStatus);
   MOZ_ASSERT(!JS_IsExceptionPending(aCx));
 
   // If this is the first time our status has changed then we need to clear the
   // main event queue.
   if (previousStatus == Running) {
     // NB: If we're in a sync loop, we can't clear the queue immediately,
     // because this is the wrong queue. So we have to defer it until later.
-    if (mSyncLoopStack.Length()) {
+    if (!mSyncLoopStack.IsEmpty()) {
       mPendingEventQueueClearing = true;
     } else {
       ClearMainEventQueue(WorkerRan);
     }
   }
 
   // If we've run the close handler, we don't need to do anything else.
   if (mCloseHandlerFinished) {
--- a/dom/workers/WorkerRunnable.cpp
+++ b/dom/workers/WorkerRunnable.cpp
@@ -654,8 +654,92 @@ WorkerSameThreadRunnable::PostDispatch(W
   aWorkerPrivate->AssertIsOnWorkerThread();
   if (aDispatchResult) {
     DebugOnly<bool> willIncrement = aWorkerPrivate->ModifyBusyCountFromWorker(true);
     // Should never fail since if this thread is still running, so should the
     // parent and it should be able to process a control runnable.
     MOZ_ASSERT(willIncrement);
   }
 }
+
+WorkerProxyToMainThreadRunnable::WorkerProxyToMainThreadRunnable(WorkerPrivate* aWorkerPrivate)
+  : mWorkerPrivate(aWorkerPrivate)
+{
+  MOZ_ASSERT(mWorkerPrivate);
+  mWorkerPrivate->AssertIsOnWorkerThread();
+}
+
+WorkerProxyToMainThreadRunnable::~WorkerProxyToMainThreadRunnable()
+{}
+
+bool
+WorkerProxyToMainThreadRunnable::Dispatch()
+{
+  mWorkerPrivate->AssertIsOnWorkerThread();
+
+  if (NS_WARN_IF(!mWorkerPrivate->ModifyBusyCountFromWorker(true))) {
+    RunBackOnWorkerThread();
+    return false;
+  }
+
+  if (NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(this)))) {
+    mWorkerPrivate->ModifyBusyCountFromWorker(false);
+    RunBackOnWorkerThread();
+    return false;
+  }
+
+  return true;
+}
+
+NS_IMETHODIMP
+WorkerProxyToMainThreadRunnable::Run()
+{
+  AssertIsOnMainThread();
+  RunOnMainThread();
+  PostDispatchOnMainThread();
+  return NS_OK;
+}
+
+void
+WorkerProxyToMainThreadRunnable::PostDispatchOnMainThread()
+{
+  class ReleaseRunnable final : public MainThreadWorkerControlRunnable
+  {
+    RefPtr<WorkerProxyToMainThreadRunnable> mRunnable;
+
+  public:
+    ReleaseRunnable(WorkerPrivate* aWorkerPrivate,
+                    WorkerProxyToMainThreadRunnable* aRunnable)
+      : MainThreadWorkerControlRunnable(aWorkerPrivate)
+      , mRunnable(aRunnable)
+    {
+      MOZ_ASSERT(aRunnable);
+    }
+
+    // We must call RunBackOnWorkerThread() also if the runnable is cancelled.
+    nsresult
+    Cancel() override
+    {
+      WorkerRun(nullptr, mWorkerPrivate);
+      return NS_OK;
+    }
+
+    virtual bool
+    WorkerRun(JSContext* aCx, workers::WorkerPrivate* aWorkerPrivate) override
+    {
+      MOZ_ASSERT(aWorkerPrivate);
+      aWorkerPrivate->AssertIsOnWorkerThread();
+
+      mRunnable->RunBackOnWorkerThread();
+
+      aWorkerPrivate->ModifyBusyCountFromWorker(true);
+      return true;
+    }
+
+  private:
+    ~ReleaseRunnable()
+    {}
+  };
+
+  RefPtr<WorkerControlRunnable> runnable =
+    new ReleaseRunnable(mWorkerPrivate, this);
+  NS_WARN_IF(!runnable->Dispatch());
+}
--- a/dom/workers/WorkerRunnable.h
+++ b/dom/workers/WorkerRunnable.h
@@ -404,16 +404,44 @@ public:
   // fails, or if the worker is shut down while dispatching, an error will be
   // reported on aRv.  In that case the error MUST be propagated out to script.
   void Dispatch(ErrorResult& aRv);
 
 private:
   NS_IMETHOD Run() override;
 };
 
+// This runnable is an helper class for dispatching something from a worker
+// thread to the main-thread and back to the worker-thread. During this
+// operation, this class will keep the worker alive.
+class WorkerProxyToMainThreadRunnable : public Runnable
+{
+protected:
+  explicit WorkerProxyToMainThreadRunnable(WorkerPrivate* aWorkerPrivate);
+
+  virtual ~WorkerProxyToMainThreadRunnable();
+
+  // First this method is called on the main-thread.
+  virtual void RunOnMainThread() = 0;
+
+  // After this second method is called on the worker-thread.
+  virtual void RunBackOnWorkerThread() = 0;
+
+public:
+  bool Dispatch();
+
+private:
+  NS_IMETHOD Run() override;
+
+  void PostDispatchOnMainThread();
+
+protected:
+  WorkerPrivate* mWorkerPrivate;
+};
+
 // Class for checking API exposure.  This totally violates the "MUST" in the
 // comments on WorkerMainThreadRunnable::Dispatch, because API exposure checks
 // can't throw.  Maybe we should change it so they _could_ throw.  But for now
 // we are bad people and should be ashamed of ourselves.  Let's hope none of
 // them happen while a worker is shutting down.
 //
 // Do NOT copy what this class is doing elsewhere.  Just don't.
 class WorkerCheckAPIExposureOnMainThreadRunnable
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -913,16 +913,18 @@ ContainerLayerComposite::Prepare(const R
 {
   ContainerPrepare(this, mCompositeManager, aClipRect);
 }
 
 void
 ContainerLayerComposite::CleanupResources()
 {
   mLastIntermediateSurface = nullptr;
+  mVRRenderTargetSet = nullptr;
+  mPrepared = nullptr;
 
   for (Layer* l = GetFirstChild(); l; l = l->GetNextSibling()) {
     LayerComposite* layerToCleanup = static_cast<LayerComposite*>(l->ImplData());
     layerToCleanup->CleanupResources();
   }
 }
 
 RefLayerComposite::RefLayerComposite(LayerManagerComposite* aManager)
@@ -965,12 +967,14 @@ RefLayerComposite::Prepare(const RenderT
   ContainerPrepare(this, mCompositeManager, aClipRect);
 }
 
 
 void
 RefLayerComposite::CleanupResources()
 {
   mLastIntermediateSurface = nullptr;
+  mVRRenderTargetSet = nullptr;
+  mPrepared = nullptr;
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -1091,37 +1091,48 @@ ShadowLayerForwarder::DestroySurfaceDesc
       NS_RUNTIMEABORT("surface type not implemented!");
   }
   *aSurface = SurfaceDescriptor();
 }
 
 void
 ShadowLayerForwarder::UpdateFwdTransactionId()
 {
-  GetCompositorBridgeChild()->UpdateFwdTransactionId();
+  auto compositorBridge = GetCompositorBridgeChild();
+  if (compositorBridge) {
+    compositorBridge->UpdateFwdTransactionId();
+  }
 }
 
 uint64_t
 ShadowLayerForwarder::GetFwdTransactionId()
 {
-  return GetCompositorBridgeChild()->GetFwdTransactionId();
+  auto compositorBridge = GetCompositorBridgeChild();
+  MOZ_DIAGNOSTIC_ASSERT(compositorBridge);
+  return compositorBridge ? compositorBridge->GetFwdTransactionId() : 0;
 }
 
 void
 ShadowLayerForwarder::CancelWaitForRecycle(uint64_t aTextureId)
 {
-  GetCompositorBridgeChild()->CancelWaitForRecycle(aTextureId);
+  auto compositorBridge = GetCompositorBridgeChild();
+  if (compositorBridge) {
+    compositorBridge->CancelWaitForRecycle(aTextureId);
+  }
 }
 
 CompositorBridgeChild*
 ShadowLayerForwarder::GetCompositorBridgeChild()
 {
   if (mCompositorBridgeChild) {
     return mCompositorBridgeChild;
   }
+  if (!mShadowManager) {
+    return nullptr;
+  }
   mCompositorBridgeChild = static_cast<CompositorBridgeChild*>(mShadowManager->Manager());
   return mCompositorBridgeChild;
 }
 
 TextureForwarder*
 ShadowLayerForwarder::AsTextureForwarder()
 {
   return GetCompositorBridgeChild();
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -8044,20 +8044,20 @@ uint8_t*
 AsmJSGlobal::serialize(uint8_t* cursor) const
 {
     cursor = WriteBytes(cursor, &pod, sizeof(pod));
     cursor = field_.serialize(cursor);
     return cursor;
 }
 
 const uint8_t*
-AsmJSGlobal::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
+AsmJSGlobal::deserialize(const uint8_t* cursor)
 {
     (cursor = ReadBytes(cursor, &pod, sizeof(pod))) &&
-    (cursor = field_.deserialize(cx, cursor));
+    (cursor = field_.deserialize(cursor));
     return cursor;
 }
 
 size_t
 AsmJSGlobal::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
 {
     return field_.sizeOfExcludingThis(mallocSizeOf);
 }
@@ -8087,27 +8087,27 @@ AsmJSMetadata::serialize(uint8_t* cursor
     cursor = SerializeVector(cursor, asmJSFuncNames);
     cursor = globalArgumentName.serialize(cursor);
     cursor = importArgumentName.serialize(cursor);
     cursor = bufferArgumentName.serialize(cursor);
     return cursor;
 }
 
 const uint8_t*
-AsmJSMetadata::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
-{
-    (cursor = Metadata::deserialize(cx, cursor)) &&
+AsmJSMetadata::deserialize(const uint8_t* cursor)
+{
+    (cursor = Metadata::deserialize(cursor)) &&
     (cursor = ReadBytes(cursor, &pod(), sizeof(pod()))) &&
-    (cursor = DeserializeVector(cx, cursor, &asmJSGlobals)) &&
-    (cursor = DeserializePodVector(cx, cursor, &asmJSImports)) &&
-    (cursor = DeserializePodVector(cx, cursor, &asmJSExports)) &&
-    (cursor = DeserializeVector(cx, cursor, &asmJSFuncNames)) &&
-    (cursor = globalArgumentName.deserialize(cx, cursor)) &&
-    (cursor = importArgumentName.deserialize(cx, cursor)) &&
-    (cursor = bufferArgumentName.deserialize(cx, cursor));
+    (cursor = DeserializeVector(cursor, &asmJSGlobals)) &&
+    (cursor = DeserializePodVector(cursor, &asmJSImports)) &&
+    (cursor = DeserializePodVector(cursor, &asmJSExports)) &&
+    (cursor = DeserializeVector(cursor, &asmJSFuncNames)) &&
+    (cursor = globalArgumentName.deserialize(cursor)) &&
+    (cursor = importArgumentName.deserialize(cursor)) &&
+    (cursor = bufferArgumentName.deserialize(cursor));
     cacheResult = CacheResult::Hit;
     return cursor;
 }
 
 size_t
 AsmJSMetadata::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
 {
     return Metadata::sizeOfExcludingThis(mallocSizeOf) +
@@ -8211,17 +8211,17 @@ class ModuleCharsForStore : ModuleChars
     }
 };
 
 class ModuleCharsForLookup : ModuleChars
 {
     Vector<char16_t, 0, SystemAllocPolicy> chars_;
 
   public:
-    const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor) {
+    const uint8_t* deserialize(const uint8_t* cursor) {
         uint32_t uncompressedSize;
         cursor = ReadScalar<uint32_t>(cursor, &uncompressedSize);
 
         uint32_t compressedSize;
         cursor = ReadScalar<uint32_t>(cursor, &compressedSize);
 
         if (!chars_.resize(uncompressedSize / sizeof(char16_t)))
             return nullptr;
@@ -8230,17 +8230,17 @@ class ModuleCharsForLookup : ModuleChars
         char* dest = reinterpret_cast<char*>(chars_.begin());
         if (!LZ4::decompress(source, dest, uncompressedSize))
             return nullptr;
 
         cursor += compressedSize;
 
         cursor = ReadScalar<uint32_t>(cursor, &isFunCtor_);
         if (isFunCtor_)
-            cursor = DeserializeVector(cx, cursor, &funCtorArgs_);
+            cursor = DeserializeVector(cursor, &funCtorArgs_);
 
         return cursor;
     }
 
     bool match(AsmJSParser& parser) const {
         const char16_t* parseBegin = parser.tokenStream.rawCharPtrAt(beginOffset(parser));
         const char16_t* parseLimit = parser.tokenStream.rawLimit();
         MOZ_ASSERT(parseLimit >= parseBegin);
@@ -8369,34 +8369,36 @@ LookupAsmJSModuleInCache(ExclusiveContex
 
     ScopedCacheEntryOpenedForRead entry(cx);
     if (!open(cx->global(), begin, limit, &entry.serializedSize, &entry.memory, &entry.handle))
         return true;
 
     const uint8_t* cursor = entry.memory;
 
     MachineId cachedMachineId;
-    cursor = cachedMachineId.deserialize(cx, cursor);
+    cursor = cachedMachineId.deserialize(cursor);
     if (!cursor)
         return false;
     if (machineId != cachedMachineId)
         return true;
 
     ModuleCharsForLookup moduleChars;
-    cursor = moduleChars.deserialize(cx, cursor);
+    cursor = moduleChars.deserialize(cursor);
     if (!moduleChars.match(parser))
         return true;
 
     MutableAsmJSMetadata asmJSMetadata = cx->new_<AsmJSMetadata>();
     if (!asmJSMetadata)
         return false;
 
-    cursor = Module::deserialize(cx, cursor, module, asmJSMetadata.get());
-    if (!cursor)
-        return false;
+    cursor = Module::deserialize(cursor, module, asmJSMetadata.get());
+    if (!cursor) {
+        ReportOutOfMemory(cx);
+        return false;
+    }
 
     // See AsmJSMetadata comment as well as ModuleValidator::init().
     asmJSMetadata->srcStart = parser.pc->maybeFunction->pn_body->pn_pos.begin;
     asmJSMetadata->srcBodyStart = parser.tokenStream.currentToken().pos.end;
     asmJSMetadata->strict = parser.pc->sc->strict() && !parser.pc->sc->hasExplicitUseStrict();
     asmJSMetadata->scriptSource.reset(parser.ss);
 
     bool atEnd = cursor == entry.memory + entry.serializedSize;
--- a/js/src/asmjs/WasmBaselineCompile.cpp
+++ b/js/src/asmjs/WasmBaselineCompile.cpp
@@ -151,51 +151,93 @@ typedef bool HandleNaNSpecially;
 
 #ifdef JS_CODEGEN_ARM64
 // FIXME: This is not correct, indeed for ARM64 there is no reliable
 // StackPointer and we'll need to change the abstractions that use
 // SP-relative addressing.  There's a guard in emitFunction() below to
 // prevent this workaround from having any consequence.  This hack
 // exists only as a stopgap; there is no ARM64 JIT support yet.
 static const Register StackPointer = RealStackPointer;
-
-// FIXME: This should somehow use vixl::UseScratchRegisterScope, or we
-// should define our own scratch register independent of the masm.
-class ScratchRegisterScope
-{
-  public:
-    ScratchRegisterScope(MacroAssembler& masm) {}
-    operator Register() const { return ScratchReg; }
-};
 #endif
 
 #ifdef JS_CODEGEN_X86
 // The selection of EBX here steps gingerly around: the need for EDX
 // to be allocatable for multiply/divide; ECX to be allocatable for
 // shift/rotate; EAX (= ReturnReg) to be allocatable as the joinreg;
 // EBX not being one of the WasmTableCall registers; and needing a
 // temp register for load/store that has a single-byte persona.
 static const Register ScratchRegX86 = ebx;
-
-// FIXME: We want this to have teeth.  One way to ensure that is to
-// pass BaseCompiler to ScratchRegisterScope instead of masm, and then
-// have a property on BaseCompiler that tracks availability.  On other
-// platforms than x86 we'd delegate from our private
-// ScratchRegisterScope to the standard one by inheritance, passing
-// BaseCompiler->masm to the base constructor.
-class ScratchRegisterScope
-{
-  public:
-    ScratchRegisterScope(MacroAssembler& masm) {}
-    operator Register() const { return ScratchRegX86; }
-};
 #endif
 
 class BaseCompiler
 {
+    // We define our own ScratchRegister abstractions, deferring to
+    // the platform's when possible.
+
+#if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_ARM)
+    typedef ScratchDoubleScope ScratchF64;
+#else
+    class ScratchF64
+    {
+      public:
+        ScratchF64(BaseCompiler& b) {}
+        operator FloatRegister() const {
+            MOZ_CRASH("BaseCompiler platform hook - ScratchF64");
+        }
+    };
+#endif
+
+#if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_ARM)
+    typedef ScratchFloat32Scope ScratchF32;
+#else
+    class ScratchF32
+    {
+      public:
+        ScratchF32(BaseCompiler& b) {}
+        operator FloatRegister() const {
+            MOZ_CRASH("BaseCompiler platform hook - ScratchF32");
+        }
+    };
+#endif
+
+#if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM)
+    typedef ScratchRegisterScope ScratchI32;
+#elif defined(JS_CODEGEN_X86)
+    class ScratchI32
+    {
+# ifdef DEBUG
+        BaseCompiler& bc;
+      public:
+        ScratchI32(BaseCompiler& bc) : bc(bc) {
+            MOZ_ASSERT(!bc.scratchRegisterTaken());
+            bc.setScratchRegisterTaken(true);
+        }
+        ~ScratchI32() {
+            MOZ_ASSERT(bc.scratchRegisterTaken());
+            bc.setScratchRegisterTaken(false);
+        }
+# else
+      public:
+        ScratchI32(BaseCompiler& bc) {}
+# endif
+        operator Register() const {
+            return ScratchRegX86;
+        }
+    };
+#else
+    class ScratchI32
+    {
+      public:
+        ScratchI32(BaseCompiler& bc) {}
+        operator Register() const {
+            MOZ_CRASH("BaseCompiler platform hook - ScratchI32");
+        }
+    };
+#endif
+
     // A Label in the code, allocated out of a temp pool in the
     // TempAllocator attached to the compilation.
 
     struct PooledLabel : public Label, public TempObject, public InlineListNode<PooledLabel>
     {
         PooledLabel() : f(nullptr) {}
         explicit PooledLabel(BaseCompiler* f) : f(f) {}
         BaseCompiler* f;
@@ -296,23 +338,27 @@ class BaseCompiler
     // Control node, representing labels and stack heights at join points.
 
     struct Control
     {
         Control(uint32_t framePushed, uint32_t stackSize)
             : label(nullptr),
               otherLabel(nullptr),
               framePushed(framePushed),
-              stackSize(stackSize)
+              stackSize(stackSize),
+              deadOnArrival(false),
+              deadThenBranch(false)
         {}
 
         PooledLabel* label;
         PooledLabel* otherLabel;        // Used for the "else" branch of if-then-else
         uint32_t framePushed;           // From masm
         uint32_t stackSize;             // Value stack height
+        bool deadOnArrival;             // deadCode_ was set on entry to the region
+        bool deadThenBranch;            // deadCode_ was set on exit from "then"
     };
 
     // Volatile registers except ReturnReg.
 
     static LiveRegisterSet VolatileReturnGPR;
 
     // The baseline compiler will use OOL code more sparingly than
     // Baldr since our code is not high performance and frills like
@@ -370,28 +416,32 @@ class BaseCompiler
     const FuncBytes&            func_;
     size_t                      lastReadCallSite_;
     TempAllocator&              alloc_;
     const ValTypeVector&        locals_;         // Types of parameters and locals
     int32_t                     localSize_;      // Size of local area in bytes (stable after beginFunction)
     int32_t                     varLow_;         // Low byte offset of local area for true locals (not parameters)
     int32_t                     varHigh_;        // High byte offset + 1 of local area for true locals
     int32_t                     maxFramePushed_; // Max value of masm.framePushed() observed
+    bool                        deadCode_;       // Flag indicating we should decode & discard the opcode
     ValTypeVector               SigDD_;
     ValTypeVector               SigD_;
     ValTypeVector               SigF_;
     Label                       returnLabel_;
     Label                       outOfLinePrologue_;
     Label                       bodyLabel_;
 
     FuncCompileResults&         compileResults_;
     MacroAssembler&             masm;            // No '_' suffix - too tedious...
 
     AllocatableGeneralRegisterSet availGPR_;
     AllocatableFloatRegisterSet availFPU_;
+#ifdef DEBUG
+    bool                        scratchRegisterTaken_;
+#endif
 
     TempObjectPool<PooledLabel> labelPool_;
 
     Vector<Local, 8, SystemAllocPolicy> localInfo_;
     Vector<OutOfLineCode*, 8, SystemAllocPolicy> outOfLine_;
 
     // On specific platforms we sometimes need to use specific registers.
 
@@ -428,16 +478,28 @@ class BaseCompiler
     MOZ_MUST_USE
     bool init();
 
     void finish();
 
     MOZ_MUST_USE
     bool emitFunction();
 
+    // Used by some of the ScratchRegister implementations.
+    operator MacroAssembler&() const { return masm; }
+
+#ifdef DEBUG
+    bool scratchRegisterTaken() const {
+        return scratchRegisterTaken_;
+    }
+    void setScratchRegisterTaken(bool state) {
+        scratchRegisterTaken_ = state;
+    }
+#endif
+
   private:
 
     ////////////////////////////////////////////////////////////
     //
     // Out of line code management.
 
     MOZ_MUST_USE
     OutOfLineCode* addOutOfLineCode(OutOfLineCode* ool) {
@@ -1007,35 +1069,31 @@ class BaseCompiler
                 break;
             }
         }
 
         for (size_t i = start; i < lim; i++) {
             Stk& v = stk_[i];
             switch (v.kind()) {
               case Stk::LocalI32: {
-#if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_ARM)
-                ScratchRegisterScope scratch(masm);
+                ScratchI32 scratch(*this);
                 loadLocalI32(scratch, v);
                 masm.Push(scratch);
-#else
-                MOZ_CRASH("BaseCompiler platform hook: sync LocalI32");
-#endif
                 v.setOffs(Stk::MemI32, masm.framePushed());
                 break;
               }
               case Stk::RegisterI32: {
                 masm.Push(v.i32reg().reg);
                 freeI32(v.i32reg());
                 v.setOffs(Stk::MemI32, masm.framePushed());
                 break;
               }
               case Stk::LocalI64: {
 #ifdef JS_PUNBOX64
-                ScratchRegisterScope scratch(masm);
+                ScratchI32 scratch(*this);
                 loadI64(Register64(scratch), v);
                 masm.Push(scratch);
 #else
                 MOZ_CRASH("BaseCompiler platform hook: sync LocalI64");
 #endif
                 v.setOffs(Stk::MemI64, masm.framePushed());
                 break;
               }
@@ -1045,40 +1103,32 @@ class BaseCompiler
                 freeI64(v.i64reg());
 #else
                 MOZ_CRASH("BaseCompiler platform hook: sync RegI64");
 #endif
                 v.setOffs(Stk::MemI64, masm.framePushed());
                 break;
               }
               case Stk::LocalF64: {
-#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM)
-                ScratchDoubleScope scratch(masm);
+                ScratchF64 scratch(*this);
                 loadF64(scratch, v);
                 masm.Push(scratch);
-#else
-                MOZ_CRASH("BaseCompiler platform hook: sync LocalF64");
-#endif
                 v.setOffs(Stk::MemF64, masm.framePushed());
                 break;
               }
               case Stk::RegisterF64: {
                 masm.Push(v.f64reg().reg);
                 freeF64(v.f64reg());
                 v.setOffs(Stk::MemF64, masm.framePushed());
                 break;
               }
               case Stk::LocalF32: {
-#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM)
-                ScratchFloat32Scope scratch(masm);
+                ScratchF32 scratch(*this);
                 loadF32(scratch, v);
                 masm.Push(scratch);
-#else
-                MOZ_CRASH("BaseCompiler platform hook: sync LocalF32");
-#endif
                 v.setOffs(Stk::MemF32, masm.framePushed());
                 break;
               }
               case Stk::RegisterF32: {
                 masm.Push(v.f32reg().reg);
                 freeF32(v.f32reg());
                 v.setOffs(Stk::MemF32, masm.framePushed());
                 break;
@@ -1574,18 +1624,22 @@ class BaseCompiler
     }
 
     // Before exiting a nested control region, pop the execution stack
     // to the level expected by the nesting region, and free the
     // stack.
 
     void popStackOnBlockExit(uint32_t framePushed) {
         uint32_t frameHere = masm.framePushed();
-        if (frameHere > framePushed)
-            masm.freeStack(frameHere - framePushed);
+        if (frameHere > framePushed) {
+            if (deadCode_)
+                masm.adjustStack(frameHere - framePushed);
+            else
+                masm.freeStack(frameHere - framePushed);
+        }
     }
 
     // Peek at the stack, for calls.
 
     Stk& peek(uint32_t relativeDepth) {
         return stk_[stk_.length()-1-relativeDepth];
     }
 
@@ -1597,24 +1651,26 @@ class BaseCompiler
 
     MOZ_MUST_USE
     bool pushControl(UniquePooledLabel* label, UniquePooledLabel* otherLabel = nullptr) {
         uint32_t framePushed = masm.framePushed();
         uint32_t stackSize = stk_.length();
 
         // Always a void value at the beginning of a block, ensures
         // stack is never empty even if the block has no expressions.
-        pushVoid();
+        if (!deadCode_)
+            pushVoid();
 
         if (!ctl_.emplaceBack(Control(framePushed, stackSize)))
             return false;
         if (label)
             ctl_.back().label = label->release();
         if (otherLabel)
             ctl_.back().otherLabel = otherLabel->release();
+        ctl_.back().deadOnArrival = deadCode_;
         return true;
     }
 
     void popControl() {
         Control last = ctl_.popCopy();
         if (last.label)
             freeLabel(last.label);
         if (last.otherLabel)
@@ -1699,24 +1755,20 @@ class BaseCompiler
         // TODO / OPTIMIZE: On SSE2 or better SIMD systems we may be
         // able to store 128 bits at a time.  (I suppose on some
         // systems we have 512-bit SIMD for that matter.)
         //
         // TODO / OPTIMIZE: if we have only one initializing store
         // then it's better to store a zero literal, probably.
 
         if (varLow_ < varHigh_) {
-#if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_ARM)
-            ScratchRegisterScope scratch(masm);
+            ScratchI32 scratch(*this);
             masm.mov(ImmWord(0), scratch);
             for (int32_t i = varLow_ ; i < varHigh_ ; i+=4)
                 storeToFrameI32(scratch, i+4);
-#else
-            MOZ_CRASH("BaseCompiler platform hook: init frame");
-#endif
         }
     }
 
     bool endFunction() {
         // Out-of-line prologue.  Assumes that the in-line prologue has
         // been executed and that a frame of size = localSize_ + sizeof(AsmJSFrame)
         // has been allocated.
 
@@ -1880,68 +1932,56 @@ class BaseCompiler
     // we have the outgoing size at low cost, and then we can pass
     // args based on the info we read.
 
     void passArg(FunctionCall& call, ValType type, Stk& arg) {
         switch (type) {
           case ValType::I32: {
             ABIArg argLoc = call.abi_.next(MIRType::Int32);
             if (argLoc.kind() == ABIArg::Stack) {
-#if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_ARM)
-                ScratchRegisterScope scratch(masm);
+                ScratchI32 scratch(*this);
                 loadI32(scratch, arg);
                 masm.store32(scratch, Address(StackPointer, argLoc.offsetFromArgBase()));
-#else
-                MOZ_CRASH("BaseCompiler platform hook: passArg");
-#endif
             } else {
                 loadI32(argLoc.reg().gpr(), arg);
             }
             break;
           }
           case ValType::I64: {
 #ifdef JS_CODEGEN_X64
             ABIArg argLoc = call.abi_.next(MIRType::Int64);
             if (argLoc.kind() == ABIArg::Stack) {
-                ScratchRegisterScope scratch(masm);
+                ScratchI32 scratch(*this);
                 loadI64(Register64(scratch), arg);
                 masm.movq(scratch, Operand(StackPointer, argLoc.offsetFromArgBase()));
             } else {
                 loadI64(Register64(argLoc.reg().gpr()), arg);
             }
 #else
             MOZ_CRASH("BaseCompiler platform hook: passArg I64");
 #endif
             break;
           }
           case ValType::F64: {
             ABIArg argLoc = call.abi_.next(MIRType::Double);
             if (argLoc.kind() == ABIArg::Stack) {
-#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM)
-                ScratchDoubleScope scratch(masm);
+                ScratchF64 scratch(*this);
                 loadF64(scratch, arg);
                 masm.storeDouble(scratch, Address(StackPointer, argLoc.offsetFromArgBase()));
-#else
-                MOZ_CRASH("BaseCompiler platform hook: passArg F64");
-#endif
             } else {
                 loadF64(argLoc.reg().fpu(), arg);
             }
             break;
           }
           case ValType::F32: {
             ABIArg argLoc = call.abi_.next(MIRType::Float32);
             if (argLoc.kind() == ABIArg::Stack) {
-#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM)
-                ScratchFloat32Scope scratch(masm);
+                ScratchF32 scratch(*this);
                 loadF32(scratch, arg);
                 masm.storeFloat32(scratch, Address(StackPointer, argLoc.offsetFromArgBase()));
-#else
-                MOZ_CRASH("BaseCompiler platform hook: passArg F32");
-#endif
             } else {
                 loadF32(argLoc.reg().fpu(), arg);
             }
             break;
           }
           default:
             MOZ_CRASH("Function argument type");
         }
@@ -1979,17 +2019,17 @@ class BaseCompiler
             masm.branch32(Assembler::Condition::AboveOrEqual, ptrReg, Imm32(length),
                           wasm::JumpTarget::OutOfBounds);
             masm.move32(Imm32(sigIndex), WasmTableCallSigReg);
         }
 
 #if defined(JS_CODEGEN_X64)
         // CodeGeneratorX64::visitAsmJSLoadFuncPtr()
         {
-            ScratchRegisterScope scratch(masm);
+            ScratchI32 scratch(*this);
             CodeOffset label = masm.leaRipRelative(scratch);
             masm.loadPtr(Operand(scratch, ptrReg, TimesEight, 0), ptrReg);
             masm.append(AsmJSGlobalAccess(label, globalDataOffset));
         }
 #elif defined(JS_CODEGEN_X86)
         // CodeGeneratorX86::visitAsmJSLoadFuncPtr()
         CodeOffset label = masm.movlWithPatch(PatchedAbsoluteAddress(), ptrReg, TimesFour, ptrReg);
         masm.append(AsmJSGlobalAccess(label, globalDataOffset));
@@ -2048,17 +2088,17 @@ class BaseCompiler
         }
 #else
         MOZ_CRASH("BaseCompiler platform hook: jumpTable");
 #endif
     }
 
     void tableSwitch(Label* theTable, RegI32 switchValue) {
 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
-        ScratchRegisterScope scratch(masm);
+        ScratchI32 scratch(*this);
         CodeLabel tableCl;
 
         masm.mov(tableCl.patchAt(), scratch);
 
         tableCl.target()->bind(theTable->offset());
         masm.addCodeLabel(tableCl);
 
         masm.jmp(Operand(scratch, switchValue.reg, ScalePointer));
@@ -2201,17 +2241,17 @@ class BaseCompiler
         masm.bind(&notMin);
     }
 
     void checkDivideSignedOverflowI64(RegI64 rhs, RegI64 srcDest, Label* done, bool zeroOnOverflow) {
         MOZ_ASSERT(!isCompilingAsmJS());
 #ifdef JS_CODEGEN_X64
         Label notMin;
         {
-            ScratchRegisterScope scratch(masm);
+            ScratchI32 scratch(*this);
             masm.move64(Imm64(INT64_MIN), Register64(scratch));
             masm.cmpq(scratch, srcDest.reg.reg);
         }
         masm.j(Assembler::NotEqual, &notMin);
         masm.cmpq(Imm32(-1), rhs.reg.reg);
         if (zeroOnOverflow) {
             masm.j(Assembler::NotEqual, &notMin);
             masm.xorq(srcDest.reg.reg, srcDest.reg.reg);
@@ -2862,16 +2902,18 @@ class BaseCompiler
     bool emitBrIf();
     MOZ_MUST_USE
     bool emitBrTable();
     MOZ_MUST_USE
     bool emitReturn();
     MOZ_MUST_USE
     bool emitCallArgs(const ValTypeVector& args, FunctionCall& baselineCall);
     MOZ_MUST_USE
+    bool skipCall(const ValTypeVector& args, ExprType maybeReturnType = ExprType::Limit);
+    MOZ_MUST_USE
     bool emitCall(uint32_t callOffset);
     MOZ_MUST_USE
     bool emitCallIndirect(uint32_t callOffset);
     MOZ_MUST_USE
     bool emitCallImport(uint32_t callOffset);
     MOZ_MUST_USE
     bool emitUnaryMathBuiltinCall(uint32_t callOffset, SymbolicAddress callee, ValType operandType);
     MOZ_MUST_USE
@@ -2893,16 +2935,17 @@ class BaseCompiler
     MOZ_MUST_USE
     bool emitSelect();
 
     void endBlock();
     void endLoop();
     void endIfThen();
     void endIfThenElse();
 
+    void doReturn(ExprType returnType);
     void pushReturned(ExprType type);
     void pushBuiltinReturned(ExprType type);
 
     void emitCompareI32(JSOp compareOp, MCompare::CompareType compareType);
     void emitCompareI64(JSOp compareOp, MCompare::CompareType compareType);
     void emitCompareF32(JSOp compareOp, MCompare::CompareType compareType);
     void emitCompareF64(JSOp compareOp, MCompare::CompareType compareType);
 
@@ -4101,87 +4144,104 @@ BaseCompiler::emitBlock()
 {
     if (!iter_.readBlock())
         return false;
 
     UniquePooledLabel blockEnd(newLabel());
     if (!blockEnd)
         return false;
 
-    sync();                    // Simplifies branching out from block
+    if (!deadCode_)
+        sync();                    // Simplifies branching out from block
 
     return pushControl(&blockEnd);
 }
 
 void
 BaseCompiler::endBlock()
 {
+    Control& block = controlItem(0);
+
     // Save the value.
-    AnyReg r = popJoinReg();
+    AnyReg r;
+    if (!deadCode_)
+        r = popJoinReg();
 
     // Leave the block.
-    Control& block = controlItem(0);
     popStackOnBlockExit(block.framePushed);
 
     // Bind after cleanup: branches out will have popped the stack.
-    masm.bind(block.label);
+    if (block.label->used()) {
+        masm.bind(block.label);
+        deadCode_ = false;
+    }
 
     popValueStackTo(block.stackSize);
     popControl();
 
-    // Retain the value.  Branches out of the block will have targeted
-    // the same register for the result value.
-    pushJoinReg(r);
+    // Retain the value stored in joinReg by all paths.
+    if (!deadCode_)
+        pushJoinReg(r);
 }
 
 bool
 BaseCompiler::emitLoop()
 {
     if (!iter_.readLoop())
         return false;
 
     UniquePooledLabel blockEnd(newLabel());
     if (!blockEnd)
         return false;
 
     UniquePooledLabel blockCont(newLabel());
     if (!blockCont)
         return false;
 
-    sync();                    // Simplifies branching out from block
+    if (!deadCode_)
+        sync();                    // Simplifies branching out from block
 
     if (!pushControl(&blockEnd))
         return false;
 
     if (!pushControl(&blockCont))
         return false;
 
-    masm.bind(controlItem(0).label);
-
-    addInterruptCheck();
+    if (!deadCode_) {
+        masm.bind(controlItem(0).label);
+        addInterruptCheck();
+    }
 
     return true;
 }
 
 void
 BaseCompiler::endLoop()
 {
-    AnyReg r = popJoinReg();
-
     Control& block = controlItem(1);
+
+    AnyReg r;
+    if (!deadCode_)
+        r = popJoinReg();
+
     popStackOnBlockExit(block.framePushed);
 
     // Bind after cleanup: branches out will have popped the stack.
-    masm.bind(block.label);
+    if (block.label->used()) {
+        masm.bind(block.label);
+        deadCode_ = false;
+    }
 
     popValueStackTo(block.stackSize);
     popControl();
     popControl();
 
-    pushJoinReg(r);
+    // Retain the value stored in joinReg by all paths.
+    if (!deadCode_)
+        pushJoinReg(r);
 }
 
 // The bodies of the "then" and "else" arms can be arbitrary sequences
 // of expressions, they push control and increment the nesting and can
 // even be targeted by jumps.  A branch to the "if" block branches to
 // the exit of the if, ie, it's like "break".  Consider:
 //
 //      (func (result i32)
@@ -4203,94 +4263,131 @@ BaseCompiler::emitIf()
     UniquePooledLabel endLabel(newLabel());
     if (!endLabel)
         return false;
 
     UniquePooledLabel elseLabel(newLabel());
     if (!elseLabel)
         return false;
 
-    RegI32 rc = popI32();
-    sync();                    // Simplifies branching out from the arms
+    RegI32 rc;
+    if (!deadCode_) {
+        rc = popI32();
+        sync();                    // Simplifies branching out from the arms
+    }
 
     if (!pushControl(&endLabel, &elseLabel))
         return false;
 
-    masm.branch32(Assembler::Equal, rc.reg, Imm32(0), controlItem(0).otherLabel);
-
-    freeI32(rc);
+    if (!deadCode_) {
+        masm.branch32(Assembler::Equal, rc.reg, Imm32(0), controlItem(0).otherLabel);
+        freeI32(rc);
+    }
 
     return true;
 }
 
 void
 BaseCompiler::endIfThen()
 {
-    Control& here = controlItem(0);
-
-    popStackOnBlockExit(here.framePushed);
-    masm.bind(here.label);
-    masm.bind(here.otherLabel);
-
-    popValueStackTo(here.stackSize);
+    Control& ifThen = controlItem(0);
+
+    popStackOnBlockExit(ifThen.framePushed);
+
+    if (ifThen.otherLabel->used())
+        masm.bind(ifThen.otherLabel);
+
+    if (ifThen.label->used())
+        masm.bind(ifThen.label);
+
+    deadCode_ = ifThen.deadOnArrival;
+
+    popValueStackTo(ifThen.stackSize);
     popControl();
-    pushVoid();
+
+    // No value to preserve.
+    if (!deadCode_)
+        pushVoid();
 }
 
 bool
 BaseCompiler::emitElse()
 {
     ExprType thenType;
     Nothing unused_thenValue;
     if (!iter_.readElse(&thenType, &unused_thenValue))
         return false;
 
     Control& ifThenElse = controlItem(0);
 
     // See comment in endIfThenElse, below.
 
-    AnyReg r = popJoinReg();
+    // Exit the "then" branch.
+
+    ifThenElse.deadThenBranch = deadCode_;
+
+    AnyReg r;
+    if (!deadCode_)
+        r = popJoinReg();
 
     popStackOnBlockExit(ifThenElse.framePushed);
-    masm.jump(ifThenElse.label);
-    masm.bind(ifThenElse.otherLabel);
+
+    if (!deadCode_)
+        masm.jump(ifThenElse.label);
+
+    if (ifThenElse.otherLabel->used())
+        masm.bind(ifThenElse.otherLabel);
+
+    // Reset to the "else" branch.
 
     popValueStackTo(ifThenElse.stackSize);
 
+    if (!deadCode_)
+        freeJoinReg(r);
+
+    deadCode_ = ifThenElse.deadOnArrival;
+
     // The following pushVoid() duplicates the pushVoid() in
     // pushControl() that sets up a value in the "then" block: a block
     // never leaves the stack empty, and both the "then" and "else"
     // arms are implicit blocks.
-    pushVoid();
-
-    freeJoinReg(r);
+    if (!deadCode_)
+        pushVoid();
 
     return true;
 }
 
 void
 BaseCompiler::endIfThenElse()
 {
     Control& ifThenElse = controlItem(0);
 
     // The expression type is not a reliable guide to what we'll find
     // on the stack, we could have (if E (i32.const 1) (unreachable))
     // in which case the "else" arm is AnyType but the type of the
     // full expression is I32.  So restore whatever's there, not what
     // we want to find there.  The "then" arm has the same constraint.
 
-    AnyReg r = popJoinReg();
+    AnyReg r;
+    if (!deadCode_)
+        r = popJoinReg();
 
     popStackOnBlockExit(ifThenElse.framePushed);
-    masm.bind(ifThenElse.label);
+
+    if (ifThenElse.label->used())
+        masm.bind(ifThenElse.label);
+
+    deadCode_ = ifThenElse.deadOnArrival ||
+                (ifThenElse.deadThenBranch && deadCode_ && !ifThenElse.label->bound());
 
     popValueStackTo(ifThenElse.stackSize);
     popControl();
 
-    pushJoinReg(r);
+    if (!deadCode_)
+        pushJoinReg(r);
 }
 
 bool
 BaseCompiler::emitEnd()
 {
     LabelKind kind;
     ExprType type;
     Nothing unused_value;
@@ -4311,19 +4408,23 @@ bool
 BaseCompiler::emitBr()
 {
     uint32_t relativeDepth;
     ExprType type;
     Nothing unused_value;
     if (!iter_.readBr(&relativeDepth, &type, &unused_value))
         return false;
 
+    if (deadCode_)
+        return true;
+
     Control& target = controlItem(relativeDepth);
 
-    // If there is no value then generate one.
+    // If there is no value then generate one for popJoinReg() to
+    // consume.
 
     if (IsVoid(type))
         pushVoid();
 
     // Save any value in the designated join register, where the
     // normal block exit code will also leave it.
 
     AnyReg r = popJoinReg();
@@ -4331,30 +4432,33 @@ BaseCompiler::emitBr()
     popStackBeforeBranch(target.framePushed);
     masm.jump(target.label);
 
     // The register holding the join value is free for the remainder
     // of this block.
 
     freeJoinReg(r);
 
-    pushVoid();
+    deadCode_ = true;
 
     return true;
 }
 
 bool
 BaseCompiler::emitBrIf()
 {
     uint32_t relativeDepth;
     ExprType type;
     Nothing unused_value, unused_condition;
     if (!iter_.readBrIf(&relativeDepth, &type, &unused_value, &unused_condition))
         return false;
 
+    if (deadCode_)
+        return true;
+
     Control& target = controlItem(relativeDepth);
 
     Label notTaken;
 
     // Conditional branches are a little awkward.  If the branch is
     // taken we must pop the execution stack along that edge, which
     // means that the branch instruction becomes inverted to jump
     // around a cleanup + unconditional branch pair.
@@ -4392,16 +4496,17 @@ BaseCompiler::emitBrIf()
     masm.jump(target.label);
 
     masm.bind(&notTaken);
 
     // These registers are free in the remainder of the block.
     freeI32(rc);
     freeJoinReg(r);
 
+    // The non-taken edge currently carries a void value.
     pushVoid();
 
     return true;
 }
 
 bool
 BaseCompiler::emitBrTable()
 {
@@ -4425,16 +4530,19 @@ BaseCompiler::emitBrTable()
             return false;
         depths.infallibleAppend(depth);
     }
 
     uint32_t defaultDepth;
     if (!iter_.readBrTableEntry(type, &defaultDepth))
         return false;
 
+    if (deadCode_)
+        return true;
+
     // We'll need the joinreg later, so don't use it for rc.
     // We assume joinRegI32 and joinRegI64 overlap.
     if (type == ExprType::I32 || type == ExprType::I64)
         needI32(joinRegI32);
 
     // Table switch value always on top.
     RegI32 rc = popI32();
 
@@ -4476,38 +4584,33 @@ BaseCompiler::emitBrTable()
     Label theTable;
     masm.bind(&theTable);
     jumpTable(stubs);
 
     // Emit indirect jump.  rc is live here.
 
     masm.bind(&dispatchCode);
     tableSwitch(&theTable, rc);
-    pushVoid();
+
+    deadCode_ = true;
 
     // Clean up.
 
     freeI32(rc);
     freeJoinReg(r);
 
     for (uint32_t i = 0; i < tableLength; i++)
         freeLabel(stubs[i]);
 
     return true;
 }
 
-bool
-BaseCompiler::emitReturn()
+void
+BaseCompiler::doReturn(ExprType type)
 {
-    Nothing unused_value;
-    if (!iter_.readReturn(&unused_value))
-        return false;
-
-    ExprType type = func_.sig().ret();
-
     switch (type) {
       case ExprType::Void: {
         returnVoid();
         break;
       }
       case ExprType::I32: {
         RegI32 rv = popI32();
         returnI32(rv);
@@ -4531,23 +4634,39 @@ BaseCompiler::emitReturn()
         returnF32(rv);
         freeF32(rv);
         break;
       }
       default: {
         MOZ_CRASH("Function return type");
       }
     }
-    pushVoid();
+}
+
+bool
+BaseCompiler::emitReturn()
+{
+    Nothing unused_value;
+    if (!iter_.readReturn(&unused_value))
+        return false;
+
+    if (deadCode_)
+        return true;
+
+    doReturn(func_.sig().ret());
+    deadCode_ = true;
+
     return true;
 }
 
 bool
 BaseCompiler::emitCallArgs(const ValTypeVector& args, FunctionCall& baselineCall)
 {
+    MOZ_ASSERT(!deadCode_);
+
     startCallArgs(baselineCall, stackArgAreaSize(args));
 
     uint32_t numArgs = args.length();
     for (size_t i = 0; i < numArgs; ++i) {
         ValType argType = args[i];
         Nothing arg_;
         if (!iter_.readCallArg(argType, numArgs, i, &arg_))
             return false;
@@ -4556,16 +4675,40 @@ BaseCompiler::emitCallArgs(const ValType
     }
 
     if (!iter_.readCallArgsEnd(numArgs))
         return false;
 
     return true;
 }
 
+bool
+BaseCompiler::skipCall(const ValTypeVector& args, ExprType maybeReturnType)
+{
+    MOZ_ASSERT(deadCode_);
+
+    uint32_t numArgs = args.length();
+    for (size_t i = 0; i < numArgs; ++i) {
+        ValType argType = args[i];
+        Nothing arg_;
+        if (!iter_.readCallArg(argType, numArgs, i, &arg_))
+            return false;
+    }
+
+    if (!iter_.readCallArgsEnd(numArgs))
+        return false;
+
+    if (maybeReturnType != ExprType::Limit) {
+        if (!iter_.readCallReturn(maybeReturnType))
+            return false;
+    }
+
+    return true;
+}
+
 void
 BaseCompiler::pushReturned(ExprType type)
 {
     switch (type) {
       case ExprType::Void: {
         pushVoid();
         break;
       }
@@ -4640,16 +4783,19 @@ BaseCompiler::emitCall(uint32_t callOffs
 
     uint32_t calleeIndex;
     uint32_t arity;
     if (!iter_.readCall(&calleeIndex, &arity))
         return false;
 
     const Sig& sig = *mg_.funcSigs[calleeIndex];
 
+    if (deadCode_)
+        return skipCall(sig.args(), sig.ret());
+
     sync();
 
     uint32_t numArgs = sig.args().length();
     size_t stackSpace = stackConsumed(numArgs);
 
     FunctionCall baselineCall(lineOrBytecode);
     beginCall(baselineCall, false);
 
@@ -4679,32 +4825,38 @@ BaseCompiler::emitCallIndirect(uint32_t 
 {
     uint32_t lineOrBytecode = readCallSiteLineOrBytecode(callOffset);
 
     uint32_t sigIndex;
     uint32_t arity;
     if (!iter_.readCallIndirect(&sigIndex, &arity))
         return false;
 
+    Nothing callee_;
+
     const Sig& sig = mg_.sigs[sigIndex];
 
+    if (deadCode_) {
+        return skipCall(sig.args()) && iter_.readCallIndirectCallee(&callee_) &&
+               iter_.readCallReturn(sig.ret());
+    }
+
     sync();
 
     // Stack: ... index arg1 .. argn
 
     uint32_t numArgs = sig.args().length();
     size_t stackSpace = stackConsumed(numArgs+1);
 
     FunctionCall baselineCall(lineOrBytecode);
     beginCall(baselineCall, false);
 
     if (!emitCallArgs(sig.args(), baselineCall))
         return false;
 
-    Nothing callee_;
     if (!iter_.readCallIndirectCallee(&callee_))
         return false;
 
     if (!iter_.readCallReturn(sig.ret()))
         return false;
 
     Stk& callee = peek(numArgs);
     const TableModuleGeneratorData& table = isCompilingAsmJS()
@@ -4733,16 +4885,19 @@ BaseCompiler::emitCallImport(uint32_t ca
     uint32_t importIndex;
     uint32_t arity;
     if (!iter_.readCallImport(&importIndex, &arity))
         return false;
 
     const ImportModuleGeneratorData& import = mg_.imports[importIndex];
     const Sig& sig = *import.sig;
 
+    if (deadCode_)
+        return skipCall(sig.args(), sig.ret());
+
     sync();
 
     uint32_t numArgs = sig.args().length();
     size_t stackSpace = stackConsumed(numArgs);
 
     FunctionCall baselineCall(lineOrBytecode);
     beginCall(baselineCall, true);
 
@@ -4764,18 +4919,29 @@ BaseCompiler::emitCallImport(uint32_t ca
 
     pushReturned(sig.ret());
 
     return true;
 }
 
 bool
 BaseCompiler::emitUnaryMathBuiltinCall(uint32_t callOffset, SymbolicAddress callee,
-                                           ValType operandType)
+                                       ValType operandType)
 {
+    if (deadCode_) {
+        switch (operandType) {
+          case ValType::F64:
+            return skipCall(SigD_, ExprType::F64);
+          case ValType::F32:
+            return skipCall(SigF_, ExprType::F32);
+          default:
+            MOZ_CRASH("Compiler bug: not a float type");
+        }
+    }
+
     uint32_t lineOrBytecode = readCallSiteLineOrBytecode(callOffset);
 
     sync();
 
     uint32_t numArgs = 1;
     size_t stackSpace = stackConsumed(numArgs);
 
     FunctionCall baselineCall(lineOrBytecode);
@@ -4812,20 +4978,23 @@ BaseCompiler::emitUnaryMathBuiltinCall(u
 
     pushBuiltinReturned(retType);
 
     return true;
 }
 
 bool
 BaseCompiler::emitBinaryMathBuiltinCall(uint32_t callOffset, SymbolicAddress callee,
-                                            ValType operandType)
+                                        ValType operandType)
 {
     MOZ_ASSERT(operandType == ValType::F64);
 
+    if (deadCode_)
+        return skipCall(SigDD_, ExprType::F64);
+
     uint32_t lineOrBytecode = 0;
     if (callee == SymbolicAddress::ModD) {
         // Not actually a call in the binary representation
     } else {
         readCallSiteLineOrBytecode(callOffset);
     }
 
     sync();
@@ -4860,16 +5029,19 @@ BaseCompiler::emitBinaryMathBuiltinCall(
 
 bool
 BaseCompiler::emitGetLocal()
 {
     uint32_t slot;
     if (!iter_.readGetLocal(locals_, &slot))
         return false;
 
+    if (deadCode_)
+        return true;
+
     // Local loads are pushed unresolved, ie, they may be deferred
     // until needed, until they may be affected by a store, or until a
     // sync.  This is intended to reduce register pressure.
 
     switch (locals_[slot]) {
       case ValType::I32:
         pushLocalI32(slot);
         break;
@@ -4892,16 +5064,19 @@ BaseCompiler::emitGetLocal()
 bool
 BaseCompiler::emitSetLocal()
 {
     uint32_t slot;
     Nothing unused_value;
     if (!iter_.readSetLocal(locals_, &slot, &unused_value))
         return false;
 
+    if (deadCode_)
+        return true;
+
     switch (locals_[slot]) {
       case ValType::I32: {
         RegI32 rv = popI32();
         syncLocal(slot);
         storeToFrameI32(rv.reg, frameOffsetFromSlot(slot, MIRType::Int32));
         pushI32(rv);
         break;
       }
@@ -4935,16 +5110,19 @@ BaseCompiler::emitSetLocal()
 
 bool
 BaseCompiler::emitGetGlobal()
 {
     uint32_t id;
     if (!iter_.readGetGlobal(mg_.globals, &id))
         return false;
 
+    if (deadCode_)
+        return true;
+
     const GlobalDesc& global = mg_.globals[id];
 
     switch (global.type) {
       case ValType::I32: {
         RegI32 rv = needI32();
         loadGlobalVarI32(global.globalDataOffset, rv);
         pushI32(rv);
         break;
@@ -4977,16 +5155,19 @@ BaseCompiler::emitGetGlobal()
 bool
 BaseCompiler::emitSetGlobal()
 {
     uint32_t id;
     Nothing unused_value;
     if (!iter_.readSetGlobal(mg_.globals, &id, &unused_value))
         return false;
 
+    if (deadCode_)
+        return true;
+
     const GlobalDesc& global = mg_.globals[id];
 
     switch (global.type) {
       case ValType::I32: {
         RegI32 rv = popI32();
         storeGlobalVarI32(global.globalDataOffset, rv);
         pushI32(rv);
         break;
@@ -5018,16 +5199,19 @@ BaseCompiler::emitSetGlobal()
 
 bool
 BaseCompiler::emitLoad(ValType type, Scalar::Type viewType)
 {
     LinearMemoryAddress<Nothing> addr;
     if (!iter_.readLoad(type, Scalar::byteSize(viewType), &addr))
         return false;
 
+    if (deadCode_)
+        return true;
+
     // TODO / OPTIMIZE: Disable bounds checking on constant accesses
     // below the minimum heap length.
 
     MWasmMemoryAccess access(viewType, addr.align);
     access.setOffset(addr.offset);
 
     switch (type) {
       case ValType::I32: {
@@ -5065,16 +5249,19 @@ BaseCompiler::emitLoad(ValType type, Sca
 bool
 BaseCompiler::emitStore(ValType resultType, Scalar::Type viewType)
 {
     LinearMemoryAddress<Nothing> addr;
     Nothing unused_value;
     if (!iter_.readStore(resultType, Scalar::byteSize(viewType), &addr, &unused_value))
         return false;
 
+    if (deadCode_)
+        return true;
+
     // TODO / OPTIMIZE: Disable bounds checking on constant accesses
     // below the minimum heap length.
 
     MWasmMemoryAccess access(viewType, addr.align);
     access.setOffset(addr.offset);
 
     switch (resultType) {
       case ValType::I32: {
@@ -5116,16 +5303,19 @@ BaseCompiler::emitSelect()
 {
     ExprType type;
     Nothing unused_trueValue;
     Nothing unused_falseValue;
     Nothing unused_condition;
     if (!iter_.readSelect(&type, &unused_trueValue, &unused_falseValue, &unused_condition))
         return false;
 
+    if (deadCode_)
+        return true;
+
     // I32 condition on top, then false, then true.
 
     RegI32 rc = popI32();
     switch (type) {
       case AnyType:
       case ExprType::Void: {
         popValueStackBy(2);
         pushVoid();
@@ -5339,16 +5529,19 @@ BaseCompiler::emitCompareF64(JSOp compar
 bool
 BaseCompiler::emitStoreWithCoercion(ValType resultType, Scalar::Type viewType)
 {
     LinearMemoryAddress<Nothing> addr;
     Nothing unused_value;
     if (!iter_.readStore(resultType, Scalar::byteSize(viewType), &addr, &unused_value))
         return false;
 
+    if (deadCode_)
+        return true;
+
     // TODO / OPTIMIZE: Disable bounds checking on constant accesses
     // below the minimum heap length.
 
     MWasmMemoryAccess access(viewType, addr.align);
     access.setOffset(addr.offset);
 
     if (resultType == ValType::F32 && viewType == Scalar::Float64) {
         RegF32 rv = popF32();
@@ -5381,29 +5574,29 @@ BaseCompiler::emitBody()
 {
     uint32_t overhead = 0;
 
     for (;;) {
 
         Nothing unused_a, unused_b;
 
 #define emitBinary(doEmit, type) \
-        iter_.readBinary(type, &unused_a, &unused_b) && (doEmit(), true)
+        iter_.readBinary(type, &unused_a, &unused_b) && (deadCode_ || (doEmit(), true))
 
 #define emitUnary(doEmit, type) \
-        iter_.readUnary(type, &unused_a) && (doEmit(), true)
+        iter_.readUnary(type, &unused_a) && (deadCode_ || (doEmit(), true))
 
 #define emitComparison(doEmit, operandType, compareOp, compareType) \
-        iter_.readComparison(operandType, &unused_a, &unused_b) && (doEmit(compareOp, compareType), true)
+        iter_.readComparison(operandType, &unused_a, &unused_b) && (deadCode_ || (doEmit(compareOp, compareType), true))
 
 #define emitConversion(doEmit, inType, outType) \
-        iter_.readConversion(inType, outType, &unused_a) && (doEmit(), true)
+        iter_.readConversion(inType, outType, &unused_a) && (deadCode_ || (doEmit(), true))
 
 #define emitConversionOOM(doEmit, inType, outType) \
-        iter_.readConversion(inType, outType, &unused_a) && doEmit()
+        iter_.readConversion(inType, outType, &unused_a) && (deadCode_ || doEmit())
 
 #define CHECK(E)      if (!(E)) goto done
 #define NEXT()        continue
 #define CHECK_NEXT(E) if (!(E)) goto done; continue
 
         // TODO / EVALUATE: Not obvious that this attempt at reducing
         // overhead is really paying off relative to making the check
         // every iteration.
@@ -5431,17 +5624,18 @@ BaseCompiler::emitBody()
 
         Expr expr;
         CHECK(iter_.readExpr(&expr));
 
         switch (expr) {
           // Control opcodes
           case Expr::Nop:
             CHECK(iter_.readNullary());
-            pushVoid();
+            if (!deadCode_)
+                pushVoid();
             NEXT();
           case Expr::Block:
             CHECK_NEXT(emitBlock());
           case Expr::Loop:
             CHECK_NEXT(emitLoop());
           case Expr::If:
             CHECK_NEXT(emitIf());
           case Expr::Else:
@@ -5453,18 +5647,20 @@ BaseCompiler::emitBody()
           case Expr::BrIf:
             CHECK_NEXT(emitBrIf());
           case Expr::BrTable:
             CHECK_NEXT(emitBrTable());
           case Expr::Return:
             CHECK_NEXT(emitReturn());
           case Expr::Unreachable:
             CHECK(iter_.readUnreachable());
-            unreachableTrap();
-            pushVoid();
+            if (!deadCode_) {
+                unreachableTrap();
+                deadCode_ = true;
+            }
             NEXT();
 
           // Calls
           case Expr::Call:
             CHECK_NEXT(emitCall(exprOffset));
           case Expr::CallIndirect:
             CHECK_NEXT(emitCallIndirect(exprOffset));
           case Expr::CallImport:
@@ -5483,17 +5679,18 @@ BaseCompiler::emitBody()
           // Select
           case Expr::Select:
             CHECK_NEXT(emitSelect());
 
           // I32
           case Expr::I32Const: {
             int32_t i32;
             CHECK(iter_.readI32Const(&i32));
-            pushI32(i32);
+            if (!deadCode_)
+                pushI32(i32);
             NEXT();
           }
           case Expr::I32Add:
             CHECK_NEXT(emitBinary(emitAddI32, ValType::I32));
           case Expr::I32Sub:
             CHECK_NEXT(emitBinary(emitSubtractI32, ValType::I32));
           case Expr::I32Mul:
             CHECK_NEXT(emitBinary(emitMultiplyI32, ValType::I32));
@@ -5567,17 +5764,18 @@ BaseCompiler::emitBody()
             CHECK_NEXT(emitBinary(emitRotrI32, ValType::I32));
           case Expr::I32Rotl:
             CHECK_NEXT(emitBinary(emitRotlI32, ValType::I32));
 
           // I64
           case Expr::I64Const: {
             int64_t i64;
             CHECK(iter_.readI64Const(&i64));
-            pushI64(i64);
+            if (!deadCode_)
+                pushI64(i64);
             NEXT();
           }
           case Expr::I64Add:
             CHECK_NEXT(emitBinary(emitAddI64, ValType::I64));
           case Expr::I64Sub:
             CHECK_NEXT(emitBinary(emitSubtractI64, ValType::I64));
           case Expr::I64Mul:
             CHECK_NEXT(emitBinary(emitMultiplyI64, ValType::I64));
@@ -5649,17 +5847,18 @@ BaseCompiler::emitBody()
             CHECK_NEXT(emitStore(ValType::I64, Scalar::Int32));
           case Expr::I64Store:
             MOZ_CRASH("BaseCompiler platform hook: int64 store");
 
           // F32
           case Expr::F32Const: {
             float f32;
             CHECK(iter_.readF32Const(&f32));
-            pushF32(f32);
+            if (!deadCode_)
+                pushF32(f32);
             NEXT();
           }
           case Expr::F32Add:
             CHECK_NEXT(emitBinary(emitAddF32, ValType::F32));
           case Expr::F32Sub:
             CHECK_NEXT(emitBinary(emitSubtractF32, ValType::F32));
           case Expr::F32Mul:
             CHECK_NEXT(emitBinary(emitMultiplyF32, ValType::F32));
@@ -5703,17 +5902,18 @@ BaseCompiler::emitBody()
             CHECK_NEXT(emitUnaryMathBuiltinCall(exprOffset, SymbolicAddress::NearbyIntF, ValType::F32));
           case Expr::F32Trunc:
             CHECK_NEXT(emitUnaryMathBuiltinCall(exprOffset, SymbolicAddress::TruncF, ValType::F32));
 
           // F64
           case Expr::F64Const: {
             double f64;
             CHECK(iter_.readF64Const(&f64));
-            pushF64(f64);
+            if (!deadCode_)
+                pushF64(f64);
             NEXT();
           }
           case Expr::F64Add:
             CHECK_NEXT(emitBinary(emitAddF64, ValType::F64));
           case Expr::F64Sub:
             CHECK_NEXT(emitBinary(emitSubtractF64, ValType::F64));
           case Expr::F64Mul:
             CHECK_NEXT(emitBinary(emitMultiplyF64, ValType::F64));
@@ -5972,50 +6172,18 @@ BaseCompiler::emitFunction()
         return false;
 
     const Sig& sig = func_.sig();
 
     Nothing unused_value;
     if (!iter_.readFunctionEnd(sig.ret(), &unused_value))
         return false;
 
-    switch (sig.ret()) {
-      case ExprType::Void: {
-        returnVoid();
-        break;
-      }
-      case ExprType::I32: {
-        RegI32 r0 = popI32();
-        returnI32(r0);
-        freeI32(r0);
-        break;
-      }
-      case ExprType::I64: {
-        RegI64 r0 = popI64();
-        returnI64(r0);
-        freeI64(r0);
-        break;
-      }
-      case ExprType::F64: {
-        RegF64 r0 = popF64();
-        returnF64(r0);
-        freeF64(r0);
-        break;
-      }
-      case ExprType::F32: {
-        RegF32 r0 = popF32();
-        returnF32(r0);
-        freeF32(r0);
-        break;
-      }
-      default: {
-        MOZ_CRASH("Function return type");
-        break;
-      }
-    }
+    if (!deadCode_)
+        doReturn(sig.ret());
 
     popStackOnBlockExit(ctl_[0].framePushed);
     popControl();
 
     if (!endFunction())
         return false;
 
     return true;
@@ -6031,20 +6199,24 @@ BaseCompiler::BaseCompiler(const ModuleG
       func_(func),
       lastReadCallSite_(0),
       alloc_(compileResults.alloc()),
       locals_(locals),
       localSize_(0),
       varLow_(0),
       varHigh_(0),
       maxFramePushed_(0),
+      deadCode_(false),
       compileResults_(compileResults),
       masm(compileResults_.masm()),
       availGPR_(GeneralRegisterSet::All()),
       availFPU_(FloatRegisterSet::All()),
+#ifdef DEBUG
+      scratchRegisterTaken_(false),
+#endif
 #ifdef JS_CODEGEN_X64
       specific_rax(RegI64(Register64(rax))),
       specific_rcx(RegI64(Register64(rcx))),
       specific_rdx(RegI64(Register64(rdx))),
 #endif
 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
       specific_eax(RegI32(eax)),
       specific_ecx(RegI32(ecx)),
--- a/js/src/asmjs/WasmCode.cpp
+++ b/js/src/asmjs/WasmCode.cpp
@@ -242,70 +242,39 @@ CodeSegment::~CodeSegment()
 
     MOZ_ASSERT(wasmCodeAllocations > 0);
     wasmCodeAllocations--;
 
     MOZ_ASSERT(totalLength() > 0);
     DeallocateExecutableMemory(bytes_, totalLength(), gc::SystemPageSize());
 }
 
-size_t
-CodeSegment::serializedSize() const
-{
-    return sizeof(uint32_t) +
-           sizeof(uint32_t) +
-           codeLength_;
-}
-
-uint8_t*
-CodeSegment::serialize(uint8_t* cursor) const
-{
-    cursor = WriteScalar<uint32_t>(cursor, codeLength_);
-    cursor = WriteScalar<uint32_t>(cursor, globalDataLength_);
-    cursor = WriteBytes(cursor, bytes_, codeLength_);
-    return cursor;
-}
-
-const uint8_t*
-CodeSegment::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
-{
-    cursor = ReadScalar<uint32_t>(cursor, &codeLength_);
-    cursor = ReadScalar<uint32_t>(cursor, &globalDataLength_);
-
-    bytes_ = AllocateCodeSegment(cx, codeLength_ + globalDataLength_);
-    if (!bytes_)
-        return nullptr;
-
-    cursor = ReadBytes(cursor, bytes_, codeLength_);
-    return cursor;
-}
-
 static size_t
 SerializedSigSize(const Sig& sig)
 {
     return sizeof(ExprType) +
            SerializedPodVectorSize(sig.args());
 }
 
 static uint8_t*
 SerializeSig(uint8_t* cursor, const Sig& sig)
 {
     cursor = WriteScalar<ExprType>(cursor, sig.ret());
     cursor = SerializePodVector(cursor, sig.args());
     return cursor;
 }
 
 static const uint8_t*
-DeserializeSig(ExclusiveContext* cx, const uint8_t* cursor, Sig* sig)
+DeserializeSig(const uint8_t* cursor, Sig* sig)
 {
     ExprType ret;
     cursor = ReadScalar<ExprType>(cursor, &ret);
 
     ValTypeVector args;
-    cursor = DeserializePodVector(cx, cursor, &args);
+    cursor = DeserializePodVector(cursor, &args);
     if (!cursor)
         return nullptr;
 
     *sig = Sig(Move(args), ret);
     return cursor;
 }
 
 static size_t
@@ -325,19 +294,19 @@ uint8_t*
 Export::serialize(uint8_t* cursor) const
 {
     cursor = SerializeSig(cursor, sig_);
     cursor = WriteBytes(cursor, &pod, sizeof(pod));
     return cursor;
 }
 
 const uint8_t*
-Export::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
+Export::deserialize(const uint8_t* cursor)
 {
-    (cursor = DeserializeSig(cx, cursor, &sig_)) &&
+    (cursor = DeserializeSig(cursor, &sig_)) &&
     (cursor = ReadBytes(cursor, &pod, sizeof(pod)));
     return cursor;
 }
 
 size_t
 Export::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
 {
     return SizeOfSigExcludingThis(sig_, mallocSizeOf);
@@ -354,19 +323,19 @@ uint8_t*
 Import::serialize(uint8_t* cursor) const
 {
     cursor = SerializeSig(cursor, sig_);
     cursor = WriteBytes(cursor, &pod, sizeof(pod));
     return cursor;
 }
 
 const uint8_t*
-Import::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
+Import::deserialize(const uint8_t* cursor)
 {
-    (cursor = DeserializeSig(cx, cursor, &sig_)) &&
+    (cursor = DeserializeSig(cursor, &sig_)) &&
     (cursor = ReadBytes(cursor, &pod, sizeof(pod)));
     return cursor;
 }
 
 size_t
 Import::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
 {
     return SizeOfSigExcludingThis(sig_, mallocSizeOf);
@@ -446,23 +415,23 @@ CacheableChars::serialize(uint8_t* curso
 {
     uint32_t lengthWithNullChar = StringLengthWithNullChar(get());
     cursor = WriteScalar<uint32_t>(cursor, lengthWithNullChar);
     cursor = WriteBytes(cursor, get(), lengthWithNullChar);
     return cursor;
 }
 
 const uint8_t*
-CacheableChars::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
+CacheableChars::deserialize(const uint8_t* cursor)
 {
     uint32_t lengthWithNullChar;
     cursor = ReadBytes(cursor, &lengthWithNullChar, sizeof(uint32_t));
 
     if (lengthWithNullChar) {
-        reset(cx->pod_malloc<char>(lengthWithNullChar));
+        reset(js_pod_malloc<char>(lengthWithNullChar));
         if (!get())
             return nullptr;
 
         cursor = ReadBytes(cursor, get(), lengthWithNullChar);
     } else {
         MOZ_ASSERT(!get());
     }
 
@@ -502,28 +471,28 @@ Metadata::serialize(uint8_t* cursor) con
     cursor = SerializePodVector(cursor, callSites);
     cursor = SerializePodVector(cursor, callThunks);
     cursor = SerializePodVector(cursor, funcNames);
     cursor = filename.serialize(cursor);
     return cursor;
 }
 
 /* static */ const uint8_t*
-Metadata::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
+Metadata::deserialize(const uint8_t* cursor)
 {
     (cursor = ReadBytes(cursor, &pod(), sizeof(pod()))) &&
-    (cursor = DeserializeVector(cx, cursor, &imports)) &&
-    (cursor = DeserializeVector(cx, cursor, &exports)) &&
-    (cursor = DeserializePodVector(cx, cursor, &memoryAccesses)) &&
-    (cursor = DeserializePodVector(cx, cursor, &boundsChecks)) &&
-    (cursor = DeserializePodVector(cx, cursor, &codeRanges)) &&
-    (cursor = DeserializePodVector(cx, cursor, &callSites)) &&
-    (cursor = DeserializePodVector(cx, cursor, &callThunks)) &&
-    (cursor = DeserializePodVector(cx, cursor, &funcNames)) &&
-    (cursor = filename.deserialize(cx, cursor));
+    (cursor = DeserializeVector(cursor, &imports)) &&
+    (cursor = DeserializeVector(cursor, &exports)) &&
+    (cursor = DeserializePodVector(cursor, &memoryAccesses)) &&
+    (cursor = DeserializePodVector(cursor, &boundsChecks)) &&
+    (cursor = DeserializePodVector(cursor, &codeRanges)) &&
+    (cursor = DeserializePodVector(cursor, &callSites)) &&
+    (cursor = DeserializePodVector(cursor, &callThunks)) &&
+    (cursor = DeserializePodVector(cursor, &funcNames)) &&
+    (cursor = filename.deserialize(cursor));
     return cursor;
 }
 
 size_t
 Metadata::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
 {
     return SizeOfVectorExcludingThis(imports, mallocSizeOf) +
            SizeOfVectorExcludingThis(exports, mallocSizeOf) +
--- a/js/src/asmjs/WasmCode.h
+++ b/js/src/asmjs/WasmCode.h
@@ -88,18 +88,16 @@ class CodeSegment
     // enter/exit.
 
     bool containsFunctionPC(void* pc) const {
         return pc >= code() && pc < (code() + functionCodeLength_);
     }
     bool containsCodePC(void* pc) const {
         return pc >= code() && pc < (code() + codeLength_);
     }
-
-    WASM_DECLARE_SERIALIZABLE(CodeSegment)
 };
 
 // This reusable base class factors out the logic for a resource that is shared
 // by multiple instances/modules but should only be counted once when computing
 // about:memory stats.
 
 template <class T>
 struct ShareableBase : RefCounted<T>
--- a/js/src/asmjs/WasmModule.cpp
+++ b/js/src/asmjs/WasmModule.cpp
@@ -70,20 +70,20 @@ uint8_t*
 LinkData::SymbolicLinkArray::serialize(uint8_t* cursor) const
 {
     for (const Uint32Vector& offsets : *this)
         cursor = SerializePodVector(cursor, offsets);
     return cursor;
 }
 
 const uint8_t*
-LinkData::SymbolicLinkArray::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
+LinkData::SymbolicLinkArray::deserialize(const uint8_t* cursor)
 {
     for (Uint32Vector& offsets : *this) {
-        cursor = DeserializePodVector(cx, cursor, &offsets);
+        cursor = DeserializePodVector(cursor, &offsets);
         if (!cursor)
             return nullptr;
     }
     return cursor;
 }
 
 size_t
 LinkData::SymbolicLinkArray::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
@@ -105,20 +105,20 @@ uint8_t*
 LinkData::FuncTable::serialize(uint8_t* cursor) const
 {
     cursor = WriteBytes(cursor, &globalDataOffset, sizeof(globalDataOffset));
     cursor = SerializePodVector(cursor, elemOffsets);
     return cursor;
 }
 
 const uint8_t*
-LinkData::FuncTable::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
+LinkData::FuncTable::deserialize(const uint8_t* cursor)
 {
     (cursor = ReadBytes(cursor, &globalDataOffset, sizeof(globalDataOffset))) &&
-    (cursor = DeserializePodVector(cx, cursor, &elemOffsets));
+    (cursor = DeserializePodVector(cursor, &elemOffsets));
     return cursor;
 }
 
 size_t
 LinkData::FuncTable::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
 {
     return elemOffsets.sizeOfExcludingThis(mallocSizeOf);
 }
@@ -138,22 +138,22 @@ LinkData::serialize(uint8_t* cursor) con
     cursor = WriteBytes(cursor, &pod(), sizeof(pod()));
     cursor = SerializePodVector(cursor, internalLinks);
     cursor = symbolicLinks.serialize(cursor);
     cursor = SerializeVector(cursor, funcTables);
     return cursor;
 }
 
 const uint8_t*
-LinkData::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
+LinkData::deserialize(const uint8_t* cursor)
 {
     (cursor = ReadBytes(cursor, &pod(), sizeof(pod()))) &&
-    (cursor = DeserializePodVector(cx, cursor, &internalLinks)) &&
-    (cursor = symbolicLinks.deserialize(cx, cursor)) &&
-    (cursor = DeserializeVector(cx, cursor, &funcTables));
+    (cursor = DeserializePodVector(cursor, &internalLinks)) &&
+    (cursor = symbolicLinks.deserialize(cursor)) &&
+    (cursor = DeserializeVector(cursor, &funcTables));
     return cursor;
 }
 
 size_t
 LinkData::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
 {
     return internalLinks.sizeOfExcludingThis(mallocSizeOf) +
            symbolicLinks.sizeOfExcludingThis(mallocSizeOf) +
@@ -171,20 +171,20 @@ uint8_t*
 ImportName::serialize(uint8_t* cursor) const
 {
     cursor = module.serialize(cursor);
     cursor = func.serialize(cursor);
     return cursor;
 }
 
 const uint8_t*
-ImportName::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
+ImportName::deserialize(const uint8_t* cursor)
 {
-    (cursor = module.deserialize(cx, cursor)) &&
-    (cursor = func.deserialize(cx, cursor));
+    (cursor = module.deserialize(cursor)) &&
+    (cursor = func.deserialize(cursor));
     return cursor;
 }
 
 size_t
 ImportName::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
 {
     return module.sizeOfExcludingThis(mallocSizeOf) +
            func.sizeOfExcludingThis(mallocSizeOf);
@@ -201,20 +201,20 @@ uint8_t*
 ExportMap::serialize(uint8_t* cursor) const
 {
     cursor = SerializeVector(cursor, fieldNames);
     cursor = SerializePodVector(cursor, fieldsToExports);
     return cursor;
 }
 
 const uint8_t*
-ExportMap::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
+ExportMap::deserialize(const uint8_t* cursor)
 {
-    (cursor = DeserializeVector(cx, cursor, &fieldNames)) &&
-    (cursor = DeserializePodVector(cx, cursor, &fieldsToExports));
+    (cursor = DeserializeVector(cursor, &fieldNames)) &&
+    (cursor = DeserializePodVector(cursor, &fieldsToExports));
     return cursor;
 }
 
 size_t
 ExportMap::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
 {
     return SizeOfVectorExcludingThis(fieldNames, mallocSizeOf) &&
            fieldsToExports.sizeOfExcludingThis(mallocSizeOf);
@@ -241,71 +241,70 @@ Module::serialize(uint8_t* cursor) const
     cursor = exportMap_.serialize(cursor);
     cursor = SerializePodVector(cursor, dataSegments_);
     cursor = metadata_->serialize(cursor);
     cursor = SerializePodVector(cursor, bytecode_->bytes);
     return cursor;
 }
 
 /* static */ const uint8_t*
-Module::deserialize(ExclusiveContext* cx, const uint8_t* cursor, UniquePtr<Module>* module,
-                    Metadata* maybeMetadata)
+Module::deserialize(const uint8_t* cursor, UniquePtr<Module>* module, Metadata* maybeMetadata)
 {
     Bytes code;
-    cursor = DeserializePodVector(cx, cursor, &code);
+    cursor = DeserializePodVector(cursor, &code);
     if (!cursor)
         return nullptr;
 
     LinkData linkData;
-    cursor = linkData.deserialize(cx, cursor);
+    cursor = linkData.deserialize(cursor);
     if (!cursor)
         return nullptr;
 
     ImportNameVector importNames;
-    cursor = DeserializeVector(cx, cursor, &importNames);
+    cursor = DeserializeVector(cursor, &importNames);
     if (!cursor)
         return nullptr;
 
     ExportMap exportMap;
-    cursor = exportMap.deserialize(cx, cursor);
+    cursor = exportMap.deserialize(cursor);
     if (!cursor)
         return nullptr;
 
     DataSegmentVector dataSegments;
-    cursor = DeserializePodVector(cx, cursor, &dataSegments);
+    cursor = DeserializePodVector(cursor, &dataSegments);
     if (!cursor)
         return nullptr;
 
     MutableMetadata metadata;
     if (maybeMetadata) {
         metadata = maybeMetadata;
     } else {
-        metadata = cx->new_<Metadata>();
+        metadata = js_new<Metadata>();
         if (!metadata)
             return nullptr;
     }
-    cursor = metadata->deserialize(cx, cursor);
+    cursor = metadata->deserialize(cursor);
     if (!cursor)
         return nullptr;
     MOZ_RELEASE_ASSERT(!!maybeMetadata == metadata->isAsmJS());
 
-    MutableBytes bytecode = cx->new_<ShareableBytes>();
+    MutableBytes bytecode = js_new<ShareableBytes>();
     if (!bytecode)
         return nullptr;
-    cursor = DeserializePodVector(cx, cursor, &bytecode->bytes);
+    cursor = DeserializePodVector(cursor, &bytecode->bytes);
     if (!cursor)
         return nullptr;
 
-    *module = cx->make_unique<Module>(Move(code),
-                                      Move(linkData),
-                                      Move(importNames),
-                                      Move(exportMap),
-                                      Move(dataSegments),
-                                      *metadata,
-                                      *bytecode);
+    *module = js::MakeUnique<Module>(Move(code),
+                                     Move(linkData),
+                                     Move(importNames),
+                                     Move(exportMap),
+                                     Move(dataSegments),
+                                     *metadata,
+                                     *bytecode);
     if (!*module)
         return nullptr;
 
     return cursor;
 }
 
 /* virtual */ void
 Module::addSizeOfMisc(MallocSizeOf mallocSizeOf,
--- a/js/src/asmjs/WasmModule.h
+++ b/js/src/asmjs/WasmModule.h
@@ -192,18 +192,17 @@ class Module
                      Handle<FunctionVector> funcImports,
                      Handle<ArrayBufferObjectMaybeShared*> asmJSHeap,
                      HandleWasmInstanceObject instanceObj) const;
 
     // Structured clone support:
 
     size_t serializedSize() const;
     uint8_t* serialize(uint8_t* cursor) const;
-    static const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor,
-                                      UniquePtr<Module>* module,
+    static const uint8_t* deserialize(const uint8_t* cursor, UniquePtr<Module>* module,
                                       Metadata* maybeMetadata = nullptr);
 
     // about:memory reporting:
 
     void addSizeOfMisc(MallocSizeOf mallocSizeOf,
                        Metadata::SeenSet* seenMetadata,
                        ShareableBytes::SeenSet* seenBytes,
                        size_t* code, size_t* data) const;
--- a/js/src/asmjs/WasmSerialize.h
+++ b/js/src/asmjs/WasmSerialize.h
@@ -74,25 +74,24 @@ SerializeVector(uint8_t* cursor, const m
     cursor = WriteScalar<uint32_t>(cursor, vec.length());
     for (size_t i = 0; i < vec.length(); i++)
         cursor = vec[i].serialize(cursor);
     return cursor;
 }
 
 template <class T, size_t N>
 static inline const uint8_t*
-DeserializeVector(ExclusiveContext* cx, const uint8_t* cursor,
-                  mozilla::Vector<T, N, SystemAllocPolicy>* vec)
+DeserializeVector(const uint8_t* cursor, mozilla::Vector<T, N, SystemAllocPolicy>* vec)
 {
     uint32_t length;
     cursor = ReadScalar<uint32_t>(cursor, &length);
     if (!vec->resize(length))
         return nullptr;
     for (size_t i = 0; i < vec->length(); i++) {
-        if (!(cursor = (*vec)[i].deserialize(cx, cursor)))
+        if (!(cursor = (*vec)[i].deserialize(cursor)))
             return nullptr;
     }
     return cursor;
 }
 
 template <class T, size_t N>
 static inline size_t
 SizeOfVectorExcludingThis(const mozilla::Vector<T, N, SystemAllocPolicy>& vec,
@@ -118,18 +117,17 @@ SerializePodVector(uint8_t* cursor, cons
 {
     cursor = WriteScalar<uint32_t>(cursor, vec.length());
     cursor = WriteBytes(cursor, vec.begin(), vec.length() * sizeof(T));
     return cursor;
 }
 
 template <class T, size_t N>
 static inline const uint8_t*
-DeserializePodVector(ExclusiveContext* cx, const uint8_t* cursor,
-                     mozilla::Vector<T, N, SystemAllocPolicy>* vec)
+DeserializePodVector(const uint8_t* cursor, mozilla::Vector<T, N, SystemAllocPolicy>* vec)
 {
     uint32_t length;
     cursor = ReadScalar<uint32_t>(cursor, &length);
     if (!vec->initLengthUninitialized(length))
         return nullptr;
     cursor = ReadBytes(cursor, vec->begin(), length * sizeof(T));
     return cursor;
 }
@@ -193,19 +191,19 @@ class MachineId
     }
 
     uint8_t* serialize(uint8_t* cursor) const {
         cursor = WriteScalar<uint32_t>(cursor, cpuId_);
         cursor = SerializePodVector(cursor, buildId_);
         return cursor;
     }
 
-    const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor) {
+    const uint8_t* deserialize(const uint8_t* cursor) {
         (cursor = ReadScalar<uint32_t>(cursor, &cpuId_)) &&
-        (cursor = DeserializePodVector(cx, cursor, &buildId_));
+        (cursor = DeserializePodVector(cursor, &buildId_));
         return cursor;
     }
 
     bool operator==(const MachineId& rhs) const {
         return cpuId_ == rhs.cpuId_ &&
                buildId_.length() == rhs.buildId_.length() &&
                mozilla::PodEqual(buildId_.begin(), rhs.buildId_.begin(), buildId_.length());
     }
--- a/js/src/asmjs/WasmTypes.h
+++ b/js/src/asmjs/WasmTypes.h
@@ -67,29 +67,29 @@ typedef Vector<Type, 0, SystemAllocPolic
 // deserialization. Some data can be simply copied as raw bytes and,
 // as a convention, is stored in an inline CacheablePod struct. Everything else
 // should implement the below methods which are called recusively by the
 // containing Module.
 
 #define WASM_DECLARE_SERIALIZABLE(Type)                                         \
     size_t serializedSize() const;                                              \
     uint8_t* serialize(uint8_t* cursor) const;                                  \
-    const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor);    \
+    const uint8_t* deserialize(const uint8_t* cursor);                          \
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
 
 #define WASM_DECLARE_SERIALIZABLE_VIRTUAL(Type)                                 \
     virtual size_t serializedSize() const;                                      \
     virtual uint8_t* serialize(uint8_t* cursor) const;                          \
-    virtual const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor);\
+    virtual const uint8_t* deserialize(const uint8_t* cursor);                  \
     virtual size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
 
 #define WASM_DECLARE_SERIALIZABLE_OVERRIDE(Type)                                \
     size_t serializedSize() const override;                                     \
     uint8_t* serialize(uint8_t* cursor) const override;                         \
-    const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor) override;\
+    const uint8_t* deserialize(const uint8_t* cursor) override;                 \
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const override;
 
 // ValType/ExprType utilities
 
 // ExprType::Limit is an out-of-band value and has no wasm-semantic meaning. For
 // the purpose of recursive validation, we use this value to represent the type
 // of branch/return instructions that don't actually return to the parent
 // expression and can thus be used in any context.
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -102,17 +102,17 @@ PromiseObject::create(JSContext* cx, Han
         // the current one.
         // All state stored in a Promise's fixed slots must be created in the
         // same compartment, so we get all of that out of the way here.
         // (Except for the resolution functions, which are created below.)
         mozilla::Maybe<AutoCompartment> ac;
         if (wrappedProto)
             ac.emplace(cx, usedProto);
 
-        promise = &NewObjectWithClassProto(cx, &class_, usedProto)->as<PromiseObject>();
+        promise = NewObjectWithClassProto<PromiseObject>(cx, usedProto);
         if (!promise)
             return nullptr;
 
         // Step 4.
         promise->setFixedSlot(PROMISE_STATE_SLOT, Int32Value(PROMISE_STATE_PENDING));
 
         // Step 5.
         RootedArrayObject reactions(cx, NewDenseEmptyArray(cx));
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/tracelogger/bug1282743.js
@@ -0,0 +1,14 @@
+
+du = new Debugger();
+if (typeof du.setupTraceLogger === "function" &&
+    typeof oomTest === 'function')
+{
+    du.setupTraceLogger({Scripts: true});
+    for (var idx = 0; idx < 1; idx++) {
+      oomTest(function() {
+          m = parseModule("x");
+          m.declarationInstantiation();
+          m.evaluation();
+      })
+    }
+}
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -3698,17 +3698,17 @@ CodeGenerator::visitPostWriteElementBarr
 {
     auto ool = new(alloc()) OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index());
     visitPostWriteBarrierCommonV(lir, ool);
 }
 
 void
 CodeGenerator::visitCallNative(LCallNative* call)
 {
-    JSFunction* target = call->getSingleTarget();
+    WrappedFunction* target = call->getSingleTarget();
     MOZ_ASSERT(target);
     MOZ_ASSERT(target->isNative());
 
     int callargslot = call->argslot();
     int unusedStack = StackOffsetOfPassedArg(callargslot);
 
     // Registers used for callWithABI() argument-passing.
     const Register argContextReg   = ToRegister(call->getArgContextReg());
@@ -3727,17 +3727,17 @@ CodeGenerator::visitCallNative(LCallNati
     // Where vp[0] is space for an outparam, vp[1] is |this|, and vp[2] onward
     // are the function arguments.
 
     // Allocate space for the outparam, moving the StackPointer to what will be &vp[1].
     masm.adjustStack(unusedStack);
 
     // Push a Value containing the callee object: natives are allowed to access their callee before
     // setitng the return value. The StackPointer is moved to &vp[0].
-    masm.Push(ObjectValue(*target));
+    masm.Push(ObjectValue(*target->rawJSFunction()));
 
     // Preload arguments into registers.
     masm.loadJSContext(argContextReg);
     masm.move32(Imm32(call->numActualArgs()), argUintNReg);
     masm.moveStackPtrTo(argVpReg);
 
     masm.Push(argUintNReg);
 
@@ -3793,17 +3793,17 @@ LoadDOMPrivate(MacroAssembler& masm, Reg
     masm.loadPrivate(Address(obj, NativeObject::getFixedSlotOffset(0)), priv);
 
     masm.bind(&done);
 }
 
 void
 CodeGenerator::visitCallDOMNative(LCallDOMNative* call)
 {
-    JSFunction* target = call->getSingleTarget();
+    WrappedFunction* target = call->getSingleTarget();
     MOZ_ASSERT(target);
     MOZ_ASSERT(target->isNative());
     MOZ_ASSERT(target->jitInfo());
     MOZ_ASSERT(call->mir()->isCallDOMNative());
 
     int callargslot = call->argslot();
     int unusedStack = StackOffsetOfPassedArg(callargslot);
 
@@ -3828,17 +3828,17 @@ CodeGenerator::visitCallDOMNative(LCallD
     // &vp[1]
     masm.adjustStack(unusedStack);
     // argObj is filled with the extracted object, then returned.
     Register obj = masm.extractObject(Address(masm.getStackPointer(), 0), argObj);
     MOZ_ASSERT(obj == argObj);
 
     // Push a Value containing the callee object: natives are allowed to access their callee before
     // setitng the return value. After this the StackPointer points to &vp[0].
-    masm.Push(ObjectValue(*target));
+    masm.Push(ObjectValue(*target->rawJSFunction()));
 
     // Now compute the argv value.  Since StackPointer is pointing to &vp[0] and
     // argv is &vp[2] we just need to add 2*sizeof(Value) to the current
     // StackPointer.
     JS_STATIC_ASSERT(JSJitMethodCallArgsTraits::offsetOfArgv == 0);
     JS_STATIC_ASSERT(JSJitMethodCallArgsTraits::offsetOfArgc ==
                      IonDOMMethodExitFrameLayoutTraits::offsetOfArgcFromArgv);
     masm.computeEffectiveAddress(Address(masm.getStackPointer(), 2 * sizeof(Value)), argArgs);
@@ -4044,17 +4044,17 @@ CodeGenerator::emitCallInvokeFunctionShu
 }
 
 void
 CodeGenerator::visitCallKnown(LCallKnown* call)
 {
     Register calleereg = ToRegister(call->getFunction());
     Register objreg    = ToRegister(call->getTempObject());
     uint32_t unusedStack = StackOffsetOfPassedArg(call->argslot());
-    JSFunction* target = call->getSingleTarget();
+    WrappedFunction* target = call->getSingleTarget();
     Label end, uncompiled;
 
     // Native single targets are handled by LCallNative.
     MOZ_ASSERT(!target->isNative());
     // Missing arguments must have been explicitly appended by the IonBuilder.
     DebugOnly<unsigned> numNonArgsOnStack = 1 + call->isConstructing();
     MOZ_ASSERT(target->nargs() <= call->mir()->numStackArgs() - numNonArgsOnStack);
 
@@ -9352,29 +9352,29 @@ CodeGenerator::link(JSContext* cx, Compi
 
 #ifdef JS_TRACE_LOGGING
     bool TLFailed = false;
     TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime());
 
     for (uint32_t i = 0; i < patchableTLEvents_.length(); i++) {
         // Create an event on the mainthread.
         TraceLoggerEvent event(logger, patchableTLEvents_[i].event);
-        if (!ionScript->addTraceLoggerEvent(event)) {
+        if (!event.hasPayload() || !ionScript->addTraceLoggerEvent(event)) {
             TLFailed = true;
             break;
         }
         Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, patchableTLEvents_[i].offset),
                 ImmPtr((void*) uintptr_t(event.payload()->textId())),
                 ImmPtr((void*)0));
     }
 
     if (!TLFailed && patchableTLScripts_.length() > 0) {
         MOZ_ASSERT(TraceLogTextIdEnabled(TraceLogger_Scripts));
         TraceLoggerEvent event(logger, TraceLogger_Scripts, script);
-        if (!ionScript->addTraceLoggerEvent(event))
+        if (!event.hasPayload() || !ionScript->addTraceLoggerEvent(event))
             TLFailed = true;
         if (!TLFailed) {
             uint32_t textId = event.payload()->textId();
             for (uint32_t i = 0; i < patchableTLScripts_.length(); i++) {
                 Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, patchableTLScripts_[i]),
                                                    ImmPtr((void*) uintptr_t(textId)),
                                                    ImmPtr((void*)0));
             }
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -1942,21 +1942,20 @@ IsExclusiveFirstArg(MCall* call, MDefini
 
 static bool
 IsRegExpHoistableCall(MCall* call, MDefinition* def)
 {
     if (call->isConstructing())
         return false;
 
     JSAtom* name;
-    JSFunction* fun = call->getSingleTarget();
-    if (fun) {
+    if (WrappedFunction* fun = call->getSingleTarget()) {
         if (!fun->isSelfHostedBuiltin())
             return false;
-        name = GetSelfHostedFunctionName(fun);
+        name = GetSelfHostedFunctionName(fun->rawJSFunction());
     } else {
         MDefinition* funDef = call->getFunction();
         if (funDef->isDebugCheckSelfHosted())
             funDef = funDef->toDebugCheckSelfHosted()->input();
         if (funDef->isTypeBarrier())
             funDef = funDef->toTypeBarrier()->input();
 
         if (!funDef->isCallGetIntrinsicValue())
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -2819,17 +2819,20 @@ IonBuilder::createBreakCatchBlock(Deferr
 
     // No need to use addPredecessor for first edge,
     // because it is already predecessor.
     edge->block->end(MGoto::New(alloc(), successor));
     edge = edge->next;
 
     // Finish up remaining breaks.
     while (edge) {
-        edge->block->end(MGoto::New(alloc(), successor));
+        MGoto* brk = MGoto::New(alloc().fallible(), successor);
+        if (!brk)
+            return nullptr;
+        edge->block->end(brk);
         if (!successor->addPredecessor(alloc(), edge->block))
             return nullptr;
         edge = edge->next;
     }
 
     return successor;
 }
 
@@ -6585,17 +6588,18 @@ IonBuilder::jsop_funapplyarray(uint32_t 
 
     // Unwrap the (JSFunction *) parameter.
     MDefinition* argFunc = current->pop();
 
     // Pop apply function.
     MDefinition* nativeFunc = current->pop();
     nativeFunc->setImplicitlyUsedUnchecked();
 
-    MApplyArray* apply = MApplyArray::New(alloc(), target, argFunc, elements, argThis);
+    WrappedFunction* wrappedTarget = target ? new(alloc()) WrappedFunction(target) : nullptr;
+    MApplyArray* apply = MApplyArray::New(alloc(), wrappedTarget, argFunc, elements, argThis);
     current->add(apply);
     current->push(apply);
     if (!resumeAfter(apply))
         return false;
 
     TemporaryTypeSet* types = bytecodeTypes(pc);
     return pushTypeBarrier(apply, types, BarrierKind::TypeSet);
 }
@@ -6632,17 +6636,18 @@ IonBuilder::jsop_funapplyarguments(uint3
 
         // Pop apply function.
         MDefinition* nativeFunc = current->pop();
         nativeFunc->setImplicitlyUsedUnchecked();
 
         MArgumentsLength* numArgs = MArgumentsLength::New(alloc());
         current->add(numArgs);
 
-        MApplyArgs* apply = MApplyArgs::New(alloc(), target, argFunc, numArgs, argThis);
+        WrappedFunction* wrappedTarget = target ? new(alloc()) WrappedFunction(target) : nullptr;
+        MApplyArgs* apply = MApplyArgs::New(alloc(), wrappedTarget, argFunc, numArgs, argThis);
         current->add(apply);
         current->push(apply);
         if (!resumeAfter(apply))
             return false;
 
         TemporaryTypeSet* types = bytecodeTypes(pc);
         return pushTypeBarrier(apply, types, BarrierKind::TypeSet);
     }
@@ -6935,17 +6940,17 @@ IonBuilder::makeCall(JSFunction* target,
 
     current->push(call);
     if (call->isEffectful() && !resumeAfter(call))
         return false;
 
     TemporaryTypeSet* types = bytecodeTypes(pc);
 
     if (call->isCallDOMNative())
-        return pushDOMTypeBarrier(call, types, call->getSingleTarget());
+        return pushDOMTypeBarrier(call, types, call->getSingleTarget()->rawJSFunction());
 
     return pushTypeBarrier(call, types, BarrierKind::TypeSet);
 }
 
 bool
 IonBuilder::jsop_eval(uint32_t argc)
 {
     int calleeDepth = -((int)argc + 2);
--- a/js/src/jit/IonCode.h
+++ b/js/src/jit/IonCode.h
@@ -432,16 +432,17 @@ struct IonScript
     }
     void clearHasProfilingInstrumentation() {
         hasProfilingInstrumentation_ = false;
     }
     bool hasProfilingInstrumentation() const {
         return hasProfilingInstrumentation_;
     }
     MOZ_MUST_USE bool addTraceLoggerEvent(TraceLoggerEvent& event) {
+        MOZ_ASSERT(event.hasPayload());
         return traceLoggerEvents_.append(Move(event));
     }
     const uint8_t* snapshots() const {
         return reinterpret_cast<const uint8_t*>(this) + snapshots_;
     }
     size_t snapshotsListSize() const {
         return snapshotsListSize_;
     }
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -483,18 +483,17 @@ LIRGenerator::visitCall(MCall* call)
     MOZ_ASSERT(call->getFunction()->type() == MIRType::Object);
 
     // In case of oom, skip the rest of the allocations.
     if (!lowerCallArguments(call)) {
         gen->abort("OOM: LIRGenerator::visitCall");
         return;
     }
 
-    // Height of the current argument vector.
-    JSFunction* target = call->getSingleTarget();
+    WrappedFunction* target = call->getSingleTarget();
 
     LInstruction* lir;
 
     if (call->isCallDOMNative()) {
         // Call DOM functions.
         MOZ_ASSERT(target && target->isNative());
         Register cxReg, objReg, privReg, argsReg;
         GetTempRegForIntArg(0, 0, &cxReg);
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -1851,27 +1851,37 @@ bool
 MParameter::congruentTo(const MDefinition* ins) const
 {
     if (!ins->isParameter())
         return false;
 
     return ins->toParameter()->index() == index_;
 }
 
+WrappedFunction::WrappedFunction(JSFunction* fun)
+  : fun_(fun),
+    nargs_(fun->nargs()),
+    isNative_(fun->isNative()),
+    isConstructor_(fun->isConstructor()),
+    isClassConstructor_(fun->isClassConstructor()),
+    isSelfHostedBuiltin_(fun->isSelfHostedBuiltin())
+{}
+
 MCall*
 MCall::New(TempAllocator& alloc, JSFunction* target, size_t maxArgc, size_t numActualArgs,
            bool construct, bool isDOMCall)
 {
+    WrappedFunction* wrappedTarget = target ? new(alloc) WrappedFunction(target) : nullptr;
     MOZ_ASSERT(maxArgc >= numActualArgs);
     MCall* ins;
     if (isDOMCall) {
         MOZ_ASSERT(!construct);
-        ins = new(alloc) MCallDOMNative(target, numActualArgs);
+        ins = new(alloc) MCallDOMNative(wrappedTarget, numActualArgs);
     } else {
-        ins = new(alloc) MCall(target, numActualArgs, construct);
+        ins = new(alloc) MCall(wrappedTarget, numActualArgs, construct);
     }
     if (!ins->init(alloc, maxArgc + NumNonArgumentOperands))
         return nullptr;
     return ins;
 }
 
 AliasSet
 MCallDOMNative::getAliasSet() const
@@ -2070,16 +2080,23 @@ MTableSwitch::New(TempAllocator& alloc, 
 MGoto*
 MGoto::New(TempAllocator& alloc, MBasicBlock* target)
 {
     MOZ_ASSERT(target);
     return new(alloc) MGoto(target);
 }
 
 MGoto*
+MGoto::New(TempAllocator::Fallible alloc, MBasicBlock* target)
+{
+    MOZ_ASSERT(target);
+    return new(alloc) MGoto(target);
+}
+
+MGoto*
 MGoto::NewAsm(TempAllocator& alloc)
 {
     return new(alloc) MGoto(nullptr);
 }
 
 void
 MUnbox::printOpcode(GenericPrinter& out) const
 {
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -2908,16 +2908,17 @@ class MGoto
 {
     explicit MGoto(MBasicBlock* target) {
         setSuccessor(0, target);
     }
 
   public:
     INSTRUCTION_HEADER(Goto)
     static MGoto* New(TempAllocator& alloc, MBasicBlock* target);
+    static MGoto* New(TempAllocator::Fallible alloc, MBasicBlock* target);
 
     // Factory for asm, which may patch the target later.
     static MGoto* NewAsm(TempAllocator& alloc);
 
     static const size_t TargetIndex = 0;
 
     MBasicBlock* target() {
         return getSuccessor(0);
@@ -3817,39 +3818,68 @@ class MInitElemGetterSetter
 
   public:
     INSTRUCTION_HEADER(InitElemGetterSetter)
     TRIVIAL_NEW_WRAPPERS
     NAMED_OPERANDS((0, object), (1, idValue), (2, value))
 
 };
 
+// WrappedFunction wraps a JSFunction so it can safely be used off-thread.
+// In particular, a function's flags can be modified on the main thread as
+// functions are relazified and delazified, so we must be careful not to access
+// these flags off-thread.
+class WrappedFunction : public TempObject
+{
+    CompilerFunction fun_;
+    uint16_t nargs_;
+    bool isNative_ : 1;
+    bool isConstructor_ : 1;
+    bool isClassConstructor_ : 1;
+    bool isSelfHostedBuiltin_ : 1;
+
+  public:
+    explicit WrappedFunction(JSFunction* fun);
+    size_t nargs() const { return nargs_; }
+    bool isNative() const { return isNative_; }
+    bool isConstructor() const { return isConstructor_; }
+    bool isClassConstructor() const { return isClassConstructor_; }
+    bool isSelfHostedBuiltin() const { return isSelfHostedBuiltin_; }
+
+    // fun->native() and fun->jitInfo() can safely be called off-thread: these
+    // fields never change.
+    JSNative native() const { return fun_->native(); }
+    const JSJitInfo* jitInfo() const { return fun_->jitInfo(); }
+
+    JSFunction* rawJSFunction() const { return fun_; }
+};
+
 class MCall
   : public MVariadicInstruction,
     public CallPolicy::Data
 {
   private:
     // An MCall uses the MPrepareCall, MDefinition for the function, and
     // MPassArg instructions. They are stored in the same list.
     static const size_t FunctionOperandIndex   = 0;
     static const size_t NumNonArgumentOperands = 1;
 
   protected:
     // Monomorphic cache of single target from TI, or nullptr.
-    CompilerFunction target_;
+    WrappedFunction* target_;
 
     // Original value of argc from the bytecode.
     uint32_t numActualArgs_;
 
     // True if the call is for JSOP_NEW.
     bool construct_;
 
     bool needsArgCheck_;
 
-    MCall(JSFunction* target, uint32_t numActualArgs, bool construct)
+    MCall(WrappedFunction* target, uint32_t numActualArgs, bool construct)
       : target_(target),
         numActualArgs_(numActualArgs),
         construct_(construct),
         needsArgCheck_(true)
     {
         setResultType(MIRType::Value);
     }
 
@@ -3888,17 +3918,17 @@ class MCall
     static size_t IndexOfArgument(size_t index) {
         return NumNonArgumentOperands + index + 1; // +1 to skip |this|.
     }
     static size_t IndexOfStackArg(size_t index) {
         return NumNonArgumentOperands + index;
     }
 
     // For TI-informed monomorphic callsites.
-    JSFunction* getSingleTarget() const {
+    WrappedFunction* getSingleTarget() const {
         return target_;
     }
 
     bool isConstructing() const {
         return construct_;
     }
 
     // The number of stack arguments is the max between the number of formal
@@ -3932,17 +3962,17 @@ class MCall
 
 class MCallDOMNative : public MCall
 {
     // A helper class for MCalls for DOM natives.  Note that this is NOT
     // actually a separate MIR op from MCall, because all sorts of places use
     // isCall() to check for calls and all we really want is to overload a few
     // virtual things from MCall.
   protected:
-    MCallDOMNative(JSFunction* target, uint32_t numActualArgs)
+    MCallDOMNative(WrappedFunction* target, uint32_t numActualArgs)
         : MCall(target, numActualArgs, false)
     {
         MOZ_ASSERT(getJitInfo()->type() != JSJitInfo::InlinableNative);
 
         // If our jitinfo is not marked eliminatable, that means that our C++
         // implementation is fallible or that it never wants to be eliminated or
         // that we have no hope of ever doing the sort of argument analysis that
         // would allow us to detemine that we're side-effect-free.  In the
@@ -3991,67 +4021,67 @@ class MArraySplice
 
 // fun.apply(self, arguments)
 class MApplyArgs
   : public MAryInstruction<3>,
     public Mix3Policy<ObjectPolicy<0>, IntPolicy<1>, BoxPolicy<2> >::Data
 {
   protected:
     // Monomorphic cache of single target from TI, or nullptr.
-    CompilerFunction target_;
-
-    MApplyArgs(JSFunction* target, MDefinition* fun, MDefinition* argc, MDefinition* self)
+    WrappedFunction* target_;
+
+    MApplyArgs(WrappedFunction* target, MDefinition* fun, MDefinition* argc, MDefinition* self)
       : target_(target)
     {
         initOperand(0, fun);
         initOperand(1, argc);
         initOperand(2, self);
         setResultType(MIRType::Value);
     }
 
   public:
     INSTRUCTION_HEADER(ApplyArgs)
     TRIVIAL_NEW_WRAPPERS
     NAMED_OPERANDS((0, getFunction), (1, getArgc), (2, getThis))
 
     // For TI-informed monomorphic callsites.
-    JSFunction* getSingleTarget() const {
+    WrappedFunction* getSingleTarget() const {
         return target_;
     }
 
     bool possiblyCalls() const override {
         return true;
     }
 };
 
 // fun.apply(fn, array)
 class MApplyArray
   : public MAryInstruction<3>,
     public Mix3Policy<ObjectPolicy<0>, ObjectPolicy<1>, BoxPolicy<2> >::Data
 {
   protected:
     // Monomorphic cache of single target from TI, or nullptr.
-    CompilerFunction target_;
-
-    MApplyArray(JSFunction* target, MDefinition* fun, MDefinition* elements, MDefinition* self)
+    WrappedFunction* target_;
+
+    MApplyArray(WrappedFunction* target, MDefinition* fun, MDefinition* elements, MDefinition* self)
       : target_(target)
     {
         initOperand(0, fun);
         initOperand(1, elements);
         initOperand(2, self);
         setResultType(MIRType::Value);
     }
 
   public:
     INSTRUCTION_HEADER(ApplyArray)
     TRIVIAL_NEW_WRAPPERS
     NAMED_OPERANDS((0, getFunction), (1, getElements), (2, getThis))
 
     // For TI-informed monomorphic callsites.
-    JSFunction* getSingleTarget() const {
+    WrappedFunction* getSingleTarget() const {
         return target_;
     }
 
     bool possiblyCalls() const override {
         return true;
     }
 };
 
--- a/js/src/jit/mips32/Assembler-mips32.cpp
+++ b/js/src/jit/mips32/Assembler-mips32.cpp
@@ -310,29 +310,65 @@ Assembler::bind(InstImm* inst, uintptr_t
 void
 Assembler::bind(RepatchLabel* label)
 {
     BufferOffset dest = nextOffset();
     if (label->used() && !oom()) {
         // If the label has a use, then change this use to refer to
         // the bound label;
         BufferOffset b(label->offset());
-        InstImm* inst1 = (InstImm*)editSrc(b);
+        InstImm* inst = (InstImm*)editSrc(b);
+        InstImm inst_beq = InstImm(op_beq, zero, zero, BOffImm16(0));
+        uint32_t offset = dest.getOffset() - label->offset();
 
-        // If first instruction is branch, then this is a loop backedge.
-        if (inst1->extractOpcode() == ((uint32_t)op_beq >> OpcodeShift)) {
-            // Backedges are short jumps when bound, but can become long
-            // when patched.
-            uint32_t offset = dest.getOffset() - label->offset();
+        // If first instruction is lui, then this is a long jump.
+        // If second instruction is lui, then this is a loop backedge.
+        if (inst[0].extractOpcode() == (uint32_t(op_lui) >> OpcodeShift)) {
+            // For unconditional long branches generated by ma_liPatchable,
+            // such as under:
+            //     jumpWithpatch
+            Assembler::UpdateLuiOriValue(inst, inst->next(), dest.getOffset());
+        } else if (inst[1].extractOpcode() == (uint32_t(op_lui) >> OpcodeShift) ||
+                   BOffImm16::IsInRange(offset))
+        {
+            // Handle code produced by:
+            //     backedgeJump
+            //     branchWithCode
             MOZ_ASSERT(BOffImm16::IsInRange(offset));
-            inst1->setBOffImm16(BOffImm16(offset));
+            MOZ_ASSERT(inst[0].extractOpcode() == (uint32_t(op_beq) >> OpcodeShift) ||
+                       inst[0].extractOpcode() == (uint32_t(op_bne) >> OpcodeShift) ||
+                       inst[0].extractOpcode() == (uint32_t(op_blez) >> OpcodeShift) ||
+                       inst[0].extractOpcode() == (uint32_t(op_bgtz) >> OpcodeShift));
+            inst[0].setBOffImm16(BOffImm16(offset));
+        } else if (inst[0].encode() == inst_beq.encode()) {
+            // Handle open long unconditional jumps created by
+            // MacroAssemblerMIPSShared::ma_b(..., wasm::JumpTarget, ...).
+            // We need to add it to long jumps array here.
+            // See MacroAssemblerMIPS::branchWithCode().
+            MOZ_ASSERT(inst[1].encode() == NopInst);
+            MOZ_ASSERT(inst[2].encode() == NopInst);
+            MOZ_ASSERT(inst[3].encode() == NopInst);
+            addLongJump(BufferOffset(label->offset()));
+            Assembler::WriteLuiOriInstructions(inst, &inst[1], ScratchRegister, dest.getOffset());
+            inst[2] = InstReg(op_special, ScratchRegister, zero, zero, ff_jr).encode();
         } else {
-            Assembler::UpdateLuiOriValue(inst1, inst1->next(), dest.getOffset());
+            // Handle open long conditional jumps created by
+            // MacroAssemblerMIPSShared::ma_b(..., wasm::JumpTarget, ...).
+            inst[0] = invertBranch(inst[0], BOffImm16(5 * sizeof(void*)));
+            // No need for a "nop" here because we can clobber scratch.
+            // We need to add it to long jumps array here.
+            // See MacroAssemblerMIPS::branchWithCode().
+            MOZ_ASSERT(inst[1].encode() == NopInst);
+            MOZ_ASSERT(inst[2].encode() == NopInst);
+            MOZ_ASSERT(inst[3].encode() == NopInst);
+            MOZ_ASSERT(inst[4].encode() == NopInst);
+            addLongJump(BufferOffset(label->offset() + sizeof(void*)));
+            Assembler::WriteLuiOriInstructions(&inst[1], &inst[2], ScratchRegister, dest.getOffset());
+            inst[3] = InstReg(op_special, ScratchRegister, zero, zero, ff_jr).encode();
         }
-
     }
     label->bind(dest.getOffset());
 }
 
 uint32_t
 Assembler::PatchWrite_NearCallSize()
 {
     return 4 * sizeof(uint32_t);
--- a/js/src/jit/mips64/Assembler-mips64.cpp
+++ b/js/src/jit/mips64/Assembler-mips64.cpp
@@ -305,29 +305,69 @@ Assembler::bind(InstImm* inst, uintptr_t
 void
 Assembler::bind(RepatchLabel* label)
 {
     BufferOffset dest = nextOffset();
     if (label->used() && !oom()) {
         // If the label has a use, then change this use to refer to
         // the bound label;
         BufferOffset b(label->offset());
-        InstImm* inst1 = (InstImm*)editSrc(b);
+        InstImm* inst = (InstImm*)editSrc(b);
+        InstImm inst_beq = InstImm(op_beq, zero, zero, BOffImm16(0));
+        uint64_t offset = dest.getOffset() - label->offset();
 
-        // If first instruction is branch, then this is a loop backedge.
-        if (inst1->extractOpcode() == ((uint32_t)op_beq >> OpcodeShift)) {
-            // Backedges are short jumps when bound, but can become long
-            // when patched.
-            uint64_t offset = dest.getOffset() - label->offset();
+        // If first instruction is lui, then this is a long jump.
+        // If second instruction is lui, then this is a loop backedge.
+        if (inst[0].extractOpcode() == (uint32_t(op_lui) >> OpcodeShift)) {
+            // For unconditional long branches generated by ma_liPatchable,
+            // such as under:
+            //     jumpWithpatch
+            Assembler::UpdateLoad64Value(inst, dest.getOffset());
+        } else if (inst[1].extractOpcode() == (uint32_t(op_lui) >> OpcodeShift) ||
+                   BOffImm16::IsInRange(offset))
+        {
+            // Handle code produced by:
+            //     backedgeJump
+            //     branchWithCode
             MOZ_ASSERT(BOffImm16::IsInRange(offset));
-            inst1->setBOffImm16(BOffImm16(offset));
+            MOZ_ASSERT(inst[0].extractOpcode() == (uint32_t(op_beq) >> OpcodeShift) ||
+                       inst[0].extractOpcode() == (uint32_t(op_bne) >> OpcodeShift) ||
+                       inst[0].extractOpcode() == (uint32_t(op_blez) >> OpcodeShift) ||
+                       inst[0].extractOpcode() == (uint32_t(op_bgtz) >> OpcodeShift));
+            inst[0].setBOffImm16(BOffImm16(offset));
+        } else if (inst[0].encode() == inst_beq.encode()) {
+            // Handle open long unconditional jumps created by
+            // MacroAssemblerMIPSShared::ma_b(..., wasm::JumpTarget, ...).
+            // We need to add it to long jumps array here.
+            // See MacroAssemblerMIPS64::branchWithCode().
+            MOZ_ASSERT(inst[1].encode() == NopInst);
+            MOZ_ASSERT(inst[2].encode() == NopInst);
+            MOZ_ASSERT(inst[3].encode() == NopInst);
+            MOZ_ASSERT(inst[4].encode() == NopInst);
+            MOZ_ASSERT(inst[5].encode() == NopInst);
+            addLongJump(BufferOffset(label->offset()));
+            Assembler::WriteLoad64Instructions(inst, ScratchRegister, dest.getOffset());
+            inst[4] = InstReg(op_special, ScratchRegister, zero, zero, ff_jr).encode();
         } else {
-            Assembler::UpdateLoad64Value(inst1, dest.getOffset());
+            // Handle open long conditional jumps created by
+            // MacroAssemblerMIPSShared::ma_b(..., wasm::JumpTarget, ...).
+            inst[0] = invertBranch(inst[0], BOffImm16(7 * sizeof(uint32_t)));
+            // No need for a "nop" here because we can clobber scratch.
+            // We need to add it to long jumps array here.
+            // See MacroAssemblerMIPS64::branchWithCode().
+            MOZ_ASSERT(inst[1].encode() == NopInst);
+            MOZ_ASSERT(inst[2].encode() == NopInst);
+            MOZ_ASSERT(inst[3].encode() == NopInst);
+            MOZ_ASSERT(inst[4].encode() == NopInst);
+            MOZ_ASSERT(inst[5].encode() == NopInst);
+            MOZ_ASSERT(inst[6].encode() == NopInst);
+            addLongJump(BufferOffset(label->offset() + sizeof(uint32_t)));
+            Assembler::WriteLoad64Instructions(&inst[1], ScratchRegister, dest.getOffset());
+            inst[5] = InstReg(op_special, ScratchRegister, zero, zero, ff_jr).encode();
         }
-
     }
     label->bind(dest.getOffset());
 }
 
 uint32_t
 Assembler::PatchWrite_NearCallSize()
 {
     // Load an address needs 4 instructions, and a jump with a delay slot.
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -1734,16 +1734,18 @@ CodeGeneratorShared::emitTracelogScript(
     Register logger = regs.takeAnyGeneral();
     Register script = regs.takeAnyGeneral();
 
     masm.Push(logger);
 
     CodeOffset patchLogger = masm.movWithPatch(ImmPtr(nullptr), logger);
     masm.propagateOOM(patchableTraceLoggers_.append(patchLogger));
 
+    masm.branchTest32(Assembler::Zero, logger, logger, &done);
+
     Address enabledAddress(logger, TraceLoggerThread::offsetOfEnabled());
     masm.branch32(Assembler::Equal, enabledAddress, Imm32(0), &done);
 
     masm.Push(script);
 
     CodeOffset patchScript = masm.movWithPatch(ImmWord(0), script);
     masm.propagateOOM(patchableTLScripts_.append(patchScript));
 
@@ -1769,16 +1771,18 @@ CodeGeneratorShared::emitTracelogTree(bo
     AllocatableRegisterSet regs(RegisterSet::Volatile());
     Register logger = regs.takeAnyGeneral();
 
     masm.Push(logger);
 
     CodeOffset patchLocation = masm.movWithPatch(ImmPtr(nullptr), logger);
     masm.propagateOOM(patchableTraceLoggers_.append(patchLocation));
 
+    masm.branchTest32(Assembler::Zero, logger, logger, &done);
+
     Address enabledAddress(logger, TraceLoggerThread::offsetOfEnabled());
     masm.branch32(Assembler::Equal, enabledAddress, Imm32(0), &done);
 
     if (isStart)
         masm.tracelogStartId(logger, textId);
     else
         masm.tracelogStopId(logger, textId);
 
@@ -1800,16 +1804,18 @@ CodeGeneratorShared::emitTracelogTree(bo
     Register loggerReg = regs.takeAnyGeneral();
     Register eventReg = regs.takeAnyGeneral();
 
     masm.Push(loggerReg);
 
     CodeOffset patchLocation = masm.movWithPatch(ImmPtr(nullptr), loggerReg);
     masm.propagateOOM(patchableTraceLoggers_.append(patchLocation));
 
+    masm.branchTest32(Assembler::Zero, loggerReg, loggerReg, &done);
+
     Address enabledAddress(loggerReg, TraceLoggerThread::offsetOfEnabled());
     masm.branch32(Assembler::Equal, enabledAddress, Imm32(0), &done);
 
     masm.Push(eventReg);
 
     PatchableTLEvent patchEvent(masm.movWithPatch(ImmWord(0), eventReg), text);
     masm.propagateOOM(patchableTLEvents_.append(Move(patchEvent)));
 
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -1814,17 +1814,17 @@ class LJSCallInstructionHelper : public 
     }
     MCall* mir() const {
         return this->mir_->toCall();
     }
 
     bool hasSingleTarget() const {
         return getSingleTarget() != nullptr;
     }
-    JSFunction* getSingleTarget() const {
+    WrappedFunction* getSingleTarget() const {
         return mir()->getSingleTarget();
     }
 
     // Does not include |this|.
     uint32_t numActualArgs() const {
         return mir()->numActualArgs();
     }
 
@@ -2078,17 +2078,17 @@ class LApplyArgsGeneric : public LCallIn
 
     MApplyArgs* mir() const {
         return mir_->toApplyArgs();
     }
 
     bool hasSingleTarget() const {
         return getSingleTarget() != nullptr;
     }
-    JSFunction* getSingleTarget() const {
+    WrappedFunction* getSingleTarget() const {
         return mir()->getSingleTarget();
     }
 
     const LAllocation* getFunction() {
         return getOperand(0);
     }
     const LAllocation* getArgc() {
         return getOperand(1);
@@ -2121,17 +2121,17 @@ class LApplyArrayGeneric : public LCallI
 
     MApplyArray* mir() const {
         return mir_->toApplyArray();
     }
 
     bool hasSingleTarget() const {
         return getSingleTarget() != nullptr;
     }
-    JSFunction* getSingleTarget() const {
+    WrappedFunction* getSingleTarget() const {
         return mir()->getSingleTarget();
     }
 
     const LAllocation* getFunction() {
         return getOperand(0);
     }
     const LAllocation* getElements() {
         return getOperand(1);
--- a/js/src/vm/TraceLogging.cpp
+++ b/js/src/vm/TraceLogging.cpp
@@ -790,48 +790,48 @@ TraceLoggerThreadState::init()
 void
 TraceLoggerThreadState::enableTextId(JSContext* cx, uint32_t textId)
 {
     MOZ_ASSERT(TLTextIdIsToggable(textId));
 
     if (enabledTextIds[textId])
         return;
 
+    ReleaseAllJITCode(cx->runtime()->defaultFreeOp());
+
     enabledTextIds[textId] = true;
     if (textId == TraceLogger_Engine) {
         enabledTextIds[TraceLogger_IonMonkey] = true;
         enabledTextIds[TraceLogger_Baseline] = true;
         enabledTextIds[TraceLogger_Interpreter] = true;
     }
 
-    ReleaseAllJITCode(cx->runtime()->defaultFreeOp());
-
     if (textId == TraceLogger_Scripts)
         jit::ToggleBaselineTraceLoggerScripts(cx->runtime(), true);
     if (textId == TraceLogger_Engine)
         jit::ToggleBaselineTraceLoggerEngine(cx->runtime(), true);
 
 }
 void
 TraceLoggerThreadState::disableTextId(JSContext* cx, uint32_t textId)
 {
     MOZ_ASSERT(TLTextIdIsToggable(textId));
 
     if (!enabledTextIds[textId])
         return;
 
+    ReleaseAllJITCode(cx->runtime()->defaultFreeOp());
+
     enabledTextIds[textId] = false;
     if (textId == TraceLogger_Engine) {
         enabledTextIds[TraceLogger_IonMonkey] = false;
         enabledTextIds[TraceLogger_Baseline] = false;
         enabledTextIds[TraceLogger_Interpreter] = false;
     }
 
-    ReleaseAllJITCode(cx->runtime()->defaultFreeOp());
-
     if (textId == TraceLogger_Scripts)
         jit::ToggleBaselineTraceLoggerScripts(cx->runtime(), false);
     if (textId == TraceLogger_Engine)
         jit::ToggleBaselineTraceLoggerEngine(cx->runtime(), false);
 }
 
 
 TraceLoggerThread*
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -9818,19 +9818,16 @@ PresShell::ProcessReflowCommands(bool aI
     mShouldUnsuppressPainting = false;
     UnsuppressAndInvalidate();
   }
 
   if (mDocument->GetRootElement()) {
     TimeDuration elapsed = TimeStamp::Now() - timerStart;
     int32_t intElapsed = int32_t(elapsed.ToMilliseconds());
 
-    if (!mDocument->GetRootElement()->IsXULElement() && mIsActive) {
-      Telemetry::Accumulate(Telemetry::HTML_FOREGROUND_REFLOW_MS_2, intElapsed);
-    }
     if (intElapsed > NS_LONG_REFLOW_TIME_MS) {
       Telemetry::Accumulate(Telemetry::LONG_REFLOW_INTERRUPTIBLE,
                             aInterruptible ? 1 : 0);
     }
   }
 
   return !interrupted;
 }
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -1413,17 +1413,20 @@ HttpBaseChannel::SetReferrerWithPolicy(n
     }
   }
 
   // for cross-origin-based referrer changes (not just host-based), figure out
   // if the referrer is being sent cross-origin.
   nsCOMPtr<nsIURI> triggeringURI;
   bool isCrossOrigin = true;
   if (mLoadInfo) {
-    mLoadInfo->TriggeringPrincipal()->GetURI(getter_AddRefs(triggeringURI));
+    nsCOMPtr<nsIPrincipal> triggeringPrincipal = mLoadInfo->TriggeringPrincipal();
+    if (triggeringPrincipal) {
+      triggeringPrincipal->GetURI(getter_AddRefs(triggeringURI));
+    }
   }
   if (triggeringURI) {
     if (LOG_ENABLED()) {
       nsAutoCString triggeringURISpec;
       rv = triggeringURI->GetAsciiSpec(triggeringURISpec);
       if (!NS_FAILED(rv)) {
         LOG(("triggeringURI=%s\n", triggeringURISpec.get()));
       }
--- a/security/apps/AppTrustDomain.cpp
+++ b/security/apps/AppTrustDomain.cpp
@@ -373,9 +373,15 @@ AppTrustDomain::CheckValidityIsAcceptabl
 Result
 AppTrustDomain::NetscapeStepUpMatchesServerAuth(Time /*notBefore*/,
                                                 /*out*/ bool& matches)
 {
   matches = false;
   return Success;
 }
 
+void
+AppTrustDomain::NoteAuxiliaryExtension(AuxiliaryExtension /*extension*/,
+                                       Input /*extensionData*/)
+{
+}
+
 } } // namespace mozilla::psm
--- a/security/apps/AppTrustDomain.h
+++ b/security/apps/AppTrustDomain.h
@@ -59,16 +59,19 @@ public:
                    mozilla::pkix::Input subjectPublicKeyInfo) override;
   virtual Result CheckValidityIsAcceptable(
                    mozilla::pkix::Time notBefore, mozilla::pkix::Time notAfter,
                    mozilla::pkix::EndEntityOrCA endEntityOrCA,
                    mozilla::pkix::KeyPurposeId keyPurpose) override;
   virtual Result NetscapeStepUpMatchesServerAuth(
                    mozilla::pkix::Time notBefore,
                    /*out*/ bool& matches) override;
+  virtual void NoteAuxiliaryExtension(
+                   mozilla::pkix::AuxiliaryExtension extension,
+                   mozilla::pkix::Input extensionData) override;
   virtual Result DigestBuf(mozilla::pkix::Input item,
                            mozilla::pkix::DigestAlgorithm digestAlg,
                            /*out*/ uint8_t* digestBuf,
                            size_t digestBufLen) override;
 
 private:
   /*out*/ UniqueCERTCertList& mCertChain;
   void* mPinArg; // non-owning!
--- a/security/certverifier/NSSCertDBTrustDomain.cpp
+++ b/security/certverifier/NSSCertDBTrustDomain.cpp
@@ -953,16 +953,22 @@ NSSCertDBTrustDomain::NetscapeStepUpMatc
       matches = false;
       return Success;
     default:
       MOZ_ASSERT_UNREACHABLE("unhandled NetscapeStepUpPolicy type");
   }
   return Result::FATAL_ERROR_LIBRARY_FAILURE;
 }
 
+void
+NSSCertDBTrustDomain::NoteAuxiliaryExtension(AuxiliaryExtension /*extension*/,
+                                             Input /*extensionData*/)
+{
+}
+
 SECStatus
 InitializeNSS(const char* dir, bool readOnly, bool loadPKCS11Modules)
 {
   // The NSS_INIT_NOROOTINIT flag turns off the loading of the root certs
   // module by NSS_Initialize because we will load it in InstallLoadableRoots
   // later.  It also allows us to work around a bug in the system NSS in
   // Ubuntu 8.04, which loads any nonexistent "<configdir>/libnssckbi.so" as
   // "/usr/lib/nss/libnssckbi.so".
--- a/security/certverifier/NSSCertDBTrustDomain.h
+++ b/security/certverifier/NSSCertDBTrustDomain.h
@@ -136,16 +136,20 @@ public:
                    mozilla::pkix::Duration validityDuration,
       /*optional*/ const mozilla::pkix::Input* stapledOCSPResponse,
       /*optional*/ const mozilla::pkix::Input* aiaExtension)
                    override;
 
   virtual Result IsChainValid(const mozilla::pkix::DERArray& certChain,
                               mozilla::pkix::Time time) override;
 
+  virtual void NoteAuxiliaryExtension(
+                   mozilla::pkix::AuxiliaryExtension extension,
+                   mozilla::pkix::Input extensionData) override;
+
   CertVerifier::OCSPStaplingStatus GetOCSPStaplingStatus() const
   {
     return mOCSPStaplingStatus;
   }
   void ResetOCSPStaplingStatus()
   {
     mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_NEVER_CHECKED;
   }
--- a/security/certverifier/OCSPVerificationTrustDomain.cpp
+++ b/security/certverifier/OCSPVerificationTrustDomain.cpp
@@ -103,15 +103,22 @@ OCSPVerificationTrustDomain::CheckValidi
 
 Result
 OCSPVerificationTrustDomain::NetscapeStepUpMatchesServerAuth(Time notBefore,
                                                      /*out*/ bool& matches)
 {
   return mCertDBTrustDomain.NetscapeStepUpMatchesServerAuth(notBefore, matches);
 }
 
+void
+OCSPVerificationTrustDomain::NoteAuxiliaryExtension(
+  AuxiliaryExtension extension, Input extensionData)
+{
+  mCertDBTrustDomain.NoteAuxiliaryExtension(extension, extensionData);
+}
+
 Result
 OCSPVerificationTrustDomain::DigestBuf(
   Input item, DigestAlgorithm digestAlg,
   /*out*/ uint8_t* digestBuf, size_t digestBufLen)
 {
   return mCertDBTrustDomain.DigestBuf(item, digestAlg, digestBuf, digestBufLen);
 }
--- a/security/certverifier/OCSPVerificationTrustDomain.h
+++ b/security/certverifier/OCSPVerificationTrustDomain.h
@@ -68,16 +68,20 @@ public:
                    mozilla::pkix::Duration validityDuration,
       /*optional*/ const mozilla::pkix::Input* stapledOCSPResponse,
       /*optional*/ const mozilla::pkix::Input* aiaExtension)
                    override;
 
   virtual Result IsChainValid(const mozilla::pkix::DERArray& certChain,
                               mozilla::pkix::Time time) override;
 
+  virtual void NoteAuxiliaryExtension(
+                   mozilla::pkix::AuxiliaryExtension extension,
+                   mozilla::pkix::Input extensionData) override;
+
 private:
   NSSCertDBTrustDomain& mCertDBTrustDomain;
 };
 
 
 } } // namespace mozilla::psm
 
 #endif // mozilla_psm__OCSPVerificationTrustDomain_h
--- a/security/manager/ssl/CSTrustDomain.cpp
+++ b/security/manager/ssl/CSTrustDomain.cpp
@@ -210,16 +210,22 @@ CSTrustDomain::CheckValidityIsAcceptable
 Result
 CSTrustDomain::NetscapeStepUpMatchesServerAuth(Time notBefore,
                                                /*out*/ bool& matches)
 {
   matches = false;
   return Success;
 }
 
+void
+CSTrustDomain::NoteAuxiliaryExtension(AuxiliaryExtension /*extension*/,
+                                      Input /*extensionData*/)
+{
+}
+
 Result
 CSTrustDomain::DigestBuf(Input item, DigestAlgorithm digestAlg,
                          /*out*/ uint8_t* digestBuf, size_t digestBufLen)
 {
   return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen);
 }
 
 } } // end namespace mozilla::psm
--- a/security/manager/ssl/CSTrustDomain.h
+++ b/security/manager/ssl/CSTrustDomain.h
@@ -57,16 +57,19 @@ public:
     const mozilla::pkix::SignedDigest& signedDigest,
     mozilla::pkix::Input subjectPublicKeyInfo) override;
   virtual Result CheckValidityIsAcceptable(
     mozilla::pkix::Time notBefore, mozilla::pkix::Time notAfter,
     mozilla::pkix::EndEntityOrCA endEntityOrCA,
     mozilla::pkix::KeyPurposeId keyPurpose) override;
   virtual Result NetscapeStepUpMatchesServerAuth(
     mozilla::pkix::Time notBefore, /*out*/ bool& matches) override;
+  virtual void NoteAuxiliaryExtension(
+    mozilla::pkix::AuxiliaryExtension extension,
+    mozilla::pkix::Input extensionData) override;
   virtual Result DigestBuf(mozilla::pkix::Input item,
                            mozilla::pkix::DigestAlgorithm digestAlg,
                            /*out*/ uint8_t* digestBuf,
                            size_t digestBufLen) override;
 
 private:
   /*out*/ UniqueCERTCertList& mCertChain;
   nsCOMPtr<nsICertBlocklist> mCertBlocklist;
--- a/security/manager/ssl/tests/unit/tlsserver/lib/OCSPCommon.cpp
+++ b/security/manager/ssl/tests/unit/tlsserver/lib/OCSPCommon.cpp
@@ -179,17 +179,17 @@ GetOCSPResponseForType(OCSPResponseType 
       0x1a, 0x85, 0x1a, 0x01, 0x83, 0x74, 0x09, 0x02
     };
 
     extension.id.assign(tlv_some_Mozilla_OID, sizeof(tlv_some_Mozilla_OID));
     extension.critical = (aORT == ORTCriticalExtension);
     extension.value.push_back(0x05); // tag: NULL
     extension.value.push_back(0x00); // length: 0
     extension.next = nullptr;
-    context.extensions = &extension;
+    context.responseExtensions = &extension;
   }
   if (aORT == ORTEmptyExtensions) {
     context.includeEmptyExtensions = true;
   }
 
   if (!signerCert) {
     signerCert.reset(CERT_DupCertificate(issuerCert.get()));
   }
--- a/security/pkix/include/pkix/pkixtypes.h
+++ b/security/pkix/include/pkix/pkixtypes.h
@@ -100,16 +100,31 @@ struct CertPolicyId final
 enum class TrustLevel
 {
   TrustAnchor = 1,        // certificate is a trusted root CA certificate or
                           // equivalent *for the given policy*.
   ActivelyDistrusted = 2, // certificate is known to be bad
   InheritsTrust = 3       // certificate must chain to a trust anchor
 };
 
+// Extensions extracted during the verification flow.
+// See TrustDomain::NoteAuxiliaryExtension.
+enum class AuxiliaryExtension
+{
+  // Certificate Transparency data, specifically Signed Certificate
+  // Timestamps (SCTs). See RFC 6962.
+
+  // SCT list embedded in the end entity certificate. Called by BuildCertChain
+  // after the certificate containing the SCTs has passed the revocation checks.
+  EmbeddedSCTList = 1,
+  // SCT list from OCSP response. Called by VerifyEncodedOCSPResponse
+  // when its result is a success and the SCT list is present.
+  SCTListFromOCSPResponse = 2
+};
+
 // CertID references the information needed to do revocation checking for the
 // certificate issued by the given issuer with the given serial number.
 //
 // issuer must be the DER-encoded issuer field from the certificate for which
 // revocation checking is being done, **NOT** the subject field of the issuer
 // certificate. (Those two fields must be equal to each other, but they may not
 // be encoded exactly the same, and the encoding matters for OCSP.)
 // issuerSubjectPublicKeyInfo is the entire DER-encoded subjectPublicKeyInfo
@@ -332,16 +347,23 @@ public:
   // contains the id-Netscape-stepUp OID but does not contain the
   // id-kp-serverAuth OID may be considered valid for issuing server auth
   // certificates. This function allows TrustDomain implementations to control
   // this setting based on the start of the validity period of the certificate
   // in question.
   virtual Result NetscapeStepUpMatchesServerAuth(Time notBefore,
                                                  /*out*/ bool& matches) = 0;
 
+  // Some certificate or OCSP response extensions do not directly participate
+  // in the verification flow, but might still be of interest to the clients
+  // (notably Certificate Transparency data, RFC 6962). Such extensions are
+  // extracted and passed to this function for further processing.
+  virtual void NoteAuxiliaryExtension(AuxiliaryExtension extension,
+                                      Input extensionData) = 0;
+
   // Compute a digest of the data in item using the given digest algorithm.
   //
   // item contains the data to hash.
   // digestBuf points to a buffer to where the digest will be written.
   // digestBufLen will be the size of the digest output (20 for SHA-1,
   // 32 for SHA-256, etc.).
   //
   // TODO: Taking the output buffer as (uint8_t*, size_t) is counter to our
--- a/security/pkix/lib/pkixbuild.cpp
+++ b/security/pkix/lib/pkixbuild.cpp
@@ -239,16 +239,30 @@ PathBuildingStep::Check(Input potentialI
     }
     Duration validityDuration(notAfter, notBefore);
     rv = trustDomain.CheckRevocation(subject.endEntityOrCA, certID, time,
                                      validityDuration, stapledOCSPResponse,
                                      subject.GetAuthorityInfoAccess());
     if (rv != Success) {
       return RecordResult(rv, keepGoing);
     }
+
+    if (subject.endEntityOrCA == EndEntityOrCA::MustBeEndEntity) {
+      const Input* sctExtension = subject.GetSignedCertificateTimestamps();
+      if (sctExtension) {
+        Input sctList;
+        rv = ExtractSignedCertificateTimestampListFromExtension(*sctExtension,
+                                                                sctList);
+        if (rv != Success) {
+          return RecordResult(rv, keepGoing);
+        }
+        trustDomain.NoteAuxiliaryExtension(AuxiliaryExtension::EmbeddedSCTList,
+                                           sctList);
+      }
+    }
   }
 
   return RecordResult(Success, keepGoing);
 }
 
 // Recursively build the path from the given subject certificate to the root.
 //
 // Be very careful about changing the order of checks. The order is significant
--- a/security/pkix/lib/pkixcert.cpp
+++ b/security/pkix/lib/pkixcert.cpp
@@ -218,16 +218,21 @@ BackCert::RememberExtension(Reader& extn
   // python DottedOIDToCode.py Netscape-certificate-type 2.16.840.1.113730.1.1
   static const uint8_t Netscape_certificate_type[] = {
     0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x01
   };
   // python DottedOIDToCode.py id-pe-tlsfeature 1.3.6.1.5.5.7.1.24
   static const uint8_t id_pe_tlsfeature[] = {
     0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x18
   };
+  // python DottedOIDToCode.py id-embeddedSctList 1.3.6.1.4.1.11129.2.4.2
+  // See Section 3.3 of RFC 6962.
+  static const uint8_t id_embeddedSctList[] = {
+    0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x04, 0x02
+  };
 
   Input* out = nullptr;
 
   // We already enforce the maximum possible constraints for policies so we
   // can safely ignore even critical policy constraint extensions.
   //
   // XXX: Doing it this way won't allow us to detect duplicate
   // policyConstraints extensions, but that's OK because (and only because) we
@@ -264,16 +269,18 @@ BackCert::RememberExtension(Reader& extn
   } else if (extnID.MatchRest(id_ce_extKeyUsage)) {
     out = &extKeyUsage;
   } else if (extnID.MatchRest(id_ce_inhibitAnyPolicy)) {
     out = &inhibitAnyPolicy;
   } else if (extnID.MatchRest(id_pe_authorityInfoAccess)) {
     out = &authorityInfoAccess;
   } else if (extnID.MatchRest(id_pe_tlsfeature)) {
     out = &requiredTLSFeatures;
+  } else if (extnID.MatchRest(id_embeddedSctList)) {
+    out = &signedCertificateTimestamps;
   } else if (extnID.MatchRest(id_pkix_ocsp_nocheck) && critical) {
     // We need to make sure we don't reject delegated OCSP response signing
     // certificates that contain the id-pkix-ocsp-nocheck extension marked as
     // critical when validating OCSP responses. Without this, an application
     // that implements soft-fail OCSP might ignore a valid Revoked or Unknown
     // response, and an application that implements hard-fail OCSP might fail
     // to connect to a server given a valid Good response.
     out = &dummyOCSPNocheck;
@@ -295,9 +302,22 @@ BackCert::RememberExtension(Reader& extn
       return Result::ERROR_EXTENSION_VALUE_INVALID;
     }
     understood = true;
   }
 
   return Success;
 }
 
+Result
+ExtractSignedCertificateTimestampListFromExtension(Input extnValue,
+                                                   Input& sctList)
+{
+  Reader decodedValue;
+  Result rv = der::ExpectTagAndGetValueAtEnd(extnValue, der::OCTET_STRING,
+                                             decodedValue);
+  if (rv != Success) {
+    return rv;
+  }
+  return decodedValue.SkipToEnd(sctList);
+}
+
 } } // namespace mozilla::pkix
--- a/security/pkix/lib/pkixocsp.cpp
+++ b/security/pkix/lib/pkixocsp.cpp
@@ -71,16 +71,18 @@ public:
   const CertID& certID;
   const Time time;
   const uint16_t maxLifetimeInDays;
   CertStatus certStatus;
   Time* thisUpdate;
   Time* validThrough;
   bool expired;
 
+  Input signedCertificateTimestamps;
+
   // Keep track of whether the OCSP response contains the status of the
   // certificate we're interested in. Responders might reply without
   // including the status of any of the requested certs, we should
   // indicate a server failure in those cases.
   bool matchFound;
 
   Context(const Context&) = delete;
   void operator=(const Context&) = delete;
@@ -163,16 +165,19 @@ static inline Result BasicResponse(Reade
 static inline Result ResponseData(
                        Reader& tbsResponseData,
                        Context& context,
                        const der::SignedDataWithSignature& signedResponseData,
                        const DERArray& certs);
 static inline Result SingleResponse(Reader& input, Context& context);
 static Result ExtensionNotUnderstood(Reader& extnID, Input extnValue,
                                      bool critical, /*out*/ bool& understood);
+static Result RememberSingleExtension(Context& context, Reader& extnID,
+                                      Input extnValue, bool critical,
+                                      /*out*/ bool& understood);
 static inline Result CertID(Reader& input,
                             const Context& context,
                             /*out*/ bool& match);
 static Result MatchKeyHash(TrustDomain& trustDomain,
                            Input issuerKeyHash,
                            Input issuerSubjectPublicKeyInfo,
                            /*out*/ bool& match);
 static Result KeyHash(TrustDomain& trustDomain,
@@ -325,16 +330,26 @@ VerifyEncodedOCSPResponse(TrustDomain& t
 
   expired = context.expired;
 
   switch (context.certStatus) {
     case CertStatus::Good:
       if (expired) {
         return Result::ERROR_OCSP_OLD_RESPONSE;
       }
+      if (context.signedCertificateTimestamps.GetLength()) {
+        Input sctList;
+        rv = ExtractSignedCertificateTimestampListFromExtension(
+          context.signedCertificateTimestamps, sctList);
+        if (rv != Success) {
+          return MapBadDERToMalformedOCSPResponse(rv);
+        }
+        context.trustDomain.NoteAuxiliaryExtension(
+          AuxiliaryExtension::SCTListFromOCSPResponse, sctList);
+      }
       return Success;
     case CertStatus::Revoked:
       return Result::ERROR_REVOKED_CERTIFICATE;
     case CertStatus::Unknown:
       return Result::ERROR_OCSP_UNKNOWN_CERT;
      MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
   }
 }
@@ -646,19 +661,25 @@ SingleResponse(Reader& input, Context& c
     // This could only happen if we're dealing with times beyond the year
     // 10,000AD.
     return Result::ERROR_OCSP_FUTURE_RESPONSE;
   }
   if (context.time > notAfterPlusSlop) {
     context.expired = true;
   }
 
-  rv = der::OptionalExtensions(input,
-                               der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1,
-                               ExtensionNotUnderstood);
+  rv = der::OptionalExtensions(
+    input,
+    der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1,
+    [&context](Reader& extnID, const Input& extnValue, bool critical,
+               /*out*/ bool& understood) {
+      return RememberSingleExtension(context, extnID, extnValue, critical,
+                                     understood);
+    });
+
   if (rv != Success) {
     return rv;
   }
 
   if (context.thisUpdate) {
     *context.thisUpdate = thisUpdate;
   }
   if (context.validThrough) {
@@ -821,16 +842,46 @@ KeyHash(TrustDomain& trustDomain, const 
 Result
 ExtensionNotUnderstood(Reader& /*extnID*/, Input /*extnValue*/,
                        bool /*critical*/, /*out*/ bool& understood)
 {
   understood = false;
   return Success;
 }
 
+Result
+RememberSingleExtension(Context& context, Reader& extnID, Input extnValue,
+                        bool /*critical*/, /*out*/ bool& understood)
+{
+  understood = false;
+
+  // SingleExtension for Signed Certificate Timestamp List.
+  // See Section 3.3 of RFC 6962.
+  // python DottedOIDToCode.py
+  //   id_ocsp_singleExtensionSctList 1.3.6.1.4.1.11129.2.4.5
+  static const uint8_t id_ocsp_singleExtensionSctList[] = {
+    0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x04, 0x05
+  };
+
+  if (extnID.MatchRest(id_ocsp_singleExtensionSctList)) {
+    // Empty values are not allowed for this extension. Note that
+    // we assume this later, when checking if the extension was present.
+    if (extnValue.GetLength() == 0) {
+      return Result::ERROR_EXTENSION_VALUE_INVALID;
+    }
+    if (context.signedCertificateTimestamps.Init(extnValue) != Success) {
+      // Duplicate extension.
+      return Result::ERROR_EXTENSION_VALUE_INVALID;
+    }
+    understood = true;
+  }
+
+  return Success;
+}
+
 //   1. The certificate identified in a received response corresponds to
 //      the certificate that was identified in the corresponding request;
 //   2. The signature on the response is valid;
 //   3. The identity of the signer matches the intended recipient of the
 //      request;
 //   4. The signer is currently authorized to provide a response for the
 //      certificate in question;
 //   5. The time at which the status being indicated is known to be
--- a/security/pkix/lib/pkixutil.h
+++ b/security/pkix/lib/pkixutil.h
@@ -101,16 +101,20 @@ public:
   const Input* GetSubjectAltName() const
   {
     return MaybeInput(subjectAltName);
   }
   const Input* GetRequiredTLSFeatures() const
   {
     return MaybeInput(requiredTLSFeatures);
   }
+  const Input* GetSignedCertificateTimestamps() const
+  {
+    return MaybeInput(signedCertificateTimestamps);
+  }
 
 private:
   const Input der;
 
 public:
   const EndEntityOrCA endEntityOrCA;
   BackCert const* const childCert;
 
@@ -144,16 +148,17 @@ private:
   Input certificatePolicies;
   Input extKeyUsage;
   Input inhibitAnyPolicy;
   Input keyUsage;
   Input nameConstraints;
   Input subjectAltName;
   Input criticalNetscapeCertificateType;
   Input requiredTLSFeatures;
+  Input signedCertificateTimestamps; // RFC 6962 (Certificate Transparency)
 
   Result RememberExtension(Reader& extnID, Input extnValue, bool critical,
                            /*out*/ bool& understood);
 
   BackCert(const BackCert&) = delete;
   void operator=(const BackCert&) = delete;
 };
 
@@ -192,16 +197,22 @@ public:
 private:
   Input items[MAX_LENGTH]; // avoids any heap allocations
   size_t numItems;
 
   NonOwningDERArray(const NonOwningDERArray&) = delete;
   void operator=(const NonOwningDERArray&) = delete;
 };
 
+// Extracts the SignedCertificateTimestampList structure which is encoded as an
+// OCTET STRING within the X.509v3 / OCSP extensions (see RFC 6962 section 3.3).
+Result
+ExtractSignedCertificateTimestampListFromExtension(Input extnValue,
+                                                   Input& sctList);
+
 inline unsigned int
 DaysBeforeYear(unsigned int year)
 {
   assert(year <= 9999);
   return ((year - 1u) * 365u)
        + ((year - 1u) / 4u)    // leap years are every 4 years,
        - ((year - 1u) / 100u)  // except years divisible by 100,
        + ((year - 1u) / 400u); // except years divisible by 400.
--- a/security/pkix/test/gtest/pkixbuild_tests.cpp
+++ b/security/pkix/test/gtest/pkixbuild_tests.cpp
@@ -26,53 +26,61 @@
 // When building with -D_HAS_EXCEPTIONS=0, MSVC's <xtree> header triggers
 // warning C4702: unreachable code.
 // https://connect.microsoft.com/VisualStudio/feedback/details/809962
 #pragma warning(push)
 #pragma warning(disable: 4702)
 #endif
 
 #include <map>
+#include <vector>
 
 #if defined(_MSC_VER) && _MSC_VER < 1900
 #pragma warning(pop)
 #endif
 
+#include "pkixder.h"
 #include "pkixgtest.h"
 
 using namespace mozilla::pkix;
 using namespace mozilla::pkix::test;
 
 static ByteString
 CreateCert(const char* issuerCN, // null means "empty name"
            const char* subjectCN, // null means "empty name"
            EndEntityOrCA endEntityOrCA,
            /*optional modified*/ std::map<ByteString, ByteString>*
-             subjectDERToCertDER = nullptr)
+             subjectDERToCertDER = nullptr,
+           /*optional*/ const ByteString* extension = nullptr)
 {
   static long serialNumberValue = 0;
   ++serialNumberValue;
   ByteString serialNumber(CreateEncodedSerialNumber(serialNumberValue));
   EXPECT_FALSE(ENCODING_FAILED(serialNumber));
 
   ByteString issuerDER(issuerCN ? CNToDERName(issuerCN) : Name(ByteString()));
   ByteString subjectDER(subjectCN ? CNToDERName(subjectCN) : Name(ByteString()));
 
-  ByteString extensions[2];
+  std::vector<ByteString> extensions;
   if (endEntityOrCA == EndEntityOrCA::MustBeCA) {
-    extensions[0] =
+    ByteString basicConstraints =
       CreateEncodedBasicConstraints(true, nullptr, Critical::Yes);
-    EXPECT_FALSE(ENCODING_FAILED(extensions[0]));
+    EXPECT_FALSE(ENCODING_FAILED(basicConstraints));
+    extensions.push_back(basicConstraints);
   }
+  if (extension) {
+    extensions.push_back(*extension);
+  }
+  extensions.push_back(ByteString()); // marks the end of the list
 
   ScopedTestKeyPair reusedKey(CloneReusedKeyPair());
   ByteString certDER(CreateEncodedCertificate(
                        v3, sha256WithRSAEncryption(), serialNumber, issuerDER,
                        oneDayBeforeNow, oneDayAfterNow, subjectDER,
-                       *reusedKey, extensions, *reusedKey,
+                       *reusedKey, extensions.data(), *reusedKey,
                        sha256WithRSAEncryption()));
   EXPECT_FALSE(ENCODING_FAILED(certDER));
 
   if (subjectDERToCertDER) {
     (*subjectDERToCertDER)[subjectDER] = certDER;
   }
 
   return certDER;
@@ -234,25 +242,25 @@ TEST_F(pkixbuild, BeyondMaxAcceptableCer
                              EndEntityOrCA::MustBeEndEntity,
                              KeyUsage::noParticularKeyUsageRequired,
                              KeyPurposeId::id_kp_serverAuth,
                              CertPolicyId::anyPolicy,
                              nullptr/*stapledOCSPResponse*/));
   }
 }
 
-// A TrustDomain that explicitly fails if CheckRevocation is called.
+// A TrustDomain that checks certificates against a given root certificate.
 // It is initialized with the DER encoding of a root certificate that
 // is treated as a trust anchor and is assumed to have issued all certificates
 // (i.e. FindIssuer always attempts to build the next step in the chain with
 // it).
-class ExpiredCertTrustDomain final : public DefaultCryptoTrustDomain
+class SingleRootTrustDomain : public DefaultCryptoTrustDomain
 {
 public:
-  explicit ExpiredCertTrustDomain(ByteString rootDER)
+  explicit SingleRootTrustDomain(ByteString rootDER)
     : rootDER(rootDER)
   {
   }
 
   // The CertPolicyId argument is unused because we don't care about EV.
   Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input candidateCert,
                       /*out*/ TrustLevel& trustLevel) override
   {
@@ -283,20 +291,46 @@ public:
     return checker.Check(rootCert, nullptr, keepGoing);
   }
 
   Result IsChainValid(const DERArray&, Time) override
   {
     return Success;
   }
 
+  Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
+                         /*optional*/ const Input*, /*optional*/ const Input*)
+                         override
+  {
+    return Success;
+  }
+
 private:
   ByteString rootDER;
 };
 
+// A TrustDomain that explicitly fails if CheckRevocation is called.
+class ExpiredCertTrustDomain final : public SingleRootTrustDomain
+{
+public:
+  explicit ExpiredCertTrustDomain(ByteString rootDER)
+    : SingleRootTrustDomain(rootDER)
+  {
+  }
+
+  Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
+                         /*optional*/ const Input*, /*optional*/ const Input*)
+                         override
+  {
+    ADD_FAILURE();
+    return NotReached("CheckRevocation should not be called",
+                      Result::FATAL_ERROR_LIBRARY_FAILURE);
+  }
+};
+
 TEST_F(pkixbuild, NoRevocationCheckingForExpiredCert)
 {
   const char* rootCN = "Root CA";
   ByteString rootDER(CreateCert(rootCN, rootCN, EndEntityOrCA::MustBeCA,
                                 nullptr));
   EXPECT_FALSE(ENCODING_FAILED(rootDER));
   ExpiredCertTrustDomain expiredCertTrustDomain(rootDER);
 
@@ -469,8 +503,76 @@ TEST_P(pkixbuild_IssuerNameCheck, Matchi
                            KeyUsage::noParticularKeyUsageRequired,
                            KeyPurposeId::id_kp_serverAuth,
                            CertPolicyId::anyPolicy,
                            nullptr/*stapledOCSPResponse*/));
 }
 
 INSTANTIATE_TEST_CASE_P(pkixbuild_IssuerNameCheck, pkixbuild_IssuerNameCheck,
                         testing::ValuesIn(ISSUER_NAME_CHECK_PARAMS));
+
+
+// Records the embedded SCT list extension for later examination.
+class EmbeddedSCTListTestTrustDomain final : public SingleRootTrustDomain
+{
+public:
+  explicit EmbeddedSCTListTestTrustDomain(ByteString rootDER)
+    : SingleRootTrustDomain(rootDER)
+  {
+  }
+
+  virtual void NoteAuxiliaryExtension(AuxiliaryExtension extension,
+                                      Input extensionData) override
+  {
+    if (extension == AuxiliaryExtension::EmbeddedSCTList) {
+      signedCertificateTimestamps = InputToByteString(extensionData);
+    } else {
+      ADD_FAILURE();
+    }
+  }
+
+  ByteString signedCertificateTimestamps;
+};
+
+TEST_F(pkixbuild, CertificateTransparencyExtension)
+{
+  // python security/pkix/tools/DottedOIDToCode.py --tlv
+  //   id-embeddedSctList 1.3.6.1.4.1.11129.2.4.2
+  static const uint8_t tlv_id_embeddedSctList[] = {
+    0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x04, 0x02
+  };
+  static const uint8_t dummySctList[] = {
+    0x01, 0x02, 0x03, 0x04, 0x05
+  };
+
+  ByteString ctExtension = TLV(der::SEQUENCE,
+    BytesToByteString(tlv_id_embeddedSctList) +
+    Boolean(false) +
+    TLV(der::OCTET_STRING,
+      // SignedCertificateTimestampList structure is encoded as an OCTET STRING
+      // within the X.509v3 extension (see RFC 6962 section 3.3).
+      // pkix decodes it internally and returns the actual structure.
+      TLV(der::OCTET_STRING, BytesToByteString(dummySctList))));
+
+  const char* rootCN = "Root CA";
+  ByteString rootDER(CreateCert(rootCN, rootCN, EndEntityOrCA::MustBeCA));
+  ASSERT_FALSE(ENCODING_FAILED(rootDER));
+
+  ByteString certDER(CreateCert(rootCN, "Cert with SCT list",
+                                EndEntityOrCA::MustBeEndEntity,
+                                nullptr, /*subjectDERToCertDER*/
+                                &ctExtension));
+  ASSERT_FALSE(ENCODING_FAILED(certDER));
+
+  Input certInput;
+  ASSERT_EQ(Success, certInput.Init(certDER.data(), certDER.length()));
+
+  EmbeddedSCTListTestTrustDomain extTrustDomain(rootDER);
+  ASSERT_EQ(Success,
+            BuildCertChain(extTrustDomain, certInput, Now(),
+                           EndEntityOrCA::MustBeEndEntity,
+                           KeyUsage::noParticularKeyUsageRequired,
+                           KeyPurposeId::anyExtendedKeyUsage,
+                           CertPolicyId::anyPolicy,
+                           nullptr /*stapledOCSPResponse*/));
+  ASSERT_EQ(BytesToByteString(dummySctList),
+            extTrustDomain.signedCertificateTimestamps);
+}
--- a/security/pkix/test/gtest/pkixgtest.h
+++ b/security/pkix/test/gtest/pkixgtest.h
@@ -173,16 +173,21 @@ public:
   }
 
   Result NetscapeStepUpMatchesServerAuth(Time, bool&) override
   {
     ADD_FAILURE();
     return NotReached("NetscapeStepUpMatchesServerAuth should not be called",
                       Result::FATAL_ERROR_LIBRARY_FAILURE);
   }
+
+  virtual void NoteAuxiliaryExtension(AuxiliaryExtension, Input) override
+  {
+    ADD_FAILURE();
+  }
 };
 
 class DefaultCryptoTrustDomain : public EverythingFailsByDefaultTrustDomain
 {
   Result DigestBuf(Input item, DigestAlgorithm digestAlg,
                    /*out*/ uint8_t* digestBuf, size_t digestBufLen) override
   {
     return TestDigestBuf(item, digestAlg, digestBuf, digestBufLen);
@@ -223,16 +228,20 @@ class DefaultCryptoTrustDomain : public 
     return Success;
   }
 
   Result NetscapeStepUpMatchesServerAuth(Time, /*out*/ bool& matches) override
   {
     matches = true;
     return Success;
   }
+
+  void NoteAuxiliaryExtension(AuxiliaryExtension, Input) override
+  {
+  }
 };
 
 class DefaultNameMatchingPolicy : public NameMatchingPolicy
 {
 public:
   virtual Result FallBackToCommonName(
     Time, /*out*/ FallBackToSearchWithinSubject& fallBackToCommonName) override
   {
--- a/security/pkix/test/gtest/pkixocsp_VerifyEncodedOCSPResponse.cpp
+++ b/security/pkix/test/gtest/pkixocsp_VerifyEncodedOCSPResponse.cpp
@@ -17,16 +17,17 @@
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
+#include "pkixder.h"
 #include "pkixgtest.h"
 
 using namespace mozilla::pkix;
 using namespace mozilla::pkix::test;
 
 const uint16_t END_ENTITY_MAX_LIFETIME_IN_DAYS = 10;
 
 // Note that CheckRevocation is never called for OCSP signing certificates.
@@ -38,16 +39,29 @@ public:
   Result GetCertTrust(EndEntityOrCA endEntityOrCA, const CertPolicyId&,
                       Input, /*out*/ TrustLevel& trustLevel)
                       /*non-final*/ override
   {
     EXPECT_EQ(endEntityOrCA, EndEntityOrCA::MustBeEndEntity);
     trustLevel = TrustLevel::InheritsTrust;
     return Success;
   }
+
+  virtual void NoteAuxiliaryExtension(AuxiliaryExtension extension,
+                                      Input extensionData) override
+  {
+    if (extension == AuxiliaryExtension::SCTListFromOCSPResponse) {
+      signedCertificateTimestamps = InputToByteString(extensionData);
+    } else {
+      // We do not currently expect to receive any other extension here.
+      ADD_FAILURE();
+    }
+  }
+
+  ByteString signedCertificateTimestamps;
 };
 
 namespace {
 char const* const rootName = "Test CA 1";
 void deleteCertID(CertID* certID) { delete certID; }
 } // namespace
 
 class pkixocsp_VerifyEncodedResponse : public ::testing::Test
@@ -194,29 +208,33 @@ public:
   ByteString CreateEncodedOCSPSuccessfulResponse(
                     OCSPResponseContext::CertStatus certStatus,
                     const CertID& certID,
        /*optional*/ const char* signerName,
                     const TestKeyPair& signerKeyPair,
                     time_t producedAt, time_t thisUpdate,
        /*optional*/ const time_t* nextUpdate,
                     const TestSignatureAlgorithm& signatureAlgorithm,
-       /*optional*/ const ByteString* certs = nullptr)
+       /*optional*/ const ByteString* certs = nullptr,
+       /*optional*/ OCSPResponseExtension* singleExtensions = nullptr,
+       /*optional*/ OCSPResponseExtension* responseExtensions = nullptr)
   {
     OCSPResponseContext context(certID, producedAt);
     if (signerName) {
       context.signerNameDER = CNToDERName(signerName);
       EXPECT_FALSE(ENCODING_FAILED(context.signerNameDER));
     }
     context.signerKeyPair.reset(signerKeyPair.Clone());
     EXPECT_TRUE(context.signerKeyPair.get());
     context.responseStatus = OCSPResponseContext::successful;
     context.producedAt = producedAt;
     context.signatureAlgorithm = signatureAlgorithm;
     context.certs = certs;
+    context.singleExtensions = singleExtensions;
+    context.responseExtensions = responseExtensions;
 
     context.certStatus = static_cast<uint8_t>(certStatus);
     context.thisUpdate = thisUpdate;
     context.nextUpdate = nextUpdate ? *nextUpdate : 0;
     context.includeNextUpdate = nextUpdate != nullptr;
 
     return CreateEncodedOCSPResponse(context);
   }
@@ -392,16 +410,56 @@ TEST_F(pkixocsp_VerifyEncodedResponse_su
     ASSERT_EQ(Result::ERROR_OCSP_OLD_RESPONSE,
               VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID,
                                         noLongerValid, END_ENTITY_MAX_LIFETIME_IN_DAYS,
                                         response, expired));
     ASSERT_TRUE(expired);
   }
 }
 
+TEST_F(pkixocsp_VerifyEncodedResponse_successful, ct_extension)
+{
+  // python DottedOIDToCode.py --tlv
+  //   id_ocsp_singleExtensionSctList 1.3.6.1.4.1.11129.2.4.5
+  static const uint8_t tlv_id_ocsp_singleExtensionSctList[] = {
+    0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x04, 0x05
+  };
+  static const uint8_t dummySctList[] = {
+    0x01, 0x02, 0x03, 0x04, 0x05
+  };
+
+  OCSPResponseExtension ctExtension;
+  ctExtension.id = BytesToByteString(tlv_id_ocsp_singleExtensionSctList);
+  // SignedCertificateTimestampList structure is encoded as an OCTET STRING
+  // within the extension value (see RFC 6962 section 3.3).
+  // pkix decodes it internally and returns the actual structure.
+  ctExtension.value = TLV(der::OCTET_STRING, BytesToByteString(dummySctList));
+
+  ByteString responseString(
+               CreateEncodedOCSPSuccessfulResponse(
+                         OCSPResponseContext::good, *endEntityCertID, byKey,
+                         *rootKeyPair, oneDayBeforeNow,
+                         oneDayBeforeNow, &oneDayAfterNow,
+                         sha256WithRSAEncryption(),
+                         /*certs*/ nullptr,
+                         &ctExtension));
+  Input response;
+  ASSERT_EQ(Success,
+            response.Init(responseString.data(), responseString.length()));
+
+  bool expired;
+  ASSERT_EQ(Success,
+            VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID,
+                                      Now(), END_ENTITY_MAX_LIFETIME_IN_DAYS,
+                                      response, expired));
+  ASSERT_FALSE(expired);
+  ASSERT_EQ(BytesToByteString(dummySctList),
+            trustDomain.signedCertificateTimestamps);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // indirect responses (signed by a delegated OCSP responder cert)
 
 class pkixocsp_VerifyEncodedResponse_DelegatedResponder
   : public pkixocsp_VerifyEncodedResponse_successful
 {
 protected:
   // certSubjectName should be unique for each call. This way, we avoid any
--- a/security/pkix/test/lib/pkixtestutil.cpp
+++ b/security/pkix/test/lib/pkixtestutil.cpp
@@ -139,22 +139,31 @@ TLV(uint8_t tag, size_t length, const By
     // It is MUCH more convenient for TLV to be infallible than for it to have
     // "proper" error handling.
     abort();
   }
   result.append(value);
   return result;
 }
 
+OCSPResponseExtension::OCSPResponseExtension()
+  : id()
+  , critical(false)
+  , value()
+  , next(nullptr)
+{
+}
+
 OCSPResponseContext::OCSPResponseContext(const CertID& certID, time_t time)
   : certID(certID)
   , responseStatus(successful)
   , skipResponseBytes(false)
   , producedAt(time)
-  , extensions(nullptr)
+  , singleExtensions(nullptr)
+  , responseExtensions(nullptr)
   , includeEmptyExtensions(false)
   , signatureAlgorithm(sha256WithRSAEncryption())
   , badSignature(false)
   , certs(nullptr)
 
   , certStatus(good)
   , revocationTime(0)
   , thisUpdate(time)
@@ -892,20 +901,20 @@ OCSPExtension(OCSPResponseExtension& ext
   encoded.append(value);
   return TLV(der::SEQUENCE, encoded);
 }
 
 // Extensions ::= [1] {
 //   SEQUENCE OF Extension
 // }
 static ByteString
-Extensions(OCSPResponseContext& context)
+OCSPExtensions(OCSPResponseExtension* extensions)
 {
   ByteString value;
-  for (OCSPResponseExtension* extension = context.extensions;
+  for (OCSPResponseExtension* extension = extensions;
        extension; extension = extension->next) {
     ByteString extensionEncoded(OCSPExtension(*extension));
     if (ENCODING_FAILED(extensionEncoded)) {
       return ByteString();
     }
     value.append(extensionEncoded);
   }
   ByteString sequence(TLV(der::SEQUENCE, value));
@@ -930,18 +939,18 @@ ResponseData(OCSPResponseContext& contex
     return ByteString();
   }
   ByteString response(SingleResponse(context));
   if (ENCODING_FAILED(response)) {
     return ByteString();
   }
   ByteString responses(TLV(der::SEQUENCE, response));
   ByteString responseExtensions;
-  if (context.extensions || context.includeEmptyExtensions) {
-    responseExtensions = Extensions(context);
+  if (context.responseExtensions || context.includeEmptyExtensions) {
+    responseExtensions = OCSPExtensions(context.responseExtensions);
   }
 
   ByteString value;
   value.append(responderID);
   value.append(producedAtEncoded);
   value.append(responses);
   value.append(responseExtensions);
   return TLV(der::SEQUENCE, value);
@@ -1010,22 +1019,27 @@ SingleResponse(OCSPResponseContext& cont
   if (context.includeNextUpdate) {
     ByteString nextUpdateEncoded(TimeToGeneralizedTime(context.nextUpdate));
     if (ENCODING_FAILED(nextUpdateEncoded)) {
       return ByteString();
     }
     nextUpdateEncodedNested = TLV(der::CONSTRUCTED | der::CONTEXT_SPECIFIC | 0,
                                   nextUpdateEncoded);
   }
+  ByteString singleExtensions;
+  if (context.singleExtensions || context.includeEmptyExtensions) {
+    singleExtensions = OCSPExtensions(context.singleExtensions);
+  }
 
   ByteString value;
   value.append(certID);
   value.append(certStatus);
   value.append(thisUpdateEncoded);
   value.append(nextUpdateEncodedNested);
+  value.append(singleExtensions);
   return TLV(der::SEQUENCE, value);
 }
 
 // CertID          ::=     SEQUENCE {
 //        hashAlgorithm       AlgorithmIdentifier,
 //        issuerNameHash      OCTET STRING, -- Hash of issuer's DN
 //        issuerKeyHash       OCTET STRING, -- Hash of issuer's public key
 //        serialNumber        CertificateSerialNumber }
--- a/security/pkix/test/lib/pkixtestutil.h
+++ b/security/pkix/test/lib/pkixtestutil.h
@@ -371,16 +371,18 @@ ByteString CreateEncodedBasicConstraints
 ByteString CreateEncodedEKUExtension(Input eku, Critical critical);
 
 ///////////////////////////////////////////////////////////////////////////////
 // Encode OCSP responses
 
 class OCSPResponseExtension final
 {
 public:
+  OCSPResponseExtension();
+
   ByteString id;
   bool critical;
   ByteString value;
   OCSPResponseExtension* next;
 };
 
 class OCSPResponseContext final
 {
@@ -407,17 +409,20 @@ public:
 
   // responderID
   ByteString signerNameDER; // If set, responderID will use the byName
                             // form; otherwise responderID will use the
                             // byKeyHash form.
 
   std::time_t producedAt;
 
-  OCSPResponseExtension* extensions;
+  // SingleResponse extensions (for the certID given in the constructor).
+  OCSPResponseExtension* singleExtensions;
+  // ResponseData extensions.
+  OCSPResponseExtension* responseExtensions;
   bool includeEmptyExtensions; // If true, include the extension wrapper
                                // regardless of if there are any actual
                                // extensions.
   ScopedTestKeyPair signerKeyPair;
   TestSignatureAlgorithm signatureAlgorithm;
   bool badSignature; // If true, alter the signature to fail verification
   const ByteString* certs; // optional; array terminated by an empty string
 
--- a/security/sandbox/chromium/sandbox/linux/services/x86_32_linux_syscalls.h
+++ b/security/sandbox/chromium/sandbox/linux/services/x86_32_linux_syscalls.h
@@ -1417,10 +1417,78 @@
 #if !defined(__NR_getrandom)
 #define __NR_getrandom 355
 #endif
 
 #if !defined(__NR_memfd_create)
 #define __NR_memfd_create 356
 #endif
 
+#if !defined(__NR_bpf)
+#define __NR_bpf 357
+#endif
+
+#if !defined(__NR_execveat)
+#define __NR_execveat 358
+#endif
+
+#if !defined(__NR_socket)
+#define __NR_socket 359
+#endif
+
+#if !defined(__NR_socketpair)
+#define __NR_socketpair 360
+#endif
+
+#if !defined(__NR_bind)
+#define __NR_bind 361
+#endif
+
+#if !defined(__NR_connect)
+#define __NR_connect 362
+#endif
+
+#if !defined(__NR_listen)
+#define __NR_listen 363
+#endif
+
+#if !defined(__NR_accept4)
+#define __NR_accept4 364
+#endif
+
+#if !defined(__NR_getsockopt)
+#define __NR_getsockopt 365
+#endif
+
+#if !defined(__NR_setsockopt)
+#define __NR_setsockopt 366
+#endif
+
+#if !defined(__NR_getsockname)
+#define __NR_getsockname 367
+#endif
+
+#if !defined(__NR_getpeername)
+#define __NR_getpeername 368
+#endif
+
+#if !defined(__NR_sendto)
+#define __NR_sendto 369
+#endif
+
+#if !defined(__NR_sendmsg)
+#define __NR_sendmsg 370
+#endif
+
+#if !defined(__NR_recvfrom)
+#define __NR_recvfrom 371
+#endif
+
+#if !defined(__NR_recvmsg)
+#define __NR_recvmsg 372
+#endif
+
+#if !defined(__NR_shutdown)
+#define __NR_shutdown 373
+#endif
+
 #endif  // SANDBOX_LINUX_SERVICES_X86_32_LINUX_SYSCALLS_H_
 
--- a/security/sandbox/linux/SandboxFilter.cpp
+++ b/security/sandbox/linux/SandboxFilter.cpp
@@ -435,16 +435,17 @@ public:
 #else // #ifdef DESKTOP
     case SYS_RECV:
     case SYS_SEND:
     case SYS_SOCKET: // DANGEROUS
     case SYS_CONNECT: // DANGEROUS
     case SYS_ACCEPT:
     case SYS_BIND:
     case SYS_LISTEN:
+    case SYS_GETSOCKOPT:
     case SYS_SETSOCKOPT:
     case SYS_GETSOCKNAME:
     case SYS_GETPEERNAME:
     case SYS_SHUTDOWN:
       return Some(Allow());
 #endif
     default:
       return SandboxPolicyCommon::EvaluateSocketCall(aCall);
--- a/security/sandbox/linux/SandboxFilterUtil.cpp
+++ b/security/sandbox/linux/SandboxFilterUtil.cpp
@@ -57,25 +57,28 @@ SandboxPolicyBase::EvaluateSyscall(int a
         // Optimize out cases that are equal to the default.
         if (thisCase) {
           acc.reset(new Caser<int>(acc->Case(i, *thisCase)));
         }
       }
       return acc->Default(InvalidSyscall());
     }
 #endif // ANDROID
-#else // __NR_socketcall
-#define DISPATCH_SOCKETCALL(sysnum, socketnum)         \
-    case sysnum:                                       \
+#endif // __NR_socketcall
+#define DISPATCH_SOCKETCALL(sysnum, socketnum)                       \
+    case sysnum:                                                     \
       return EvaluateSocketCall(socketnum).valueOr(InvalidSyscall())
+#ifdef __NR_socket
       DISPATCH_SOCKETCALL(__NR_socket,      SYS_SOCKET);
       DISPATCH_SOCKETCALL(__NR_bind,        SYS_BIND);
       DISPATCH_SOCKETCALL(__NR_connect,     SYS_CONNECT);
       DISPATCH_SOCKETCALL(__NR_listen,      SYS_LISTEN);
+#ifdef __NR_accept
       DISPATCH_SOCKETCALL(__NR_accept,      SYS_ACCEPT);
+#endif
       DISPATCH_SOCKETCALL(__NR_getsockname, SYS_GETSOCKNAME);
       DISPATCH_SOCKETCALL(__NR_getpeername, SYS_GETPEERNAME);
       DISPATCH_SOCKETCALL(__NR_socketpair,  SYS_SOCKETPAIR);
 #ifdef __NR_send
       DISPATCH_SOCKETCALL(__NR_send,        SYS_SEND);
       DISPATCH_SOCKETCALL(__NR_recv,        SYS_RECV);
 #endif // __NR_send
       DISPATCH_SOCKETCALL(__NR_sendto,      SYS_SENDTO);
@@ -83,17 +86,19 @@ SandboxPolicyBase::EvaluateSyscall(int a
       DISPATCH_SOCKETCALL(__NR_shutdown,    SYS_SHUTDOWN);
       DISPATCH_SOCKETCALL(__NR_setsockopt,  SYS_SETSOCKOPT);
       DISPATCH_SOCKETCALL(__NR_getsockopt,  SYS_GETSOCKOPT);
       DISPATCH_SOCKETCALL(__NR_sendmsg,     SYS_SENDMSG);
       DISPATCH_SOCKETCALL(__NR_recvmsg,     SYS_RECVMSG);
       DISPATCH_SOCKETCALL(__NR_accept4,     SYS_ACCEPT4);
       DISPATCH_SOCKETCALL(__NR_recvmmsg,    SYS_RECVMMSG);
       DISPATCH_SOCKETCALL(__NR_sendmmsg,    SYS_SENDMMSG);
+#endif // __NR_socket
 #undef DISPATCH_SOCKETCALL
+#ifndef __NR_socketcall
 #ifndef ANDROID
 #define DISPATCH_SYSVCALL(sysnum, ipcnum)         \
     case sysnum:                                  \
       return EvaluateIpcCall(ipcnum).valueOr(InvalidSyscall())
       DISPATCH_SYSVCALL(__NR_semop,       SEMOP);
       DISPATCH_SYSVCALL(__NR_semget,      SEMGET);
       DISPATCH_SYSVCALL(__NR_semctl,      SEMCTL);
       DISPATCH_SYSVCALL(__NR_semtimedop,  SEMTIMEDOP);
--- a/security/sandbox/modifications-to-chromium-to-reapply-after-upstream-merge.txt
+++ b/security/sandbox/modifications-to-chromium-to-reapply-after-upstream-merge.txt
@@ -3,8 +3,9 @@ Also, please update any existing links t
 
 https://hg.mozilla.org/mozilla-central/rev/efa7518e43b0
 https://hg.mozilla.org/mozilla-central/rev/421446ab2347
 https://hg.mozilla.org/mozilla-central/rev/beb74056ac2d
 https://hg.mozilla.org/mozilla-central/rev/7df8d6639971
 https://hg.mozilla.org/mozilla-central/rev/1178c11561bc
 https://hg.mozilla.org/mozilla-central/rev/afa4f68de47c
 https://bugzilla.mozilla.org/show_bug.cgi?id=1035125 bug1035125part7.patch
+https://bugzilla.mozilla.org/show_bug.cgi?id=1273852
--- a/testing/marionette/harness/marionette/__init__.py
+++ b/testing/marionette/harness/marionette/__init__.py
@@ -1,13 +1,13 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-__version__ = '3.0.0'
+__version__ = '3.1.0'
 
 from .marionette_test import (
     CommonTestCase,
     expectedFailure,
     MarionetteJSTestCase,
     MarionetteTestCase,
     skip,
     skip_if_desktop,
--- a/testing/mozbase/mozrunner/mozrunner/devices/android_device.py
+++ b/testing/mozbase/mozrunner/mozrunner/devices/android_device.py
@@ -17,16 +17,18 @@ import urlparse
 import urllib2
 from distutils.spawn import find_executable
 
 from mozdevice import DeviceManagerADB, DMError
 from mozprocess import ProcessHandler
 
 EMULATOR_HOME_DIR = os.path.join(os.path.expanduser('~'), '.mozbuild', 'android-device')
 
+EMULATOR_AUTH_FILE = os.path.join(os.path.expanduser('~'), '.emulator_console_auth_token')
+
 TOOLTOOL_URL = 'https://raw.githubusercontent.com/mozilla/build-tooltool/master/tooltool.py'
 
 TRY_URL = 'https://hg.mozilla.org/try/raw-file/default'
 
 MANIFEST_PATH = 'testing/config/tooltool-manifests'
 
 verbose_logging = False
 
@@ -365,16 +367,23 @@ class AndroidEmulator(object):
             _download_file(url, 'releng.manifest', EMULATOR_HOME_DIR)
             _tooltool_fetch()
             self._update_avd_paths()
 
     def start(self):
         """
            Launch the emulator.
         """
+        if os.path.exists(EMULATOR_AUTH_FILE):
+            os.remove(EMULATOR_AUTH_FILE)
+            _log_debug("deleted %s" % EMULATOR_AUTH_FILE)
+        # create an empty auth file to disable emulator authentication
+        auth_file = open(EMULATOR_AUTH_FILE, 'w')
+        auth_file.close()
+
         def outputHandler(line):
             self.emulator_log.write("<%s>\n" % line)
         env = os.environ
         env['ANDROID_AVD_HOME'] = os.path.join(EMULATOR_HOME_DIR, "avd")
         command = [self.emulator_path, "-avd",
                    self.avd_info.name, "-port", "5554"]
         if self.avd_info.extra_args:
             # -enable-kvm option is not valid on OSX
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -36386,16 +36386,22 @@
         ],
         "editing/run/multitest.html": [
           {
             "path": "editing/run/multitest.html",
             "timeout": "long",
             "url": "/editing/run/multitest.html"
           }
         ],
+        "service-workers/service-worker/controller-on-disconnect.https.html": [
+          {
+            "path": "service-workers/service-worker/controller-on-disconnect.https.html",
+            "url": "/service-workers/service-worker/controller-on-disconnect.https.html"
+          }
+        ],
         "web-animations/animation-model/keyframe-effects/spacing-keyframes.html": [
           {
             "path": "web-animations/animation-model/keyframe-effects/spacing-keyframes.html",
             "url": "/web-animations/animation-model/keyframe-effects/spacing-keyframes.html"
           }
         ],
         "web-animations/interfaces/DocumentTimeline/constructor.html": [
           {
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/controller-on-disconnect.https.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<title>Service Worker: Controller on load</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/test-helpers.sub.js"></script>
+<body>
+<script>
+var t = async_test('controller is cleared on disconnected window');
+t.step(function() {
+    var url = 'resources/empty-worker.js';
+    var scope = 'resources/blank.html';
+    var registration;
+    var controller;
+    var frame;
+    service_worker_unregister_and_register(t, url, scope)
+      .then(t.step_func(function(swr) {
+          registration = swr;
+          return wait_for_state(t, registration.installing, 'activated');
+        }))
+      .then(t.step_func(function() {
+          return with_iframe(scope)
+        }))
+      .then(t.step_func(function(f) {
+          frame = f;
+          var w = frame.contentWindow;
+          var swc = w.navigator.serviceWorker;
+          assert_true(swc.controller instanceof w.ServiceWorker,
+                      'controller should be a ServiceWorker object');
+
+          frame.remove();
+
+          assert_equals(swc.controller, null,
+                        'disconnected frame should not be controlled');
+
+          service_worker_unregister_and_done(t, scope);
+        }))
+      .catch(unreached_rejection(t));
+  });
+</script>
+</body>
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -3264,52 +3264,25 @@
   "NETWORK_ID": {
     "alert_emails": ["necko@mozilla.com"],
     "bug_numbers": [1240932],
     "expires_in_version": "never",
     "kind": "enumerated",
     "n_values": 6,
     "description": "Network identification (0=None, 1=New, 2=Same)"
   },
-  "IDLE_NOTIFY_BACK_MS": {
-    "alert_emails": ["froydnj@mozilla.com"],
-    "bug_numbers": [731004],
-    "expires_in_version": "40",
-    "kind": "exponential",
-    "high": 5000,
-    "n_buckets": 10,
-    "description": "Time spent checking for and notifying listeners that the user is back (ms)"
-  },
-  "IDLE_NOTIFY_BACK_LISTENERS": {
-    "alert_emails": ["froydnj@mozilla.com"],
-    "bug_numbers": [731004],
-    "expires_in_version": "40",
-    "kind": "linear",
-    "high": 100,
-    "n_buckets": 20,
-    "description": "Number of listeners notified that the user is back"
-  },
   "IDLE_NOTIFY_IDLE_MS": {
     "alert_emails": ["froydnj@mozilla.com"],
     "bug_numbers": [731004],
     "expires_in_version": "default",
     "kind": "exponential",
     "high": 5000,
     "n_buckets": 10,
     "description": "Time spent checking for and notifying listeners that the user is idle (ms)"
   },
-  "IDLE_NOTIFY_IDLE_LISTENERS": {
-    "alert_emails": ["froydnj@mozilla.com"],
-    "bug_numbers": [731004],
-    "expires_in_version": "40",
-    "kind": "linear",
-    "high": 100,
-    "n_buckets": 20,
-    "description": "Number of listeners notified that the user is idle"
-  },
   "URLCLASSIFIER_LOOKUP_TIME": {
     "expires_in_version": "never",
     "kind": "exponential",
     "high": 500,
     "n_buckets": 10,
     "description": "Time spent per dbservice lookup (ms)"
   },
   "URLCLASSIFIER_CL_CHECK_TIME": {
@@ -4674,23 +4647,16 @@
     "kind": "flag",
     "description": "Whether the decoder for KOI8U has been instantiated in this session."
   },
   "DECODER_INSTANTIATED_ISO_8859_5": {
     "expires_in_version": "never",
     "kind": "flag",
     "description": "Whether the decoder for ISO-8859-5 has been instantiated in this session."
   },
-  "HTML_FOREGROUND_REFLOW_MS_2": {
-    "expires_in_version": "never",
-    "kind": "exponential",
-    "high": 10000,
-    "n_buckets": 20,
-    "description": "HTML reflows in foreground windows (ms)"
-  },
   "LONG_REFLOW_INTERRUPTIBLE": {
     "expires_in_version": "never",
     "kind": "boolean",
     "description": "Long running reflow, interruptible or not"
   },
   "XMLHTTPREQUEST_ASYNC_OR_SYNC": {
     "expires_in_version": "never",
     "kind": "boolean",
--- a/toolkit/components/telemetry/histogram-whitelists.json
+++ b/toolkit/components/telemetry/histogram-whitelists.json
@@ -467,17 +467,16 @@
     "GEOLOCATION_OSX_SOURCE_IS_MLS",
     "GEOLOCATION_REQUEST_GRANTED",
     "GEOLOCATION_WATCHPOSITION_SECURE_ORIGIN",
     "GEOLOCATION_WIN8_SOURCE_IS_MLS",
     "GFX_CRASH",
     "GRADIENT_DURATION",
     "GRADIENT_RETENTION_TIME",
     "HISTORY_LASTVISITED_TREE_QUERY_TIME_MS",
-    "HTML_FOREGROUND_REFLOW_MS_2",
     "HTTPCONNMGR_TOTAL_SPECULATIVE_CONN",
     "HTTPCONNMGR_UNUSED_SPECULATIVE_CONN",
     "HTTPCONNMGR_USED_SPECULATIVE_CONN",
     "HTTP_AUTH_DIALOG_STATS",
     "HTTP_CACHE_DISPOSITION_2",
     "HTTP_CACHE_DISPOSITION_2_V2",
     "HTTP_CACHE_ENTRY_ALIVE_TIME",
     "HTTP_CACHE_ENTRY_RELOAD_TIME",
@@ -542,19 +541,16 @@
     "HTTP_SUB_OPEN_TO_FIRST_FROM_CACHE_V2",
     "HTTP_SUB_OPEN_TO_FIRST_RECEIVED",
     "HTTP_SUB_OPEN_TO_FIRST_SENT",
     "HTTP_SUB_REVALIDATION",
     "HTTP_SUB_TCP_CONNECTION",
     "HTTP_TRANSACTION_IS_SSL",
     "HTTP_TRANSACTION_USE_ALTSVC",
     "HTTP_TRANSACTION_USE_ALTSVC_OE",
-    "IDLE_NOTIFY_BACK_LISTENERS",
-    "IDLE_NOTIFY_BACK_MS",
-    "IDLE_NOTIFY_IDLE_LISTENERS",
     "IDLE_NOTIFY_IDLE_MS",
     "IMAGE_DECODE_CHUNKS",
     "IMAGE_DECODE_COUNT",
     "IMAGE_DECODE_LATENCY_US",
     "IMAGE_DECODE_ON_DRAW_LATENCY",
     "IMAGE_DECODE_SPEED_GIF",
     "IMAGE_DECODE_SPEED_JPEG",
     "IMAGE_DECODE_SPEED_PNG",
@@ -1580,17 +1576,16 @@
     "GHOST_WINDOWS",
     "GRADIENT_DURATION",
     "GRADIENT_RETENTION_TIME",
     "GRAPHICS_DRIVER_STARTUP_TEST",
     "GRAPHICS_SANITY_TEST",
     "GRAPHICS_SANITY_TEST_OS_SNAPSHOT",
     "GRAPHICS_SANITY_TEST_REASON",
     "HISTORY_LASTVISITED_TREE_QUERY_TIME_MS",
-    "HTML_FOREGROUND_REFLOW_MS_2",
     "HTTPCONNMGR_TOTAL_SPECULATIVE_CONN",
     "HTTPCONNMGR_UNUSED_SPECULATIVE_CONN",
     "HTTPCONNMGR_USED_SPECULATIVE_CONN",
     "HTTP_AUTH_DIALOG_STATS",
     "HTTP_CACHE_DISPOSITION_2",
     "HTTP_CACHE_DISPOSITION_2_V2",
     "HTTP_CACHE_ENTRY_ALIVE_TIME",
     "HTTP_CACHE_ENTRY_RELOAD_TIME",
@@ -1655,19 +1650,16 @@
     "HTTP_SUB_OPEN_TO_FIRST_FROM_CACHE_V2",
     "HTTP_SUB_OPEN_TO_FIRST_RECEIVED",
     "HTTP_SUB_OPEN_TO_FIRST_SENT",
     "HTTP_SUB_REVALIDATION",
     "HTTP_SUB_TCP_CONNECTION",
     "HTTP_TRANSACTION_IS_SSL",
     "HTTP_TRANSACTION_USE_ALTSVC",
     "HTTP_TRANSACTION_USE_ALTSVC_OE",
-    "IDLE_NOTIFY_BACK_LISTENERS",
-    "IDLE_NOTIFY_BACK_MS",
-    "IDLE_NOTIFY_IDLE_LISTENERS",
     "IDLE_NOTIFY_IDLE_MS",
     "IMAGE_DECODE_CHUNKS",
     "IMAGE_DECODE_COUNT",
     "IMAGE_DECODE_LATENCY_US",
     "IMAGE_DECODE_ON_DRAW_LATENCY",
     "IMAGE_DECODE_SPEED_GIF",
     "IMAGE_DECODE_SPEED_JPEG",
     "IMAGE_DECODE_SPEED_PNG",
--- a/widget/nsIdleService.cpp
+++ b/widget/nsIdleService.cpp
@@ -538,17 +538,16 @@ nsIdleService::ResetIdleTimeOut(uint32_t
   // If no one is idle, then we are done, any existing timers can keep running.
   if (mIdleObserverCount == 0) {
     MOZ_LOG(sLog, LogLevel::Debug,
            ("idleService: Reset idle timeout: no idle observers"));
     return NS_OK;
   }
 
   // Mark all idle services as non-idle, and calculate the next idle timeout.
-  Telemetry::AutoTimer<Telemetry::IDLE_NOTIFY_BACK_MS> timer;
   nsCOMArray<nsIObserver> notifyList;
   mDeltaToNextIdleSwitchInS = UINT32_MAX;
 
   // Loop through all listeners, and find any that have detected idle.
   for (uint32_t i = 0; i < mArrayListeners.Length(); i++) {
     IdleListener& curListener = mArrayListeners.ElementAt(i);
 
     // If the listener was idle, then he shouldn't be any longer.
@@ -564,18 +563,16 @@ nsIdleService::ResetIdleTimeOut(uint32_t
 
   // When we are done, then we wont have anyone idle.
   mIdleObserverCount = 0;
 
   // Restart the idle timer, and do so before anyone can delay us.
   ReconfigureTimer();
 
   int32_t numberOfPendingNotifications = notifyList.Count();
-  Telemetry::Accumulate(Telemetry::IDLE_NOTIFY_BACK_LISTENERS,
-                        numberOfPendingNotifications);
 
   // Bail if nothing to do.
   if (!numberOfPendingNotifications) {
     return NS_OK;
   }
 
   // Now send "active" events to all, if any should have timed out already,
   // then they will be reawaken by the timer that is already running.
@@ -750,18 +747,16 @@ nsIdleService::IdleTimerCallback(void)
     }
   }
 
   // Restart the timer before any notifications that could slow us down are
   // done.
   ReconfigureTimer();
 
   int32_t numberOfPendingNotifications = notifyList.Count();
-  Telemetry::Accumulate(Telemetry::IDLE_NOTIFY_IDLE_LISTENERS,
-                        numberOfPendingNotifications);
 
   // Bail if nothing to do.
   if (!numberOfPendingNotifications) {
     MOZ_LOG(sLog, LogLevel::Debug,
            ("idleService: **** Idle timer callback: no observers to message."));
     return;
   }