Merge m-i (and autoland) to m-c, a=merge
authorPhil Ringnalda <philringnalda@gmail.com>
Sun, 02 Oct 2016 22:32:54 -0700
changeset 316154 955840bfd3c20eb24dd5a01be27bdc55c489a285
parent 316144 d856f453dcd6c10aa5a0724824cc3b9b685db419 (current diff)
parent 316153 d2193462357aa9c9ac3acf6e01501f0dcc3cb749 (diff)
child 316159 b65e4cc8e326a0cfaf51cafe7e99893bc2d6e929
child 316212 f30756ff80e65180522ef15fbbabc6caf90f29b4
push id30764
push userphilringnalda@gmail.com
push dateMon, 03 Oct 2016 05:33:02 +0000
treeherdermozilla-central@955840bfd3c2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone52.0a1
first release with
nightly linux32
955840bfd3c2 / 52.0a1 / 20161003030438 / files
nightly linux64
955840bfd3c2 / 52.0a1 / 20161003030438 / files
nightly mac
955840bfd3c2 / 52.0a1 / 20161003030438 / files
nightly win32
955840bfd3c2 / 52.0a1 / 20161003030438 / files
nightly win64
955840bfd3c2 / 52.0a1 / 20161003030438 / 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 (and autoland) to m-c, a=merge
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -1442,45 +1442,66 @@ MediaStreamGraphImpl::ApplyStreamUpdate(
   }
 }
 
 void
 MediaStreamGraphImpl::ForceShutDown(ShutdownTicket* aShutdownTicket)
 {
   NS_ASSERTION(NS_IsMainThread(), "Must be called on main thread");
   STREAM_LOG(LogLevel::Debug, ("MediaStreamGraph %p ForceShutdown", this));
-  {
-    MonitorAutoLock lock(mMonitor);
-    mForceShutDown = true;
-    mForceShutdownTicket = aShutdownTicket;
-    if (mLifecycleState == LIFECYCLE_THREAD_NOT_STARTED) {
-      // We *could* have just sent this a message to start up, so don't
-      // yank the rug out from under it.  Tell it to startup and let it
-      // shut down.
-      RefPtr<GraphDriver> driver = CurrentDriver();
-      MonitorAutoUnlock unlock(mMonitor);
-      driver->Start();
+
+  MonitorAutoLock lock(mMonitor);
+  if (aShutdownTicket) {
+    MOZ_ASSERT(!mForceShutdownTicket);
+    // Avoid waiting forever for a graph to shut down
+    // synchronously.  Reports are that some 3rd-party audio drivers
+    // occasionally hang in shutdown (both for us and Chrome).
+    mShutdownTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
+    if (!mShutdownTimer) {
+      return;
     }
-    EnsureNextIterationLocked();
+    mShutdownTimer->InitWithCallback(this,
+                                     MediaStreamGraph::AUDIO_CALLBACK_DRIVER_SHUTDOWN_TIMEOUT,
+                                     nsITimer::TYPE_ONE_SHOT);
+  }
+  mForceShutDown = true;
+  mForceShutdownTicket = aShutdownTicket;
+  if (mLifecycleState == LIFECYCLE_THREAD_NOT_STARTED) {
+    // We *could* have just sent this a message to start up, so don't
+    // yank the rug out from under it.  Tell it to startup and let it
+    // shut down.
+    RefPtr<GraphDriver> driver = CurrentDriver();
+    MonitorAutoUnlock unlock(mMonitor);
+    driver->Start();
   }
+  EnsureNextIterationLocked();
 }
 
+NS_IMETHODIMP
+MediaStreamGraphImpl::Notify(nsITimer* aTimer)
+{
+  MonitorAutoLock lock(mMonitor);
+  NS_ASSERTION(!mForceShutdownTicket, "MediaStreamGraph took too long to shut down!");
+  // Sigh, graph took too long to shut down.  Stop blocking system
+  // shutdown and hope all is well.
+  mForceShutdownTicket = nullptr;
+  return NS_OK;
+}
+
+
 /* static */ StaticRefPtr<nsIAsyncShutdownBlocker> gMediaStreamGraphShutdownBlocker;
 
 namespace {
 
-class MediaStreamGraphShutDownRunnable : public Runnable
-                                       , public nsITimerCallback {
+class MediaStreamGraphShutDownRunnable : public Runnable {
 public:
   explicit MediaStreamGraphShutDownRunnable(MediaStreamGraphImpl* aGraph)
     : mGraph(aGraph)
   {}
-  NS_DECL_ISUPPORTS_INHERITED
-
-  NS_IMETHOD Run() override
+  NS_IMETHOD Run()
   {
     NS_ASSERTION(mGraph->mDetectedNotRunning,
                  "We should know the graph thread control loop isn't running!");
 
     LIFECYCLE_LOG("Shutting down graph %p", mGraph.get());
 
     // We've asserted the graph isn't running.  Use mDriver instead of CurrentDriver
     // to avoid thread-safety checks
@@ -1488,43 +1509,30 @@ public:
     // XXX a better test would be have setting mDetectedNotRunning make sure
     // any current callback has finished and block future ones -- or just
     // handle it all in Shutdown()!
     if (mGraph->mDriver->AsAudioCallbackDriver()) {
       MOZ_ASSERT(!mGraph->mDriver->AsAudioCallbackDriver()->InCallback());
     }
 #endif
 
-    if (mGraph->mForceShutdownTicket) {
-      // Avoid waiting forever for a callback driver to shut down
-      // synchronously.  Reports are that some 3rd-party audio drivers
-      // occasionally hang in shutdown (both for us and Chrome).
-      mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
-      if (!mTimer) {
-        return NS_ERROR_FAILURE;
-      }
-      mTimer->InitWithCallback(this,
-                               MediaStreamGraph::AUDIO_CALLBACK_DRIVER_SHUTDOWN_TIMEOUT,
-                               nsITimer::TYPE_ONE_SHOT);
-    }
-
     mGraph->mDriver->Shutdown(); // This will wait until it's shutdown since
                                  // we'll start tearing down the graph after this
 
+    // Safe to access these without the monitor since the graph isn't running.
     // We may be one of several graphs. Drop ticket to eventually unblock shutdown.
-    if (mTimer && !mGraph->mForceShutdownTicket) {
+    if (mGraph->mShutdownTimer && !mGraph->mForceShutdownTicket) {
       MOZ_ASSERT(false,
         "AudioCallbackDriver took too long to shut down and we let shutdown"
         " continue - freezing and leaking");
 
       // The timer fired, so we may be deeper in shutdown now.  Block any further
       // teardown and just leak, for safety.
       return NS_OK;
     }
-    mTimer = nullptr;
     mGraph->mForceShutdownTicket = nullptr;
 
     // We can't block past the final LIFECYCLE_WAITING_FOR_STREAM_DESTRUCTION
     // stage, since completion of that stage requires all streams to be freed,
     // which requires shutdown to proceed.
 
     // mGraph's thread is not running so it's OK to do whatever here
     if (mGraph->IsEmpty()) {
@@ -1547,40 +1555,20 @@ public:
         stream->GetStreamTracks().Clear();
       }
 
       mGraph->mLifecycleState =
         MediaStreamGraphImpl::LIFECYCLE_WAITING_FOR_STREAM_DESTRUCTION;
     }
     return NS_OK;
   }
-
-  NS_IMETHOD Notify(nsITimer* aTimer) override
-  {
-    // Sigh, driver took too long to shut down.  Stop blocking system
-    // shutdown and hope all is well.  Shutdown of this graph will proceed
-    // if the driver eventually comes back.
-    NS_ASSERTION(!(mGraph->mForceShutdownTicket),
-                 "AudioCallbackDriver took too long to shut down - probably hung");
-
-    mGraph->mForceShutdownTicket = nullptr;
-    return NS_OK;
-  }
-
 private:
-  ~MediaStreamGraphShutDownRunnable() {}
-
-  nsCOMPtr<nsITimer> mTimer;
   RefPtr<MediaStreamGraphImpl> mGraph;
 };
 
-NS_IMPL_ISUPPORTS_INHERITED(MediaStreamGraphShutDownRunnable, Runnable, nsITimerCallback)
-
-
-
 class MediaStreamGraphStableStateRunnable : public Runnable {
 public:
   explicit MediaStreamGraphStableStateRunnable(MediaStreamGraphImpl* aGraph,
                                                bool aSourceIsMSG)
     : mGraph(aGraph)
     , mSourceIsMSG(aSourceIsMSG)
   {
   }
@@ -3439,27 +3427,25 @@ MediaStreamGraph::CreateNonRealtimeInsta
 
 void
 MediaStreamGraph::DestroyNonRealtimeInstance(MediaStreamGraph* aGraph)
 {
   NS_ASSERTION(NS_IsMainThread(), "Main thread only");
   MOZ_ASSERT(aGraph->IsNonRealtime(), "Should not destroy the global graph here");
 
   MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(aGraph);
-  if (graph->mForceShutDown)
-    return; // already done
 
   if (!graph->mNonRealtimeProcessing) {
     // Start the graph, but don't produce anything
     graph->StartNonRealtimeProcessing(0);
   }
   graph->ForceShutDown(nullptr);
 }
 
-NS_IMPL_ISUPPORTS(MediaStreamGraphImpl, nsIMemoryReporter)
+NS_IMPL_ISUPPORTS(MediaStreamGraphImpl, nsIMemoryReporter, nsITimerCallback)
 
 NS_IMETHODIMP
 MediaStreamGraphImpl::CollectReports(nsIHandleReportCallback* aHandleReport,
                                      nsISupports* aData, bool aAnonymize)
 {
   if (mLifecycleState >= LIFECYCLE_WAITING_FOR_THREAD_SHUTDOWN) {
     // Shutting down, nothing to report.
     FinishCollectReports(aHandleReport, aData, nsTArray<AudioNodeSizes>());
--- a/dom/media/MediaStreamGraphImpl.h
+++ b/dom/media/MediaStreamGraphImpl.h
@@ -5,16 +5,17 @@
 
 #ifndef MOZILLA_MEDIASTREAMGRAPHIMPL_H_
 #define MOZILLA_MEDIASTREAMGRAPHIMPL_H_
 
 #include "MediaStreamGraph.h"
 
 #include "nsDataHashtable.h"
 
+#include "nsITimer.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/TimeStamp.h"
 #include "nsIMemoryReporter.h"
 #include "nsIThread.h"
 #include "nsIRunnable.h"
 #include "nsIAsyncShutdown.h"
 #include "Latency.h"
 #include "mozilla/Services.h"
@@ -92,21 +93,23 @@ public:
  * file. It's not in the anonymous namespace because MediaStream needs to
  * be able to friend it.
  *
  * There can be multiple MediaStreamGraph per process: one per AudioChannel.
  * Additionaly, each OfflineAudioContext object creates its own MediaStreamGraph
  * object too.
  */
 class MediaStreamGraphImpl : public MediaStreamGraph,
-                             public nsIMemoryReporter
+                             public nsIMemoryReporter,
+                             public nsITimerCallback
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIMEMORYREPORTER
+  NS_DECL_NSITIMERCALLBACK
 
   /**
    * Use aGraphDriverRequested with SYSTEM_THREAD_DRIVER or AUDIO_THREAD_DRIVER
    * to create a MediaStreamGraph which provides support for real-time audio
    * and/or video.  Set it to OFFLINE_THREAD_DRIVER in order to create a
    * non-realtime instance which just churns through its inputs and produces
    * output.  Those objects currently only support audio, and are used to
    * implement OfflineAudioContext.  They do not support MediaStream inputs.
@@ -803,16 +806,19 @@ public:
   RefPtr<AsyncLatencyLogger> mLatencyLog;
   AudioMixer mMixer;
 #ifdef MOZ_WEBRTC
   RefPtr<AudioOutputObserver> mFarendObserverRef;
 #endif
 
   dom::AudioChannel AudioChannel() const { return mAudioChannel; }
 
+  // used to limit graph shutdown time
+  nsCOMPtr<nsITimer> mShutdownTimer;
+
 private:
   virtual ~MediaStreamGraphImpl();
 
   MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
 
   /**
    * This class uses manual memory management, and all pointers to it are raw
    * pointers. However, in order for it to implement nsIMemoryReporter, it needs
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/compression-random-data.js
@@ -0,0 +1,26 @@
+// Deterministic Math.random, from Octane.
+var seed = 49734321;
+function rand() {
+    // Robert Jenkins' 32 bit integer hash function.
+    seed = ((seed + 0x7ed55d16) + (seed << 12))  & 0xffffffff;
+    seed = ((seed ^ 0xc761c23c) ^ (seed >>> 19)) & 0xffffffff;
+    seed = ((seed + 0x165667b1) + (seed << 5))   & 0xffffffff;
+    seed = ((seed + 0xd3a2646c) ^ (seed << 9))   & 0xffffffff;
+    seed = ((seed + 0xfd7046c5) + (seed << 3))   & 0xffffffff;
+    seed = ((seed ^ 0xb55a4f09) ^ (seed >>> 16)) & 0xffffffff;
+    return (seed & 0xfffffff) / 0x10000000;
+}
+var randomS = "";
+function test(len) {
+    const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+    var s = "var data = '";
+    while (randomS.length < len) {
+        randomS += chars.charAt((rand() * chars.length)|0);
+    }
+    s += randomS;
+    s += "'; function f() {}; assertEq(f.toSource(), 'function f() {}');";
+    eval(s);
+}
+for (var i=32740; i<32780; i += 5) {
+    test(i);
+}
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -2259,16 +2259,19 @@ IonCompile(JSContext* cx, JSScript* scri
     }
 
     // If possible, compile the script off thread.
     if (options.offThreadCompilationAvailable()) {
         JitSpew(JitSpew_IonSyncLogs, "Can't log script %s:%" PRIuSIZE
                 ". (Compiled on background thread.)",
                 builderScript->filename(), builderScript->lineno());
 
+        if (!CreateMIRRootList(*builder))
+            return AbortReason_Alloc;
+
         if (!StartOffThreadIonCompile(cx, builder)) {
             JitSpew(JitSpew_IonAbort, "Unable to start off-thread ion compilation.");
             builder->graphSpewer().endFunction();
             return AbortReason_Alloc;
         }
 
         if (!recompile)
             builderScript->setIonScript(cx->runtime(), ION_COMPILING_SCRIPT);
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -4586,8 +4586,69 @@ jit::MakeLoopsContiguous(MIRGraph& graph
 
         // Move all blocks between header and backedge that aren't marked to
         // the end of the loop, making the loop itself contiguous.
         MakeLoopContiguous(graph, header, numMarked);
     }
 
     return true;
 }
+
+MRootList::MRootList(TempAllocator& alloc)
+{
+#define INIT_VECTOR(name, _0, _1) \
+    roots_[JS::RootKind::name].emplace(alloc);
+JS_FOR_EACH_TRACEKIND(INIT_VECTOR)
+#undef INIT_VECTOR
+}
+
+template <typename T>
+static void
+TraceVector(JSTracer* trc, const MRootList::RootVector& vector, const char* name)
+{
+    for (auto ptr : vector) {
+        T ptrT = static_cast<T>(ptr);
+        TraceManuallyBarrieredEdge(trc, &ptrT, name);
+        MOZ_ASSERT(ptr == ptrT, "Shouldn't move without updating MIR pointers");
+    }
+}
+
+void
+MRootList::trace(JSTracer* trc)
+{
+#define TRACE_ROOTS(name, type, _) \
+    TraceVector<type*>(trc, *roots_[JS::RootKind::name], "mir-root-" #name);
+JS_FOR_EACH_TRACEKIND(TRACE_ROOTS)
+#undef TRACE_ROOTS
+}
+
+MOZ_MUST_USE bool
+jit::CreateMIRRootList(IonBuilder& builder)
+{
+    MOZ_ASSERT(!builder.info().isAnalysis());
+
+    TempAllocator& alloc = builder.alloc();
+    MIRGraph& graph = builder.graph();
+
+    MRootList* roots = new(alloc.fallible()) MRootList(alloc);
+    if (!roots)
+        return false;
+
+    JSScript* prevScript = nullptr;
+
+    for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); block++) {
+
+        JSScript* script = block->info().script();
+        if (script != prevScript) {
+            if (!roots->append(script))
+                return false;
+            prevScript = script;
+        }
+
+        for (MInstructionIterator iter(block->begin()), end(block->end()); iter != end; iter++) {
+            if (!iter->appendRoots(*roots))
+                return false;
+        }
+    }
+
+    builder.setRootList(*roots);
+    return true;
+}
--- a/js/src/jit/IonAnalysis.h
+++ b/js/src/jit/IonAnalysis.h
@@ -61,16 +61,19 @@ RenumberBlocks(MIRGraph& graph);
 
 MOZ_MUST_USE bool
 AccountForCFGChanges(MIRGenerator* mir, MIRGraph& graph, bool updateAliasAnalysis,
                      bool underValueNumberer = false);
 
 MOZ_MUST_USE bool
 RemoveUnmarkedBlocks(MIRGenerator* mir, MIRGraph& graph, uint32_t numMarkedBlocks);
 
+MOZ_MUST_USE bool
+CreateMIRRootList(IonBuilder& builder);
+
 void
 ClearDominatorTree(MIRGraph& graph);
 
 MOZ_MUST_USE bool
 BuildDominatorTree(MIRGraph& graph);
 
 MOZ_MUST_USE bool
 BuildPhiReverseMapping(MIRGraph& graph);
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -120,16 +120,17 @@ IonBuilder::IonBuilder(JSContext* analys
                        const OptimizationInfo* optimizationInfo,
                        BaselineFrameInspector* baselineFrame, size_t inliningDepth,
                        uint32_t loopDepth)
   : MIRGenerator(comp, options, temp, graph, info, optimizationInfo),
     backgroundCodegen_(nullptr),
     actionableAbortScript_(nullptr),
     actionableAbortPc_(nullptr),
     actionableAbortMessage_(nullptr),
+    rootList_(nullptr),
     analysisContext(analysisContext),
     baselineFrame_(baselineFrame),
     constraints_(constraints),
     analysis_(*temp, info->script()),
     thisTypes(nullptr),
     argTypes(nullptr),
     typeArray(nullptr),
     typeArrayHint(0),
@@ -14630,12 +14631,11 @@ IonBuilder::convertToBoolean(MDefinition
 }
 
 void
 IonBuilder::trace(JSTracer* trc)
 {
     if (!compartment->runtime()->runtimeMatches(trc->runtime()))
         return;
 
-    TraceManuallyBarrieredEdge(trc, &script_, "IonBuiler::script_");
-    if (actionableAbortScript_)
-        TraceManuallyBarrieredEdge(trc, &actionableAbortScript_, "IonBuiler::actionableAbortScript_");
-}
+    MOZ_ASSERT(rootList_);
+    rootList_->trace(trc);
+}
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -1109,17 +1109,23 @@ class IonBuilder
     // Some aborts are actionable (e.g., using an unsupported bytecode). When
     // optimization tracking is enabled, the location and message of the abort
     // are recorded here so they may be propagated to the script's
     // corresponding JitcodeGlobalEntry::BaselineEntry.
     JSScript* actionableAbortScript_;
     jsbytecode* actionableAbortPc_;
     const char* actionableAbortMessage_;
 
+    MRootList* rootList_;
+
   public:
+    void setRootList(MRootList& rootList) {
+        MOZ_ASSERT(!rootList_);
+        rootList_ = &rootList;
+    }
     void clearForBackEnd();
     JSObject* checkNurseryObject(JSObject* obj);
 
     JSScript* script() const { return script_; }
     bool scriptHasIonScript() const { return scriptHasIonScript_; }
 
     CodeGenerator* backgroundCodegen() const { return backgroundCodegen_; }
     void setBackgroundCodegen(CodeGenerator* codegen) { backgroundCodegen_ = codegen; }
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -5289,16 +5289,26 @@ InlinePropertyTable::buildTypeSetForFunc
         return nullptr;
     for (size_t i = 0; i < numEntries(); i++) {
         if (entries_[i]->func == func)
             types->addType(TypeSet::ObjectType(entries_[i]->group), alloc);
     }
     return types;
 }
 
+bool
+InlinePropertyTable::appendRoots(MRootList& roots) const
+{
+    for (const Entry* entry : entries_) {
+        if (!entry->appendRoots(roots))
+            return false;
+    }
+    return true;
+}
+
 SharedMem<void*>
 MLoadTypedArrayElementStatic::base() const
 {
     return someTypedArray_->as<TypedArrayObject>().viewDataEither();
 }
 
 size_t
 MLoadTypedArrayElementStatic::length() const
@@ -5374,16 +5384,105 @@ MGetPropertyPolymorphic::mightAlias(cons
         }
 
         return AliasType::MayAlias;
     }
 
     return AliasType::NoAlias;
 }
 
+bool
+MGetPropertyPolymorphic::appendRoots(MRootList& roots) const
+{
+    if (!roots.append(name_))
+        return false;
+
+    for (const PolymorphicEntry& entry : receivers_) {
+        if (!entry.appendRoots(roots))
+            return false;
+    }
+
+    return true;
+}
+
+bool
+MSetPropertyPolymorphic::appendRoots(MRootList& roots) const
+{
+    if (!roots.append(name_))
+        return false;
+
+    for (const PolymorphicEntry& entry : receivers_) {
+        if (!entry.appendRoots(roots))
+            return false;
+    }
+
+    return true;
+}
+
+bool
+MGuardReceiverPolymorphic::appendRoots(MRootList& roots) const
+{
+    for (const ReceiverGuard& guard : receivers_) {
+        if (!roots.append(guard))
+            return false;
+    }
+    return true;
+}
+
+bool
+MDispatchInstruction::appendRoots(MRootList& roots) const
+{
+    for (const Entry& entry : map_) {
+        if (!entry.appendRoots(roots))
+            return false;
+    }
+    return true;
+}
+
+bool
+MObjectGroupDispatch::appendRoots(MRootList& roots) const
+{
+    if (inlinePropertyTable_ && !inlinePropertyTable_->appendRoots(roots))
+        return false;
+    return MDispatchInstruction::appendRoots(roots);
+}
+
+bool
+MFunctionDispatch::appendRoots(MRootList& roots) const
+{
+    return MDispatchInstruction::appendRoots(roots);
+}
+
+bool
+MConstant::appendRoots(MRootList& roots) const
+{
+    switch (type()) {
+      case MIRType::String:
+        return roots.append(toString());
+      case MIRType::Symbol:
+        return roots.append(toSymbol());
+      case MIRType::Object:
+        return roots.append(&toObject());
+      case MIRType::Undefined:
+      case MIRType::Null:
+      case MIRType::Boolean:
+      case MIRType::Int32:
+      case MIRType::Double:
+      case MIRType::Float32:
+      case MIRType::MagicOptimizedArguments:
+      case MIRType::MagicOptimizedOut:
+      case MIRType::MagicHole:
+      case MIRType::MagicIsConstructing:
+      case MIRType::MagicUninitializedLexical:
+        return true;
+      default:
+        MOZ_CRASH("Unexpected type");
+    }
+}
+
 void
 MGetPropertyCache::setBlock(MBasicBlock* block)
 {
     MDefinition::setBlock(block);
     // Track where we started.
     if (!location_.pc) {
         location_.pc = block->trackedPc();
         location_.script = block->info().script();
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -1020,16 +1020,92 @@ class MUseDefIterator
     MUse* use() const {
         return *current_;
     }
     MDefinition* def() const {
         return current_->consumer()->toDefinition();
     }
 };
 
+#ifdef DEBUG
+bool
+IonCompilationCanUseNurseryPointers();
+#endif
+
+// Helper class to check that GC pointers embedded in MIR instructions are in
+// in the nursery only when the store buffer has been marked as needing to
+// cancel all ion compilations. Otherwise, off-thread Ion compilation and
+// nursery GCs can happen in parallel, so it's invalid to store pointers to
+// nursery things. There's no need to root these pointers, as GC is suppressed
+// during compilation and off-thread compilations are canceled on major GCs.
+template <typename T>
+class CompilerGCPointer
+{
+    js::gc::Cell* ptr_;
+
+  public:
+    explicit CompilerGCPointer(T ptr)
+      : ptr_(ptr)
+    {
+        MOZ_ASSERT_IF(IsInsideNursery(ptr), IonCompilationCanUseNurseryPointers());
+#ifdef DEBUG
+        PerThreadData* pt = TlsPerThreadData.get();
+        MOZ_ASSERT_IF(pt->runtimeIfOnOwnerThread(), pt->suppressGC);
+#endif
+    }
+
+    operator T() const { return static_cast<T>(ptr_); }
+    T operator->() const { return static_cast<T>(ptr_); }
+
+  private:
+    CompilerGCPointer() = delete;
+    CompilerGCPointer(const CompilerGCPointer<T>&) = delete;
+    CompilerGCPointer<T>& operator=(const CompilerGCPointer<T>&) = delete;
+};
+
+typedef CompilerGCPointer<JSObject*> CompilerObject;
+typedef CompilerGCPointer<NativeObject*> CompilerNativeObject;
+typedef CompilerGCPointer<JSFunction*> CompilerFunction;
+typedef CompilerGCPointer<JSScript*> CompilerScript;
+typedef CompilerGCPointer<PropertyName*> CompilerPropertyName;
+typedef CompilerGCPointer<Shape*> CompilerShape;
+typedef CompilerGCPointer<ObjectGroup*> CompilerObjectGroup;
+
+class MRootList : public TempObject
+{
+  public:
+    using RootVector = Vector<void*, 0, JitAllocPolicy>;
+
+  private:
+    mozilla::EnumeratedArray<JS::RootKind, JS::RootKind::Limit, mozilla::Maybe<RootVector>> roots_;
+
+    MRootList(const MRootList&) = delete;
+    void operator=(const MRootList&) = delete;
+
+  public:
+    explicit MRootList(TempAllocator& alloc);
+
+    void trace(JSTracer* trc);
+
+    template <typename T>
+    MOZ_MUST_USE bool append(T ptr) {
+        if (ptr)
+            return roots_[JS::MapTypeToRootKind<T>::kind]->append(ptr);
+        return true;
+    }
+
+    template <typename T>
+    MOZ_MUST_USE bool append(const CompilerGCPointer<T>& ptr) {
+        return append(static_cast<T>(ptr));
+    }
+    MOZ_MUST_USE bool append(const ReceiverGuard& guard) {
+        return append(guard.group) && append(guard.shape);
+    }
+};
+
 // An instruction is an SSA name that is inserted into a basic block's IR
 // stream.
 class MInstruction
   : public MDefinition,
     public InlineListNode<MInstruction>
 {
     MResumePoint* resumePoint_;
 
@@ -1081,16 +1157,22 @@ class MInstruction
     // resume points.
     virtual bool canClone() const {
         return false;
     }
     virtual MInstruction* clone(TempAllocator& alloc, const MDefinitionVector& inputs) const {
         MOZ_CRASH();
     }
 
+    // MIR instructions containing GC pointers should override this to append
+    // these pointers to the root list.
+    virtual bool appendRoots(MRootList& roots) const {
+        return true;
+    }
+
     // Instructions needing to hook into type analysis should return a
     // TypePolicy.
     virtual TypePolicy* typePolicy() = 0;
     virtual MIRType typePolicySpecialization() = 0;
 };
 
 #define INSTRUCTION_HEADER_WITHOUT_TYPEPOLICY(opcode)                       \
     static const Opcode classOpcode = MDefinition::Op_##opcode;             \
@@ -1613,16 +1695,18 @@ class MConstant : public MNullaryInstruc
             return toDouble();
         return toFloat32();
     }
 
     // Convert this constant to a js::Value. Float32 constants will be stored
     // as DoubleValue and NaNs are canonicalized. Callers must be careful: not
     // all constants can be represented by js::Value (wasm supports int64).
     Value toJSValue() const;
+
+    bool appendRoots(MRootList& roots) const override;
 };
 
 // Generic constructor of SIMD valuesX4.
 class MSimdValueX4
   : public MQuaternaryInstruction,
     public Mix4Policy<SimdScalarPolicy<0>, SimdScalarPolicy<1>,
                       SimdScalarPolicy<2>, SimdScalarPolicy<3> >::Data
 {
@@ -3114,60 +3198,16 @@ TypeSetIncludes(TypeSet* types, MIRType 
 bool
 EqualTypes(MIRType type1, TemporaryTypeSet* typeset1,
            MIRType type2, TemporaryTypeSet* typeset2);
 
 bool
 CanStoreUnboxedType(TempAllocator& alloc,
                     JSValueType unboxedType, MIRType input, TypeSet* inputTypes);
 
-#ifdef DEBUG
-bool
-IonCompilationCanUseNurseryPointers();
-#endif
-
-// Helper class to check that GC pointers embedded in MIR instructions are in
-// in the nursery only when the store buffer has been marked as needing to
-// cancel all ion compilations. Otherwise, off-thread Ion compilation and
-// nursery GCs can happen in parallel, so it's invalid to store pointers to
-// nursery things. There's no need to root these pointers, as GC is suppressed
-// during compilation and off-thread compilations are canceled on major GCs.
-template <typename T>
-class CompilerGCPointer
-{
-    js::gc::Cell* ptr_;
-
-  public:
-    explicit CompilerGCPointer(T ptr)
-      : ptr_(ptr)
-    {
-        MOZ_ASSERT_IF(IsInsideNursery(ptr), IonCompilationCanUseNurseryPointers());
-#ifdef DEBUG
-        PerThreadData* pt = TlsPerThreadData.get();
-        MOZ_ASSERT_IF(pt->runtimeIfOnOwnerThread(), pt->suppressGC);
-#endif
-    }
-
-    operator T() const { return static_cast<T>(ptr_); }
-    T operator->() const { return static_cast<T>(ptr_); }
-
-  private:
-    CompilerGCPointer() = delete;
-    CompilerGCPointer(const CompilerGCPointer<T>&) = delete;
-    CompilerGCPointer<T>& operator=(const CompilerGCPointer<T>&) = delete;
-};
-
-typedef CompilerGCPointer<JSObject*> CompilerObject;
-typedef CompilerGCPointer<NativeObject*> CompilerNativeObject;
-typedef CompilerGCPointer<JSFunction*> CompilerFunction;
-typedef CompilerGCPointer<JSScript*> CompilerScript;
-typedef CompilerGCPointer<PropertyName*> CompilerPropertyName;
-typedef CompilerGCPointer<Shape*> CompilerShape;
-typedef CompilerGCPointer<ObjectGroup*> CompilerObjectGroup;
-
 class MNewArray
   : public MUnaryInstruction,
     public NoTypePolicy::Data
 {
   private:
     // Number of elements to allocate for the array.
     uint32_t length_;
 
@@ -3262,16 +3302,20 @@ class MNewArrayCopyOnWrite : public MNul
 
     gc::InitialHeap initialHeap() const {
         return initialHeap_;
     }
 
     virtual AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
+
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(templateObject_);
+    }
 };
 
 class MNewArrayDynamicLength
   : public MUnaryInstruction,
     public IntPolicy<0>::Data
 {
     CompilerObject templateObject_;
     gc::InitialHeap initialHeap_;
@@ -3298,16 +3342,20 @@ class MNewArrayDynamicLength
     }
     gc::InitialHeap initialHeap() const {
         return initialHeap_;
     }
 
     virtual AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
+
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(templateObject_);
+    }
 };
 
 class MNewTypedArray : public MNullaryInstruction
 {
     CompilerGCPointer<TypedArrayObject*> templateObject_;
     gc::InitialHeap initialHeap_;
 
     MNewTypedArray(CompilerConstraintList* constraints, TypedArrayObject* templateObject,
@@ -3337,16 +3385,20 @@ class MNewTypedArray : public MNullaryIn
 
     gc::InitialHeap initialHeap() const {
         return initialHeap_;
     }
 
     virtual AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
+
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(templateObject_);
+    }
 };
 
 class MNewTypedArrayDynamicLength
   : public MUnaryInstruction,
     public IntPolicy<0>::Data
 {
     CompilerObject templateObject_;
     gc::InitialHeap initialHeap_;
@@ -3381,16 +3433,20 @@ class MNewTypedArrayDynamicLength
     }
     gc::InitialHeap initialHeap() const {
         return initialHeap_;
     }
 
     virtual AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
+
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(templateObject_);
+    }
 };
 
 class MNewObject
   : public MUnaryInstruction,
     public NoTypePolicy::Data
 {
   public:
     enum Mode { ObjectLiteral, ObjectCreate };
@@ -3482,16 +3538,20 @@ class MNewTypedObject : public MNullaryI
 
     gc::InitialHeap initialHeap() const {
         return initialHeap_;
     }
 
     virtual AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
+
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(templateObject_);
+    }
 };
 
 class MTypedObjectDescr
   : public MUnaryInstruction,
     public SingleObjectPolicy::Data
 {
   private:
     explicit MTypedObjectDescr(MDefinition* object)
@@ -3575,16 +3635,20 @@ class MSimdBox
         return AliasSet::None();
     }
 
     void printOpcode(GenericPrinter& out) const override;
     MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
     bool canRecoverOnBailout() const override {
         return true;
     }
+
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(templateObject_);
+    }
 };
 
 class MSimdUnbox
   : public MUnaryInstruction,
     public SingleObjectPolicy::Data
 {
   protected:
     SimdType simdType_;
@@ -3871,16 +3935,20 @@ class MInitProp
 
     PropertyName* propertyName() const {
         return name_;
     }
 
     bool possiblyCalls() const override {
         return true;
     }
+
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(name_);
+    }
 };
 
 class MInitPropGetterSetter
   : public MBinaryInstruction,
     public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> >::Data
 {
     CompilerPropertyName name_;
 
@@ -3892,16 +3960,20 @@ class MInitPropGetterSetter
   public:
     INSTRUCTION_HEADER(InitPropGetterSetter)
     TRIVIAL_NEW_WRAPPERS
     NAMED_OPERANDS((0, object), (1, value))
 
     PropertyName* name() const {
         return name_;
     }
+
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(name_);
+    }
 };
 
 class MInitElem
   : public MAryInstruction<3>,
     public Mix3Policy<ObjectPolicy<0>, BoxPolicy<1>, BoxPolicy<2> >::Data
 {
     MInitElem(MDefinition* obj, MDefinition* id, MDefinition* value)
     {
@@ -3958,16 +4030,20 @@ class WrappedFunction : public TempObjec
     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_; }
+
+    bool appendRoots(MRootList& roots) const {
+        return roots.append(fun_);
+    }
 };
 
 class MCall
   : public MVariadicInstruction,
     public CallPolicy::Data
 {
   private:
     // An MCall uses the MPrepareCall, MDefinition for the function, and
@@ -4066,16 +4142,22 @@ class MCall
     }
 
     // A method that can be called to tell the MCall to figure out whether it's
     // movable or not.  This can't be done in the constructor, because it
     // depends on the arguments to the call, and those aren't passed to the
     // constructor but are set up later via addArg.
     virtual void computeMovable() {
     }
+
+    bool appendRoots(MRootList& roots) const override {
+        if (target_)
+            return target_->appendRoots(roots);
+        return true;
+    }
 };
 
 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.
@@ -4158,16 +4240,22 @@ class MApplyArgs
     // For TI-informed monomorphic callsites.
     WrappedFunction* getSingleTarget() const {
         return target_;
     }
 
     bool possiblyCalls() const override {
         return true;
     }
+
+    bool appendRoots(MRootList& roots) const override {
+        if (target_)
+            return target_->appendRoots(roots);
+        return true;
+    }
 };
 
 // fun.apply(fn, array)
 class MApplyArray
   : public MAryInstruction<3>,
     public Mix3Policy<ObjectPolicy<0>, ObjectPolicy<1>, BoxPolicy<2> >::Data
 {
   protected:
@@ -4191,16 +4279,22 @@ class MApplyArray
     // For TI-informed monomorphic callsites.
     WrappedFunction* getSingleTarget() const {
         return target_;
     }
 
     bool possiblyCalls() const override {
         return true;
     }
+
+    bool appendRoots(MRootList& roots) const override {
+        if (target_)
+            return target_->appendRoots(roots);
+        return true;
+    }
 };
 
 class MBail : public MNullaryInstruction
 {
   protected:
     explicit MBail(BailoutKind kind)
       : MNullaryInstruction()
     {
@@ -4945,16 +5039,20 @@ class MCreateArgumentsObject
 
     AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
 
     bool possiblyCalls() const override {
         return true;
     }
+
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(templateObj_);
+    }
 };
 
 class MGetArgumentsObjectArg
   : public MUnaryInstruction,
     public ObjectPolicy<0>::Data
 {
     size_t argno_;
 
@@ -7864,16 +7962,19 @@ class MDefVar
     }
     unsigned attrs() const {
         return attrs_;
     }
 
     bool possiblyCalls() const override {
         return true;
     }
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(name_);
+    }
 };
 
 class MDefLexical
   : public MNullaryInstruction
 {
     CompilerPropertyName name_; // Target name to be defined.
     unsigned attrs_; // Attributes to be set.
 
@@ -7888,16 +7989,19 @@ class MDefLexical
     TRIVIAL_NEW_WRAPPERS
 
     PropertyName* name() const {
         return name_;
     }
     unsigned attrs() const {
         return attrs_;
     }
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(name_);
+    }
 };
 
 class MDefFun
   : public MUnaryInstruction,
     public NoTypePolicy::Data
 {
     CompilerFunction fun_;
 
@@ -7913,16 +8017,19 @@ class MDefFun
     NAMED_OPERANDS((0, environmentChain))
 
     JSFunction* fun() const {
         return fun_;
     }
     bool possiblyCalls() const override {
         return true;
     }
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(fun_);
+    }
 };
 
 class MRegExp : public MNullaryInstruction
 {
     CompilerGCPointer<RegExpObject*> source_;
     bool mustClone_;
 
     MRegExp(CompilerConstraintList* constraints, RegExpObject* source)
@@ -7947,16 +8054,19 @@ class MRegExp : public MNullaryInstructi
         return source_;
     }
     AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
     bool possiblyCalls() const override {
         return true;
     }
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(source_);
+    }
 };
 
 class MRegExpMatcher
   : public MAryInstruction<3>,
     public Mix3Policy<ObjectPolicy<0>,
                       StringPolicy<1>,
                       IntPolicy<2> >::Data
 {
@@ -8218,16 +8328,24 @@ struct LambdaFunctionInfo
       : fun(fun), flags(fun->flags()), nargs(fun->nargs()),
         scriptOrLazyScript(fun->hasScript()
                            ? (gc::Cell*) fun->nonLazyScript()
                            : (gc::Cell*) fun->lazyScript()),
         singletonType(fun->isSingleton()),
         useSingletonForClone(ObjectGroup::useSingletonForClone(fun))
     {}
 
+    bool appendRoots(MRootList& roots) const {
+        if (!roots.append(fun))
+            return false;
+        if (fun->hasScript())
+            return roots.append(fun->nonLazyScript());
+        return roots.append(fun->lazyScript());
+    }
+
   private:
     LambdaFunctionInfo(const LambdaFunctionInfo&) = delete;
     void operator=(const LambdaFunctionInfo&) = delete;
 };
 
 class MLambda
   : public MBinaryInstruction,
     public SingleObjectPolicy::Data
@@ -8252,16 +8370,19 @@ class MLambda
     }
     const LambdaFunctionInfo& info() const {
         return info_;
     }
     MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
     bool canRecoverOnBailout() const override {
         return true;
     }
+    bool appendRoots(MRootList& roots) const override {
+        return info_.appendRoots(roots);
+    }
 };
 
 class MLambdaArrow
   : public MBinaryInstruction,
     public MixPolicy<ObjectPolicy<0>, BoxPolicy<1>>::Data
 {
     const LambdaFunctionInfo info_;
 
@@ -8278,16 +8399,19 @@ class MLambdaArrow
   public:
     INSTRUCTION_HEADER(LambdaArrow)
     TRIVIAL_NEW_WRAPPERS
     NAMED_OPERANDS((0, environmentChain), (1, newTargetDef))
 
     const LambdaFunctionInfo& info() const {
         return info_;
     }
+    bool appendRoots(MRootList& roots) const override {
+        return info_.appendRoots(roots);
+    }
 };
 
 // Returns obj->slots.
 class MSlots
   : public MUnaryInstruction,
     public SingleObjectPolicy::Data
 {
     explicit MSlots(MDefinition* object)
@@ -9564,16 +9688,19 @@ class MConvertUnboxedObjectToNative
         // in this opcode).
         //
         // Later accesses can assume the object has a native representation
         // and optimize accordingly. Those accesses cannot be reordered before
         // this instruction, however. This is prevented by chaining this
         // instruction with the object itself, in the same way as MBoundsCheck.
         return AliasSet::None();
     }
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(group_);
+    }
 };
 
 // Array.prototype.pop or Array.prototype.shift on a dense array.
 class MArrayPopShift
   : public MUnaryInstruction,
     public SingleObjectPolicy::Data
 {
   public:
@@ -9688,16 +9815,19 @@ class MArraySlice
 
     AliasSet getAliasSet() const override {
         return AliasSet::Store(AliasSet::BoxedOrUnboxedElements(unboxedType()) |
                                AliasSet::ObjectFields);
     }
     bool possiblyCalls() const override {
         return true;
     }
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(templateObj_);
+    }
 };
 
 class MArrayJoin
     : public MBinaryInstruction,
       public MixPolicy<ObjectPolicy<0>, StringPolicy<1> >::Data
 {
     MArrayJoin(MDefinition* array, MDefinition* sep)
         : MBinaryInstruction(array, sep)
@@ -9950,16 +10080,20 @@ class MLoadTypedArrayElementStatic
     void setInfallible() {
         fallible_ = false;
     }
 
     void computeRange(TempAllocator& alloc) override;
     bool needTruncation(TruncateKind kind) override;
     bool canProduceFloat32() const override { return accessType() == Scalar::Float32; }
     void collectRangeInfoPreTrunc() override;
+
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(someTypedArray_);
+    }
 };
 
 // Base class for MIR ops that write unboxed scalar values.
 class StoreUnboxedScalarBase
 {
     Scalar::Type writeType_;
 
   protected:
@@ -10163,16 +10297,20 @@ class MStoreTypedArrayElementStatic :
         return AliasSet::Store(AliasSet::UnboxedElement);
     }
     TruncateKind operandTruncateKind(size_t index) const override;
 
     bool canConsumeFloat32(MUse* use) const override {
         return use == getUseFor(1) && accessType() == Scalar::Float32;
     }
     void collectRangeInfoPreTrunc() override;
+
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(someTypedArray_);
+    }
 };
 
 // Compute an "effective address", i.e., a compound computation of the form:
 //   base + index * scale + displacement
 class MEffectiveAddress
   : public MBinaryInstruction,
     public NoTypePolicy::Data
 {
@@ -10386,16 +10524,19 @@ class InlinePropertyTable : public TempO
 {
     struct Entry : public TempObject {
         CompilerObjectGroup group;
         CompilerFunction func;
 
         Entry(ObjectGroup* group, JSFunction* func)
           : group(group), func(func)
         { }
+        bool appendRoots(MRootList& roots) const {
+            return roots.append(group) && roots.append(func);
+        }
     };
 
     jsbytecode* pc_;
     MResumePoint* priorResumePoint_;
     Vector<Entry*, 4, JitAllocPolicy> entries_;
 
   public:
     InlinePropertyTable(TempAllocator& alloc, jsbytecode* pc)
@@ -10439,16 +10580,18 @@ class InlinePropertyTable : public TempO
 
     TemporaryTypeSet* buildTypeSetForFunction(JSFunction* func) const;
 
     // Remove targets that vetoed inlining from the InlinePropertyTable.
     void trimTo(const ObjectVector& targets, const BoolVector& choiceSet);
 
     // Ensure that the InlinePropertyTable's domain is a subset of |targets|.
     void trimToTargets(const ObjectVector& targets);
+
+    bool appendRoots(MRootList& roots) const;
 };
 
 class CacheLocationList : public InlineConcatList<CacheLocationList>
 {
   public:
     CacheLocationList()
       : pc(nullptr),
         script(nullptr)
@@ -10533,33 +10676,43 @@ class MGetPropertyCache
         }
         return AliasSet::Store(AliasSet::Any);
     }
 
     void setBlock(MBasicBlock* block) override;
     MOZ_MUST_USE bool updateForReplacement(MDefinition* ins) override;
 
     bool allowDoubleResult() const;
+
+    bool appendRoots(MRootList& roots) const override {
+        if (inlinePropertyTable_)
+            return inlinePropertyTable_->appendRoots(roots);
+        return true;
+    }
+};
+
+struct PolymorphicEntry {
+    // The group and/or shape to guard against.
+    ReceiverGuard receiver;
+
+    // The property to load, null for loads from unboxed properties.
+    Shape* shape;
+
+    bool appendRoots(MRootList& roots) const {
+        return roots.append(receiver) && roots.append(shape);
+    }
 };
 
 // Emit code to load a value from an object if it matches one of the receivers
 // observed by the baseline IC, else bails out.
 class MGetPropertyPolymorphic
   : public MUnaryInstruction,
     public SingleObjectPolicy::Data
 {
-    struct Entry {
-        // The group and/or shape to guard against.
-        ReceiverGuard receiver;
-
-        // The property to load, null for loads from unboxed properties.
-        Shape* shape;
-    };
-
-    Vector<Entry, 4, JitAllocPolicy> receivers_;
+    Vector<PolymorphicEntry, 4, JitAllocPolicy> receivers_;
     CompilerPropertyName name_;
 
     MGetPropertyPolymorphic(TempAllocator& alloc, MDefinition* obj, PropertyName* name)
       : MUnaryInstruction(obj),
         receivers_(alloc),
         name_(name)
     {
         setGuard();
@@ -10579,17 +10732,17 @@ class MGetPropertyPolymorphic
         if (!ins->isGetPropertyPolymorphic())
             return false;
         if (name() != ins->toGetPropertyPolymorphic()->name())
             return false;
         return congruentIfOperandsEqual(ins);
     }
 
     MOZ_MUST_USE bool addReceiver(const ReceiverGuard& receiver, Shape* shape) {
-        Entry entry;
+        PolymorphicEntry entry;
         entry.receiver = receiver;
         entry.shape = shape;
         return receivers_.append(entry);
     }
     size_t numReceivers() const {
         return receivers_.length();
     }
     const ReceiverGuard receiver(size_t i) const {
@@ -10611,33 +10764,27 @@ class MGetPropertyPolymorphic
         }
         return AliasSet::Load(AliasSet::ObjectFields |
                               AliasSet::FixedSlot |
                               AliasSet::DynamicSlot |
                               (hasUnboxedLoad ? AliasSet::UnboxedElement : 0));
     }
 
     AliasType mightAlias(const MDefinition* store) const override;
+
+    bool appendRoots(MRootList& roots) const override;
 };
 
 // Emit code to store a value to an object's slots if its shape/group matches
 // one of the shapes/groups observed by the baseline IC, else bails out.
 class MSetPropertyPolymorphic
   : public MBinaryInstruction,
     public MixPolicy<SingleObjectPolicy, NoFloatPolicy<1> >::Data
 {
-    struct Entry {
-        // The group and/or shape to guard against.
-        ReceiverGuard receiver;
-
-        // The property to store, null for stores to unboxed properties.
-        Shape* shape;
-    };
-
-    Vector<Entry, 4, JitAllocPolicy> receivers_;
+    Vector<PolymorphicEntry, 4, JitAllocPolicy> receivers_;
     CompilerPropertyName name_;
     bool needsBarrier_;
 
     MSetPropertyPolymorphic(TempAllocator& alloc, MDefinition* obj, MDefinition* value,
                             PropertyName* name)
       : MBinaryInstruction(obj, value),
         receivers_(alloc),
         name_(name),
@@ -10650,17 +10797,17 @@ class MSetPropertyPolymorphic
     NAMED_OPERANDS((0, object), (1, value))
 
     static MSetPropertyPolymorphic* New(TempAllocator& alloc, MDefinition* obj, MDefinition* value,
                                         PropertyName* name) {
         return new(alloc) MSetPropertyPolymorphic(alloc, obj, value, name);
     }
 
     MOZ_MUST_USE bool addReceiver(const ReceiverGuard& receiver, Shape* shape) {
-        Entry entry;
+        PolymorphicEntry entry;
         entry.receiver = receiver;
         entry.shape = shape;
         return receivers_.append(entry);
     }
     size_t numReceivers() const {
         return receivers_.length();
     }
     const ReceiverGuard& receiver(size_t i) const {
@@ -10686,16 +10833,17 @@ class MSetPropertyPolymorphic
                 break;
             }
         }
         return AliasSet::Store(AliasSet::ObjectFields |
                                AliasSet::FixedSlot |
                                AliasSet::DynamicSlot |
                                (hasUnboxedStore ? AliasSet::UnboxedElement : 0));
     }
+    bool appendRoots(MRootList& roots) const override;
 };
 
 class MDispatchInstruction
   : public MControlInstruction,
     public SingleObjectPolicy::Data
 {
     // Map from JSFunction* -> MBasicBlock.
     struct Entry {
@@ -10704,16 +10852,19 @@ class MDispatchInstruction
         // |funcGroup| holds the ObjectGroup for |func|, and dispatch guards
         // on the group instead of directly on the function.
         ObjectGroup* funcGroup;
         MBasicBlock* block;
 
         Entry(JSFunction* func, ObjectGroup* funcGroup, MBasicBlock* block)
           : func(func), funcGroup(funcGroup), block(block)
         { }
+        bool appendRoots(MRootList& roots) const {
+            return roots.append(func) && roots.append(funcGroup);
+        }
     };
     Vector<Entry, 4, JitAllocPolicy> map_;
 
     // An optional fallback path that uses MCall.
     MBasicBlock* fallback_;
     MUse operand_;
 
     void initOperand(size_t index, MDefinition* operand) {
@@ -10798,16 +10949,17 @@ class MDispatchInstruction
     void addFallback(MBasicBlock* block) {
         MOZ_ASSERT(!hasFallback());
         fallback_ = block;
     }
     MBasicBlock* getFallback() const {
         MOZ_ASSERT(hasFallback());
         return fallback_;
     }
+    bool appendRoots(MRootList& roots) const override;
 };
 
 // Polymorphic dispatch for inlining, keyed off incoming ObjectGroup.
 class MObjectGroupDispatch : public MDispatchInstruction
 {
     // Map ObjectGroup (of CallProp's Target Object) -> JSFunction (yielded by the CallProp).
     InlinePropertyTable* inlinePropertyTable_;
 
@@ -10823,31 +10975,33 @@ class MObjectGroupDispatch : public MDis
                                      InlinePropertyTable* table)
     {
         return new(alloc) MObjectGroupDispatch(alloc, ins, table);
     }
 
     InlinePropertyTable* propTable() const {
         return inlinePropertyTable_;
     }
+    bool appendRoots(MRootList& roots) const override;
 };
 
 // Polymorphic dispatch for inlining, keyed off incoming JSFunction*.
 class MFunctionDispatch : public MDispatchInstruction
 {
     MFunctionDispatch(TempAllocator& alloc, MDefinition* input)
       : MDispatchInstruction(alloc, input)
     { }
 
   public:
     INSTRUCTION_HEADER(FunctionDispatch)
 
     static MFunctionDispatch* New(TempAllocator& alloc, MDefinition* ins) {
         return new(alloc) MFunctionDispatch(alloc, ins);
     }
+    bool appendRoots(MRootList& roots) const override;
 };
 
 class MBindNameCache
   : public MUnaryInstruction,
     public SingleObjectPolicy::Data
 {
     CompilerPropertyName name_;
     CompilerScript script_;
@@ -10868,16 +11022,20 @@ class MBindNameCache
         return name_;
     }
     JSScript* script() const {
         return script_;
     }
     jsbytecode* pc() const {
         return pc_;
     }
+    bool appendRoots(MRootList& roots) const override {
+        // Don't append the script, all scripts are added anyway.
+        return roots.append(name_);
+    }
 };
 
 class MCallBindVar
   : public MUnaryInstruction,
     public SingleObjectPolicy::Data
 {
     explicit MCallBindVar(MDefinition* envChain)
       : MUnaryInstruction(envChain)
@@ -10944,16 +11102,19 @@ class MGuardShape
             return false;
         if (bailoutKind() != ins->toGuardShape()->bailoutKind())
             return false;
         return congruentIfOperandsEqual(ins);
     }
     AliasSet getAliasSet() const override {
         return AliasSet::Load(AliasSet::ObjectFields);
     }
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(shape_);
+    }
 };
 
 // Bail if the object's shape or unboxed group is not in the input list.
 class MGuardReceiverPolymorphic
   : public MUnaryInstruction,
     public SingleObjectPolicy::Data
 {
     Vector<ReceiverGuard, 4, JitAllocPolicy> receivers_;
@@ -10986,16 +11147,19 @@ class MGuardReceiverPolymorphic
         return receivers_[i];
     }
 
     bool congruentTo(const MDefinition* ins) const override;
 
     AliasSet getAliasSet() const override {
         return AliasSet::Load(AliasSet::ObjectFields);
     }
+
+    bool appendRoots(MRootList& roots) const override;
+
 };
 
 // Guard on an object's group, inclusively or exclusively.
 class MGuardObjectGroup
   : public MUnaryInstruction,
     public SingleObjectPolicy::Data
 {
     CompilerObjectGroup group_;
@@ -11042,16 +11206,19 @@ class MGuardObjectGroup
             return false;
         if (bailoutKind() != ins->toGuardObjectGroup()->bailoutKind())
             return false;
         return congruentIfOperandsEqual(ins);
     }
     AliasSet getAliasSet() const override {
         return AliasSet::Load(AliasSet::ObjectFields);
     }
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(group_);
+    }
 };
 
 // Guard on an object's identity, inclusively or exclusively.
 class MGuardObjectIdentity
   : public MBinaryInstruction,
     public SingleObjectPolicy::Data
 {
     bool bailOnEquality_;
@@ -11343,16 +11510,19 @@ class MGetNameCache
     NAMED_OPERANDS((0, envObj))
 
     PropertyName* name() const {
         return name_;
     }
     AccessKind accessKind() const {
         return kind_;
     }
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(name_);
+    }
 };
 
 class MCallGetIntrinsicValue : public MNullaryInstruction
 {
     CompilerPropertyName name_;
 
     explicit MCallGetIntrinsicValue(PropertyName* name)
       : name_(name)
@@ -11368,16 +11538,19 @@ class MCallGetIntrinsicValue : public MN
         return name_;
     }
     AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
     bool possiblyCalls() const override {
         return true;
     }
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(name_);
+    }
 };
 
 class MSetPropertyInstruction : public MBinaryInstruction
 {
     CompilerPropertyName name_;
     bool strict_;
 
   protected:
@@ -11390,16 +11563,19 @@ class MSetPropertyInstruction : public M
   public:
     NAMED_OPERANDS((0, object), (1, value))
     PropertyName* name() const {
         return name_;
     }
     bool strict() const {
         return strict_;
     }
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(name_);
+    }
 };
 
 class MSetElementInstruction
   : public MTernaryInstruction
 {
     bool strict_;
   protected:
     MSetElementInstruction(MDefinition* object, MDefinition* index, MDefinition* value, bool strict)
@@ -11437,16 +11613,19 @@ class MDeleteProperty
     NAMED_OPERANDS((0, value))
 
     PropertyName* name() const {
         return name_;
     }
     bool strict() const {
         return strict_;
     }
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(name_);
+    }
 };
 
 class MDeleteElement
   : public MBinaryInstruction,
     public BoxInputsPolicy::Data
 {
     bool strict_;
 
@@ -11554,16 +11733,19 @@ class MCallGetProperty
     AliasSet getAliasSet() const override {
         if (!idempotent_)
             return AliasSet::Store(AliasSet::Any);
         return AliasSet::None();
     }
     bool possiblyCalls() const override {
         return true;
     }
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(name_);
+    }
 };
 
 // Inline call to handle lhs[rhs]. The first input is a Value so that this
 // instruction can handle both objects and strings.
 class MCallGetElement
   : public MBinaryInstruction,
     public BoxInputsPolicy::Data
 {
@@ -12137,16 +12319,20 @@ class MInstanceOf
 
   public:
     INSTRUCTION_HEADER(InstanceOf)
     TRIVIAL_NEW_WRAPPERS
 
     JSObject* prototypeObject() {
         return protoObj_;
     }
+
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(protoObj_);
+    }
 };
 
 // Implementation for instanceof operator with unknown rhs.
 class MCallInstanceOf
   : public MBinaryInstruction,
     public MixPolicy<BoxPolicy<0>, ObjectPolicy<1> >::Data
 {
     MCallInstanceOf(MDefinition* obj, MDefinition* proto)
@@ -12311,16 +12497,19 @@ class MRest
     NAMED_OPERANDS((0, numActuals))
 
     AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
     bool possiblyCalls() const override {
         return true;
     }
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(templateObject());
+    }
 };
 
 class MFilterTypeSet
   : public MUnaryInstruction,
     public FilterTypeSetPolicy::Data
 {
     MFilterTypeSet(MDefinition* def, TemporaryTypeSet* types)
       : MUnaryInstruction(def)
@@ -12529,16 +12718,19 @@ class MNewNamedLambdaObject : public MNu
     TRIVIAL_NEW_WRAPPERS
 
     LexicalEnvironmentObject* templateObj() {
         return templateObj_;
     }
     AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(templateObj_);
+    }
 };
 
 class MNewCallObjectBase : public MNullaryInstruction
 {
     CompilerGCPointer<CallObject*> templateObj_;
 
   protected:
     explicit MNewCallObjectBase(CallObject* templateObj)
@@ -12550,16 +12742,19 @@ class MNewCallObjectBase : public MNulla
 
   public:
     CallObject* templateObject() {
         return templateObj_;
     }
     AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(templateObj_);
+    }
 };
 
 class MNewCallObject : public MNewCallObjectBase
 {
   public:
     INSTRUCTION_HEADER(NewCallObject)
 
     explicit MNewCallObject(CallObject* templateObj)
@@ -12604,16 +12799,20 @@ class MNewStringObject :
         setResultType(MIRType::Object);
     }
 
   public:
     INSTRUCTION_HEADER(NewStringObject)
     TRIVIAL_NEW_WRAPPERS
 
     StringObject* templateObj() const;
+
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(templateObj_);
+    }
 };
 
 // This is an alias for MLoadFixedSlot.
 class MEnclosingEnvironment : public MLoadFixedSlot
 {
     explicit MEnclosingEnvironment(MDefinition* obj)
       : MLoadFixedSlot(obj, EnvironmentObject::enclosingEnvironmentSlot())
     {
--- a/js/src/vm/Compression.cpp
+++ b/js/src/vm/Compression.cpp
@@ -28,16 +28,17 @@ zlib_free(void* cx, void* addr)
 {
     js_free(addr);
 }
 
 Compressor::Compressor(const unsigned char* inp, size_t inplen)
     : inp(inp),
       inplen(inplen),
       initialized(false),
+      finished(false),
       currentChunkSize(0),
       chunkOffsets()
 {
     MOZ_ASSERT(inplen > 0);
     zs.opaque = nullptr;
     zs.next_in = (Bytef*)inp;
     zs.avail_in = 0;
     zs.next_out = nullptr;
@@ -51,17 +52,17 @@ Compressor::Compressor(const unsigned ch
 
 Compressor::~Compressor()
 {
     if (initialized) {
         int ret = deflateEnd(&zs);
         if (ret != Z_OK) {
             // If we finished early, we can get a Z_DATA_ERROR.
             MOZ_ASSERT(ret == Z_DATA_ERROR);
-            MOZ_ASSERT(uInt(zs.next_in - inp) < inplen || !zs.avail_out);
+            MOZ_ASSERT(!finished);
         }
     }
 }
 
 // According to the zlib docs, the default value for windowBits is 15. Passing
 // -15 is treated the same, but it also forces 'raw deflate' (no zlib header or
 // trailer). Raw deflate is necessary for chunked decompression.
 static const int WindowBits = -15;
@@ -91,45 +92,49 @@ Compressor::setOutput(unsigned char* out
     zs.avail_out = outlen - outbytes;
 }
 
 Compressor::Status
 Compressor::compressMore()
 {
     MOZ_ASSERT(zs.next_out);
     uInt left = inplen - (zs.next_in - inp);
-    bool done = left <= MAX_INPUT_SIZE;
-    if (done)
+    if (left <= MAX_INPUT_SIZE)
         zs.avail_in = left;
     else if (zs.avail_in == 0)
         zs.avail_in = MAX_INPUT_SIZE;
 
     // Finish the current chunk if needed.
     bool flush = false;
     MOZ_ASSERT(currentChunkSize <= CHUNK_SIZE);
     if (currentChunkSize + zs.avail_in >= CHUNK_SIZE) {
         // Adjust avail_in, so we don't get chunks that are larger than
         // CHUNK_SIZE.
         zs.avail_in = CHUNK_SIZE - currentChunkSize;
         MOZ_ASSERT(currentChunkSize + zs.avail_in == CHUNK_SIZE);
         flush = true;
     }
 
+    MOZ_ASSERT(zs.avail_in <= left);
+    bool done = zs.avail_in == left;
+
     Bytef* oldin = zs.next_in;
     Bytef* oldout = zs.next_out;
     int ret = deflate(&zs, done ? Z_FINISH : (flush ? Z_FULL_FLUSH : Z_NO_FLUSH));
     outbytes += zs.next_out - oldout;
     currentChunkSize += zs.next_in - oldin;
     MOZ_ASSERT(currentChunkSize <= CHUNK_SIZE);
 
     if (ret == Z_MEM_ERROR) {
         zs.avail_out = 0;
         return OOM;
     }
-    if (ret == Z_BUF_ERROR || (done && ret == Z_OK)) {
+    if (ret == Z_BUF_ERROR || (ret == Z_OK && zs.avail_out == 0)) {
+        // We have to resize the output buffer. Note that we're not done yet
+        // because ret != Z_STREAM_END.
         MOZ_ASSERT(zs.avail_out == 0);
         return MOREOUTPUT;
     }
 
     if (done || currentChunkSize == CHUNK_SIZE) {
         MOZ_ASSERT_IF(!done, flush);
         MOZ_ASSERT(chunkSize(inplen, chunkOffsets.length()) == currentChunkSize);
         if (!chunkOffsets.append(outbytes))
@@ -145,32 +150,34 @@ Compressor::compressMore()
 
 size_t
 Compressor::totalBytesNeeded() const
 {
     return AlignBytes(outbytes, sizeof(uint32_t)) + sizeOfChunkOffsets();
 }
 
 void
-Compressor::finish(char* dest, size_t destBytes) const
+Compressor::finish(char* dest, size_t destBytes)
 {
     MOZ_ASSERT(!chunkOffsets.empty());
 
     CompressedDataHeader* compressedHeader = reinterpret_cast<CompressedDataHeader*>(dest);
     compressedHeader->compressedBytes = outbytes;
 
     size_t outbytesAligned = AlignBytes(outbytes, sizeof(uint32_t));
 
     // Zero the padding bytes, the ImmutableStringsCache will hash them.
     mozilla::PodZero(dest + outbytes, outbytesAligned - outbytes);
 
     uint32_t* destArr = reinterpret_cast<uint32_t*>(dest + outbytesAligned);
 
     MOZ_ASSERT(uintptr_t(dest + destBytes) == uintptr_t(destArr + chunkOffsets.length()));
     mozilla::PodCopy(destArr, chunkOffsets.begin(), chunkOffsets.length());
+
+    finished = true;
 }
 
 bool
 js::DecompressString(const unsigned char* inp, size_t inplen, unsigned char* out, size_t outlen)
 {
     MOZ_ASSERT(inplen <= UINT32_MAX);
 
     // Mark the memory we pass to zlib as initialized for MSan.
--- a/js/src/vm/Compression.h
+++ b/js/src/vm/Compression.h
@@ -32,16 +32,17 @@ class Compressor
     // Number of bytes we should hand to zlib each compressMore() call.
     static const size_t MAX_INPUT_SIZE = 2 * 1024;
 
     z_stream zs;
     const unsigned char* inp;
     size_t inplen;
     size_t outbytes;
     bool initialized;
+    bool finished;
 
     // The number of uncompressed bytes written for the current chunk. When this
     // reaches CHUNK_SIZE, we finish the current chunk and start a new chunk.
     uint32_t currentChunkSize;
 
     // At the end of each chunk (and the end of the uncompressed data if it's
     // not a chunk boundary), we record the offset in the compressed data.
     js::Vector<uint32_t, 8, SystemAllocPolicy> chunkOffsets;
@@ -62,17 +63,17 @@ class Compressor
     Status compressMore();
     size_t sizeOfChunkOffsets() const { return chunkOffsets.length() * sizeof(chunkOffsets[0]); }
 
     // Returns the number of bytes needed to store the data currently written +
     // the chunk offsets.
     size_t totalBytesNeeded() const;
 
     // Append the chunk offsets to |dest|.
-    void finish(char* dest, size_t destBytes) const;
+    void finish(char* dest, size_t destBytes);
 
     static void toChunkOffset(size_t uncompressedOffset, size_t* chunk, size_t* chunkOffset) {
         *chunk = uncompressedOffset / CHUNK_SIZE;
         *chunkOffset = uncompressedOffset % CHUNK_SIZE;
     }
     static size_t chunkSize(size_t uncompressedBytes, size_t chunk) {
         MOZ_ASSERT(uncompressedBytes > 0);
         size_t lastChunk = (uncompressedBytes - 1) / CHUNK_SIZE;
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -1776,16 +1776,30 @@ GlobalHelperThreadState::compressionTask
 void
 GlobalHelperThreadState::trace(JSTracer* trc)
 {
     AutoLockHelperThreadState lock;
     for (auto builder : ionWorklist(lock))
         builder->trace(trc);
     for (auto builder : ionFinishedList(lock))
         builder->trace(trc);
+
+    if (HelperThreadState().threads) {
+        for (auto& helper : *HelperThreadState().threads) {
+            if (auto builder = helper.ionBuilder())
+                builder->trace(trc);
+        }
+    }
+
+    jit::IonBuilder* builder = trc->runtime()->ionLazyLinkList().getFirst();
+    while (builder) {
+        builder->trace(trc);
+        builder = builder->getNext();
+    }
+
     for (auto parseTask : parseWorklist_)
         parseTask->trace(trc);
     for (auto parseTask : parseFinishedList_)
         parseTask->trace(trc);
     for (auto parseTask : parseWaitingOnGC_)
         parseTask->trace(trc);
 }
 
--- a/layout/reftests/w3c-css/submitted/masking/clip-path-mix-blend-mode-1.html
+++ b/layout/reftests/w3c-css/submitted/masking/clip-path-mix-blend-mode-1.html
@@ -1,17 +1,17 @@
 <!DOCTYPE html>
 <html>
   <head>
     <meta charset="utf-8">
     <title>CSS Masking: clip-path with mix-blend-mode</title>
     <link rel="author" title="CJ Ku" href="mailto:cku@mozilla.com">
     <link rel="author" title="Mozilla" href="https://www.mozilla.org">
     <link rel="help" href="https://www.w3.org/TR/css-masking-1/#the-clip-path">
-    <link rel="match" href="clip-path-mix-blend-mode-ref.html">
+    <link rel="match" href="clip-path-mix-blend-mode-1-ref.html">
     <meta name="assert" content="Test checks whether clip-path works with mix-blend-mode correctly or not.">
     <style type="text/css">
       div {
         position: absolute;
         width: 100px;
         height: 100px;
       }
 
--- a/media/webrtc/signaling/src/jsep/JsepSession.h
+++ b/media/webrtc/signaling/src/jsep/JsepSession.h
@@ -90,18 +90,20 @@ public:
   virtual nsresult SetBundlePolicy(JsepBundlePolicy policy) = 0;
   virtual bool RemoteIsIceLite() const = 0;
   virtual bool RemoteIceIsRestarting() const = 0;
   virtual std::vector<std::string> GetIceOptions() const = 0;
 
   virtual nsresult AddDtlsFingerprint(const std::string& algorithm,
                                       const std::vector<uint8_t>& value) = 0;
 
-  virtual nsresult AddAudioRtpExtension(const std::string& extensionName) = 0;
-  virtual nsresult AddVideoRtpExtension(const std::string& extensionName) = 0;
+  virtual nsresult AddAudioRtpExtension(const std::string& extensionName,
+                                        SdpDirectionAttribute::Direction direction) = 0;
+  virtual nsresult AddVideoRtpExtension(const std::string& extensionName,
+                                        SdpDirectionAttribute::Direction direction) = 0;
 
   // Kinda gross to be locking down the data structure type like this, but
   // returning by value is problematic due to the lack of stl move semantics in
   // our build config, since we can't use UniquePtr in the container. The
   // alternative is writing a raft of accessor functions that allow arbitrary
   // manipulation (which will be unwieldy), or allowing functors to be injected
   // that manipulate the data structure (still pretty unwieldy).
   virtual std::vector<JsepCodecDescription*>& Codecs() = 0;
--- a/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp
+++ b/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp
@@ -193,54 +193,50 @@ JsepSessionImpl::AddDtlsFingerprint(cons
   fp.mValue = value;
 
   mDtlsFingerprints.push_back(fp);
 
   return NS_OK;
 }
 
 nsresult
-JsepSessionImpl::AddAudioRtpExtension(const std::string& extensionName)
+JsepSessionImpl::AddRtpExtension(std::vector<SdpExtmapAttributeList::Extmap>& extensions,
+                                 const std::string& extensionName,
+                                 SdpDirectionAttribute::Direction direction)
 {
   mLastError.clear();
 
-  if (mAudioRtpExtensions.size() + 1 > UINT16_MAX) {
-    JSEP_SET_ERROR("Too many audio rtp extensions have been added");
+  if (extensions.size() + 1 > UINT16_MAX) {
+    JSEP_SET_ERROR("Too many rtp extensions have been added");
     return NS_ERROR_FAILURE;
   }
 
   SdpExtmapAttributeList::Extmap extmap =
-      { static_cast<uint16_t>(mAudioRtpExtensions.size() + 1),
-        SdpDirectionAttribute::kSendrecv,
-        false, // don't actually specify direction
+      { static_cast<uint16_t>(extensions.size() + 1),
+        direction,
+        direction != SdpDirectionAttribute::kSendrecv, // do we want to specify direction?
         extensionName,
         "" };
 
-  mAudioRtpExtensions.push_back(extmap);
+  extensions.push_back(extmap);
   return NS_OK;
 }
 
 nsresult
-JsepSessionImpl::AddVideoRtpExtension(const std::string& extensionName)
+JsepSessionImpl::AddAudioRtpExtension(const std::string& extensionName,
+                                      SdpDirectionAttribute::Direction direction)
 {
-  mLastError.clear();
-
-  if (mVideoRtpExtensions.size() + 1 > UINT16_MAX) {
-    JSEP_SET_ERROR("Too many video rtp extensions have been added");
-    return NS_ERROR_FAILURE;
-  }
+  return AddRtpExtension(mAudioRtpExtensions, extensionName, direction);
+}
 
-  SdpExtmapAttributeList::Extmap extmap =
-      { static_cast<uint16_t>(mVideoRtpExtensions.size() + 1),
-        SdpDirectionAttribute::kSendrecv,
-        false, // don't actually specify direction
-        extensionName, "" };
-
-  mVideoRtpExtensions.push_back(extmap);
-  return NS_OK;
+nsresult
+JsepSessionImpl::AddVideoRtpExtension(const std::string& extensionName,
+                                      SdpDirectionAttribute::Direction direction)
+{
+  return AddRtpExtension(mVideoRtpExtensions, extensionName, direction);
 }
 
 template<class T>
 std::vector<RefPtr<JsepTrack>>
 GetTracks(const std::vector<T>& wrappedTracks)
 {
   std::vector<RefPtr<JsepTrack>> result;
   for (auto i = wrappedTracks.begin(); i != wrappedTracks.end(); ++i) {
@@ -282,16 +278,35 @@ JsepSessionImpl::SetParameters(const std
                                const std::vector<JsepTrack::JsConstraints>& constraints)
 {
   auto it = FindTrackByIds(mLocalTracks, streamId, trackId);
 
   if (it == mLocalTracks.end()) {
     JSEP_SET_ERROR("Track " << streamId << "/" << trackId << " was never added.");
     return NS_ERROR_INVALID_ARG;
   }
+
+  // Add RtpStreamId Extmap
+  // SdpDirectionAttribute::Direction is a bitmask
+  SdpDirectionAttribute::Direction addVideoExt = SdpDirectionAttribute::kInactive;
+  for (auto constraintEntry: constraints) {
+    if (constraintEntry.rid != "") {
+      switch (it->mTrack->GetMediaType()) {
+        case SdpMediaSection::kVideo: {
+           addVideoExt = static_cast<SdpDirectionAttribute::Direction>(addVideoExt
+                                                                       | it->mTrack->GetDirection());
+          break;
+        }
+      }
+    }
+  }
+  if (addVideoExt != SdpDirectionAttribute::kInactive) {
+    AddVideoRtpExtension("urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id", addVideoExt);
+  }
+
   it->mTrack->SetJsConstraints(constraints);
   return NS_OK;
 }
 
 nsresult
 JsepSessionImpl::GetParameters(const std::string& streamId,
                                const std::string& trackId,
                                std::vector<JsepTrack::JsConstraints>* outConstraints)
@@ -2203,19 +2218,18 @@ JsepSessionImpl::SetupDefaultCodecs()
   // Update the redundant encodings for the RED codec with the supported
   // codecs.  Note: only uses the video codecs.
   red->UpdateRedundantEncodings(mSupportedCodecs.values);
 }
 
 void
 JsepSessionImpl::SetupDefaultRtpExtensions()
 {
-  AddAudioRtpExtension("urn:ietf:params:rtp-hdrext:ssrc-audio-level");
-  AddAudioRtpExtension("urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id");
-  AddVideoRtpExtension("urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id");
+  AddAudioRtpExtension("urn:ietf:params:rtp-hdrext:ssrc-audio-level",
+                       SdpDirectionAttribute::Direction::kSendonly);
 }
 
 void
 JsepSessionImpl::SetState(JsepSignalingState state)
 {
   if (state == mState)
     return;
 
--- a/media/webrtc/signaling/src/jsep/JsepSessionImpl.h
+++ b/media/webrtc/signaling/src/jsep/JsepSessionImpl.h
@@ -74,21 +74,28 @@ public:
   GetIceOptions() const override
   {
     return mIceOptions;
   }
 
   virtual nsresult AddDtlsFingerprint(const std::string& algorithm,
                                       const std::vector<uint8_t>& value) override;
 
+  nsresult AddRtpExtension(std::vector<SdpExtmapAttributeList::Extmap>& extensions,
+                           const std::string& extensionName,
+                           SdpDirectionAttribute::Direction direction);
   virtual nsresult AddAudioRtpExtension(
-      const std::string& extensionName) override;
+      const std::string& extensionName,
+      SdpDirectionAttribute::Direction direction =
+      SdpDirectionAttribute::Direction::kSendrecv) override;
 
   virtual nsresult AddVideoRtpExtension(
-      const std::string& extensionName) override;
+      const std::string& extensionName,
+      SdpDirectionAttribute::Direction direction =
+      SdpDirectionAttribute::Direction::kSendrecv) override;
 
   virtual std::vector<JsepCodecDescription*>&
   Codecs() override
   {
     return mSupportedCodecs.values;
   }
 
   virtual nsresult ReplaceTrack(const std::string& oldStreamId,
--- a/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
+++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
@@ -709,16 +709,29 @@ WebrtcVideoConduit::ConfigureSendMediaCo
 
   if(codecFound == false)
   {
     CSFLogError(logTag, "%s Codec Mismatch ", __FUNCTION__);
     return kMediaConduitInvalidSendCodec;
   }
   // Note: only for overriding parameters from GetCodec()!
   CodecConfigToWebRTCCodec(codecConfig, video_codec);
+  if (mSendingWidth != 0) {
+    // We're already in a call and are reconfiguring (perhaps due to
+    // ReplaceTrack).  Set to match the last frame we sent.
+
+    // We could also set mLastWidth to 0, to force immediate reconfig -
+    // more expensive, but perhaps less risk of missing something.  Really
+    // on ReplaceTrack we should just call ConfigureCodecMode(), and if the
+    // mode changed, we re-configure.
+    // Do this after CodecConfigToWebRTCCodec() to avoid messing up simulcast
+    video_codec.width = mSendingWidth;
+    video_codec.height = mSendingHeight;
+    video_codec.maxFramerate = mSendingFramerate;
+  }
   video_codec.mode = mCodecMode;
 
   if(mPtrViECodec->SetSendCodec(mChannel, video_codec) == -1)
   {
     error = mPtrViEBase->LastError();
     if(error == kViECodecInvalidCodec)
     {
       CSFLogError(logTag, "%s Invalid Send Codec", __FUNCTION__);
@@ -1563,17 +1576,17 @@ WebrtcVideoConduit::SendVideoFrame(unsig
   return SendVideoFrame(i420_frame);
 }
 
 MediaConduitErrorCode
 WebrtcVideoConduit::SendVideoFrame(webrtc::I420VideoFrame& frame)
 {
   CSFLogDebug(logTag,  "%s ", __FUNCTION__);
   // See if we need to recalculate what we're sending.
-  // Don't compate mSendingWidth/Height, since those may not be the same as the input.
+  // Don't compare mSendingWidth/Height, since those may not be the same as the input.
   {
     MutexAutoLock lock(mCodecMutex);
     if (mInReconfig) {
       // Waiting for it to finish
       return kMediaConduitNoError;
     }
     if (frame.width() != mLastWidth || frame.height() != mLastHeight) {
       CSFLogDebug(logTag, "%s: call SelectSendResolution with %ux%u",
--- a/media/webrtc/signaling/src/sdp/SdpHelper.cpp
+++ b/media/webrtc/signaling/src/sdp/SdpHelper.cpp
@@ -741,18 +741,35 @@ SdpHelper::AddCommonExtmaps(
         SdpAttribute::kExtmapAttribute)) {
     return;
   }
 
   UniquePtr<SdpExtmapAttributeList> localExtmap(new SdpExtmapAttributeList);
   auto& theirExtmap = remoteMsection.GetAttributeList().GetExtmap().mExtmaps;
   for (auto i = theirExtmap.begin(); i != theirExtmap.end(); ++i) {
     for (auto j = localExtensions.begin(); j != localExtensions.end(); ++j) {
-      if (i->extensionname == j->extensionname) {
-        localExtmap->mExtmaps.push_back(*i);
+      // verify we have a valid combination of directions.  For kInactive
+      // we'll just not add the response
+      if (i->extensionname == j->extensionname &&
+          (((i->direction == SdpDirectionAttribute::Direction::kSendrecv ||
+             i->direction == SdpDirectionAttribute::Direction::kSendonly) &&
+            (j->direction == SdpDirectionAttribute::Direction::kSendrecv ||
+             j->direction == SdpDirectionAttribute::Direction::kRecvonly)) ||
+
+           ((i->direction == SdpDirectionAttribute::Direction::kSendrecv ||
+             i->direction == SdpDirectionAttribute::Direction::kRecvonly) &&
+            (j->direction == SdpDirectionAttribute::Direction::kSendrecv ||
+             j->direction == SdpDirectionAttribute::Direction::kSendonly)))) {
+        auto k = *i; // we need to modify it
+        if (j->direction == SdpDirectionAttribute::Direction::kSendonly) {
+          k.direction = SdpDirectionAttribute::Direction::kRecvonly;
+        } else if (j->direction == SdpDirectionAttribute::Direction::kRecvonly) {
+          k.direction = SdpDirectionAttribute::Direction::kSendonly;
+        }
+        localExtmap->mExtmaps.push_back(k);
 
         // RFC 5285 says that ids >= 4096 can be used by the offerer to
         // force the answerer to pick, otherwise the value in the offer is
         // used.
         if (localExtmap->mExtmaps.back().entry >= 4096) {
           localExtmap->mExtmaps.back().entry = j->entry;
         }
       }
--- a/media/webrtc/signaling/test/jsep_session_unittest.cpp
+++ b/media/webrtc/signaling/test/jsep_session_unittest.cpp
@@ -3569,61 +3569,51 @@ TEST_F(JsepSessionTest, TestIceOptions)
   ASSERT_EQ("trickle", mSessionAns.GetIceOptions()[0]);
 }
 
 TEST_F(JsepSessionTest, TestExtmap)
 {
   AddTracks(mSessionOff, "audio");
   AddTracks(mSessionAns, "audio");
   // ssrc-audio-level will be extmap 1 for both
-  // rtp-stream-id will be extmap 2 for both
-  mSessionOff.AddAudioRtpExtension("foo"); // Default mapping of 3
-  mSessionOff.AddAudioRtpExtension("bar"); // Default mapping of 4
-  mSessionAns.AddAudioRtpExtension("bar"); // Default mapping of 3
+  mSessionOff.AddAudioRtpExtension("foo"); // Default mapping of 2
+  mSessionOff.AddAudioRtpExtension("bar"); // Default mapping of 3
+  mSessionAns.AddAudioRtpExtension("bar"); // Default mapping of 2
   std::string offer = CreateOffer();
   SetLocalOffer(offer, CHECK_SUCCESS);
   SetRemoteOffer(offer, CHECK_SUCCESS);
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer, CHECK_SUCCESS);
   SetRemoteAnswer(answer, CHECK_SUCCESS);
 
   UniquePtr<Sdp> parsedOffer(Parse(offer));
   ASSERT_EQ(1U, parsedOffer->GetMediaSectionCount());
 
   auto& offerMediaAttrs = parsedOffer->GetMediaSection(0).GetAttributeList();
   ASSERT_TRUE(offerMediaAttrs.HasAttribute(SdpAttribute::kExtmapAttribute));
   auto& offerExtmap = offerMediaAttrs.GetExtmap().mExtmaps;
-  ASSERT_EQ(4U, offerExtmap.size());
+  ASSERT_EQ(3U, offerExtmap.size());
   ASSERT_EQ("urn:ietf:params:rtp-hdrext:ssrc-audio-level",
       offerExtmap[0].extensionname);
   ASSERT_EQ(1U, offerExtmap[0].entry);
-  ASSERT_EQ("urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id",
-      offerExtmap[1].extensionname);
+  ASSERT_EQ("foo", offerExtmap[1].extensionname);
   ASSERT_EQ(2U, offerExtmap[1].entry);
-  ASSERT_EQ("foo", offerExtmap[2].extensionname);
+  ASSERT_EQ("bar", offerExtmap[2].extensionname);
   ASSERT_EQ(3U, offerExtmap[2].entry);
-  ASSERT_EQ("bar", offerExtmap[3].extensionname);
-  ASSERT_EQ(4U, offerExtmap[3].entry);
 
   UniquePtr<Sdp> parsedAnswer(Parse(answer));
   ASSERT_EQ(1U, parsedAnswer->GetMediaSectionCount());
 
   auto& answerMediaAttrs = parsedAnswer->GetMediaSection(0).GetAttributeList();
   ASSERT_TRUE(answerMediaAttrs.HasAttribute(SdpAttribute::kExtmapAttribute));
   auto& answerExtmap = answerMediaAttrs.GetExtmap().mExtmaps;
-  ASSERT_EQ(3U, answerExtmap.size());
-  ASSERT_EQ("urn:ietf:params:rtp-hdrext:ssrc-audio-level",
-      answerExtmap[0].extensionname);
-  ASSERT_EQ(1U, answerExtmap[0].entry);
-  ASSERT_EQ("urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id",
-      answerExtmap[1].extensionname);
-  ASSERT_EQ(2U, answerExtmap[1].entry);
+  ASSERT_EQ(1U, answerExtmap.size());
   // We ensure that the entry for "bar" matches what was in the offer
-  ASSERT_EQ("bar", answerExtmap[2].extensionname);
-  ASSERT_EQ(4U, answerExtmap[2].entry);
+  ASSERT_EQ("bar", answerExtmap[0].extensionname);
+  ASSERT_EQ(3U, answerExtmap[0].entry);
 }
 
 TEST_F(JsepSessionTest, TestRtcpFbStar)
 {
   AddTracks(mSessionOff, "video");
   AddTracks(mSessionAns, "video");
 
   std::string offer = CreateOffer();
--- a/testing/web-platform/meta/dom/historical.html.ini
+++ b/testing/web-platform/meta/dom/historical.html.ini
@@ -5,10 +5,10 @@
 
   [Historical DOM features must be removed: DOMError]
     expected: FAIL
 
   [Historical DOM features must be removed: createCDATASection]
     expected: FAIL
 
   [Node member must be nuked: rootNode]
-    expected: FAIL
+    disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1303802