Merge inbound to mozilla-central. a=merge
authorCosmin Sabou <csabou@mozilla.com>
Fri, 01 Jun 2018 12:41:08 +0300
changeset 475044 9900cebb1f9000bd05731ba67736b7c51f7eb812
parent 475013 50c379fc553677e03b7626043ebc47133f6c7073 (current diff)
parent 475043 a6d7c670b2f564c8c1c0fee8652dde0ec308ab10 (diff)
child 475057 17f37907d552a9b2de43c5ae14646df9435f97b3
child 475082 e99ff79303ea48b856b93e66ccc808d0aac8a68b
push id9374
push userjlund@mozilla.com
push dateMon, 18 Jun 2018 21:43:20 +0000
treeherdermozilla-beta@160e085dfb0b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone62.0a1
first release with
nightly linux32
9900cebb1f90 / 62.0a1 / 20180601100102 / files
nightly linux64
9900cebb1f90 / 62.0a1 / 20180601100102 / files
nightly mac
9900cebb1f90 / 62.0a1 / 20180601100102 / files
nightly win32
9900cebb1f90 / 62.0a1 / 20180601100102 / files
nightly win64
9900cebb1f90 / 62.0a1 / 20180601100102 / 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 inbound to mozilla-central. a=merge
js/src/gc/GC.cpp
layout/painting/nsDisplayList.cpp
layout/painting/nsDisplayList.h
taskcluster/taskgraph/transforms/job/mozharness_test.py
new file mode 100644
--- /dev/null
+++ b/browser/config/mozconfigs/macosx64/code-coverage
@@ -0,0 +1,10 @@
+. "$topsrcdir/browser/config/mozconfigs/macosx64/nightly"
+
+TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
+
+ac_add_options --disable-sandbox
+ac_add_options --enable-coverage
+
+export CFLAGS="-coverage"
+export CXXFLAGS="-coverage"
+export LDFLAGS="-coverage"
new file mode 100644
--- /dev/null
+++ b/browser/config/mozconfigs/macosx64/code-coverage-debug
@@ -0,0 +1,3 @@
+. "$topsrcdir/browser/config/mozconfigs/macosx64/code-coverage"
+
+ac_add_options --enable-debug
--- a/dom/base/nsContentList.cpp
+++ b/dom/base/nsContentList.cpp
@@ -847,26 +847,27 @@ nsContentList::MatchSelf(nsIContent *aCo
       return true;
     }
   }
 
   return false;
 }
 
 void
-nsContentList::PopulateSelf(uint32_t aNeededLength)
+nsContentList::PopulateSelf(uint32_t aNeededLength,
+                            uint32_t aExpectedElementsIfDirty)
 {
   if (!mRootNode) {
     return;
   }
 
   ASSERT_IN_SYNC;
 
   uint32_t count = mElements.Length();
-  NS_ASSERTION(mState != LIST_DIRTY || count == 0,
+  NS_ASSERTION(mState != LIST_DIRTY || count == aExpectedElementsIfDirty,
                "Reset() not called when setting state to LIST_DIRTY?");
 
   if (count >= aNeededLength) // We're all set
     return;
 
   uint32_t elementsToAppend = aNeededLength - count;
 #ifdef DEBUG
   uint32_t invariant = elementsToAppend + mElements.Length();
@@ -1148,22 +1149,24 @@ nsLabelsNodeList::MaybeResetRoot(nsINode
     mRootNode->RemoveMutationObserver(this);
   }
   mRootNode = aRootNode;
   mRootNode->AddMutationObserver(this);
   SetDirty();
 }
 
 void
-nsLabelsNodeList::PopulateSelf(uint32_t aNeededLength)
+nsLabelsNodeList::PopulateSelf(uint32_t aNeededLength,
+                               uint32_t aExpectedElementsIfDirty)
 {
   if (!mRootNode) {
     return;
   }
 
   // Start searching at the root.
   nsINode* cur = mRootNode;
   if (mElements.IsEmpty() && cur->IsElement() && Match(cur->AsElement())) {
     mElements.AppendElement(cur->AsElement());
+    ++aExpectedElementsIfDirty;
   }
 
-  nsContentList::PopulateSelf(aNeededLength);
+  nsContentList::PopulateSelf(aNeededLength, aExpectedElementsIfDirty);
 }
--- a/dom/base/nsContentList.h
+++ b/dom/base/nsContentList.h
@@ -428,18 +428,21 @@ protected:
   /**
    * Populate our list.  Stop once we have at least aNeededLength
    * elements.  At the end of PopulateSelf running, either the last
    * node we examined is the last node in our array or we have
    * traversed the whole document (or both).
    *
    * @param aNeededLength the length the list should have when we are
    *        done (unless it exhausts the document)
+   * @param aExpectedElementsIfDirty is for debugging only to
+   *        assert that mElements has expected number of entries.
    */
-  virtual void PopulateSelf(uint32_t aNeededLength);
+  virtual void PopulateSelf(uint32_t aNeededLength,
+                            uint32_t aExpectedElementsIfDirty = 0);
 
   /**
    * @param  aContainer a content node which must be a descendant of
    *         mRootNode
    * @return true if children or descendants of aContainer could match our
    *                 criterion.
    *         false otherwise.
    */
@@ -679,12 +682,15 @@ public:
 
 private:
  /**
   * Start searching at the last one if we already have nodes, otherwise
   * start searching at the root.
   *
   * @param aNeededLength The list of length should have when we are
   *                      done (unless it exhausts the document).
+  * @param aExpectedElementsIfDirty is for debugging only to
+  *        assert that mElements has expected number of entries.
   */
-  void PopulateSelf(uint32_t aNeededLength) override;
+  void PopulateSelf(uint32_t aNeededLength,
+                    uint32_t aExpectedElementsIfDirty = 0) override;
 };
 #endif // nsContentList_h___
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -275,43 +275,51 @@ nsINode::GetParentOrHostNode() const
 
   const ShadowRoot* shadowRoot = ShadowRoot::FromNode(this);
   return shadowRoot ? shadowRoot->GetHost() : nullptr;
 }
 
 nsINode*
 nsINode::SubtreeRoot() const
 {
+  auto RootOfNode = [](const nsINode* aStart) -> nsINode* {
+    const nsINode* node = aStart;
+    const nsINode* iter = node;
+    while ((iter = iter->GetParentNode())) {
+      node = iter;
+    }
+    return const_cast<nsINode*>(node);
+  };
+
   // There are four cases of interest here.  nsINodes that are really:
   // 1. nsIDocument nodes - Are always in the document.
   // 2.a nsIContent nodes not in a shadow tree - Are either in the document,
   //     or mSubtreeRoot is updated in BindToTree/UnbindFromTree.
   // 2.b nsIContent nodes in a shadow tree - Are never in the document,
   //     ignore mSubtreeRoot and return the containing shadow root.
   // 4. nsIAttribute nodes - Are never in the document, and mSubtreeRoot
   //    is always 'this' (as set in nsINode's ctor).
   nsINode* node;
   if (IsInUncomposedDoc()) {
     node = OwnerDocAsNode();
   } else if (IsContent()) {
     ShadowRoot* containingShadow = AsContent()->GetContainingShadow();
     node = containingShadow ? containingShadow : mSubtreeRoot;
+    if (!node) {
+      NS_WARNING("Using SubtreeRoot() on unlinked element?");
+      node = RootOfNode(this);
+    }
   } else {
     node = mSubtreeRoot;
   }
-  NS_ASSERTION(node, "Should always have a node here!");
+  MOZ_ASSERT(node, "Should always have a node here!");
 #ifdef DEBUG
   {
-    const nsINode* slowNode = this;
-    const nsINode* iter = slowNode;
-    while ((iter = iter->GetParentNode())) {
-      slowNode = iter;
-    }
-
-    NS_ASSERTION(slowNode == node, "These should always be in sync!");
+    const nsINode* slowNode = RootOfNode(this);
+    MOZ_ASSERT(slowNode == node, "These should always be in sync!");
   }
 #endif
   return node;
 }
 
 static nsIContent* GetRootForContentSubtree(nsIContent* aContent)
 {
   NS_ENSURE_TRUE(aContent, nullptr);
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -200,17 +200,31 @@ WebGLContext::BindFakeBlack(uint32_t tex
 
     UniquePtr<FakeBlackTexture>* slot = fnGetSlot();
     if (!slot) {
         MOZ_CRASH("GFX: fnGetSlot failed.");
     }
     UniquePtr<FakeBlackTexture>& fakeBlackTex = *slot;
 
     if (!fakeBlackTex) {
+        gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1);
+        if (IsWebGL2()) {
+            gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_PIXELS, 0);
+            gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS, 0);
+            gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES, 0);
+        }
+
         fakeBlackTex = FakeBlackTexture::Create(gl, target, fakeBlack);
+
+        gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, mPixelStore_UnpackAlignment);
+        if (IsWebGL2()) {
+            gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_PIXELS, mPixelStore_UnpackSkipPixels);
+            gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS, mPixelStore_UnpackSkipRows);
+            gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES, mPixelStore_UnpackSkipImages);
+        }
         if (!fakeBlackTex) {
             return false;
         }
     }
 
     gl->fActiveTexture(LOCAL_GL_TEXTURE0 + texUnit);
     gl->fBindTexture(target.get(), fakeBlackTex->mGLName);
     gl->fActiveTexture(LOCAL_GL_TEXTURE0 + mActiveTexture);
@@ -990,23 +1004,18 @@ WebGLContext::FakeBlackTexture::Create(g
     }
 
     UniquePtr<FakeBlackTexture> result(new FakeBlackTexture(gl));
     gl::ScopedBindTexture scopedBind(gl, result->mGLName, target.get());
 
     gl->fTexParameteri(target.get(), LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_NEAREST);
     gl->fTexParameteri(target.get(), LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_NEAREST);
 
-    // We allocate our zeros on the heap, and we overallocate (16 bytes instead of 4) to
-    // minimize the risk of running into a driver bug in texImage2D, as it is a bit
-    // unusual maybe to create 1x1 textures, and the stack may not have the alignment that
-    // TexImage2D expects.
-
     const webgl::DriverUnpackInfo dui = {texFormat, texFormat, LOCAL_GL_UNSIGNED_BYTE};
