Merge m-i to m-c, a=merge
authorPhil Ringnalda <philringnalda@gmail.com>
Mon, 04 Jul 2016 15:02:22 -0700
changeset 303593 c9a70b64f2fa
parent 303592 0842107a80e7 (current diff)
parent 303518 a4c8c1c3ddfb (diff)
child 303594 4b7053b141ad
child 303686 f47a1bc057ff
child 303690 162b6fca7d86
push id79124
push userphilringnalda@gmail.com
push date2016-07-04 22:08 +0000
treeherdermozilla-inbound@4b7053b141ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone50.0a1
first release with
nightly linux32
c9a70b64f2fa / 50.0a1 / 20160705030222 / files
nightly linux64
c9a70b64f2fa / 50.0a1 / 20160705030222 / files
nightly mac
c9a70b64f2fa / 50.0a1 / 20160705030201 / files
nightly win32
c9a70b64f2fa / 50.0a1 / 20160705030222 / files
nightly win64
c9a70b64f2fa / 50.0a1 / 20160705030222 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-i to m-c, a=merge
old-configure.in
--- 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/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;
   }