-    UniqueBuffer zeros = moz_xcalloc(1, 16); // Infallible allocation.
+    UniqueBuffer zeros = moz_xcalloc(1, 4); // Infallible allocation.
 
     MOZ_ASSERT(gl->IsCurrent());
 
     if (target == LOCAL_GL_TEXTURE_CUBE_MAP) {
         for (int i = 0; i < 6; ++i) {
             const TexImageTarget curTarget = LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + i;
             const GLenum error = DoTexImage(gl, curTarget.get(), 0, &dui, 1, 1, 1,
                                             zeros.get());
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -468,24 +468,23 @@ nsGenericHTMLElement::UnbindFromTree(boo
 
   if (GetContentEditableValue() == eTrue) {
     nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(GetComposedDoc());
     if (htmlDocument) {
       htmlDocument->ChangeContentEditableCount(this, -1);
     }
   }
 
-  // We need to consider a labels element is removed from tree,
-  // it needs to update labels list and its root as well.
+  nsStyledElement::UnbindFromTree(aDeep, aNullParent);
+
+  // Invalidate .labels list. It will be repopulated when used the next time.
   nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
   if (slots && slots->mLabelsList) {
     slots->mLabelsList->MaybeResetRoot(SubtreeRoot());
   }
-
-  nsStyledElement::UnbindFromTree(aDeep, aNullParent);
 }
 
 HTMLFormElement*
 nsGenericHTMLElement::FindAncestorForm(HTMLFormElement* aCurrentForm)
 {
   NS_ASSERTION(!HasAttr(kNameSpaceID_None, nsGkAtoms::form) ||
                IsHTMLElement(nsGkAtoms::img),
                "FindAncestorForm should not be called if @form is set!");
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -693,22 +693,26 @@ ImageBridgeChild::IdentifyCompositorText
   if (RefPtr<ImageBridgeChild> child = GetSingleton()) {
     child->UpdateTextureFactoryIdentifier(aIdentifier);
   }
 }
 
 void
 ImageBridgeChild::UpdateTextureFactoryIdentifier(const TextureFactoryIdentifier& aIdentifier)
 {
+  // ImageHost is incompatible between WebRender enabled and WebRender disabled.
+  // Then drop all ImageContainers' ImageClients during disabling WebRender.
   bool disablingWebRender = GetCompositorBackendType() == LayersBackend::LAYERS_WR &&
                             aIdentifier.mParentBackend != LayersBackend::LAYERS_WR;
+  // D3DTexture might become obsolte. To prevent to use obsoleted D3DTexture,
+  // drop all ImageContainers' ImageClients.
+  bool needsDrop = GetCompositorBackendType() == LayersBackend::LAYERS_D3D11 || disablingWebRender;
+
   IdentifyTextureHost(aIdentifier);
-  if (disablingWebRender) {
-    // ImageHost is incompatible between WebRender enabled and WebRender disabled.
-    // Then drop all ImageContainers' ImageClients during disabling WebRender.
+  if (needsDrop) {
     nsTArray<RefPtr<ImageContainerListener> > listeners;
     {
       MutexAutoLock lock(mContainerMapLock);
       for (auto iter = mImageContainerListeners.Iter(); !iter.Done(); iter.Next()) {
         RefPtr<ImageContainerListener>& listener = iter.Data();
         listeners.AppendElement(listener);
       }
     }
--- a/gfx/ots/README.mozilla
+++ b/gfx/ots/README.mozilla
@@ -1,12 +1,12 @@
 This is the Sanitiser for OpenType project, from http://code.google.com/p/ots/.
 
 Our reference repository is https://github.com/khaledhosny/ots/.
 
-Current revision: c903692702e888dd9bc3e62a129d01af07320ad8 (6.1.1)
+Current revision: 7c8f3fc9cf8b79edb241d94a4847968a5009e78d (7.0.0)
 
 Upstream files included: LICENSE, src/, include/, tests/*.cc
 
 Additional files: README.mozilla, src/moz.build
 
 Additional patch: ots-visibility.patch (bug 711079).
 Additional patch: ots-lz4.patch
--- a/gfx/ots/src/fvar.h
+++ b/gfx/ots/src/fvar.h
@@ -18,17 +18,17 @@ namespace ots {
 class OpenTypeFVAR : public Table {
  public:
   explicit OpenTypeFVAR(Font* font, uint32_t tag)
       : Table(font, tag, tag) { }
 
   bool Parse(const uint8_t* data, size_t length);
   bool Serialize(OTSStream* out);
 
-  const uint16_t AxisCount() const { return axisCount; }
+  uint16_t AxisCount() const { return axisCount; }
 
  private:
   uint16_t majorVersion;
   uint16_t minorVersion;
   uint16_t axesArrayOffset;
   uint16_t reserved;
   uint16_t axisCount;
   uint16_t axisSize;
--- a/gfx/ots/src/gvar.cc
+++ b/gfx/ots/src/gvar.cc
@@ -149,8 +149,10 @@ bool OpenTypeGVAR::Serialize(OTSStream* 
   if (!out->Write(this->m_data, this->m_length)) {
     return Error("Failed to write gvar table");
   }
 
   return true;
 }
 
 }  // namespace ots
+
+#undef TABLE_NAME
--- a/gfx/ots/src/hvar.cc
+++ b/gfx/ots/src/hvar.cc
@@ -80,8 +80,10 @@ bool OpenTypeHVAR::Serialize(OTSStream* 
   if (!out->Write(this->m_data, this->m_length)) {
     return Error("Failed to write HVAR table");
   }
 
   return true;
 }
 
 }  // namespace ots
+
+#undef TABLE_NAME
--- a/gfx/ots/src/mvar.cc
+++ b/gfx/ots/src/mvar.cc
@@ -100,8 +100,10 @@ bool OpenTypeMVAR::Serialize(OTSStream* 
   if (!out->Write(this->m_data, this->m_length)) {
     return Error("Failed to write MVAR table");
   }
 
   return true;
 }
 
 }  // namespace ots
+
+#undef TABLE_NAME
--- a/gfx/ots/src/stat.h
+++ b/gfx/ots/src/stat.h
@@ -42,54 +42,54 @@ class OpenTypeSTAT : public Table {
 
   typedef int32_t Fixed; /* 16.16 fixed-point value */
 
   struct AxisValueFormat1 {
     uint16_t axisIndex;
     uint16_t flags;
     uint16_t valueNameID;
     Fixed    value;
-    static const size_t Length() {
+    static size_t Length() {
       return 3 * sizeof(uint16_t) + sizeof(Fixed);
     }
   };
 
   struct AxisValueFormat2 {
     uint16_t axisIndex;
     uint16_t flags;
     uint16_t valueNameID;
     Fixed    nominalValue;
     Fixed    rangeMinValue;
     Fixed    rangeMaxValue;
-    static const size_t Length() {
+    static size_t Length() {
       return 3 * sizeof(uint16_t) + 3 * sizeof(Fixed);
     }
   };
 
   struct AxisValueFormat3 {
     uint16_t axisIndex;
     uint16_t flags;
     uint16_t valueNameID;
     Fixed    value;
     Fixed    linkedValue;
-    static const size_t Length() {
+    static size_t Length() {
       return 3 * sizeof(uint16_t) + 2 * sizeof(Fixed);
     }
   };
 
   struct AxisValueFormat4 {
     uint16_t axisCount;
     uint16_t flags;
     uint16_t valueNameID;
     struct AxisValue {
       uint16_t axisIndex;
       Fixed    value;
     };
     std::vector<AxisValue> axisValues;
-    const size_t Length() const {
+    size_t Length() const {
       return 3 * sizeof(uint16_t) + axisValues.size() * (sizeof(uint16_t) + sizeof(Fixed));
     }
   };
 
   struct AxisValueRecord {
     uint16_t format;
     union {
       AxisValueFormat1 format1;
--- a/gfx/ots/src/variations.cc
+++ b/gfx/ots/src/variations.cc
@@ -241,8 +241,10 @@ bool ParseVariationData(const Font* font
   }
 
   // TODO: we don't attempt to interpret the serialized data block
 
   return true;
 }
 
 } // namespace ots
+
+#undef TABLE_NAME
--- a/gfx/ots/src/vvar.cc
+++ b/gfx/ots/src/vvar.cc
@@ -90,8 +90,10 @@ bool OpenTypeVVAR::Serialize(OTSStream* 
   if (!out->Write(this->m_data, this->m_length)) {
     return Error("Failed to write VVAR table");
   }
 
   return true;
 }
 
 }  // namespace ots
+
+#undef TABLE_NAME
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -6960,47 +6960,50 @@ GCRuntime::resetIncrementalGC(gc::AbortR
     MOZ_ASSERT(incrementalState == State::NotActive);
 #endif
 
     return IncrementalResult::Reset;
 }
 
 namespace {
 
-class AutoGCSlice {
+/*
+ * Temporarily disable barriers during GC slices.
+ */
+class AutoDisableBarriers {
   public:
-    explicit AutoGCSlice(JSRuntime* rt);
-    ~AutoGCSlice();
+    explicit AutoDisableBarriers(JSRuntime* rt);
+    ~AutoDisableBarriers();
 
   private:
     JSRuntime* runtime;
     AutoSetThreadIsPerformingGC performingGC;
 };
 
 } /* anonymous namespace */
 
-AutoGCSlice::AutoGCSlice(JSRuntime* rt)
+AutoDisableBarriers::AutoDisableBarriers(JSRuntime* rt)
   : runtime(rt)
 {
     for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
         /*
          * Clear needsIncrementalBarrier early so we don't do any write
          * barriers during GC. We don't need to update the Ion barriers (which
          * is expensive) because Ion code doesn't run during GC. If need be,
-         * we'll update the Ion barriers in ~AutoGCSlice.
+         * we'll update the Ion barriers in ~AutoDisableBarriers.
          */
         if (zone->isGCMarking()) {
             MOZ_ASSERT(zone->needsIncrementalBarrier());
             zone->setNeedsIncrementalBarrier(false);
         }
         MOZ_ASSERT(!zone->needsIncrementalBarrier());
     }
 }
 
-AutoGCSlice::~AutoGCSlice()
+AutoDisableBarriers::~AutoDisableBarriers()
 {
     /* We can't use GCZonesIter if this is the end of the last slice. */
     for (ZonesIter zone(runtime, WithAtoms); !zone.done(); zone.next()) {
         MOZ_ASSERT(!zone->needsIncrementalBarrier());
         if (zone->isGCMarking())
             zone->setNeedsIncrementalBarrier(true);
     }
 }
@@ -7047,44 +7050,54 @@ GCRuntime::incrementalCollectSlice(Slice
 {
     /*
      * Drop the exclusive access lock if we are in an incremental collection
      * that does not touch the atoms zone.
      */
     if (isIncrementalGCInProgress() && !atomsZone->isCollecting())
         session.maybeLock.reset();
 
-    AutoGCSlice slice(rt);
+    AutoDisableBarriers disableBarriers(rt);
 
     bool destroyingRuntime = (reason == JS::gcreason::DESTROY_RUNTIME);
 
     initialState = incrementalState;
 
 #ifdef JS_GC_ZEAL
     /*
      * Do the incremental collection type specified by zeal mode if the
      * collection was triggered by runDebugGC() and incremental GC has not been
      * cancelled by resetIncrementalGC().
      */
     useZeal = reason == JS::gcreason::DEBUG_GC && !budget.isUnlimited();
 #else
     bool useZeal = false;
 #endif
 
+#ifdef DEBUG
+    {
+        char budgetBuffer[32];
+        budget.describe(budgetBuffer, 32);
+        stats().writeLogMessage("Incremental: %d, useZeal: %d, budget: %s",
+            bool(isIncremental), bool(useZeal), budgetBuffer);
+    }
+#endif
     MOZ_ASSERT_IF(isIncrementalGCInProgress(), isIncremental);
     if (isIncrementalGCInProgress() && budget.isUnlimited())
         changeToNonIncrementalGC();
 
     isIncremental = !budget.isUnlimited();
 
     if (useZeal && hasIncrementalTwoSliceZealMode()) {
         /*
          * Yields between slices occurs at predetermined points in these modes;
          * the budget is not used.
          */
+        stats().writeLogMessage(
+            "Using unlimited budget for two-slice zeal mode");
         budget.makeUnlimited();
     }
 
     switch (incrementalState) {
       case State::NotActive:
         initialReason = reason;
         cleanUpEverything = ShouldCleanUpEverything(reason, invocationKind);
         isCompacting = shouldCompact();
@@ -7122,33 +7135,34 @@ GCRuntime::incrementalCollectSlice(Slice
         }
 
         if (drainMarkStack(budget, gcstats::PhaseKind::MARK) == NotFinished)
             break;
 
         MOZ_ASSERT(marker.isDrained());
 
         /*
-         * In incremental GCs where we have already performed more than once
+         * In incremental GCs where we have already performed more than one
          * slice we yield after marking with the aim of starting the sweep in
          * the next slice, since the first slice of sweeping can be expensive.
          *
          * This is modified by the various zeal modes.  We don't yield in
          * YieldBeforeMarking mode and we always yield in YieldBeforeSweeping
          * mode.
          *
          * We will need to mark anything new on the stack when we resume, so
          * we stay in Mark state.
          */
         if (!lastMarkSlice && isIncremental &&
             ((initialState == State::Mark &&
               !(useZeal && hasZealMode(ZealMode::YieldBeforeMarking))) ||
              (useZeal && hasZealMode(ZealMode::YieldBeforeSweeping))))
         {
             lastMarkSlice = true;
+            stats().writeLogMessage("Yeilding before starting sweeping");
             break;
         }
 
         incrementalState = State::Sweep;
 
         beginSweepPhase(reason, session);
 
         MOZ_FALLTHROUGH;
@@ -7614,27 +7628,31 @@ GCRuntime::collect(bool nonincrementalBy
 {
     // Checks run for each request, even if we do not actually GC.
     checkCanCallAPI();
 
     // Check if we are allowed to GC at this time before proceeding.
     if (!checkIfGCAllowedInCurrentState(reason))
         return;
 
+    stats().writeLogMessage("GC starting in state %s",
+        StateName(incrementalState));
+
     AutoTraceLog logGC(TraceLoggerForCurrentThread(), TraceLogger_GC);
     AutoStopVerifyingBarriers av(rt, IsShutdownGC(reason));
     AutoEnqueuePendingParseTasksAfterGC aept(*this);
     AutoScheduleZonesForGC asz(rt);
 
     bool repeat;
     do {
         bool wasReset = gcCycle(nonincrementalByAPI, budget, reason) == IncrementalResult::Reset;
 
         if (reason == JS::gcreason::ABORT_GC) {
             MOZ_ASSERT(!isIncrementalGCInProgress());
+            stats().writeLogMessage("GC aborted by request");
             break;
         }
 
         /*
          * Sometimes when we finish a GC we need to immediately start a new one.
          * This happens in the following cases:
          *  - when we reset the current GC
          *  - when finalizers drop roots during shutdown
@@ -7664,16 +7682,17 @@ GCRuntime::collect(bool nonincrementalBy
     if (rt->hasZealMode(ZealMode::CheckHeapAfterGC)) {
         gcstats::AutoPhase ap(rt->gc.stats(), gcstats::PhaseKind::TRACE_HEAP);
         CheckHeapAfterGC(rt);
     }
     if (rt->hasZealMode(ZealMode::CheckGrayMarking) && !isIncrementalGCInProgress()) {
         MOZ_RELEASE_ASSERT(CheckGrayMarkingState(rt));
     }
 #endif
+    stats().writeLogMessage("GC ending");
 }
 
 js::AutoEnqueuePendingParseTasksAfterGC::~AutoEnqueuePendingParseTasksAfterGC()
 {
     if (!OffThreadParsingMustWaitForGC(gc_.rt))
         EnqueuePendingParseTasksAfterGC(gc_.rt);
 }
 
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -92,16 +92,40 @@ js::gcstats::ExplainAbortReason(gc::Abor
         GC_ABORT_REASONS(SWITCH_REASON)
 
         default:
           MOZ_CRASH("bad GC abort reason");
 #undef SWITCH_REASON
     }
 }
 
+static FILE*
+MaybeOpenFileFromEnv(const char* env)
+{
+    FILE *file;
+    const char* value = getenv(env);
+
+    if (!value)
+        return nullptr;
+
+    if (strcmp(value, "none") == 0) {
+        file = nullptr;
+    } else if (strcmp(value, "stdout") == 0) {
+        file = stdout;
+    } else if (strcmp(value, "stderr") == 0) {
+        file = stderr;
+    } else {
+        file = fopen(value, "a");
+        if (!file)
+            MOZ_CRASH("Failed to open log file.");
+    }
+
+    return file;
+}
+
 struct PhaseKindInfo
 {
     Phase firstPhase;
     uint8_t telemetryBucket;
 };
 
 // PhaseInfo objects form a tree.
 struct PhaseInfo
@@ -546,16 +570,34 @@ Statistics::renderNurseryJson(JSRuntime*
     Sprinter printer(nullptr, false);
     if (!printer.init())
         return UniqueChars(nullptr);
     JSONPrinter json(printer);
     rt->gc.nursery().renderProfileJSON(json);
     return UniqueChars(printer.release());
 }
 
+#ifdef DEBUG
+void
+Statistics::writeLogMessage(const char* fmt, ...)
+{
+    va_list args;
+    va_start(args, fmt);
+    if (gcDebugFile) {
+        TimeDuration sinceStart = TimeStamp::Now() -
+            TimeStamp::ProcessCreation();
+        fprintf(gcDebugFile, "%12.3f: ", sinceStart.ToMicroseconds());
+        vfprintf(gcDebugFile, fmt, args);
+        fprintf(gcDebugFile, "\n");
+        fflush(gcDebugFile);
+    }
+    va_end(args);
+}
+#endif
+
 UniqueChars
 Statistics::renderJsonMessage(uint64_t timestamp, bool includeSlices) const
 {
     /*
      * The format of the JSON message is specified by the GCMajorMarkerPayload
      * type in perf.html
      * https://github.com/devtools-html/perf.html/blob/master/src/types/markers.js#L62
      *
@@ -690,17 +732,18 @@ Statistics::formatJsonPhaseTimes(const P
         TimeDuration ownTime = phaseTimes[phase];
         if (!ownTime.IsZero())
             json.property(phases[phase].path, ownTime, JSONPrinter::MILLISECONDS);
     }
 }
 
 Statistics::Statistics(JSRuntime* rt)
   : runtime(rt),
-    fp(nullptr),
+    gcTimerFile(nullptr),
+    gcDebugFile(nullptr),
     nonincrementalReason_(gc::AbortReason::None),
     preBytes(0),
     thresholdTriggered(false),
     triggerAmount(0.0),
     triggerThreshold(0.0),
     maxPauseInInterval(0),
     sliceCallback(nullptr),
     nurseryCollectionCallback(nullptr),
@@ -729,47 +772,37 @@ Statistics::Statistics(JSRuntime* rt)
                    "totalTimes_ default-initialization should have "
                    "default-initialized every element of totalTimes_ to zero");
     }
 #endif
 
     MOZ_ALWAYS_TRUE(phaseStack.reserve(MAX_PHASE_NESTING));
     MOZ_ALWAYS_TRUE(suspendedPhases.reserve(MAX_SUSPENDED_PHASES));
 
-    const char* env = getenv("MOZ_GCTIMER");
-    if (env) {
-        if (strcmp(env, "none") == 0) {
-            fp = nullptr;
-        } else if (strcmp(env, "stdout") == 0) {
-            fp = stdout;
-        } else if (strcmp(env, "stderr") == 0) {
-            fp = stderr;
-        } else {
-            fp = fopen(env, "a");
-            if (!fp)
-                MOZ_CRASH("Failed to open MOZ_GCTIMER log file.");
-        }
-    }
+    gcTimerFile = MaybeOpenFileFromEnv("MOZ_GCTIMER");
+    gcDebugFile = MaybeOpenFileFromEnv("JS_GC_DEBUG");
 
-    env = getenv("JS_GC_PROFILE");
+    const char* env = getenv("JS_GC_PROFILE");
     if (env) {
         if (0 == strcmp(env, "help")) {
             fprintf(stderr, "JS_GC_PROFILE=N\n"
                     "\tReport major GC's taking more than N milliseconds.\n");
             exit(0);
         }
         enableProfiling_ = true;
         profileThreshold_ = TimeDuration::FromMilliseconds(atoi(env));
     }
 }
 
 Statistics::~Statistics()
 {
-    if (fp && fp != stdout && fp != stderr)
-        fclose(fp);
+    if (gcTimerFile && gcTimerFile != stdout && gcTimerFile != stderr)
+        fclose(gcTimerFile);
+    if (gcDebugFile && gcDebugFile != stdout && gcDebugFile != stderr)
+        fclose(gcDebugFile);
 }
 
 /* static */ bool
 Statistics::initialize()
 {
 #ifdef DEBUG
     // Sanity check generated tables.
     for (auto i : AllPhases()) {
@@ -913,26 +946,26 @@ LongestPhaseSelfTimeInMajorGC(const Stat
 
     return longestPhase;
 }
 
 void
 Statistics::printStats()
 {
     if (aborted) {
-        fprintf(fp, "OOM during GC statistics collection. The report is unavailable for this GC.\n");
+        fprintf(gcTimerFile, "OOM during GC statistics collection. The report is unavailable for this GC.\n");
     } else {
         UniqueChars msg = formatDetailedMessage();
         if (msg) {
             double secSinceStart =
                 (slices_[0].start - TimeStamp::ProcessCreation()).ToSeconds();
-            fprintf(fp, "GC(T+%.3fs) %s\n", secSinceStart, msg.get());
+            fprintf(gcTimerFile, "GC(T+%.3fs) %s\n", secSinceStart, msg.get());
         }
     }
-    fflush(fp);
+    fflush(gcTimerFile);
 }
 
 void
 Statistics::beginGC(JSGCInvocationKind kind)
 {
     slices_.clearAndFree();
     sccTimes.clearAndFree();
     gckind = kind;
@@ -1030,30 +1063,33 @@ Statistics::beginSlice(const ZoneGCStats
     bool wasFullGC = zoneStats.isFullCollection();
     if (sliceCallback) {
         JSContext* cx = runtime->mainContextFromOwnThread();
         JS::GCDescription desc(!wasFullGC, false, gckind, reason);
         if (first)
             (*sliceCallback)(cx, JS::GC_CYCLE_BEGIN, desc);
         (*sliceCallback)(cx, JS::GC_SLICE_BEGIN, desc);
     }
+
+    writeLogMessage("begin slice");
 }
 
 void
 Statistics::endSlice()
 {
     MOZ_ASSERT(phaseStack.empty() ||
                (phaseStack.length() == 1 && phaseStack[0] == Phase::MUTATOR));
 
     if (!aborted) {
         auto& slice = slices_.back();
         slice.end = TimeStamp::Now();
         slice.endFaults = GetPageFaultCount();
         slice.finalState = runtime->gc.state();
 
+        writeLogMessage("end slice");
         TimeDuration sliceTime = slice.end - slice.start;
         runtime->addTelemetry(JS_TELEMETRY_GC_SLICE_MS, t(sliceTime));
         runtime->addTelemetry(JS_TELEMETRY_GC_RESET, slice.wasReset());
         if (slice.wasReset())
             runtime->addTelemetry(JS_TELEMETRY_GC_RESET_REASON, uint32_t(slice.resetReason));
 
         if (slice.budget.isTimeBudget()) {
             int64_t budget_ms = slice.budget.timeBudget.budget;
@@ -1081,17 +1117,17 @@ Statistics::endSlice()
                 runtime->addTelemetry(JS_TELEMETRY_GC_BUDGET_OVERRUN, uint32_t(overrun));
         }
 
         sliceCount_++;
     }
 
     bool last = !runtime->gc.isIncrementalGCInProgress();
     if (last) {
-        if (fp)
+        if (gcTimerFile)
             printStats();
 
         if (!aborted)
             endGC();
     }
 
     if (enableProfiling_ && !aborted && slices_.back().duration() >= profileThreshold_)
         printSliceProfile();
@@ -1245,16 +1281,17 @@ Statistics::recordPhaseBegin(Phase phase
         if (now < phaseStartTimes[currentPhase()]) {
             now = phaseStartTimes[currentPhase()];
             aborted = true;
         }
     }
 
     phaseStack.infallibleAppend(phase);
     phaseStartTimes[phase] = now;
+    writeLogMessage("begin: %s", phases[phase].path);
 }
 
 void
 Statistics::recordPhaseEnd(Phase phase)
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime));
 
     MOZ_ASSERT(phaseStartTimes[phase]);
@@ -1294,16 +1331,17 @@ Statistics::recordPhaseEnd(Phase phase)
     TimeDuration t = now - phaseStartTimes[phase];
     if (!slices_.empty())
         slices_.back().phaseTimes[phase] += t;
     phaseTimes[phase] += t;
     phaseStartTimes[phase] = TimeStamp();
 
 #ifdef DEBUG
     phaseEndTimes[phase] = now;
+    writeLogMessage("end: %s", phases[phase].path);
 #endif
 }
 
 void
 Statistics::endPhase(PhaseKind phaseKind)
 {
     Phase phase = currentPhase();
     MOZ_ASSERT(phase != Phase::NONE);
--- a/js/src/gc/Statistics.h
+++ b/js/src/gc/Statistics.h
@@ -161,16 +161,18 @@ struct Statistics
         MOZ_ASSERT(reason != gc::AbortReason::None);
         if (!aborted)
             slices_.back().resetReason = reason;
     }
 
     void nonincremental(gc::AbortReason reason) {
         MOZ_ASSERT(reason != gc::AbortReason::None);
         nonincrementalReason_ = reason;
+        writeLogMessage("Non-incremental reason: %s",
+            nonincrementalReason());
     }
 
     bool nonincremental() const {
         return nonincrementalReason_ != gc::AbortReason::None;
     }
 
     const char* nonincrementalReason() const {
         return ExplainAbortReason(nonincrementalReason_);
@@ -261,21 +263,31 @@ struct Statistics
     UniqueChars renderJsonMessage(uint64_t timestamp, bool includeSlices = true) const;
 
     // Return JSON for the timings of just the given slice.
     UniqueChars renderJsonSlice(size_t sliceNum) const;
 
     // Return JSON for the previous nursery collection.
     UniqueChars renderNurseryJson(JSRuntime* rt) const;
 
+#ifdef DEBUG
+    // Print a logging message.
+    void writeLogMessage(const char* fmt, ...);
+#else
+    void writeLogMessage(const char* fmt, ...) { };
+#endif
+
   private:
     JSRuntime* runtime;
 
-    /* File pointer used for MOZ_GCTIMER output. */
-    FILE* fp;
+    /* File used for MOZ_GCTIMER output. */
+    FILE* gcTimerFile;
+
+    /* File used for JS_GC_DEBUG output. */
+    FILE* gcDebugFile;
 
     ZoneGCStats zoneStats;
 
     JSGCInvocationKind gckind;
 
     gc::AbortReason nonincrementalReason_;
 
     SliceDataVector slices_;
--- a/js/src/jit-test/tests/wasm/memory-bulk.js
+++ b/js/src/jit-test/tests/wasm/memory-bulk.js
@@ -108,23 +108,94 @@ function checkMiscPrefixed(opcode, expec
         assertErrorMessage(() => new WebAssembly.Module(binary),
                            WebAssembly.CompileError, /unrecognized opcode/);
     } else {
         assertEq(true, WebAssembly.validate(binary));
     }
 }
 
 //-----------------------------------------------------------
-// Verification cases for memory.copy/fill
+// Verification cases for memory.copy/fill opcode encodings
 
 checkMiscPrefixed(0x3f, true);  // unassigned
 checkMiscPrefixed(0x40, false); // memory.copy
 checkMiscPrefixed(0x41, false); // memory.fill
 checkMiscPrefixed(0x42, true);  // unassigned
 
+//-----------------------------------------------------------
+// Verification cases for memory.copy/fill arguments
+
+// Invalid argument types
+{
+    const tys = ['i32', 'f32', 'i64', 'f64'];
+    const ops = ['copy', 'fill'];
+    for (let ty1 of tys) {
+    for (let ty2 of tys) {
+    for (let ty3 of tys) {
+    for (let op of ops) {
+        if (ty1 == 'i32' && ty2 == 'i32' && ty3 == 'i32')
+            continue;  // this is the only valid case
+        let text =
+        `(module
+          (memory (export "memory") 1 1)
+           (func (export "testfn")
+           (memory.${op} (${ty1}.const 10) (${ty2}.const 20) (${ty3}.const 30))
+          )
+         )`;
+        assertErrorMessage(() => wasmEvalText(text),
+                           WebAssembly.CompileError, /type mismatch/);
+    }}}}
+}
+
+// Not enough, or too many, args
+{
+    for (let op of ['copy', 'fill']) {
+        let text1 =
+        `(module
+          (memory (export "memory") 1 1)
+          (func (export "testfn")
+           (i32.const 10)
+           (i32.const 20)
+           memory.${op}
+         )
+        )`;
+        assertErrorMessage(() => wasmEvalText(text1),
+                           WebAssembly.CompileError,
+                           /popping value from empty stack/);
+        let text2 =
+        `(module
+          (memory (export "memory") 1 1)
+          (func (export "testfn")
+           (i32.const 10)
+           (i32.const 20)
+           (i32.const 30)
+           (i32.const 40)
+           memory.${op}
+         )
+        )`;
+        assertErrorMessage(() => wasmEvalText(text2),
+                           WebAssembly.CompileError,
+                           /unused values not explicitly dropped by end of block/);
+    }
+}
+
+// Module doesn't have a memory
+{
+    for (let op of ['copy', 'fill']) {
+        let text =
+        `(module
+          (func (export "testfn")
+           (memory.${op} (i32.const 10) (i32.const 20) (i32.const 30))
+         )
+        )`;
+        assertErrorMessage(() => wasmEvalText(text),
+                           WebAssembly.CompileError,
+                           /can't touch memory without memory/);
+    }
+}
 
 //---------------------------------------------------------------------//
 //---------------------------------------------------------------------//
 // Run tests
 
 //-----------------------------------------------------------
 // Test helpers
 function checkRange(arr, minIx, maxIxPlusOne, expectedValue)
@@ -134,273 +205,273 @@ function checkRange(arr, minIx, maxIxPlu
     }
 }
 
 //-----------------------------------------------------------
 // Test cases for memory.fill
 
 // Range valid
 {
-    let inst = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(
+    let inst = wasmEvalText(
     `(module
        (memory (export "memory") 1 1)
        (func (export "testfn")
          (memory.fill (i32.const 0xFF00) (i32.const 0x55) (i32.const 256))
        )
      )`
-    )));
+    );
     inst.exports.testfn();
     let b = new Uint8Array(inst.exports.memory.buffer);
     checkRange(b, 0x00000, 0x0FF00, 0x00);
     checkRange(b, 0x0FF00, 0x10000, 0x55);
 }
 
 // Range invalid
 {
-    let inst = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(
+    let inst = wasmEvalText(
     `(module
        (memory (export "memory") 1 1)
        (func (export "testfn")
          (memory.fill (i32.const 0xFF00) (i32.const 0x55) (i32.const 257))
        )
      )`
-    )));
+    );
     assertErrorMessage(() => inst.exports.testfn(),
                        WebAssembly.RuntimeError, /index out of bounds/);
 }
 
 // Wraparound the end of 32-bit offset space
 {
-    let inst = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(
+    let inst = wasmEvalText(
     `(module
        (memory (export "memory") 1 1)
        (func (export "testfn")
          (memory.fill (i32.const 0xFFFFFF00) (i32.const 0x55) (i32.const 257))
        )
      )`
-    )));
+    );
     assertErrorMessage(() => inst.exports.testfn(),
                        WebAssembly.RuntimeError, /index out of bounds/);
 }
 
 // Zero len with offset in-bounds is a no-op
 {
-    let inst = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(
+    let inst = wasmEvalText(
     `(module
        (memory (export "memory") 1 1)
        (func (export "testfn")
          (memory.fill (i32.const 0x12) (i32.const 0x55) (i32.const 0))
        )
      )`
-    )));
+    );
     inst.exports.testfn();
     let b = new Uint8Array(inst.exports.memory.buffer);
     checkRange(b, 0x00000, 0x10000, 0x00);
 }
 
 // Zero len with offset out-of-bounds gets an exception
 {
-    let inst = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(
+    let inst = wasmEvalText(
     `(module
        (memory (export "memory") 1 1)
        (func (export "testfn")
          (memory.fill (i32.const 0x10000) (i32.const 0x55) (i32.const 0))
        )
      )`
-    )));
+    );
     assertErrorMessage(() => inst.exports.testfn(),
                        WebAssembly.RuntimeError, /index out of bounds/);
 }
 
 // Very large range
 {
-    let inst = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(
+    let inst = wasmEvalText(
     `(module
        (memory (export "memory") 1 1)
        (func (export "testfn")
          (memory.fill (i32.const 0x1) (i32.const 0xAA) (i32.const 0xFFFE))
        )
      )`
-    )));
+    );
     inst.exports.testfn();
     let b = new Uint8Array(inst.exports.memory.buffer);
     checkRange(b, 0x00000, 0x00001, 0x00);
     checkRange(b, 0x00001, 0x0FFFF, 0xAA);
     checkRange(b, 0x0FFFF, 0x10000, 0x00);
 }
 
 // Sequencing
 {
-    let i = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(
+    let i = wasmEvalText(
     `(module
        (memory (export "memory") 1 1)
        (func (export "testfn") (result i32)
          (memory.fill (i32.const 0x12) (i32.const 0x55) (i32.const 10))
          (memory.fill (i32.const 0x15) (i32.const 0xAA) (i32.const 4))
          i32.const 99
        )
      )`
-    )));
+    );
     i.exports.testfn();
     let b = new Uint8Array(i.exports.memory.buffer);
     checkRange(b, 0x0,     0x12+0,  0x00);
     checkRange(b, 0x12+0,  0x12+3,  0x55);
     checkRange(b, 0x12+3,  0x12+7,  0xAA);
     checkRange(b, 0x12+7,  0x12+10, 0x55);
     checkRange(b, 0x12+10, 0x10000, 0x00);
 }
 
 
 //-----------------------------------------------------------
 // Test cases for memory.copy
 
 // Both ranges valid.  Copy 5 bytes backwards by 1 (overlapping).
 // result = 0x00--(09) 0x55--(11) 0x00--(pagesize-20)
 {
-    let inst = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(
+    let inst = wasmEvalText(
     `(module
        (memory (export "memory") 1 1)
        (func (export "testfn")
          (memory.fill (i32.const 10) (i32.const 0x55) (i32.const 10))
          (memory.copy (i32.const 9) (i32.const 10) (i32.const 5))
        )
      )`
-    )));
+    );
     inst.exports.testfn();
     let b = new Uint8Array(inst.exports.memory.buffer);
     checkRange(b, 0,    0+9,     0x00);
     checkRange(b, 9,    9+11,    0x55);
     checkRange(b, 9+11, 0x10000, 0x00);
 }
 
 // Both ranges valid.  Copy 5 bytes forwards by 1 (overlapping).
 // result = 0x00--(10) 0x55--(11) 0x00--(pagesize-19)
 {
-    let inst = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(
+    let inst = wasmEvalText(
     `(module
        (memory (export "memory") 1 1)
        (func (export "testfn")
          (memory.fill (i32.const 10) (i32.const 0x55) (i32.const 10))
          (memory.copy (i32.const 16) (i32.const 15) (i32.const 5))
        )
      )`
-    )));
+    );
     inst.exports.testfn();
     let b = new Uint8Array(inst.exports.memory.buffer);
     checkRange(b, 0,     0+10,    0x00);
     checkRange(b, 10,    10+11,   0x55);
     checkRange(b, 10+11, 0x10000, 0x00);
 }
 
 // Destination range invalid
 {
-    let inst = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(
+    let inst = wasmEvalText(
     `(module
        (memory (export "memory") 1 1)
        (func (export "testfn")
          (memory.copy (i32.const 0xFF00) (i32.const 0x8000) (i32.const 257))
        )
      )`
-    )));
+    );
     assertErrorMessage(() => inst.exports.testfn(),
                        WebAssembly.RuntimeError, /index out of bounds/);
 }
 
 // Destination wraparound the end of 32-bit offset space
 {
-    let inst = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(
+    let inst = wasmEvalText(
     `(module
        (memory (export "memory") 1 1)
        (func (export "testfn")
          (memory.copy (i32.const 0xFFFFFF00) (i32.const 0x4000) (i32.const 257))
        )
      )`
-    )));
+    );
     assertErrorMessage(() => inst.exports.testfn(),
                        WebAssembly.RuntimeError, /index out of bounds/);
 }
 
 // Source range invalid
 {
-    let inst = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(
+    let inst = wasmEvalText(
     `(module
        (memory (export "memory") 1 1)
        (func (export "testfn")
          (memory.copy (i32.const 0x8000) (i32.const 0xFF00) (i32.const 257))
        )
      )`
-    )));
+    );
     assertErrorMessage(() => inst.exports.testfn(),
                        WebAssembly.RuntimeError, /index out of bounds/);
 }
 
 // Source wraparound the end of 32-bit offset space
 {
-    let inst = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(
+    let inst = wasmEvalText(
     `(module
        (memory (export "memory") 1 1)
        (func (export "testfn")
          (memory.copy (i32.const 0x4000) (i32.const 0xFFFFFF00) (i32.const 257))
        )
      )`
-    )));
+    );
     assertErrorMessage(() => inst.exports.testfn(),
                        WebAssembly.RuntimeError, /index out of bounds/);
 }
 
 // Zero len with both offsets in-bounds is a no-op
 {
-    let inst = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(
+    let inst = wasmEvalText(
     `(module
        (memory (export "memory") 1 1)
        (func (export "testfn")
          (memory.fill (i32.const 0x0000) (i32.const 0x55) (i32.const 0x8000))
          (memory.fill (i32.const 0x8000) (i32.const 0xAA) (i32.const 0x8000))
          (memory.copy (i32.const 0x9000) (i32.const 0x7000) (i32.const 0))
        )
      )`
-    )));
+    );
     inst.exports.testfn();
     let b = new Uint8Array(inst.exports.memory.buffer);
     checkRange(b, 0x00000, 0x08000, 0x55);
     checkRange(b, 0x08000, 0x10000, 0xAA);
 }
 
 // Zero len with dest offset out-of-bounds is an exception
 {
-    let inst = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(
+    let inst = wasmEvalText(
     `(module
        (memory (export "memory") 1 1)
        (func (export "testfn")
          (memory.copy (i32.const 0x10000) (i32.const 0x7000) (i32.const 0))
        )
      )`
-    )));
+    );
     assertErrorMessage(() => inst.exports.testfn(),
                        WebAssembly.RuntimeError, /index out of bounds/);
 }
 
 // Zero len with src offset out-of-bounds is an exception
 {
-    let inst = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(
+    let inst = wasmEvalText(
     `(module
        (memory (export "memory") 1 1)
        (func (export "testfn")
          (memory.copy (i32.const 0x9000) (i32.const 0x10000) (i32.const 0))
        )
      )`
-    )));
+    );
     assertErrorMessage(() => inst.exports.testfn(),
                        WebAssembly.RuntimeError, /index out of bounds/);
 }
 
 // 100 random fills followed by 100 random copies, in a single-page buffer,
 // followed by verification of the (now heavily mashed-around) buffer.
 {
-    let inst = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(
+    let inst = wasmEvalText(
     `(module
        (memory (export "memory") 1 1)
        (func (export "testfn")
          (memory.fill (i32.const 17767) (i32.const 1) (i32.const 1344))
          (memory.fill (i32.const 39017) (i32.const 2) (i32.const 1055))
          (memory.fill (i32.const 56401) (i32.const 3) (i32.const 988))
          (memory.fill (i32.const 37962) (i32.const 4) (i32.const 322))
          (memory.fill (i32.const 7977) (i32.const 5) (i32.const 1994))
@@ -596,17 +667,17 @@ function checkRange(arr, minIx, maxIxPlu
          (memory.copy (i32.const 36810) (i32.const 25196) (i32.const 3447))
          (memory.copy (i32.const 45742) (i32.const 31888) (i32.const 854))
          (memory.copy (i32.const 24236) (i32.const 31866) (i32.const 1377))
          (memory.copy (i32.const 33778) (i32.const 692) (i32.const 1594))
          (memory.copy (i32.const 60618) (i32.const 18585) (i32.const 2987))
          (memory.copy (i32.const 50370) (i32.const 41271) (i32.const 1406))
        )
      )`
-    )));
+    );
     inst.exports.testfn();
     let b = new Uint8Array(inst.exports.memory.buffer);
     checkRange(b, 0, 124, 0);
     checkRange(b, 124, 1517, 9);
     checkRange(b, 1517, 2132, 0);
     checkRange(b, 2132, 2827, 10);
     checkRange(b, 2827, 2921, 92);
     checkRange(b, 2921, 3538, 83);
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -4404,16 +4404,26 @@ nsDisplayBackgroundImage::GetBoundsInter
 
 nsDisplayTableBackgroundImage::nsDisplayTableBackgroundImage(nsDisplayListBuilder* aBuilder,
                                                              const InitData& aData,
                                                              nsIFrame* aCellFrame)
   : nsDisplayBackgroundImage(aBuilder, aData, aCellFrame)
   , mStyleFrame(aCellFrame)
   , mTableType(GetTableTypeFromFrame(mStyleFrame))
 {
+  if (aBuilder->IsRetainingDisplayList()) {
+    mStyleFrame->AddDisplayItem(this);
+  }
+}
+
+nsDisplayTableBackgroundImage::~nsDisplayTableBackgroundImage()
+{
+  if (mStyleFrame) {
+    mStyleFrame->RemoveDisplayItem(this);
+  }
 }
 
 bool
 nsDisplayTableBackgroundImage::IsInvalid(nsRect& aRect) const
 {
   bool result = mStyleFrame ? mStyleFrame->IsInvalid(aRect) : false;
   aRect += ToReferenceFrame();
   return result;
@@ -7456,16 +7466,19 @@ nsDisplayTableFixedPosition::nsDisplayTa
                                                          nsIFrame* aFrame,
                                                          nsDisplayList* aList,
                                                          uint32_t aIndex,
                                                          nsIFrame* aAncestorFrame)
   : nsDisplayFixedPosition(aBuilder, aFrame, aList, aIndex)
   , mAncestorFrame(aAncestorFrame)
   , mTableType(GetTableTypeFromFrame(aAncestorFrame))
 {
+  if (aBuilder->IsRetainingDisplayList()) {
+    mAncestorFrame->AddDisplayItem(this);
+  }
 }
 
 /* static */ nsDisplayTableFixedPosition*
 nsDisplayTableFixedPosition::CreateForFixedBackground(nsDisplayListBuilder* aBuilder,
                                                       nsIFrame* aFrame,
                                                       nsDisplayBackgroundImage* aImage,
                                                       uint32_t aIndex,
                                                       nsIFrame* aAncestorFrame)
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -4221,27 +4221,39 @@ TableType GetTableTypeFromFrame(nsIFrame
  * frame. And use mFrame and table type as key to generate DisplayItemData to
  * avoid sharing DisplayItemData.
  *
  * Also store ancestor frame as mStyleFrame for all rendering informations.
  */
 class nsDisplayTableBackgroundImage : public nsDisplayBackgroundImage {
 public:
   nsDisplayTableBackgroundImage(nsDisplayListBuilder* aBuilder, const InitData& aInitData, nsIFrame* aCellFrame);
+  ~nsDisplayTableBackgroundImage();
 
   virtual uint32_t GetPerFrameKey() const override {
     return (mLayer << (TYPE_BITS + static_cast<uint8_t>(TableTypeBits::COUNT))) |
            (static_cast<uint8_t>(mTableType) << TYPE_BITS) |
            nsDisplayItem::GetPerFrameKey();
   }
 
   virtual bool IsInvalid(nsRect& aRect) const override;
 
   virtual nsIFrame* FrameForInvalidation() const override { return mStyleFrame; }
 
+  virtual bool HasDeletedFrame() const override {
+    return !mStyleFrame || nsDisplayBackgroundImage::HasDeletedFrame();
+  }
+
+  virtual void RemoveFrame(nsIFrame* aFrame) override {
+    if (aFrame == mStyleFrame) {
+      mStyleFrame = nullptr;
+    }
+    nsDisplayBackgroundImage::RemoveFrame(aFrame);
+  }
+
   NS_DISPLAY_DECL_NAME("TableBackgroundImage", TYPE_TABLE_BACKGROUND_IMAGE)
 protected:
   virtual nsIFrame* StyleFrame() const override { return mStyleFrame; }
 
   nsIFrame* mStyleFrame;
   TableType mTableType;
 };
 
@@ -4319,25 +4331,45 @@ protected:
 class nsDisplayTableThemedBackground : public nsDisplayThemedBackground {
 public:
   nsDisplayTableThemedBackground(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                                  const nsRect& aBackgroundRect,
                                  nsIFrame* aAncestorFrame)
     : nsDisplayThemedBackground(aBuilder, aFrame, aBackgroundRect)
     , mAncestorFrame(aAncestorFrame)
     , mTableType(GetTableTypeFromFrame(aAncestorFrame))
-  { }
+  {
+    if (aBuilder->IsRetainingDisplayList()) {
+      mAncestorFrame->AddDisplayItem(this);
+    }
+  }
+  ~nsDisplayTableThemedBackground() {
+    if (mAncestorFrame) {
+      mAncestorFrame->RemoveDisplayItem(this);
+    }
+  }
 
   virtual uint32_t GetPerFrameKey() const override {
     return (static_cast<uint8_t>(mTableType) << TYPE_BITS) |
            nsDisplayItem::GetPerFrameKey();
   }
 
   virtual nsIFrame* FrameForInvalidation() const override { return mAncestorFrame; }
 
+  virtual bool HasDeletedFrame() const override {
+    return !mAncestorFrame || nsDisplayThemedBackground::HasDeletedFrame();
+  }
+
+  virtual void RemoveFrame(nsIFrame* aFrame) override {
+    if (aFrame == mAncestorFrame) {
+      mAncestorFrame = nullptr;
+    }
+    nsDisplayThemedBackground::RemoveFrame(aFrame);
+  }
+
   NS_DISPLAY_DECL_NAME("TableThemedBackground", TYPE_TABLE_THEMED_BACKGROUND_IMAGE)
 protected:
   virtual nsIFrame* StyleFrame() const override { return mAncestorFrame; }
   nsIFrame* mAncestorFrame;
   TableType mTableType;
 };
 
 class nsDisplayBackgroundColor : public nsDisplayItem
@@ -4462,20 +4494,40 @@ public:
   nsDisplayTableBackgroundColor(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                                 const nsRect& aBackgroundRect,
                                 mozilla::ComputedStyle* aBackgroundStyle,
                                 nscolor aColor,
                                 nsIFrame* aAncestorFrame)
     : nsDisplayBackgroundColor(aBuilder, aFrame, aBackgroundRect, aBackgroundStyle, aColor)
     , mAncestorFrame(aAncestorFrame)
     , mTableType(GetTableTypeFromFrame(aAncestorFrame))
-  { }
+  {
+    if (aBuilder->IsRetainingDisplayList()) {
+      mAncestorFrame->AddDisplayItem(this);
+    }
+  }
+  ~nsDisplayTableBackgroundColor() {
+    if (mAncestorFrame) {
+      mAncestorFrame->RemoveDisplayItem(this);
+    }
+  }
 
   virtual nsIFrame* FrameForInvalidation() const override { return mAncestorFrame; }
 
+  virtual bool HasDeletedFrame() const override {
+    return !mAncestorFrame || nsDisplayBackgroundColor::HasDeletedFrame();
+  }
+
+  virtual void RemoveFrame(nsIFrame* aFrame) override {
+    if (aFrame == mAncestorFrame) {
+      mAncestorFrame = nullptr;
+    }
+    nsDisplayBackgroundColor::RemoveFrame(aFrame);
+  }
+
   virtual uint32_t GetPerFrameKey() const override {
     return (static_cast<uint8_t>(mTableType) << TYPE_BITS) |
            nsDisplayItem::GetPerFrameKey();
   }
 
   NS_DISPLAY_DECL_NAME("TableBackgroundColor", TYPE_TABLE_BACKGROUND_COLOR)
 protected:
   nsIFrame* mAncestorFrame;
@@ -5454,32 +5506,56 @@ class nsDisplayTableBlendMode : public n
 public:
   nsDisplayTableBlendMode(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                           nsDisplayList* aList, uint8_t aBlendMode,
                           const ActiveScrolledRoot* aActiveScrolledRoot,
                           uint32_t aIndex, nsIFrame* aAncestorFrame)
     : nsDisplayBlendMode(aBuilder, aFrame, aList, aBlendMode, aActiveScrolledRoot, aIndex)
     , mAncestorFrame(aAncestorFrame)
     , mTableType(GetTableTypeFromFrame(aAncestorFrame))
-  {}
+  {
+    if (aBuilder->IsRetainingDisplayList()) {
+      mAncestorFrame->AddDisplayItem(this);
+    }
+  }
 
   nsDisplayTableBlendMode(nsDisplayListBuilder* aBuilder,
                           const nsDisplayTableBlendMode& aOther)
     : nsDisplayBlendMode(aBuilder, aOther)
     , mAncestorFrame(aOther.mAncestorFrame)
     , mTableType(aOther.mTableType)
-  {}
+  {
+    if (aBuilder->IsRetainingDisplayList()) {
+      mAncestorFrame->AddDisplayItem(this);
+    }
+  }
+  ~nsDisplayTableBlendMode() {
+    if (mAncestorFrame) {
+      mAncestorFrame->RemoveDisplayItem(this);
+    }
+  }
 
   virtual nsDisplayWrapList* Clone(nsDisplayListBuilder* aBuilder) const override
   {
     return MakeDisplayItem<nsDisplayTableBlendMode>(aBuilder, *this);
   }
 
   virtual nsIFrame* FrameForInvalidation() const override { return mAncestorFrame; }
 
+  virtual bool HasDeletedFrame() const override {
+    return !mAncestorFrame || nsDisplayBlendMode::HasDeletedFrame();
+  }
+
+  virtual void RemoveFrame(nsIFrame* aFrame) override {
+    if (aFrame == mAncestorFrame) {
+      mAncestorFrame = nullptr;
+    }
+    nsDisplayBlendMode::RemoveFrame(aFrame);
+  }
+
   virtual uint32_t GetPerFrameKey() const override {
     return (mIndex << (TYPE_BITS + static_cast<uint8_t>(TableTypeBits::COUNT))) |
            (static_cast<uint8_t>(mTableType) << TYPE_BITS) |
            nsDisplayItem::GetPerFrameKey();
   }
 
   NS_DISPLAY_DECL_NAME("TableBlendMode", TYPE_TABLE_BLEND_MODE)
 
@@ -5566,39 +5642,63 @@ public:
 
   virtual nsDisplayWrapList* Clone(nsDisplayListBuilder* aBuilder) const override
   {
     return MakeDisplayItem<nsDisplayTableBlendContainer>(aBuilder, *this);
   }
 
   virtual nsIFrame* FrameForInvalidation() const override { return mAncestorFrame; }
 
+  virtual bool HasDeletedFrame() const override {
+    return !mAncestorFrame || nsDisplayBlendContainer::HasDeletedFrame();
+  }
+
+  virtual void RemoveFrame(nsIFrame* aFrame) override {
+    if (aFrame == mAncestorFrame) {
+      mAncestorFrame = nullptr;
+    }
+    nsDisplayBlendContainer::RemoveFrame(aFrame);
+  }
+
   virtual uint32_t GetPerFrameKey() const override {
     return (static_cast<uint8_t>(mTableType) << TYPE_BITS) |
            nsDisplayItem::GetPerFrameKey();
   }
 
   NS_DISPLAY_DECL_NAME("TableBlendContainer", TYPE_TABLE_BLEND_CONTAINER)
 
 protected:
   nsDisplayTableBlendContainer(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                                nsDisplayList* aList,
                                const ActiveScrolledRoot* aActiveScrolledRoot,
                                bool aIsForBackground, nsIFrame* aAncestorFrame)
     : nsDisplayBlendContainer(aBuilder, aFrame, aList, aActiveScrolledRoot, aIsForBackground)
     , mAncestorFrame(aAncestorFrame)
     , mTableType(GetTableTypeFromFrame(aAncestorFrame))
-  {}
+  {
+    if (aBuilder->IsRetainingDisplayList()) {
+      mAncestorFrame->AddDisplayItem(this);
+    }
+  }
 
   nsDisplayTableBlendContainer(nsDisplayListBuilder* aBuilder,
                                const nsDisplayTableBlendContainer& aOther)
     : nsDisplayBlendContainer(aBuilder, aOther)
     , mAncestorFrame(aOther.mAncestorFrame)
     , mTableType(aOther.mTableType)
-  {}
+  {
+    if (aBuilder->IsRetainingDisplayList()) {
+      mAncestorFrame->AddDisplayItem(this);
+    }
+  }
+  ~nsDisplayTableBlendContainer() {
+    if (mAncestorFrame) {
+      mAncestorFrame->RemoveDisplayItem(this);
+    }
+  }
 
   nsIFrame* mAncestorFrame;
   TableType mTableType;
 };
 
 
 /**
  * nsDisplayOwnLayer constructor flags. If we nest this class inside
@@ -5934,16 +6034,27 @@ public:
 
   virtual nsDisplayWrapList* Clone(nsDisplayListBuilder* aBuilder) const override
   {
     return MakeDisplayItem<nsDisplayTableFixedPosition>(aBuilder, *this);
   }
 
   virtual nsIFrame* FrameForInvalidation() const override { return mAncestorFrame; }
 
+  virtual bool HasDeletedFrame() const override {
+    return !mAncestorFrame || nsDisplayFixedPosition::HasDeletedFrame();
+  }
+
+  virtual void RemoveFrame(nsIFrame* aFrame) override {
+    if (aFrame == mAncestorFrame) {
+      mAncestorFrame = nullptr;
+    }
+    nsDisplayFixedPosition::RemoveFrame(aFrame);
+  }
+
   virtual uint32_t GetPerFrameKey() const override {
     return (mIndex << (TYPE_BITS + static_cast<uint8_t>(TableTypeBits::COUNT))) |
            (static_cast<uint8_t>(mTableType) << TYPE_BITS) |
            nsDisplayItem::GetPerFrameKey();
   }
 
   NS_DISPLAY_DECL_NAME("TableFixedPosition", TYPE_TABLE_FIXED_POSITION)
 protected:
@@ -5951,17 +6062,26 @@ protected:
                               nsDisplayList* aList, uint32_t aIndex,
                               nsIFrame* aAncestorFrame);
 
   nsDisplayTableFixedPosition(nsDisplayListBuilder* aBuilder,
                               const nsDisplayTableFixedPosition& aOther)
     : nsDisplayFixedPosition(aBuilder, aOther)
     , mAncestorFrame(aOther.mAncestorFrame)
     , mTableType(aOther.mTableType)
-  {}
+  {
+    if (aBuilder->IsRetainingDisplayList()) {
+      mAncestorFrame->AddDisplayItem(this);
+    }
+  }
+  ~nsDisplayTableFixedPosition() {
+    if (mAncestorFrame) {
+      mAncestorFrame->RemoveDisplayItem(this);
+    }
+  }
 
   nsIFrame* mAncestorFrame;
   TableType mTableType;
 };
 
 /**
  * This creates an empty scrollable layer. It has no child layers.
  * It is used to record the existence of a scrollable frame in the layer
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1191,19 +1191,19 @@ fuzzy-if(webrender,4,361) == 449519-1.ht
 == 452964-1.html 452964-1-ref.html
 == 454361.html about:blank
 == 455105-1.html 455105-ref.html
 == 455105-2.html 455105-ref.html
 == 455171-5.html 455171-5-ref.html
 == 455280-1.xhtml 455280-1-ref.xhtml
 == 455826-1.html 455826-1-ref.html
 fails-if(Android||cocoaWidget||winWidget) == 456147.xul 456147-ref.html # bug 458047
-fuzzy-if(Android,11,41) fuzzy-if(winWidget||gtkWidget,4,6) fuzzy-if(d2d,15,69) fuzzy-if(skiaContent,42,154) fuzzy-if(webrender,56-60,450-497) == 456219-1a.html 456219-1-ref.html # bug 1128229
-fuzzy-if(Android,11,41) fuzzy-if(winWidget||gtkWidget,4,6) fuzzy-if(d2d,15,69) fuzzy-if(skiaContent,42,154) fuzzy-if(webrender,56-60,450-497) == 456219-1b.html 456219-1-ref.html # bug 1128229
-fuzzy-if(Android,11,41) fuzzy-if(winWidget||gtkWidget,4,6) fuzzy-if(d2d,15,69) fuzzy-if(skiaContent,42,154) fuzzy-if(webrender,56-60,450-497) == 456219-1c.html 456219-1-ref.html # bug 1128229
+fuzzy-if(Android,11,41) fuzzy-if(winWidget||gtkWidget,4,6) fuzzy-if(d2d,15,69) fuzzy-if(skiaContent,42,154) fuzzy-if(webrender,56-60,449-497) == 456219-1a.html 456219-1-ref.html # bug 1128229
+fuzzy-if(Android,11,41) fuzzy-if(winWidget||gtkWidget,4,6) fuzzy-if(d2d,15,69) fuzzy-if(skiaContent,42,154) fuzzy-if(webrender,56-60,449-497) == 456219-1b.html 456219-1-ref.html # bug 1128229
+fuzzy-if(Android,11,41) fuzzy-if(winWidget||gtkWidget,4,6) fuzzy-if(d2d,15,69) fuzzy-if(skiaContent,42,154) fuzzy-if(webrender,56-60,449-497) == 456219-1c.html 456219-1-ref.html # bug 1128229
 fuzzy-if(skiaContent,1,45) fuzzy-if(webrender,9-9,8-8) == 456219-2.html 456219-2-ref.html
 == 456330-1.gif 456330-1-ref.png
 == 456484-1.html 456484-1-ref.html
 == 457398-1.html 457398-1-ref.html
 == 457398-2.html 457398-2-ref.html
 == 458296-1a.html 458296-1a-ref.html
 == 458296-1b.html 458296-1-ref.html
 == 458296-1c.html 458296-1-ref.html
--- a/layout/reftests/w3c-css/submitted/contain/contain-paint-stacking-context-001-ref.html
+++ b/layout/reftests/w3c-css/submitted/contain/contain-paint-stacking-context-001-ref.html
@@ -1,62 +1,62 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <meta charset="utf-8">
-  <title>CSS Reftest Reference</title>
-  <link rel="author" title="Yusuf Sermet" href="mailto:ysermet@mozilla.com">
-  <style>
-    div {
-      position: relative;
-      width: 100px;
-    }
-    #div1,
-    #div3 {
-      background-color: #cfc;
-    }
-    #div1 {
-      z-index: 5;
-    }
-    #div2 {
-      z-index: 1;
-      background-color: #fdd;
-      height: 100px;
-      top: -20px;
-    }
-    #div2_1 {
-      background-color: #ffc;
-      z-index: 6;
-      top: -10px;
-    }
-    #div2_2 {
-      z-index: 3;
-      position: absolute;
-      top: -15px;
-      width: 40px;
-      height: 100px;
-      background-color: #ddf;
-    }
-    #div3 {
-      z-index: 2;
-      top: -50px;
-    }
-  </style>
-</head>
-<body>
-  <div id="div1">
-    <br/><br/>
-  </div>
-
-  <div id="div2">
-    <div id="div2_1">
-      <br/><br/>
-    </div>
-
-    <div id="div2_2">
-    </div>
-  </div>
-
-  <div id="div3">
-    <br/><br/>
-  </div>
-</body>
-</html>
\ No newline at end of file
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>CSS Reftest Reference</title>
+  <link rel="author" title="Yusuf Sermet" href="mailto:ysermet@mozilla.com">
+  <style>
+    div {
+      position: relative;
+      width: 100px;
+    }
+    #div1,
+    #div3 {
+      background-color: #cfc;
+    }
+    #div1 {
+      z-index: 5;
+    }
+    #div2 {
+      z-index: 1;
+      background-color: #fdd;
+      height: 100px;
+      top: -20px;
+    }
+    #div2_1 {
+      background-color: #ffc;
+      z-index: 6;
+      top: -10px;
+    }
+    #div2_2 {
+      z-index: 3;
+      position: absolute;
+      top: -15px;
+      width: 40px;
+      height: 100px;
+      background-color: #ddf;
+    }
+    #div3 {
+      z-index: 2;
+      top: -50px;
+    }
+  </style>
+</head>
+<body>
+  <div id="div1">
+    <br/><br/>
+  </div>
+
+  <div id="div2">
+    <div id="div2_1">
+      <br/><br/>
+    </div>
+
+    <div id="div2_2">
+    </div>
+  </div>
+
+  <div id="div3">
+    <br/><br/>
+  </div>
+</body>
+</html>
--- a/layout/reftests/w3c-css/submitted/contain/contain-paint-stacking-context-001a.html
+++ b/layout/reftests/w3c-css/submitted/contain/contain-paint-stacking-context-001a.html
@@ -1,66 +1,66 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <meta charset="utf-8">
-  <title>CSS Test: 'contain: paint' with stacking contents. Z-index is defined only for siblings and children.</title>
-  <link rel="author" title="Yusuf Sermet" href="mailto:ysermet@mozilla.com">
-
-  <link rel="help" href="https://drafts.csswg.org/css2/visuren.html#x43">
-  <link rel="help" href="https://drafts.csswg.org/css-contain/#containment-paint">
-  <link rel="match" href="contain-paint-stacking-context-001-ref.html">
-  <style>
-    div {
-      position: relative;
-      width: 100px;
-    }
-    #div1,
-    #div3 {
-      background-color: #cfc;
-    }
-    #div1 {
-      z-index: 5;
-    }
-    #div2 {
-      contain: paint;
-      background-color: #fdd;
-      height: 100px;
-      top: -20px;
-    }
-    #div2_1 {
-      background-color: #ffc;
-      z-index: 6;
-      top: -10px;
-    }
-    #div2_2 {
-      z-index: 3;
-      position: absolute;
-      top: -15px;
-      width: 40px;
-      height: 100px;
-      background-color: #ddf;
-    }
-    #div3 {
-      z-index: 2;
-      top: -50px;
-    }
-  </style>
-</head>
-<body>
-  <div id="div1">
-    <br/><br/>
-  </div>
-
-  <div id="div2">
-    <div id="div2_1">
-      <br/><br/>
-    </div>
-
-    <div id="div2_2">
-    </div>
-  </div>
-
-  <div id="div3">
-    <br/><br/>
-  </div>
-</body>
-</html>
\ No newline at end of file
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>CSS Test: 'contain: paint' with stacking contents. Z-index is defined only for siblings and children.</title>
+  <link rel="author" title="Yusuf Sermet" href="mailto:ysermet@mozilla.com">
+
+  <link rel="help" href="https://drafts.csswg.org/css2/visuren.html#x43">
+  <link rel="help" href="https://drafts.csswg.org/css-contain/#containment-paint">
+  <link rel="match" href="contain-paint-stacking-context-001-ref.html">
+  <style>
+    div {
+      position: relative;
+      width: 100px;
+    }
+    #div1,
+    #div3 {
+      background-color: #cfc;
+    }
+    #div1 {
+      z-index: 5;
+    }
+    #div2 {
+      contain: paint;
+      background-color: #fdd;
+      height: 100px;
+      top: -20px;
+    }
+    #div2_1 {
+      background-color: #ffc;
+      z-index: 6;
+      top: -10px;
+    }
+    #div2_2 {
+      z-index: 3;
+      position: absolute;
+      top: -15px;
+      width: 40px;
+      height: 100px;
+      background-color: #ddf;
+    }
+    #div3 {
+      z-index: 2;
+      top: -50px;
+    }
+  </style>
+</head>
+<body>
+  <div id="div1">
+    <br/><br/>
+  </div>
+
+  <div id="div2">
+    <div id="div2_1">
+      <br/><br/>
+    </div>
+
+    <div id="div2_2">
+    </div>
+  </div>
+
+  <div id="div3">
+    <br/><br/>
+  </div>
+</body>
+</html>
--- a/layout/reftests/w3c-css/submitted/contain/contain-paint-stacking-context-001b.html
+++ b/layout/reftests/w3c-css/submitted/contain/contain-paint-stacking-context-001b.html
@@ -1,66 +1,66 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <meta charset="utf-8">
-  <title>CSS Test: 'will-change: contain' with stacking contents. Z-index is defined only for siblings and children.</title>
-  <link rel="author" title="Yusuf Sermet" href="mailto:ysermet@mozilla.com">
-
-  <link rel="help" href="https://drafts.csswg.org/css2/visuren.html#x43">
-  <link rel="help" href="https://drafts.csswg.org/css-contain/#containment-paint">
-  <link rel="match" href="contain-paint-stacking-context-001-ref.html">
-  <style>
-    div {
-      position: relative;
-      width: 100px;
-    }
-    #div1,
-    #div3 {
-      background-color: #cfc;
-    }
-    #div1 {
-      z-index: 5;
-    }
-    #div2 {
-      will-change: contain;
-      background-color: #fdd;
-      height: 100px;
-      top: -20px;
-    }
-    #div2_1 {
-      background-color: #ffc;
-      z-index: 6;
-      top: -10px;
-    }
-    #div2_2 {
-      z-index: 3;
-      position: absolute;
-      top: -15px;
-      width: 40px;
-      height: 100px;
-      background-color: #ddf;
-    }
-    #div3 {
-      z-index: 2;
-      top: -50px;
-    }
-  </style>
-</head>
-<body>
-  <div id="div1">
-    <br/><br/>
-  </div>
-
-  <div id="div2">
-    <div id="div2_1">
-      <br/><br/>
-    </div>
-
-    <div id="div2_2">
-    </div>
-  </div>
-
-  <div id="div3">
-    <br/><br/>
-  </div>
-</body>
-</html>
\ No newline at end of file
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>CSS Test: 'will-change: contain' with stacking contents. Z-index is defined only for siblings and children.</title>
+  <link rel="author" title="Yusuf Sermet" href="mailto:ysermet@mozilla.com">
+
+  <link rel="help" href="https://drafts.csswg.org/css2/visuren.html#x43">
+  <link rel="help" href="https://drafts.csswg.org/css-contain/#containment-paint">
+  <link rel="match" href="contain-paint-stacking-context-001-ref.html">
+  <style>
+    div {
+      position: relative;
+      width: 100px;
+    }
+    #div1,
+    #div3 {
+      background-color: #cfc;
+    }
+    #div1 {
+      z-index: 5;
+    }
+    #div2 {
+      will-change: contain;
+      background-color: #fdd;
+      height: 100px;
+      top: -20px;
+    }
+    #div2_1 {
+      background-color: #ffc;
+      z-index: 6;
+      top: -10px;
+    }
+    #div2_2 {
+      z-index: 3;
+      position: absolute;
+      top: -15px;
+      width: 40px;
+      height: 100px;
+      background-color: #ddf;
+    }
+    #div3 {
+      z-index: 2;
+      top: -50px;
+    }
+  </style>
+</head>
+<body>
+  <div id="div1">
+    <br/><br/>
+  </div>
+
+  <div id="div2">
+    <div id="div2_1">
+      <br/><br/>
+    </div>
+
+    <div id="div2_2">
+    </div>
+  </div>
+
+  <div id="div3">
+    <br/><br/>
+  </div>
+</body>
+</html>
--- a/mozglue/misc/interceptor/VMSharingPolicies.h
+++ b/mozglue/misc/interceptor/VMSharingPolicies.h
@@ -79,179 +79,79 @@ public:
     return *this;
   }
 
 private:
   uint32_t  mNextChunkIndex;
 };
 
 template <typename MMPolicy, uint32_t kChunkSize>
-class VMSharingPolicyShared : public MMPolicyBase
-{
-  typedef VMSharingPolicyUnique<MMPolicy, kChunkSize> ValueT;
-
-  // We use pid instead of HANDLE for mapping, since more than one handle may
-  // map to the same pid. We don't worry about pid reuse becuase each mVMPolicy
-  // holds an open handle to pid, thus keeping the pid reserved at least for the
-  // lifetime of mVMPolicy.
-  struct ProcMapEntry
-  {
-    ProcMapEntry()
-      : mPid(::GetCurrentProcessId())
-    {
-    }
-
-    explicit ProcMapEntry(HANDLE aProc)
-      : mPid(::GetProcessId(aProc))
-      , mVMPolicy(aProc)
-    {
-    }
+class VMSharingPolicyShared;
 
-    ProcMapEntry(ProcMapEntry&& aOther)
-      : mPid(aOther.mPid)
-      , mVMPolicy(Move(aOther.mVMPolicy))
-    {
-      aOther.mPid = 0;
-    }
-
-    ProcMapEntry(const ProcMapEntry&) = delete;
-    ProcMapEntry& operator=(const ProcMapEntry&) = delete;
-
-    ProcMapEntry& operator=(ProcMapEntry&& aOther)
-    {
-      mPid = aOther.mPid;
-      mVMPolicy = Move(aOther.mVMPolicy);
-      aOther.mPid = 0;
-      return *this;
-    }
-
-    bool operator==(DWORD aPid) const
-    {
-      return mPid == aPid;
-    }
-
-    DWORD   mPid;
-    ValueT  mVMPolicy;
-  };
-
-  // We normally expect to reference only one other process at a time, but this
-  // is not a requirement.
-  typedef Vector<ProcMapEntry, 1> MapT;
+// We only support this policy for in-proc MMPolicy
+template <uint32_t kChunkSize>
+class VMSharingPolicyShared<MMPolicyInProcess, kChunkSize> : public MMPolicyBase
+{
+  typedef VMSharingPolicyUnique<MMPolicyInProcess, kChunkSize> UniquePolicyT;
 
 public:
-  typedef MMPolicy MMPolicyT;
+  typedef MMPolicyInProcess MMPolicyT;
 
-  template <typename... Args>
-  explicit VMSharingPolicyShared(Args... aArgs)
-    : mPid(GetPid(aArgs...))
+  VMSharingPolicyShared()
   {
     static const bool isAlloc = []() -> bool {
-      sPerProcVM = new MapT();
       DWORD flags = 0;
 #if defined(RELEASE_OR_BETA)
       flags |= CRITICAL_SECTION_NO_DEBUG_INFO;
 #endif // defined(RELEASE_OR_BETA)
       ::InitializeCriticalSectionEx(&sCS, 4000, flags);
       return true;
     }();
-
-    MOZ_ASSERT(mPid);
-    if (!mPid) {
-      return;
-    }
-
-    AutoCriticalSection lock(&sCS);
-
-    if (find(mPid)) {
-      return;
-    }
-
-    bool appended = sPerProcVM->append(ProcMapEntry(aArgs...));
-    MOZ_RELEASE_ASSERT(appended);
   }
 
   explicit operator bool() const
   {
     AutoCriticalSection lock(&sCS);
-
-    ProcMapEntry* entry;
-    bool found = find(mPid, &entry);
-    MOZ_RELEASE_ASSERT(found);
-
-    return !!entry->mVMPolicy;
+    return !!sUniqueVM;
   }
 
-  operator const MMPolicy&() const
+  operator const MMPolicyInProcess&() const
   {
     AutoCriticalSection lock(&sCS);
-
-    ProcMapEntry* entry;
-    bool found = find(mPid, &entry);
-    MOZ_RELEASE_ASSERT(found);
-
-    return entry->mVMPolicy;
+    return sUniqueVM;
   }
 
   bool ShouldUnhookUponDestruction() const
   {
     AutoCriticalSection lock(&sCS);
-
-    ProcMapEntry* entry;
-    if (!find(mPid, &entry)) {
-      return 0;
-    }
-
-    return entry->mVMPolicy.ShouldUnhookUponDestruction();
+    return sUniqueVM.ShouldUnhookUponDestruction();
   }
 
   bool Reserve(uint32_t aCount)
   {
     AutoCriticalSection lock(&sCS);
-
-    ProcMapEntry* entry;
-    if (!find(mPid, &entry)) {
-      return false;
-    }
-
-    return entry->mVMPolicy.Reserve(aCount);
+    return sUniqueVM.Reserve(aCount);
   }
 
   bool IsPageAccessible(void* aVAddress) const
   {
     AutoCriticalSection lock(&sCS);
-
-    ProcMapEntry* entry;
-    if (!find(mPid, &entry)) {
-      return false;
-    }
-
-    return entry->mVMPolicy.IsPageAccessible(aVAddress);
+    return sUniqueVM.IsPageAccessible(aVAddress);
   }
 
-  Trampoline<MMPolicy> GetNextTrampoline()
+  Trampoline<MMPolicyInProcess> GetNextTrampoline()
   {
     AutoCriticalSection lock(&sCS);
-
-    ProcMapEntry* entry;
-    if (!find(mPid, &entry)) {
-      return nullptr;
-    }
-
-    return entry->mVMPolicy.GetNextTrampoline();
+    return sUniqueVM.GetNextTrampoline();
   }
 
-  TrampolineCollection<MMPolicy> Items() const
+  TrampolineCollection<MMPolicyInProcess> Items() const
   {
     AutoCriticalSection lock(&sCS);
-
-    ProcMapEntry* entry;
-    bool found = find(mPid, &entry);
-    MOZ_RELEASE_ASSERT(found);
-
-    TrampolineCollection<MMPolicy> items(Move(entry->mVMPolicy.Items()));
+    TrampolineCollection<MMPolicyInProcess> items(Move(sUniqueVM.Items()));
 
     // We need to continue holding the lock until items is destroyed.
     items.Lock(sCS);
 
     return Move(items);
   }
 
   void Clear()
@@ -263,51 +163,24 @@ public:
   ~VMSharingPolicyShared() = default;
 
   VMSharingPolicyShared(const VMSharingPolicyShared&) = delete;
   VMSharingPolicyShared(VMSharingPolicyShared&&) = delete;
   VMSharingPolicyShared& operator=(const VMSharingPolicyShared&) = delete;
   VMSharingPolicyShared& operator=(VMSharingPolicyShared&&) = delete;
 
 private:
-  static bool find(DWORD aPid, ProcMapEntry** aOutEntry = nullptr)
-  {
-    MOZ_ASSERT(sPerProcVM);
-    if (!sPerProcVM) {
-      return false;
-    }
-
-    if (aOutEntry) {
-      *aOutEntry = nullptr;
-    }
-
-    for (auto&& mapping : *sPerProcVM) {
-      if (mapping == aPid) {
-        if (aOutEntry) {
-          *aOutEntry = &mapping;
-        }
-        return true;
-      }
-    }
-
-    return false;
-  }
-
-  static DWORD GetPid() { return ::GetCurrentProcessId(); }
-  static DWORD GetPid(HANDLE aHandle) { return ::GetProcessId(aHandle); }
-
-  DWORD mPid;
-  static MapT* sPerProcVM;
+  static UniquePolicyT sUniqueVM;
   static CRITICAL_SECTION sCS;
 };
 
-template <typename MMPolicy, uint32_t kChunkSize>
-typename VMSharingPolicyShared<MMPolicy, kChunkSize>::MapT *
-  VMSharingPolicyShared<MMPolicy, kChunkSize>::sPerProcVM;
+template <uint32_t kChunkSize>
+typename VMSharingPolicyShared<MMPolicyInProcess, kChunkSize>::UniquePolicyT
+  VMSharingPolicyShared<MMPolicyInProcess, kChunkSize>::sUniqueVM;
 
-template <typename MMPolicy, uint32_t kChunkSize>
-CRITICAL_SECTION VMSharingPolicyShared<MMPolicy, kChunkSize>::sCS;
+template <uint32_t kChunkSize>
+CRITICAL_SECTION VMSharingPolicyShared<MMPolicyInProcess, kChunkSize>::sCS;
 
 } // namespace interceptor
 } // namespace mozilla
 
 #endif // mozilla_interceptor_VMSharingPolicies_h
 
--- a/netwerk/base/nsBufferedStreams.cpp
+++ b/netwerk/base/nsBufferedStreams.cpp
@@ -135,17 +135,17 @@ nsBufferedStream::Seek(int32_t whence, i
     // If the underlying stream isn't a random access store, then fail early.
     // We could possibly succeed for the case where the seek position denotes
     // something that happens to be read into the buffer, but that would make
     // the failure data-dependent.
     nsresult rv;
     nsCOMPtr<nsISeekableStream> ras = do_QueryInterface(mStream, &rv);
     if (NS_FAILED(rv)) {
 #ifdef DEBUG
-        NS_ERROR("mStream doesn't QI to nsISeekableStream");
+        NS_WARNING("mStream doesn't QI to nsISeekableStream");
 #endif
         return rv;
     }
 
     int64_t absPos = 0;
     switch (whence) {
       case nsISeekableStream::NS_SEEK_SET:
         absPos = offset;
--- a/taskcluster/ci/build/macosx.yml
+++ b/taskcluster/ci/build/macosx.yml
@@ -272,8 +272,42 @@ macosx64-nightly/opt:
     toolchains:
         - linux64-cctools-port
         - linux64-clang
         - linux64-hfsplus
         - linux64-libdmg
         - linux64-llvm-dsymutil
         - linux64-rust-macos
         - linux64-sccache
+
+macosx64-ccov/debug:
+    description: "MacOS X x64 Cross-compile Code Coverage"
+    index:
+        product: firefox
+        job-name: macosx64-ccov-debug
+    treeherder:
+        platform: osx-cross-ccov/debug
+        symbol: B
+        tier: 1
+    worker-type: aws-provisioner-v1/gecko-{level}-b-macosx64
+    worker:
+        max-run-time: 5400
+        env:
+            TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/macosx64/cross-releng.manifest"
+    run:
+        using: mozharness
+        actions: [get-secrets build update]
+        config:
+            - builds/releng_base_firefox.py
+            - builds/releng_base_mac_64_cross_builds.py
+        script: "mozharness/scripts/fx_desktop_build.py"
+        secrets: true
+        custom-build-variant-cfg: code-coverage-debug
+        tooltool-downloads: internal
+    run-on-projects: ['try']
+    toolchains:
+        - linux64-cctools-port
+        - linux64-clang-6-pre-macosx-cross
+        - linux64-hfsplus
+        - linux64-libdmg
+        - linux64-llvm-dsymutil
+        - linux64-rust-macos
+        - linux64-sccache
--- a/taskcluster/ci/test/test-platforms.yml
+++ b/taskcluster/ci/test/test-platforms.yml
@@ -256,16 +256,21 @@ macosx64-qr/opt:
     build-platform: macosx64/opt
     test-sets:
         - macosx64-qr-tests
 macosx64-qr/debug:
     build-platform: macosx64/debug
     test-sets:
         - macosx64-qr-tests
 
+macosx64-ccov/debug:
+    build-platform: macosx64-ccov/debug
+    test-sets:
+        - macosx64-tests
+
 ##
 # Android platforms (matching /android.*/)
 
 android-4.3-arm7-api-16/debug:
     build-platform: android-api-16/debug
     test-sets:
         - android-common-tests
         - android-gradle-tests
--- a/taskcluster/taskgraph/transforms/job/mozharness_test.py
+++ b/taskcluster/taskgraph/transforms/job/mozharness_test.py
@@ -28,16 +28,17 @@ BUILDER_NAME_PREFIX = {
     'linux64-jsdcov': 'Ubuntu Code Coverage VM 12.04 x64',
     'linux64-qr': 'Ubuntu VM 12.04 x64',
     'linux64-stylo-disabled': 'Ubuntu VM 12.04 x64',
     'linux64-stylo-sequential': 'Ubuntu VM 12.04 x64',
     'linux64-devedition': 'Ubuntu VM 12.04 x64',
     'linux64-devedition-nightly': 'Ubuntu VM 12.04 x64',
     'macosx64': 'Rev7 MacOSX Yosemite 10.10.5',
     'macosx64-devedition': 'Rev7 MacOSX Yosemite 10.10.5 DevEdition',
+    'macosx64-ccov': 'Rev7 MacOSX Yosemite 10.10.5 Code Coverage',
     'android-4.3-arm7-api-16': 'Android 4.3 armv7 api-16+',
     'android-4.2-x86': 'Android 4.2 x86 Emulator',
     'android-4.3-arm7-api-16-gradle': 'Android 4.3 armv7 api-16+',
     'windows10-64': 'Windows 10 64-bit',
     'windows10-64-nightly': 'Windows 10 64-bit',
     'windows10-64-devedition': 'Windows 10 64-bit DevEdition',
     'windows10-64-pgo': 'Windows 10 64-bit',
     'windows10-64-asan': 'Windows 10 64-bit',
--- a/taskcluster/taskgraph/transforms/tests.py
+++ b/taskcluster/taskgraph/transforms/tests.py
@@ -697,18 +697,17 @@ def handle_suite_category(config, tests)
             if not any(arg.startswith(category_arg) for arg in extra):
                 extra.append('{}={}'.format(category_arg, flavor))
 
         yield test
 
 
 @transforms.add
 def enable_code_coverage(config, tests):
-    """Enable code coverage for the linux64-ccov/.* & linux64-jsdcov/.* & win64-ccov/.*
-    build-platforms"""
+    """Enable code coverage for the ccov and jsdcov build-platforms"""
     for test in tests:
         if 'ccov' in test['build-platform'] and not test['test-name'].startswith('test-verify'):
             test['mozharness'].setdefault('extra-options', []).append('--code-coverage')
             test['instance-size'] = 'xlarge'
             # Ensure we don't run on inbound/autoland/beta, but if the test is try only, ignore it
             if 'mozilla-central' in test['run-on-projects'] or \
                     test['run-on-projects'] == 'built-projects':
                 test['run-on-projects'] = ['mozilla-central', 'try']
new file mode 100644
--- /dev/null
+++ b/testing/mozharness/configs/builds/releng_sub_mac_configs/64_code_coverage_debug.py
@@ -0,0 +1,29 @@
+import os
+
+config = {
+    'default_actions': [
+        'clobber',
+        'build',
+        'check-test',
+        'update',  # decided by query_is_nightly()
+    ],
+    'stage_platform': 'macosx64-ccov-debug',
+    'debug_build': True,
+    #### 64 bit build specific #####
+    'env': {
+        'MOZBUILD_STATE_PATH': os.path.join(os.getcwd(), '.mozbuild'),
+        'HG_SHARE_BASE_DIR': '/builds/hg-shared',
+        'MOZ_OBJDIR': '%(abs_obj_dir)s',
+        'TINDERBOX_OUTPUT': '1',
+        'TOOLTOOL_CACHE': '/builds/tooltool_cache',
+        'TOOLTOOL_HOME': '/builds',
+        'MOZ_CRASHREPORTER_NO_REPORT': '1',
+        'LC_ALL': 'C',
+        ## 64 bit specific
+        'PATH': '/tools/python/bin:/opt/local/bin:/usr/bin:'
+                '/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin',
+        ##
+    },
+    'mozconfig_variant': 'code-coverage-debug',
+    #######################
+}
--- a/testing/web-platform/meta/html/semantics/forms/the-label-element/label-attributes.html.ini
+++ b/testing/web-platform/meta/html/semantics/forms/the-label-element/label-attributes.html.ini
@@ -1,3 +1,2 @@
 [label-attributes.html]
   prefs: [dom.webcomponents.shadowdom.enabled:true]
-  max-asserts: 8
--- a/testing/web-platform/tests/html/semantics/forms/the-label-element/label-attributes.html
+++ b/testing/web-platform/tests/html/semantics/forms/the-label-element/label-attributes.html
@@ -222,32 +222,38 @@
                     "The number of labels should be 2 after the labelable element is moved to iframe.");
     });
 
     iframe.setAttribute('src', 'http://web-platform.test:8000/html/semantics/forms/the-label-element/iframe-label-attributes.html');
     document.body.appendChild(iframe);
   }, "A labelable element is moved to iframe.");
 
   test(function () {
-    var labels1 = document.getElementById("test14").labels;
+    var test14 = document.getElementById("test14");
+    var labels1 = test14.labels;
     var labels2 = document.getElementById("test15").labels;
     assert_true(labels1 instanceof NodeList,
                 "A form control's 'labels' property should be an instance of a NodeList.");
     assert_equals(labels1.length, 1,
                   "The form control has an ancestor with no explicit associated label, and is the first labelable descendant.");
     assert_equals(labels2.length, 1,
                   "The number of labels associated with a form control should be the number of label elements for which it is a labeled control.");
     assert_array_equals(labels1, [document.getElementById("lbl14")],
                         "The labels for a form control should be returned in tree order.");
+    assert_array_equals(labels2, [document.getElementById("lbl15")],
+                        "The labels for a form control should be returned in tree order.");
 
     document.getElementById('div6').removeChild(document.getElementById('div7'));
+    assert_equals(labels1.length, 1,
+                  "The number of labels should be 1 after the labelable element is removed but label element is still in the same tree.");
+    assert_equals(labels2.length, 0,
+                  "The number of labels should be 0 since there is no label with a 'for' attribute associated with this labelable element.");
+    test14.remove();
     assert_equals(labels1.length, 0,
                   "The number of labels should be 0 after the labelable element is removed.");
-    assert_equals(labels2.length, 0,
-                  "The number of labels should be 0 since there is no label with a 'for' attribute associated with this labelable element.");
   }, "A div element which contains labelable element is removed.");
 
   test(function () {
     // <label><input id="test16"><label for="test16"></label></label>
     var label1 = document.createElement('label');
     label1.innerHTML = "<input id='test16'>";
     var label2 = document.createElement('label');
     label2.htmlFor = "test16";
--- a/xpcom/io/nsMultiplexInputStream.cpp
+++ b/xpcom/io/nsMultiplexInputStream.cpp
@@ -12,26 +12,29 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/SystemGroup.h"
 
 #include "base/basictypes.h"
 
 #include "nsMultiplexInputStream.h"
+#include "nsIBufferedStreams.h"
 #include "nsICloneableInputStream.h"
 #include "nsIMultiplexInputStream.h"
 #include "nsISeekableStream.h"
 #include "nsCOMPtr.h"
 #include "nsCOMArray.h"
 #include "nsIClassInfoImpl.h"
 #include "nsIIPCSerializableInputStream.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 #include "nsIAsyncInputStream.h"
 #include "nsIInputStreamLength.h"
+#include "nsNetUtil.h"
+#include "nsStreamUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::ipc;
 
 using mozilla::DeprecatedAbs;
 using mozilla::Maybe;
 using mozilla::Nothing;
 using mozilla::Some;
@@ -64,29 +67,33 @@ public:
   void AsyncWaitCompleted();
 
   // This is used for nsIAsyncInputStreamLength::AsyncLengthWait
   void AsyncWaitCompleted(int64_t aLength,
                           const MutexAutoLock& aProofOfLock);
 
   struct StreamData
   {
-    void Initialize(nsIInputStream* aStream)
+    void Initialize(nsIInputStream* aStream, bool aBuffered)
     {
       mStream = aStream;
       mAsyncStream = do_QueryInterface(aStream);
       mSeekableStream = do_QueryInterface(aStream);
+      mBuffered = aBuffered;
     }
 
     nsCOMPtr<nsIInputStream> mStream;
 
     // This can be null.
     nsCOMPtr<nsIAsyncInputStream> mAsyncStream;
     // This can be null.
     nsCOMPtr<nsISeekableStream> mSeekableStream;
+
+    // True if the stream is wrapped with nsIBufferedInputStream.
+    bool mBuffered;
   };
 
   Mutex& GetLock()
   {
     return mLock;
   }
 
 private:
@@ -235,24 +242,36 @@ nsMultiplexInputStream::GetCount(uint32_
   MutexAutoLock lock(mLock);
   *aCount = mStreams.Length();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsMultiplexInputStream::AppendStream(nsIInputStream* aStream)
 {
+  nsCOMPtr<nsIInputStream> stream = aStream;
+
+  bool buffered = false;
+  if (!NS_InputStreamIsBuffered(stream)) {
+    nsCOMPtr<nsIInputStream> bufferedStream;
+    nsresult rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream),
+                                            stream.forget(), 4096);
+    NS_ENSURE_SUCCESS(rv, rv);
+    stream = bufferedStream.forget();
+    buffered = true;
+  }
+
   MutexAutoLock lock(mLock);
 
   StreamData* streamData = mStreams.AppendElement();
   if (NS_WARN_IF(!streamData)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  streamData->Initialize(aStream);
+  streamData->Initialize(stream, buffered);
 
   UpdateQIMap(*streamData, 1);
 
   if (mStatus == NS_BASE_STREAM_CLOSED) {
     // We were closed, but now we have more data to read.
     mStatus = NS_OK;
   }
 
@@ -266,16 +285,27 @@ nsMultiplexInputStream::GetStream(uint32
 
   if (aIndex >= mStreams.Length()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   StreamData& streamData = mStreams.ElementAt(aIndex);
 
   nsCOMPtr<nsIInputStream> stream = streamData.mStream;
+
+  if (streamData.mBuffered) {
+    nsCOMPtr<nsIBufferedInputStream> bufferedStream = do_QueryInterface(stream);
+    MOZ_ASSERT(bufferedStream);
+
+    nsresult rv = bufferedStream->GetData(getter_AddRefs(stream));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
   stream.forget(aResult);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsMultiplexInputStream::Close()
 {
   nsTArray<nsCOMPtr<nsIInputStream>> streams;
--- a/xpcom/tests/gtest/TestMultiplexInputStream.cpp
+++ b/xpcom/tests/gtest/TestMultiplexInputStream.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gtest/gtest.h"
 #include "nsIAsyncInputStream.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIInputStream.h"
 #include "nsIMultiplexInputStream.h"
 #include "nsISeekableStream.h"
+#include "nsStreamUtils.h"
 #include "nsThreadUtils.h"
 #include "Helpers.h"
 
 TEST(MultiplexInputStream, Seek_SET)
 {
   nsCString buf1;
   nsCString buf2;
   nsCString buf3;
@@ -225,18 +226,17 @@ public:
     MOZ_CRASH("This should not be called!");
     return NS_OK;
   }
 
   NS_IMETHOD
   ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
                uint32_t aCount, uint32_t *aResult) override
   {
-    MOZ_CRASH("This should not be called!");
-    return NS_OK;
+    return NS_ERROR_NOT_IMPLEMENTED;
   }
 
   NS_IMETHOD
   Close() override { return NS_OK; }
 
   NS_IMETHOD
   IsNonBlocking(bool* aNonBlocking) override
   {
@@ -276,18 +276,17 @@ public:
     MOZ_CRASH("This should not be called!");
     return NS_OK;
   }
 
   NS_IMETHOD
   ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
                uint32_t aCount, uint32_t *aResult) override
   {
-    MOZ_CRASH("This should not be called!");
-    return NS_OK;
+    return NS_ERROR_NOT_IMPLEMENTED;
   }
 
   NS_IMETHOD
   Close() override
   {
     mState = eClosed;
     return NS_OK;
   }
@@ -382,16 +381,100 @@ TEST(TestMultiplexInputStream, Available
 
   asyncStream->Close();
 
   rv = s->Available(&length);
   ASSERT_EQ(NS_OK, rv);
   ASSERT_EQ(buffer.Length(), length);
 }
 
+class NonBufferableStringStream final : public nsIInputStream
+{
+  nsCOMPtr<nsIInputStream> mStream;
+
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+
+  explicit NonBufferableStringStream(const nsACString& aBuffer)
+  {
+    NS_NewCStringInputStream(getter_AddRefs(mStream), aBuffer);
+  }
+
+  NS_IMETHOD
+  Available(uint64_t* aLength) override
+  {
+    return mStream->Available(aLength);
+  }
+
+  NS_IMETHOD
+  Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount) override
+  {
+    return mStream->Read(aBuffer, aCount, aReadCount);
+  }
+
+  NS_IMETHOD
+  ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
+               uint32_t aCount, uint32_t *aResult) override
+  {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  NS_IMETHOD
+  Close() override
+  {
+    return mStream->Close();
+  }
+
+  NS_IMETHOD
+  IsNonBlocking(bool* aNonBlocking) override
+  {
+    return mStream->IsNonBlocking(aNonBlocking);
+  }
+
+private:
+  ~NonBufferableStringStream() = default;
+};
+
+NS_IMPL_ISUPPORTS(NonBufferableStringStream, nsIInputStream)
+
+TEST(TestMultiplexInputStream, Bufferable) {
+  nsCOMPtr<nsIMultiplexInputStream> multiplexStream =
+    do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1");
+
+  nsCOMPtr<nsIInputStream> s = do_QueryInterface(multiplexStream);
+  ASSERT_TRUE(!!s);
+
+  nsCString buf1;
+  buf1.AssignLiteral("Hello ");
+  nsCOMPtr<nsIInputStream> inputStream1;
+  nsresult rv = NS_NewCStringInputStream(getter_AddRefs(inputStream1), buf1);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  nsCString buf2;
+  buf2.AssignLiteral("world");
+  nsCOMPtr<nsIInputStream> inputStream2 = new NonBufferableStringStream(buf2);
+
+  rv = multiplexStream->AppendStream(inputStream1);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  rv = multiplexStream->AppendStream(inputStream2);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  nsCOMPtr<nsIInputStream> stream(do_QueryInterface(multiplexStream));
+  ASSERT_TRUE(!!stream);
+
+  char buf3[1024];
+  uint32_t size = 0;
+  rv = stream->ReadSegments(NS_CopySegmentToBuffer, buf3, sizeof(buf3), &size);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  ASSERT_EQ(size, buf1.Length() + buf2.Length());
+  ASSERT_TRUE(!strncmp(buf3, "Hello world", size));
+}
+
 TEST(TestMultiplexInputStream, QILengthInputStream) {
   nsCString buf;
   buf.AssignLiteral("Hello world");
 
   nsCOMPtr<nsIMultiplexInputStream> multiplexStream =
     do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1");
 
   // nsMultiplexInputStream doesn't expose nsIInputStreamLength if there are