Merge mozilla-central to mozilla-inbound.
authorCosmin Sabou <csabou@mozilla.com>
Mon, 29 Apr 2019 12:58:22 +0300
changeset 530551 75a329d3bce4f654214b1d14c2cfb6b22770e8ee
parent 530550 3a711ac6b192a428bf44029853cb27239aed5c79 (current diff)
parent 530531 420e18a75314b8123b515d8a93cbacd145ecb03c (diff)
child 530552 5f7126784c3bbc2df74fe29a46fb7142ffd97519
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to mozilla-inbound.
--- a/dom/media/MediaData.h
+++ b/dom/media/MediaData.h
@@ -295,19 +295,16 @@ class MediaData {
   bool mKeyframe;
 
   media::TimeUnit GetEndTime() const { return mTime + mDuration; }
 
   // Return true if the adjusted time is valid. Caller should handle error when
   // the result is invalid.
   virtual bool AdjustForStartTime(const media::TimeUnit& aStartTime) {
     mTime -= aStartTime;
-    if (mTime.IsNegative()) {
-      NS_WARNING("Negative start time after time-adjustment!");
-    }
     return mTime.IsValid();
   }
 
   template <typename ReturnType>
   const ReturnType* As() const {
     MOZ_ASSERT(this->mType == ReturnType::sType);
     return static_cast<const ReturnType*>(this);
   }
--- a/dom/media/platforms/android/RemoteDataDecoder.cpp
+++ b/dom/media/platforms/android/RemoteDataDecoder.cpp
@@ -168,28 +168,24 @@ class RemoteVideoDecoder : public Remote
       mSeekTarget.reset();
       mLatestOutputTime.reset();
       return RemoteDataDecoder::ProcessFlush();
     });
   }
 
   RefPtr<MediaDataDecoder::DecodePromise> Decode(
       MediaRawData* aSample) override {
-    RefPtr<RemoteVideoDecoder> self = this;
-    RefPtr<MediaRawData> sample = aSample;
-    return InvokeAsync(mTaskQueue, __func__, [self, this, sample]() {
-      const VideoInfo* config =
-          sample->mTrackInfo ? sample->mTrackInfo->GetAsVideoInfo() : &mConfig;
-      MOZ_ASSERT(config);
+    const VideoInfo* config =
+        aSample->mTrackInfo ? aSample->mTrackInfo->GetAsVideoInfo() : &mConfig;
+    MOZ_ASSERT(config);
 
-      InputInfo info(sample->mDuration.ToMicroseconds(), config->mImage,
-                     config->mDisplay);
-      mInputInfos.Insert(sample->mTime.ToMicroseconds(), info);
-      return RemoteDataDecoder::ProcessDecode(sample);
-    });
+    InputInfo info(aSample->mDuration.ToMicroseconds(), config->mImage,
+                   config->mDisplay);
+    mInputInfos.Insert(aSample->mTime.ToMicroseconds(), info);
+    return RemoteDataDecoder::Decode(aSample);
   }
 
   bool SupportDecoderRecycling() const override {
     return mIsCodecSupportAdaptivePlayback;
   }
 
   void SetSeekThreshold(const TimeUnit& aTime) override {
     RefPtr<RemoteVideoDecoder> self = this;
@@ -363,36 +359,16 @@ class RemoteAudioDecoder : public Remote
     if (mJavaDecoder == nullptr) {
       return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR,
                                           __func__);
     }
 
     return InitPromise::CreateAndResolve(TrackInfo::kAudioTrack, __func__);
   }
 
-  RefPtr<FlushPromise> Flush() override {
-    RefPtr<RemoteAudioDecoder> self = this;
-    return InvokeAsync(mTaskQueue, __func__, [self, this]() {
-      mFirstDemuxedSampleTime.reset();
-      return RemoteDataDecoder::ProcessFlush();
-    });
-  }
-
-  RefPtr<DecodePromise> Decode(MediaRawData* aSample) override {
-    RefPtr<RemoteAudioDecoder> self = this;
-    RefPtr<MediaRawData> sample = aSample;
-    return InvokeAsync(mTaskQueue, __func__, [self, sample, this]() {
-      if (mFirstDemuxedSampleTime.isNothing()) {
-        MOZ_ASSERT(sample->mTime.IsValid());
-        mFirstDemuxedSampleTime.emplace(sample->mTime);
-      }
-      return RemoteDataDecoder::ProcessDecode(sample);
-    });
-  }
-
  private:
   class CallbacksSupport final : public JavaCallbacksSupport {
    public:
     explicit CallbacksSupport(RemoteAudioDecoder* aDecoder)
         : mDecoder(aDecoder) {}
 
     void HandleInput(int64_t aTimestamp, bool aProcessed) override {
       mDecoder->UpdateInputStatus(aTimestamp, aProcessed);
@@ -427,20 +403,16 @@ class RemoteAudioDecoder : public Remote
     void HandleError(const MediaResult& aError) override {
       mDecoder->Error(aError);
     }
 
    private:
     RemoteAudioDecoder* mDecoder;
   };
 
-  bool IsSampleTimeSmallerThanFirstDemuxedSampleTime(int64_t aTime) const {
-    return TimeUnit::FromMicroseconds(aTime) < mFirstDemuxedSampleTime.ref();
-  }
-
   // Param and LocalRef are only valid for the duration of a JNI method call.
   // Use GlobalRef as the parameter type to keep the Java object referenced
   // until running.
   void ProcessOutput(Sample::GlobalRef&& aSample,
                      SampleBuffer::GlobalRef&& aBuffer) {
     if (!mTaskQueue->IsCurrentThreadIn()) {
       nsresult rv = mTaskQueue->Dispatch(
           NewRunnableMethod<Sample::GlobalRef&&, SampleBuffer::GlobalRef&&>(
@@ -471,18 +443,17 @@ class RemoteAudioDecoder : public Remote
     ok &= NS_SUCCEEDED(info->Offset(&offset));
 
     int64_t presentationTimeUs;
     ok &= NS_SUCCEEDED(info->PresentationTimeUs(&presentationTimeUs));
 
     int32_t size;
     ok &= NS_SUCCEEDED(info->Size(&size));
 
-    if (!ok ||
-        IsSampleTimeSmallerThanFirstDemuxedSampleTime(presentationTimeUs)) {
+    if (!ok) {
       Error(MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__));
       return;
     }
 
     if (size > 0) {
 #ifdef MOZ_SAMPLE_TYPE_S16
       const int32_t numSamples = size / 2;
 #else
@@ -524,17 +495,16 @@ class RemoteAudioDecoder : public Remote
     AssertOnTaskQueue();
 
     mOutputChannels = aChannels;
     mOutputSampleRate = aSampleRate;
   }
 
   int32_t mOutputChannels;
   int32_t mOutputSampleRate;
-  Maybe<TimeUnit> mFirstDemuxedSampleTime;
 };
 
 already_AddRefed<MediaDataDecoder> RemoteDataDecoder::CreateAudioDecoder(
     const CreateDecoderParams& aParams, const nsString& aDrmStubId,
     CDMProxy* aProxy) {
   const AudioInfo& config = aParams.AudioConfig();
   MediaFormat::LocalRef format;
   NS_ENSURE_SUCCESS(
@@ -707,37 +677,33 @@ static CryptoInfo::LocalRef GetCryptoInf
   cryptoInfo->Set(numSubSamples, numBytesOfPlainData, numBytesOfEncryptedData,
                   keyId, iv, MediaCodec::CRYPTO_MODE_AES_CTR);
 
   return cryptoInfo;
 }
 
 RefPtr<MediaDataDecoder::DecodePromise> RemoteDataDecoder::Decode(
     MediaRawData* aSample) {
+  MOZ_ASSERT(aSample != nullptr);
+
   RefPtr<RemoteDataDecoder> self = this;
   RefPtr<MediaRawData> sample = aSample;
-  return InvokeAsync(mTaskQueue, __func__, [self, this, sample]() {
-    return RemoteDataDecoder::ProcessDecode(sample);
-  });
-}
+  return InvokeAsync(mTaskQueue, __func__, [self, sample]() {
+    jni::ByteBuffer::LocalRef bytes = jni::ByteBuffer::New(
+        const_cast<uint8_t*>(sample->Data()), sample->Size());
 
-RefPtr<MediaDataDecoder::DecodePromise> RemoteDataDecoder::ProcessDecode(
-    MediaRawData* aSample) {
-  AssertOnTaskQueue();
-  MOZ_ASSERT(aSample != nullptr);
-  jni::ByteBuffer::LocalRef bytes = jni::ByteBuffer::New(
-      const_cast<uint8_t*>(aSample->Data()), aSample->Size());
-
-  SetState(State::DRAINABLE);
-  mInputBufferInfo->Set(0, aSample->Size(), aSample->mTime.ToMicroseconds(), 0);
-  return mJavaDecoder->Input(bytes, mInputBufferInfo,
-                             GetCryptoInfoFromSample(aSample))
-             ? mDecodePromise.Ensure(__func__)
-             : DecodePromise::CreateAndReject(
-                   MediaResult(NS_ERROR_OUT_OF_MEMORY, __func__), __func__);
+    self->SetState(State::DRAINABLE);
+    self->mInputBufferInfo->Set(0, sample->Size(),
+                                sample->mTime.ToMicroseconds(), 0);
+    return self->mJavaDecoder->Input(bytes, self->mInputBufferInfo,
+                                     GetCryptoInfoFromSample(sample))
+               ? self->mDecodePromise.Ensure(__func__)
+               : DecodePromise::CreateAndReject(
+                     MediaResult(NS_ERROR_OUT_OF_MEMORY, __func__), __func__);
+  });
 }
 
 void RemoteDataDecoder::UpdatePendingInputStatus(PendingOp aOp) {
   AssertOnTaskQueue();
   switch (aOp) {
     case PendingOp::INCREASE:
       mNumPendingInputs++;
       break;
--- a/dom/media/platforms/android/RemoteDataDecoder.h
+++ b/dom/media/platforms/android/RemoteDataDecoder.h
@@ -38,17 +38,16 @@ class RemoteDataDecoder : public MediaDa
  protected:
   virtual ~RemoteDataDecoder() {}
   RemoteDataDecoder(MediaData::Type aType, const nsACString& aMimeType,
                     java::sdk::MediaFormat::Param aFormat,
                     const nsString& aDrmStubId, TaskQueue* aTaskQueue);
 
   // Methods only called on mTaskQueue.
   RefPtr<FlushPromise> ProcessFlush();
-  RefPtr<DecodePromise> ProcessDecode(MediaRawData* aSample);
   RefPtr<ShutdownPromise> ProcessShutdown();
   void UpdateInputStatus(int64_t aTimestamp, bool aProcessed);
   void UpdateOutputStatus(RefPtr<MediaData>&& aSample);
   void ReturnDecodedData();
   void DrainComplete();
   void Error(const MediaResult& aError);
   void AssertOnTaskQueue() { MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); }
 
--- a/dom/tests/mochitest/chrome/test_parsingMode.html
+++ b/dom/tests/mochitest/chrome/test_parsingMode.html
@@ -1,16 +1,19 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>CSSStyleSheet parsingMode test - bug 1230491</title>
   <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
 <script type="application/javascript">
   SimpleTest.waitForExplicitFinish();
+  function sheetText(sheet) {
+    return [...sheet.cssRules].map(r => r.cssText).join();
+  }
   function run() {
     const sss = Cc["@mozilla.org/content/style-sheet-service;1"]
       .getService(Ci.nsIStyleSheetService);
     const utils = window.windowUtils;
 
     const userUrl = encodeURI("data:text/css,body { color: seagreen; -moz-window-transform: none }");
     utils.loadSheetUsingURIString(userUrl, sss.USER_SHEET);
 
@@ -37,21 +40,24 @@
         continue;
       } else {
         // Ignore sheets we don't care about.
         continue;
       }
 
       // Check that re-parsing preserves the mode.
       let mode = sheet.parsingMode;
-      try {
-        InspectorUtils.parseStyleSheet(sheet, "body { color: chartreuse; }");
-        isnot(sheet.parsingMode, "agent", "Agent sheets cannot be reparsed");
-      } catch (ex) {
-        is(sheet.parsingMode, "agent", "Agent sheets cannot be reparsed");
+      let text = sheetText(sheet);
+      InspectorUtils.parseStyleSheet(sheet, "body { color: chartreuse; }");
+      if (mode == "agent") {
+        is(sheetText(sheet), text,
+           "Reparsing should not have changed a UA sheet");
+      } else {
+        isnot(sheetText(sheet), text,
+           "Reparsing should have changed a non-UA sheet");
       }
       is(sheet.parsingMode, mode,
          "check that re-parsing preserved mode " + mode);
     }
 
     ok(results[sss.AGENT_SHEET] && results[sss.USER_SHEET] &&
       results[sss.AUTHOR_SHEET],
       "all sheets seen");
--- a/layout/inspector/tests/chrome/chrome.ini
+++ b/layout/inspector/tests/chrome/chrome.ini
@@ -24,9 +24,10 @@ support-files =
 support-files =
   test_fontFeaturesAPI.css
   DejaVuSans.ttf
 [test_fontVariationsAPI.xul]
 skip-if = ((os == 'win' && bits!=64) || (os=='linux' && bits==32) || os == 'mac') # bug 1456855, bug 1456856
 support-files =
   test_fontVariationsAPI.css
 [test_fontFaceGeneric.xul]
+[test_ua_rule_modification.html]
 [test_ua_sheet_disable.html]
new file mode 100644
--- /dev/null
+++ b/layout/inspector/tests/chrome/test_ua_rule_modification.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<title>Test for bug 1539159</title>
+<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+<p></p>
+<script>
+function start() {
+  let rules = InspectorUtils.getCSSStyleRules(document.querySelector("p"));
+  ok(rules.length > 0, "Should have found some rules");
+  is(rules[0].type, CSSRule.STYLE_RULE, "Should have found a style rule");
+
+  let selector = rules[0].selectorText;
+  isnot(selector, ".xxxxx", "Rule selector should not be something strange");
+
+  try {
+    rules[0].selectorText = "img";
+  } catch (ex) {
+  }
+  is(rules[0].selectorText, selector, "Selector text should be unchanged");
+
+  SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+document.addEventListener('DOMContentLoaded', start)
+</script>
--- a/layout/painting/RetainedDisplayListBuilder.cpp
+++ b/layout/painting/RetainedDisplayListBuilder.cpp
@@ -116,91 +116,163 @@ static AnimatedGeometryRoot* SelectAGRFo
 
 // Removes any display items that belonged to a frame that was deleted,
 // and mark frames that belong to a different AGR so that get their
 // items built again.
 // TODO: We currently descend into all children even if we don't have an AGR
 // to mark, as child stacking contexts might. It would be nice if we could
 // jump into those immediately rather than walking the entire thing.
 bool RetainedDisplayListBuilder::PreProcessDisplayList(
-    RetainedDisplayList* aList, AnimatedGeometryRoot* aAGR, uint32_t aCallerKey,
-    uint32_t aNestingDepth) {
+    RetainedDisplayList* aList, AnimatedGeometryRoot* aAGR,
+    PartialUpdateResult& aUpdated, uint32_t aCallerKey, uint32_t aNestingDepth,
+    bool aKeepLinked) {
   // The DAG merging algorithm does not have strong mechanisms in place to keep
   // the complexity of the resulting DAG under control. In some cases we can
   // build up edges very quickly. Detect those cases and force a full display
   // list build if we hit them.
   static const uint32_t kMaxEdgeRatio = 5;
-  bool initializeDAG = !aList->mDAG.Length();
+  const bool initializeDAG = !aList->mDAG.Length();
   if (!initializeDAG && aList->mDAG.mDirectPredecessorList.Length() >
                             (aList->mDAG.mNodesInfo.Length() * kMaxEdgeRatio)) {
     return false;
   }
 
-  MOZ_RELEASE_ASSERT(initializeDAG || aList->mDAG.Length() == aList->Count());
+  // If we had aKeepLinked=true for this list on the previous paint, then
+  // mOldItems will already be initialized as it won't have been consumed during
+  // a merge.
+  const bool initializeOldItems = aList->mOldItems.IsEmpty();
+  if (initializeOldItems) {
+    aList->mOldItems.SetCapacity(aList->Count());
+  } else {
+    MOZ_RELEASE_ASSERT(!initializeDAG);
+  }
 
-  nsDisplayList saved;
-  aList->mOldItems.SetCapacity(aList->Count());
-  MOZ_RELEASE_ASSERT(aList->mOldItems.IsEmpty());
+  MOZ_RELEASE_ASSERT(
+      initializeDAG ||
+      aList->mDAG.Length() ==
+          (initializeOldItems ? aList->Count() : aList->mOldItems.Length()));
+
+  nsDisplayList out;
+
+  size_t i = 0;
   while (nsDisplayItem* item = aList->RemoveBottom()) {
 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
     item->SetMergedPreProcessed(false, true);
 #endif
 
-    if (!item->CanBeReused() || item->HasDeletedFrame()) {
-      size_t i = aList->mOldItems.Length();
-      aList->mOldItems.AppendElement(OldItemInfo(nullptr));
-      item->Destroy(&mBuilder);
-
-      if (initializeDAG) {
-        if (i == 0) {
-          aList->mDAG.AddNode(Span<const MergedListIndex>());
-        } else {
-          MergedListIndex previous(i - 1);
-          aList->mDAG.AddNode(Span<const MergedListIndex>(&previous, 1));
-        }
+    // If we have a previously initialized old items list, then it can differ
+    // from the current list due to items removed for having a deleted frame.
+    // We can't easily remove these, since the DAG has entries for those indices
+    // and it's hard to rewrite in-place.
+    // Skip over entries with no current item to keep the iterations in sync.
+    if (!initializeOldItems) {
+      while (!aList->mOldItems[i].mItem) {
+        i++;
       }
-      continue;
     }
 
-    size_t i = aList->mOldItems.Length();
-    aList->mOldItems.AppendElement(OldItemInfo(item));
-    item->SetOldListIndex(aList, OldListIndex(i), aCallerKey, aNestingDepth);
     if (initializeDAG) {
       if (i == 0) {
         aList->mDAG.AddNode(Span<const MergedListIndex>());
       } else {
         MergedListIndex previous(i - 1);
         aList->mDAG.AddNode(Span<const MergedListIndex>(&previous, 1));
       }
     }
 
+    if (!item->CanBeReused() || item->HasDeletedFrame()) {
+      if (initializeOldItems) {
+        aList->mOldItems.AppendElement(OldItemInfo(nullptr));
+      } else {
+        MOZ_RELEASE_ASSERT(aList->mOldItems[i].mItem == item);
+        aList->mOldItems[i].mItem = nullptr;
+      }
+      item->Destroy(&mBuilder);
+
+      i++;
+      aUpdated = PartialUpdateResult::Updated;
+      continue;
+    }
+
+    if (initializeOldItems) {
+      aList->mOldItems.AppendElement(OldItemInfo(item));
+    }
+
+    // If we're not going to keep the list linked, then this old item entry
+    // is the only pointer to the item. Let it know that it now strongly
+    // owns the item, so it can destroy it if it goes away.
+    aList->mOldItems[i].mOwnsItem = !aKeepLinked;
+
+    item->SetOldListIndex(aList, OldListIndex(i), aCallerKey, aNestingDepth);
+
     nsIFrame* f = item->Frame();
 
     if (item->GetChildren()) {
-      if (!PreProcessDisplayList(item->GetChildren(),
-                                 SelectAGRForFrame(f, aAGR),
-                                 item->GetPerFrameKey(), aNestingDepth + 1)) {
+      // If children inside this list were invalid, then we'd have walked the
+      // ancestors and set ForceDescendIntoVisible on the current frame. If an
+      // ancestor is modified, then we'll throw this away entirely. Either way,
+      // we won't need to run merging on this sublist, and we can keep the items
+      // linked into their display list.
+      // The caret can move without invalidating, but we always set the force
+      // descend into frame state bit on that frame, so check for that too.
+      // TODO: AGR marking below can call MarkFrameForDisplayIfVisible and make
+      // us think future siblings need to be merged, even though we don't really
+      // need to.
+      bool keepLinked = aKeepLinked;
+      nsIFrame* invalid = item->FrameForInvalidation();
+      if (!invalid->ForceDescendIntoIfVisible() &&
+          !(invalid->GetStateBits() &
+            NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
+        keepLinked = true;
+      }
+
+      if (!PreProcessDisplayList(
+              item->GetChildren(), SelectAGRForFrame(f, aAGR), aUpdated,
+              item->GetPerFrameKey(), aNestingDepth + 1, keepLinked)) {
         return false;
       }
     }
 
     // TODO: We should be able to check the clipped bounds relative
     // to the common AGR (of both the existing item and the invalidated
     // frame) and determine if they can ever intersect.
+    // TODO: We only really need to build the ancestor container item that is a
+    // sibling of the changed thing to get correct ordering. The changed content
+    // is a frame though, and it's hard to map that to container items in this
+    // list.
     if (aAGR && item->GetAnimatedGeometryRoot()->GetAsyncAGR() != aAGR) {
       mBuilder.MarkFrameForDisplayIfVisible(f, mBuilder.RootReferenceFrame());
     }
 
     // TODO: This is here because we sometimes reuse the previous display list
     // completely. For optimization, we could only restore the state for reused
     // display items.
     item->RestoreState();
+
+    // If we're going to keep this linked list and not merge it, then mark the
+    // item as used and put it back into the list.
+    if (aKeepLinked) {
+      item->SetReused(true);
+      if (item->GetChildren()) {
+        item->UpdateBounds(Builder());
+      }
+      if (item->GetType() == DisplayItemType::TYPE_SUBDOCUMENT) {
+        IncrementSubDocPresShellPaintCount(item);
+      }
+      out.AppendToTop(item);
+    }
+    i++;
   }
+
   MOZ_RELEASE_ASSERT(aList->mOldItems.Length() == aList->mDAG.Length());
   aList->RestoreState();
+
+  if (aKeepLinked) {
+    aList->AppendToTop(&out);
+  }
   return true;
 }
 
 void RetainedDisplayListBuilder::IncrementSubDocPresShellPaintCount(
     nsDisplayItem* aItem) {
   MOZ_ASSERT(aItem->GetType() == DisplayItemType::TYPE_SUBDOCUMENT);
 
   nsSubDocumentFrame* subDocFrame =
@@ -274,18 +346,37 @@ static void UpdateASR(nsDisplayItem* aIt
   }
 
   wrapList->SetActiveScrolledRoot(ActiveScrolledRoot::PickAncestor(
       wrapList->GetFrameActiveScrolledRoot(), *asr));
 
   wrapList->UpdateHitTestInfoActiveScrolledRoot(*asr);
 }
 
+static void CopyASR(nsDisplayItem* aOld, nsDisplayItem* aNew) {
+  const ActiveScrolledRoot* hitTest = nullptr;
+  if (aOld->HasHitTestInfo()) {
+    MOZ_ASSERT(aNew->HasHitTestInfo());
+    const HitTestInfo& info =
+        static_cast<nsDisplayHitTestInfoItem*>(aOld)->GetHitTestInfo();
+    hitTest = info.mASR;
+  }
+
+  aNew->SetActiveScrolledRoot(aOld->GetActiveScrolledRoot());
+
+  // SetActiveScrolledRoot for most items will also set the hit-test info item's
+  // asr, so we need to manually set that again to what we saved earlier.
+  if (aOld->HasHitTestInfo()) {
+    static_cast<nsDisplayHitTestInfoItem*>(aNew)
+        ->UpdateHitTestInfoActiveScrolledRoot(hitTest);
+  }
+}
+
 OldItemInfo::OldItemInfo(nsDisplayItem* aItem)
-    : mItem(aItem), mUsed(false), mDiscarded(false) {
+    : mItem(aItem), mUsed(false), mDiscarded(false), mOwnsItem(false) {
   if (mItem) {
     // Clear cached modified frame state when adding an item to the old list.
     mItem->SetModifiedFrame(false);
   }
 }
 
 void OldItemInfo::AddedMatchToMergedList(RetainedDisplayListBuilder* aBuilder,
                                          MergedListIndex aIndex) {
@@ -293,16 +384,17 @@ void OldItemInfo::AddedMatchToMergedList
 }
 
 void OldItemInfo::Discard(RetainedDisplayListBuilder* aBuilder,
                           nsTArray<MergedListIndex>&& aDirectPredecessors) {
   MOZ_ASSERT(!IsUsed());
   mUsed = mDiscarded = true;
   mDirectPredecessors = std::move(aDirectPredecessors);
   if (mItem) {
+    MOZ_ASSERT(mOwnsItem);
     mItem->Destroy(aBuilder->Builder());
   }
   mItem = nullptr;
 }
 
 bool OldItemInfo::IsChanged() {
   return !mItem || !mItem->CanBeReused() || mItem->HasDeletedFrame();
 }
@@ -352,27 +444,17 @@ class MergeState {
           destItem = oldItem;
           // The building rect can depend on the overflow rect (when the parent
           // frame is position:fixed), which can change without invalidating
           // the frame/items. If we're using the old item, copy the building
           // rect across from the new item.
           oldItem->SetBuildingRect(aNewItem->GetBuildingRect());
         }
 
-        if (aNewItem->GetChildren()) {
-          Maybe<const ActiveScrolledRoot*> containerASRForChildren;
-          if (mBuilder->MergeDisplayLists(
-                  aNewItem->GetChildren(), oldItem->GetChildren(),
-                  destItem->GetChildren(), containerASRForChildren, aNewItem)) {
-            destItem->InvalidateCachedChildInfo(mBuilder->Builder());
-            mResultIsModified = true;
-          }
-          UpdateASR(destItem, containerASRForChildren);
-          destItem->UpdateBounds(mBuilder->Builder());
-        }
+        MergeChildLists(aNewItem, oldItem, destItem);
 
         AutoTArray<MergedListIndex, 2> directPredecessors =
             ProcessPredecessorsOfOldNode(oldIndex);
         MergedListIndex newIndex = AddNewNode(
             destItem, Some(oldIndex), directPredecessors, aPreviousItem);
         mOldItems[oldIndex.val].AddedMatchToMergedList(mBuilder, newIndex);
         if (destItem == aNewItem) {
           oldItem->Destroy(mBuilder->Builder());
@@ -382,16 +464,43 @@ class MergeState {
         return Some(newIndex);
       }
     }
     mResultIsModified = true;
     return Some(AddNewNode(aNewItem, Nothing(), Span<MergedListIndex>(),
                            aPreviousItem));
   }
 
+  void MergeChildLists(nsDisplayItem* aNewItem, nsDisplayItem* aOldItem,
+                       nsDisplayItem* aOutItem) {
+    if (!aOutItem->GetChildren()) {
+      return;
+    }
+
+    Maybe<const ActiveScrolledRoot*> containerASRForChildren;
+    nsDisplayList empty;
+    const bool modified = mBuilder->MergeDisplayLists(
+        aNewItem ? aNewItem->GetChildren() : &empty, aOldItem->GetChildren(),
+        aOutItem->GetChildren(), containerASRForChildren, aOutItem);
+    if (modified) {
+      aOutItem->InvalidateCachedChildInfo(mBuilder->Builder());
+      UpdateASR(aOutItem, containerASRForChildren);
+      mResultIsModified = true;
+    } else if (aOutItem == aNewItem) {
+      // If nothing changed, but we copied the contents across to
+      // the new item, then also copy the ASR data.
+      CopyASR(aOldItem, aNewItem);
+    }
+    // Ideally we'd only UpdateBounds if something changed, but
+    // nsDisplayWrapList also uses this to update the clip chain for the
+    // current ASR, which gets reset during RestoreState(), so we always need
+    // to run it again.
+    aOutItem->UpdateBounds(mBuilder->Builder());
+  }
+
   bool ShouldUseNewItem(nsDisplayItem* aNewItem) {
     // Generally we want to use the old item when the frame isn't marked as
     // modified so that any cached information on the item (or referencing the
     // item) gets retained. Quite a few FrameLayerBuilder performance
     // improvements benefit by this. Sometimes, however, we can end up where the
     // new item paints something different from the old item, even though we
     // haven't modified the frame, and it's hard to fix. In these cases we just
     // always use the new item to be safe.
@@ -527,28 +636,18 @@ class MergeState {
 
   void ProcessOldNode(OldListIndex aNode,
                       nsTArray<MergedListIndex>&& aDirectPredecessors) {
     nsDisplayItem* item = mOldItems[aNode.val].mItem;
     if (mOldItems[aNode.val].IsChanged() || HasModifiedFrame(item)) {
       mOldItems[aNode.val].Discard(mBuilder, std::move(aDirectPredecessors));
       mResultIsModified = true;
     } else {
-      if (item->GetChildren()) {
-        Maybe<const ActiveScrolledRoot*> containerASRForChildren;
-        nsDisplayList empty;
-        if (mBuilder->MergeDisplayLists(&empty, item->GetChildren(),
-                                        item->GetChildren(),
-                                        containerASRForChildren, item)) {
-          item->InvalidateCachedChildInfo(mBuilder->Builder());
-          mResultIsModified = true;
-        }
-        UpdateASR(item, containerASRForChildren);
-        item->UpdateBounds(mBuilder->Builder());
-      }
+      MergeChildLists(nullptr, item, item);
+
       if (item->GetType() == DisplayItemType::TYPE_SUBDOCUMENT) {
         mBuilder->IncrementSubDocPresShellPaintCount(item);
       }
       item->SetReused(true);
       mOldItems[aNode.val].AddedToMergedList(
           AddNewNode(item, Some(aNode), aDirectPredecessors, Nothing()));
     }
   }
@@ -633,16 +732,29 @@ class MergeState {
   // since they internally encode the type with the mOps pointer,
   // and assert when we try swap the contents
   nsDisplayList mMergedItems;
   DirectedAcyclicGraph<MergedListUnits> mMergedDAG;
   nsDisplayItem* mOuterItem;
   bool mResultIsModified;
 };
 
+#ifdef DEBUG
+void VerifyNotModified(nsDisplayList* aList) {
+  for (nsDisplayItem* item = aList->GetBottom(); item;
+       item = item->GetAbove()) {
+    MOZ_ASSERT(!AnyContentAncestorModified(item->FrameForInvalidation()));
+
+    if (item->GetChildren()) {
+      VerifyNotModified(item->GetChildren());
+    }
+  }
+}
+#endif
+
 /**
  * Takes two display lists and merges them into an output list.
  *
  * Display lists wthout an explicit DAG are interpreted as linear DAGs (with a
  * maximum of one direct predecessor and one direct successor per node). We add
  * the two DAGs together, and then output the topological sorted ordering as the
  * final display list.
  *
@@ -651,16 +763,33 @@ class MergeState {
  */
 bool RetainedDisplayListBuilder::MergeDisplayLists(
     nsDisplayList* aNewList, RetainedDisplayList* aOldList,
     RetainedDisplayList* aOutList,
     mozilla::Maybe<const mozilla::ActiveScrolledRoot*>& aOutContainerASR,
     nsDisplayItem* aOuterItem) {
   AUTO_PROFILER_LABEL_CATEGORY_PAIR(GRAPHICS_DisplayListMerging);
 
+  if (!aOldList->IsEmpty()) {
+    // If we still have items in the actual list, then it is because
+    // PreProcessDisplayList decided that it was sure it can't be modified. We
+    // can just use it directly, and throw any new items away.
+
+    aNewList->DeleteAll(&mBuilder);
+#ifdef DEBUG
+    VerifyNotModified(aOldList);
+#endif
+
+    if (aOldList != aOutList) {
+      *aOutList = std::move(*aOldList);
+    }
+
+    return false;
+  }
+
   MergeState merge(this, *aOldList, aOuterItem);
 
   Maybe<MergedListIndex> previousItemIndex;
   while (nsDisplayItem* item = aNewList->RemoveBottom()) {
     previousItemIndex = merge.ProcessItemFromNewList(item, previousItemIndex);
   }
 
   *aOutList = merge.Finalize();
@@ -1259,20 +1388,21 @@ auto RetainedDisplayListBuilder::Attempt
       }
     }
 
     mPreviousCaret = mBuilder.GetCaretFrame();
   }
 
   nsRect modifiedDirty;
   AnimatedGeometryRoot* modifiedAGR = nullptr;
+  PartialUpdateResult result = PartialUpdateResult::NoChange;
   if (!shouldBuildPartial ||
       !ComputeRebuildRegion(modifiedFrames.Frames(), &modifiedDirty,
                             &modifiedAGR, framesWithProps.Frames()) ||
-      !PreProcessDisplayList(&mList, modifiedAGR)) {
+      !PreProcessDisplayList(&mList, modifiedAGR, result)) {
     mBuilder.LeavePresShell(mBuilder.RootReferenceFrame(), nullptr);
     mList.DeleteAll(&mBuilder);
     return PartialUpdateResult::Failed;
   }
 
   // This is normally handled by EnterPresShell, but we skipped it so that we
   // didn't call MarkFrameForDisplayIfVisible before ComputeRebuildRegion.
   nsIScrollableFrame* sf = mBuilder.RootReferenceFrame()
@@ -1326,17 +1456,16 @@ auto RetainedDisplayListBuilder::Attempt
   // display list merging to prune unused items (for example, items that
   // are not visible anymore) from the old list.
   // TODO: Optimization opportunity. In this case, MergeDisplayLists()
   // unnecessarily creates a hashtable of the old items.
   // TODO: Ideally we could skip this if result is NoChange, but currently when
   // we call RestoreState on nsDisplayWrapList it resets the clip to the base
   // clip, and we need the UpdateBounds call (within MergeDisplayLists) to
   // move it to the correct inner clip.
-  PartialUpdateResult result = PartialUpdateResult::NoChange;
   Maybe<const ActiveScrolledRoot*> dummy;
   if (MergeDisplayLists(&modifiedDL, &mList, &mList, dummy)) {
     result = PartialUpdateResult::Updated;
   }
 
   // printf_stderr("Painting --- Merged list:\n");
   // nsFrame::PrintDisplayList(&mBuilder, mList);
 
--- a/layout/painting/RetainedDisplayListBuilder.h
+++ b/layout/painting/RetainedDisplayListBuilder.h
@@ -113,28 +113,43 @@ struct RetainedDisplayListBuilder {
    * Also clears the frame properties set by RetainedDisplayListBuilder for all
    * the frames in the modified frame lists.
    */
   void ClearFramesWithProps();
 
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(Cached, RetainedDisplayListBuilder)
 
  private:
+  /**
+   * Recursively pre-processes the old display list tree before building the
+   * new partial display lists, and serializes the old list into an array,
+   * recording indices on items for fast lookup during merging. Builds an
+   * initial linear DAG for the list if we don't have an existing one. Finds
+   * items that have a different AGR from the specified one, and marks them to
+   * also be built so that we get relative ordering correct. Passes
+   * aKeepLinked=true internally for sub-lists that can't be changed to keep the
+   * original list structure linked for fast re-use.
+   */
   bool PreProcessDisplayList(RetainedDisplayList* aList,
                              AnimatedGeometryRoot* aAGR,
+                             PartialUpdateResult& aUpdated,
                              uint32_t aCallerKey = 0,
-                             uint32_t aNestingDepth = 0);
+                             uint32_t aNestingDepth = 0,
+                             bool aKeepLinked = false);
 
   /**
    * Merges items from aNewList into non-invalidated items from aOldList and
    * stores the result in aOutList.
    *
    * aOuterItem is a pointer to an item that owns one of the lists, if
    * available. If both lists are populated, then both outer items must not be
    * invalidated, and identical, so either can be passed here.
+   *
+   * Returns true if changes were made, and the resulting display list (in
+   * aOutList) is different from aOldList.
    */
   bool MergeDisplayLists(
       nsDisplayList* aNewList, RetainedDisplayList* aOldList,
       RetainedDisplayList* aOutList,
       mozilla::Maybe<const mozilla::ActiveScrolledRoot*>& aOutContainerASR,
       nsDisplayItem* aOuterItem = nullptr);
 
   bool ComputeRebuildRegion(nsTArray<nsIFrame*>& aModifiedFrames,
--- a/layout/painting/RetainedDisplayListHelpers.h
+++ b/layout/painting/RetainedDisplayListHelpers.h
@@ -164,14 +164,15 @@ struct OldItemInfo {
 
   bool IsChanged();
 
   nsDisplayItem* mItem;
   nsTArray<MergedListIndex> mDirectPredecessors;
   MergedListIndex mIndex;
   bool mUsed;
   bool mDiscarded;
+  bool mOwnsItem;
 };
 
 bool AnyContentAncestorModified(nsIFrame* aFrame,
                                 nsIFrame* aStopAtFrame = nullptr);
 
 #endif  // RETAINEDDISPLAYLISTHELPERS_H_
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -2080,16 +2080,17 @@ MOZ_ALWAYS_INLINE T* MakeDisplayItem(nsD
  * highest in z-order.
  */
 class nsDisplayItemLink {
   // This is never instantiated directly, so no need to count constructors and
   // destructors.
  protected:
   nsDisplayItemLink() : mAbove(nullptr) {}
   nsDisplayItemLink(const nsDisplayItemLink&) : mAbove(nullptr) {}
+  ~nsDisplayItemLink() { MOZ_RELEASE_ASSERT(!mAbove); }
   nsDisplayItem* mAbove;
 
   friend class nsDisplayList;
 };
 
 /*
  * nsDisplayItemBase is a base-class for all display items. It is mainly
  * responsible for handling the frame-display item 1:n relationship, as well as
@@ -3675,23 +3676,27 @@ class RetainedDisplayList : public nsDis
     MOZ_ASSERT(mOldItems.IsEmpty(), "Must empty list before destroying");
   }
 
   RetainedDisplayList& operator=(RetainedDisplayList&& aOther) {
     MOZ_ASSERT(!Count(), "Can only move into an empty list!");
     MOZ_ASSERT(mOldItems.IsEmpty(), "Can only move into an empty list!");
     AppendToTop(&aOther);
     mDAG = std::move(aOther.mDAG);
+    mOldItems = std::move(aOther.mOldItems);
     return *this;
   }
 
   void DeleteAll(nsDisplayListBuilder* aBuilder) override {
     for (OldItemInfo& i : mOldItems) {
-      if (i.mItem) {
+      if (i.mItem && i.mOwnsItem) {
         i.mItem->Destroy(aBuilder);
+        MOZ_ASSERT(!GetBottom(),
+                   "mOldItems should not be owning items if we also have items "
+                   "in the normal list");
       }
     }
     mOldItems.Clear();
     mDAG.Clear();
     nsDisplayList::DeleteAll(aBuilder);
   }
 
   DirectedAcyclicGraph<MergedListUnits> mDAG;
--- a/layout/reftests/css-valuesandunits/reftest.list
+++ b/layout/reftests/css-valuesandunits/reftest.list
@@ -9,11 +9,11 @@ fuzzy-if(webrender&&winWidget,0-27,0-4) 
 == unit-vh-vw.html unit-vh-vw-ref.html
 == unit-vh-vw-zoom.html unit-vh-vw-zoom-ref.html
 skip-if(gtkWidget) == unit-vh-vw-overflow-auto.html unit-vh-vw-overflow-auto-ref.html
 
 # These tests should probably be removed, see bug 1393603.
 fails-if(!Android) == unit-vh-vw-overflow-scroll.html unit-vh-vw-overflow-scroll-ref.html
 fails-if(!Android) == unit-vh-vw-overflow-scroll-x.html unit-vh-vw-overflow-scroll-x-ref.html
 fails-if(!Android) == unit-vh-vw-overflow-scroll-y.html unit-vh-vw-overflow-scroll-y-ref.html
-skip-if(Android) fails != unit-vh-vw-overflow-auto.html unit-vh-vw-overflow-scroll.html
+skip-if(Android||gtkWidget) fails != unit-vh-vw-overflow-auto.html unit-vh-vw-overflow-scroll.html
 
 == ch-width-1.html ch-width-1-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/display-list/1544948-1-ref.html
@@ -0,0 +1,32 @@
+<html>
+<head>
+<style>
+  div {
+    width:10px;
+    height:10px;
+    background-color:green;
+    display: inline-block;
+    position: relative;
+  }
+  #wrapper {
+    opacity: 0.5;
+    width: 200px;
+    height: 200px;
+    background-color: transparent;
+  }
+
+  #first {
+    z-index: 1;
+  }
+
+  #second {
+    background-color: blue;
+  }
+</style>
+</head>
+<body id="body">
+  <div id="wrapper">
+    <div id="first" class="reftest-no-display-list"></div><div id="second" class="reftest-no-display-list"></div>
+  </div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/display-list/1544948-1.html
@@ -0,0 +1,43 @@
+<html class="reftest-wait">
+<head>
+<style>
+  div {
+    width:10px;
+    height:10px;
+    background-color:green;
+    display: inline-block;
+    position: relative;
+  }
+  #wrapper {
+    opacity: 0.5;
+    width: 200px;
+    height: 200px;
+    background-color: transparent;
+  }
+
+  #first {
+    z-index: 1;
+  }
+</style>
+<script>
+function doTest2() {
+  var elem = document.getElementById("second");
+  elem.style.backgroundColor = "blue";
+}
+
+function doTest() {
+  var elem = document.getElementById("third");
+  elem.parentNode.removeChild(elem);
+  document.documentElement.removeAttribute("class");
+  requestAnimationFrame(doTest2);
+}
+
+window.addEventListener("MozReftestInvalidate", doTest);
+</script>
+</head>
+<body id="body">
+  <div id="wrapper">
+    <div id="first" class="reftest-no-display-list"></div><div id="second"></div><div id="third"></div>
+  </div>
+</body>
+</html>
--- a/layout/reftests/display-list/reftest.list
+++ b/layout/reftests/display-list/reftest.list
@@ -35,8 +35,9 @@ skip-if(!asyncPan) == 1437374-1.html 143
 == 1453541-1.html 1453541-ref.html
 == 1453541-2.html 1453541-ref.html
 == 1452805-1.html 1452805-ref.html
 == 1461231-1.html about:blank
 fuzzy(0-2,0-40000) skip-if(!asyncPan) == 1464288-1.html 1464288-ref.html
 == 1482403-1.html 1482403-1-ref.html
 == 1504233-1.html 1504233-1-ref.html
 == 1533317-1.html 1533317-1-ref.html
+== 1544948-1.html 1544948-1-ref.html
--- a/layout/reftests/svg/symbol-01.svg
+++ b/layout/reftests/svg/symbol-01.svg
@@ -1,15 +1,21 @@
 <!--
      Any copyright is dedicated to the Public Domain.
      http://creativecommons.org/publicdomain/zero/1.0/
 -->
 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink='http://www.w3.org/1999/xlink'>
+  <style type="text/css"><![CDATA[
+    #s {
+      display: inline;
+    }
+  ]]></style>
   <!--
     A basic test that referencing a <symbol> with a <use> works.
     Note that the fill specified on the <use> inherits into the
     <use> shadow tree.
+    Also <symbol> itself should have |display:none !important|.
     -->
   <symbol id='s' viewBox='100 100 10 10'>
     <circle cx='105' cy='105' r='4'/>
   </symbol>
   <use xlink:href='#s' x='0' y='0' width='100' height='100' fill='green'/>
 </svg>
--- a/layout/style/CSSCounterStyleRule.cpp
+++ b/layout/style/CSSCounterStyleRule.cpp
@@ -38,31 +38,37 @@ void CSSCounterStyleRule::GetCssText(nsA
 void CSSCounterStyleRule::GetName(nsAString& aName) {
   aName.Truncate();
   nsAtom* name = Servo_CounterStyleRule_GetName(mRawRule);
   nsDependentAtomString nameStr(name);
   nsStyleUtil::AppendEscapedCSSIdent(nameStr, aName);
 }
 
 void CSSCounterStyleRule::SetName(const nsAString& aName) {
+  if (IsReadOnly()) {
+    return;
+  }
   NS_ConvertUTF16toUTF8 name(aName);
   if (Servo_CounterStyleRule_SetName(mRawRule, &name)) {
     if (StyleSheet* sheet = GetStyleSheet()) {
       sheet->RuleChanged(this);
     }
   }
 }
 
 #define CSS_COUNTER_DESC(name_, method_)                            \
   void CSSCounterStyleRule::Get##method_(nsAString& aValue) {       \
     aValue.Truncate();                                              \
     Servo_CounterStyleRule_GetDescriptorCssText(                    \
         mRawRule, eCSSCounterDesc_##method_, &aValue);              \
   }                                                                 \
   void CSSCounterStyleRule::Set##method_(const nsAString& aValue) { \
+    if (IsReadOnly()) {                                             \
+      return;                                                       \
+    }                                                               \
     NS_ConvertUTF16toUTF8 value(aValue);                            \
     if (Servo_CounterStyleRule_SetDescriptor(                       \
             mRawRule, eCSSCounterDesc_##method_, &value)) {         \
       if (StyleSheet* sheet = GetStyleSheet()) {                    \
         sheet->RuleChanged(this);                                   \
       }                                                             \
     }                                                               \
   }
--- a/layout/style/CSSFontFaceRule.cpp
+++ b/layout/style/CSSFontFaceRule.cpp
@@ -44,16 +44,19 @@ void CSSFontFaceRuleDecl::GetPropertyVal
 void CSSFontFaceRuleDecl::GetCssText(nsAString& aCssText) {
   aCssText.Truncate();
   Servo_FontFaceRule_GetDeclCssText(mRawRule, &aCssText);
 }
 
 void CSSFontFaceRuleDecl::SetCssText(const nsAString& aCssText,
                                      nsIPrincipal* aSubjectPrincipal,
                                      ErrorResult& aRv) {
+  if (ContainingRule()->IsReadOnly()) {
+    return;
+  }
   aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);  // bug 443978
 }
 
 NS_IMETHODIMP
 CSSFontFaceRuleDecl::GetPropertyValue(const nsAString& aPropName,
                                       nsAString& aResult) {
   aResult.Truncate();
   nsCSSFontDesc descID = nsCSSProps::LookupFontDesc(aPropName);
@@ -65,16 +68,20 @@ CSSFontFaceRuleDecl::GetPropertyValue(co
 
 NS_IMETHODIMP
 CSSFontFaceRuleDecl::RemoveProperty(const nsAString& aPropName,
                                     nsAString& aResult) {
   nsCSSFontDesc descID = nsCSSProps::LookupFontDesc(aPropName);
   NS_ASSERTION(descID >= eCSSFontDesc_UNKNOWN && descID < eCSSFontDesc_COUNT,
                "LookupFontDesc returned value out of range");
 
+  if (ContainingRule()->IsReadOnly()) {
+    return NS_OK;
+  }
+
   aResult.Truncate();
   if (descID != eCSSFontDesc_UNKNOWN) {
     GetPropertyValue(descID, aResult);
     Servo_FontFaceRule_ResetDescriptor(mRawRule, descID);
   }
   return NS_OK;
 }
 
@@ -87,16 +94,20 @@ void CSSFontFaceRuleDecl::GetPropertyPri
 NS_IMETHODIMP
 CSSFontFaceRuleDecl::SetProperty(const nsAString& aPropName,
                                  const nsAString& aValue,
                                  const nsAString& aPriority,
                                  nsIPrincipal* aSubjectPrincipal) {
   // FIXME(heycam): If we are changing unicode-range, then a FontFace object
   // representing this rule must have its mUnicodeRange value invalidated.
 
+  if (ContainingRule()->IsReadOnly()) {
+    return NS_OK;
+  }
+
   return NS_ERROR_NOT_IMPLEMENTED;  // bug 443978
 }
 
 uint32_t CSSFontFaceRuleDecl::Length() {
   return Servo_FontFaceRule_Length(mRawRule);
 }
 
 void CSSFontFaceRuleDecl::IndexedGetter(uint32_t aIndex, bool& aFound,
--- a/layout/style/CSSFontFeatureValuesRule.cpp
+++ b/layout/style/CSSFontFeatureValuesRule.cpp
@@ -41,21 +41,29 @@ void CSSFontFeatureValuesRule::GetFontFa
 }
 
 void CSSFontFeatureValuesRule::GetValueText(nsAString& aValueText) {
   Servo_FontFeatureValuesRule_GetValueText(mRawRule, &aValueText);
 }
 
 void CSSFontFeatureValuesRule::SetFontFamily(const nsAString& aFontFamily,
                                              ErrorResult& aRv) {
+  if (IsReadOnly()) {
+    return;
+  }
+
   aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
 }
 
 void CSSFontFeatureValuesRule::SetValueText(const nsAString& aValueText,
                                             ErrorResult& aRv) {
+  if (IsReadOnly()) {
+    return;
+  }
+
   aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
 }
 
 // If this ever gets its own cycle-collection bits, reevaluate our IsCCLeaf
 // implementation.
 
 bool CSSFontFeatureValuesRule::IsCCLeaf() const { return Rule::IsCCLeaf(); }
 
--- a/layout/style/CSSKeyframeRule.cpp
+++ b/layout/style/CSSKeyframeRule.cpp
@@ -135,16 +135,20 @@ void CSSKeyframeRule::List(FILE* out, in
   }
   Servo_Keyframe_Debug(mRaw, &str);
   fprintf_stderr(out, "%s\n", str.get());
 }
 #endif
 
 template <typename Func>
 void CSSKeyframeRule::UpdateRule(Func aCallback) {
+  if (IsReadOnly()) {
+    return;
+  }
+
   aCallback();
 
   if (StyleSheet* sheet = GetStyleSheet()) {
     sheet->RuleChanged(this);
   }
 }
 
 void CSSKeyframeRule::GetKeyText(nsAString& aKeyText) {
--- a/layout/style/CSSKeyframesRule.cpp
+++ b/layout/style/CSSKeyframesRule.cpp
@@ -62,19 +62,24 @@ class CSSKeyframeList : public dom::CSSR
     if (aIndex >= mRules.Length()) {
       aFound = false;
       return nullptr;
     }
     aFound = true;
     return GetRule(aIndex);
   }
 
-  void AppendRule() { mRules.AppendObject(nullptr); }
+  void AppendRule() {
+    MOZ_ASSERT(!mParentRule->IsReadOnly());
+    mRules.AppendObject(nullptr);
+  }
 
   void RemoveRule(uint32_t aIndex) {
+    MOZ_ASSERT(!mParentRule->IsReadOnly());
+
     if (aIndex >= mRules.Length()) {
       return;
     }
     if (css::Rule* child = mRules[aIndex]) {
       child->DropReferences();
     }
     mRules.RemoveObjectAt(aIndex);
   }
@@ -209,21 +214,27 @@ void CSSKeyframesRule::DropSheetReferenc
 static const uint32_t kRuleNotFound = std::numeric_limits<uint32_t>::max();
 
 uint32_t CSSKeyframesRule::FindRuleIndexForKey(const nsAString& aKey) {
   NS_ConvertUTF16toUTF8 key(aKey);
   return Servo_KeyframesRule_FindRule(mRawRule, &key);
 }
 
 template <typename Func>
-void CSSKeyframesRule::UpdateRule(Func aCallback) {
+nsresult CSSKeyframesRule::UpdateRule(Func aCallback) {
+  if (IsReadOnly()) {
+    return NS_OK;
+  }
+
   aCallback();
   if (StyleSheet* sheet = GetStyleSheet()) {
     sheet->RuleChanged(this);
   }
+
+  return NS_OK;
 }
 
 void CSSKeyframesRule::GetName(nsAString& aName) const {
   nsAtom* name = Servo_KeyframesRule_GetName(mRawRule);
   aName = nsDependentAtomString(name);
 }
 
 void CSSKeyframesRule::SetName(const nsAString& aName) {
--- a/layout/style/CSSKeyframesRule.h
+++ b/layout/style/CSSKeyframesRule.h
@@ -44,17 +44,17 @@ class CSSKeyframesRule final : public cs
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const final;
 
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) final;
 
  private:
   uint32_t FindRuleIndexForKey(const nsAString& aKey);
 
   template <typename Func>
-  void UpdateRule(Func aCallback);
+  nsresult UpdateRule(Func aCallback);
 
   virtual ~CSSKeyframesRule();
 
   RefPtr<RawServoKeyframesRule> mRawRule;
   RefPtr<CSSKeyframeList> mKeyframeList;  // lazily constructed
 };
 
 }  // namespace dom
--- a/layout/style/CSSMediaRule.cpp
+++ b/layout/style/CSSMediaRule.cpp
@@ -69,16 +69,19 @@ void CSSMediaRule::List(FILE* out, int32
 #endif
 
 void CSSMediaRule::GetConditionText(nsAString& aConditionText) {
   Media()->GetMediaText(aConditionText);
 }
 
 void CSSMediaRule::SetConditionText(const nsAString& aConditionText,
                                     ErrorResult& aRv) {
+  if (IsReadOnly()) {
+    return;
+  }
   Media()->SetMediaText(aConditionText);
 }
 
 /* virtual */
 void CSSMediaRule::GetCssText(nsAString& aCssText) const {
   Servo_MediaRule_GetCssText(mRawRule, &aCssText);
 }
 
--- a/layout/style/CSSMozDocumentRule.cpp
+++ b/layout/style/CSSMozDocumentRule.cpp
@@ -99,16 +99,20 @@ void CSSMozDocumentRule::List(FILE* out,
 #endif
 
 void CSSMozDocumentRule::GetConditionText(nsAString& aConditionText) {
   Servo_MozDocumentRule_GetConditionText(mRawRule, &aConditionText);
 }
 
 void CSSMozDocumentRule::SetConditionText(const nsAString& aConditionText,
                                           ErrorResult& aRv) {
+  if (IsReadOnly()) {
+    return;
+  }
+
   aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
 }
 
 /* virtual */
 void CSSMozDocumentRule::GetCssText(nsAString& aCssText) const {
   Servo_MozDocumentRule_GetCssText(mRawRule, &aCssText);
 }
 
--- a/layout/style/CSSPageRule.cpp
+++ b/layout/style/CSSPageRule.cpp
@@ -50,16 +50,20 @@ DeclarationBlock* CSSPageRuleDeclaration
   return mDecls;
 }
 
 nsresult CSSPageRuleDeclaration::SetCSSDeclaration(
     DeclarationBlock* aDecl, MutationClosureData* aClosureData) {
   MOZ_ASSERT(aDecl, "must be non-null");
   CSSPageRule* rule = Rule();
 
+  if (rule->IsReadOnly()) {
+    return NS_OK;
+  }
+
   if (aDecl != mDecls) {
     mDecls->SetOwningRule(nullptr);
     RefPtr<DeclarationBlock> decls = aDecl;
     Servo_PageRule_SetStyle(rule->Raw(), decls->Raw());
     mDecls = decls.forget();
     mDecls->SetOwningRule(rule);
   }
 
--- a/layout/style/CSSStyleRule.cpp
+++ b/layout/style/CSSStyleRule.cpp
@@ -52,16 +52,21 @@ nsINode* CSSStyleRuleDeclaration::GetPar
 DeclarationBlock* CSSStyleRuleDeclaration::GetOrCreateCSSDeclaration(
     Operation aOperation, DeclarationBlock** aCreated) {
   return mDecls;
 }
 
 nsresult CSSStyleRuleDeclaration::SetCSSDeclaration(
     DeclarationBlock* aDecl, MutationClosureData* aClosureData) {
   CSSStyleRule* rule = Rule();
+
+  if (rule->IsReadOnly()) {
+    return NS_OK;
+  }
+
   if (RefPtr<StyleSheet> sheet = rule->GetStyleSheet()) {
     if (aDecl != mDecls) {
       mDecls->SetOwningRule(nullptr);
       RefPtr<DeclarationBlock> decls = aDecl;
       Servo_StyleRule_SetStyle(rule->Raw(), decls->Raw());
       mDecls = decls.forget();
       mDecls->SetOwningRule(rule);
     }
@@ -153,16 +158,20 @@ nsICSSDeclaration* CSSStyleRule::Style()
 
 /* CSSStyleRule implementation */
 
 void CSSStyleRule::GetSelectorText(nsAString& aSelectorText) {
   Servo_StyleRule_GetSelectorText(mRawRule, &aSelectorText);
 }
 
 void CSSStyleRule::SetSelectorText(const nsAString& aSelectorText) {
+  if (IsReadOnly()) {
+    return;
+  }
+
   if (RefPtr<StyleSheet> sheet = GetStyleSheet()) {
     // StyleRule lives inside of the Inner, it is unsafe to call WillDirty
     // if sheet does not already have a unique Inner.
     sheet->AssertHasUniqueInner();
     sheet->WillDirty();
 
     const RawServoStyleSheetContents* contents = sheet->RawContents();
     if (Servo_StyleRule_SetSelectorText(contents, mRawRule, &aSelectorText)) {
--- a/layout/style/CSSSupportsRule.cpp
+++ b/layout/style/CSSSupportsRule.cpp
@@ -42,16 +42,20 @@ void CSSSupportsRule::List(FILE* out, in
 #endif
 
 void CSSSupportsRule::GetConditionText(nsAString& aConditionText) {
   Servo_SupportsRule_GetConditionText(mRawRule, &aConditionText);
 }
 
 void CSSSupportsRule::SetConditionText(const nsAString& aConditionText,
                                        ErrorResult& aRv) {
+  if (IsReadOnly()) {
+    return;
+  }
+
   aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
 }
 
 /* virtual */
 void CSSSupportsRule::GetCssText(nsAString& aCssText) const {
   Servo_SupportsRule_GetCssText(mRawRule, &aCssText);
 }
 
--- a/layout/style/DeclarationBlock.cpp
+++ b/layout/style/DeclarationBlock.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/DeclarationBlock.h"
 
+#include "mozilla/css/Rule.h"
 #include "mozilla/ServoBindings.h"
 
 #include "nsCSSProps.h"
 
 namespace mozilla {
 
 /* static */
 already_AddRefed<DeclarationBlock> DeclarationBlock::FromCssText(
@@ -18,9 +19,14 @@ already_AddRefed<DeclarationBlock> Decla
     css::Loader* aLoader) {
   NS_ConvertUTF16toUTF8 value(aCssText);
   RefPtr<RawServoDeclarationBlock> raw =
       Servo_ParseStyleAttribute(&value, aExtraData, aMode, aLoader).Consume();
   RefPtr<DeclarationBlock> decl = new DeclarationBlock(raw.forget());
   return decl.forget();
 }
 
+bool DeclarationBlock::OwnerIsReadOnly() const {
+  css::Rule* rule = GetOwningRule();
+  return rule && rule->IsReadOnly();
+}
+
 }  // namespace mozilla
--- a/layout/style/DeclarationBlock.h
+++ b/layout/style/DeclarationBlock.h
@@ -54,16 +54,17 @@ class DeclarationBlock final {
    */
   bool IsMutable() const { return !mImmutable; }
 
   /**
    * Crash in debug builds if |this| cannot be modified.
    */
   void AssertMutable() const {
     MOZ_ASSERT(IsMutable(), "someone forgot to call EnsureMutable");
+    MOZ_ASSERT(!OwnerIsReadOnly(), "User Agent sheets shouldn't be modified");
   }
 
   /**
    * Mark this declaration as unmodifiable.
    */
   void SetImmutable() { mImmutable = true; }
 
   /**
@@ -80,16 +81,18 @@ class DeclarationBlock final {
    * Mark this declaration as not dirty.
    */
   void UnsetDirty() { mIsDirty = false; }
 
   /**
    * Copy |this|, if necessary to ensure that it can be modified.
    */
   already_AddRefed<DeclarationBlock> EnsureMutable() {
+    MOZ_ASSERT(!OwnerIsReadOnly());
+
     if (!IsDirty()) {
       // In stylo, the old DeclarationBlock is stored in element's rule node
       // tree directly, to avoid new values replacing the DeclarationBlock in
       // the tree directly, we need to copy the old one here if we haven't yet
       // copied. As a result the new value does not replace rule node tree until
       // traversal happens.
       //
       // FIXME(emilio): This is a hack for ::first-line and transitions starting
@@ -131,16 +134,18 @@ class DeclarationBlock final {
     if (!(mContainer.mRaw & 0x1)) {
       return nullptr;
     }
     auto c = mContainer;
     c.mRaw &= ~uintptr_t(1);
     return c.mHTMLCSSStyleSheet;
   }
 
+  bool IsReadOnly() const;
+
   static already_AddRefed<DeclarationBlock> FromCssText(
       const nsAString& aCssText, URLExtraData* aExtraData,
       nsCompatibility aMode, css::Loader* aLoader);
 
   RawServoDeclarationBlock* Raw() const { return mRaw; }
   RawServoDeclarationBlock* const* RefRaw() const {
     static_assert(sizeof(RefPtr<RawServoDeclarationBlock>) ==
                       sizeof(RawServoDeclarationBlock*),
@@ -197,16 +202,19 @@ class DeclarationBlock final {
   bool RemovePropertyByID(nsCSSPropertyID aProperty,
                           DeclarationBlockMutationClosure aClosure = {}) {
     AssertMutable();
     return Servo_DeclarationBlock_RemovePropertyById(mRaw, aProperty, aClosure);
   }
 
  private:
   ~DeclarationBlock() = default;
+
+  bool OwnerIsReadOnly() const;
+
   union {
     // We only ever have one of these since we have an
     // nsHTMLCSSStyleSheet only for style attributes, and style
     // attributes never have an owning rule.
 
     // It's an nsHTMLCSSStyleSheet if the low bit is set.
 
     uintptr_t mRaw;
--- a/layout/style/FontFace.cpp
+++ b/layout/style/FontFace.cpp
@@ -451,16 +451,19 @@ bool FontFace::SetDescriptor(nsCSSFontDe
                              ErrorResult& aRv) {
   // FIXME We probably don't need to distinguish between this anymore
   // since we have common backend now.
   NS_ASSERTION(!HasRule(), "we don't handle rule backed FontFace objects yet");
   if (HasRule()) {
     return false;
   }
 
+  // FIXME(heycam): Should not allow modification of FontFaces that are
+  // CSS-connected and whose rule is read only.
+
   NS_ConvertUTF16toUTF8 value(aValue);
   RefPtr<URLExtraData> url = GetURLExtraData();
   if (!Servo_FontFaceRule_SetDescriptor(GetData(), aFontDesc, &value, url)) {
     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     return false;
   }
 
   if (aFontDesc == eCSSFontDesc_UnicodeRange) {
--- a/layout/style/GroupRule.cpp
+++ b/layout/style/GroupRule.cpp
@@ -69,16 +69,20 @@ void GroupRule::DropSheetReference() {
   if (mRuleList) {
     mRuleList->DropSheetReference();
   }
   Rule::DropSheetReference();
 }
 
 uint32_t GroupRule::InsertRule(const nsAString& aRule, uint32_t aIndex,
                                ErrorResult& aRv) {
+  if (IsReadOnly()) {
+    return 0;
+  }
+
   StyleSheet* sheet = GetStyleSheet();
   if (NS_WARN_IF(!sheet)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return 0;
   }
 
   uint32_t count = StyleRuleCount();
   if (aIndex > count) {
@@ -92,16 +96,20 @@ uint32_t GroupRule::InsertRule(const nsA
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return 0;
   }
   return aIndex;
 }
 
 void GroupRule::DeleteRule(uint32_t aIndex, ErrorResult& aRv) {
+  if (IsReadOnly()) {
+    return;
+  }
+
   StyleSheet* sheet = GetStyleSheet();
   if (NS_WARN_IF(!sheet)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   uint32_t count = StyleRuleCount();
   if (aIndex >= count) {
--- a/layout/style/MediaList.cpp
+++ b/layout/style/MediaList.cpp
@@ -36,16 +36,20 @@ JSObject* MediaList::WrapObject(JSContex
 void MediaList::SetStyleSheet(StyleSheet* aSheet) {
   MOZ_ASSERT(aSheet == mStyleSheet || !aSheet || !mStyleSheet,
              "Multiple style sheets competing for one media list");
   mStyleSheet = aSheet;
 }
 
 template <typename Func>
 nsresult MediaList::DoMediaChange(Func aCallback) {
+  if (IsReadOnly()) {
+    return NS_OK;
+  }
+
   if (mStyleSheet) {
     mStyleSheet->WillDirty();
   }
 
   nsresult rv = aCallback();
   if (NS_FAILED(rv)) {
     return rv;
   }
@@ -80,16 +84,20 @@ void MediaList::GetText(nsAString& aMedi
 /* static */
 already_AddRefed<MediaList> MediaList::Create(const nsAString& aMedia,
                                               CallerType aCallerType) {
   RefPtr<MediaList> mediaList = new MediaList(aMedia, aCallerType);
   return mediaList.forget();
 }
 
 void MediaList::SetText(const nsAString& aMediaText) {
+  if (IsReadOnly()) {
+    return;
+  }
+
   SetTextInternal(aMediaText, CallerType::NonSystem);
 }
 
 void MediaList::GetMediaText(nsAString& aMediaText) { GetText(aMediaText); }
 
 void MediaList::SetTextInternal(const nsAString& aMediaText,
                                 CallerType aCallerType) {
   NS_ConvertUTF16toUTF8 mediaText(aMediaText);
@@ -102,31 +110,33 @@ void MediaList::IndexedGetter(uint32_t a
                               nsAString& aReturn) {
   aFound = Servo_MediaList_GetMediumAt(mRawList, aIndex, &aReturn);
   if (!aFound) {
     SetDOMStringToNull(aReturn);
   }
 }
 
 nsresult MediaList::Delete(const nsAString& aOldMedium) {
+  MOZ_ASSERT(!IsReadOnly());
   NS_ConvertUTF16toUTF8 oldMedium(aOldMedium);
   if (Servo_MediaList_DeleteMedium(mRawList, &oldMedium)) {
     return NS_OK;
   }
   return NS_ERROR_DOM_NOT_FOUND_ERR;
 }
 
 bool MediaList::Matches(const Document& aDocument) const {
   const RawServoStyleSet* rawSet =
       aDocument.StyleSetForPresShellOrMediaQueryEvaluation()->RawSet();
   MOZ_ASSERT(rawSet, "The RawServoStyleSet should be valid!");
   return Servo_MediaList_Matches(mRawList, rawSet);
 }
 
 nsresult MediaList::Append(const nsAString& aNewMedium) {
+  MOZ_ASSERT(!IsReadOnly());
   if (aNewMedium.IsEmpty()) {
     return NS_ERROR_DOM_NOT_FOUND_ERR;
   }
   NS_ConvertUTF16toUTF8 newMedium(aNewMedium);
   Servo_MediaList_AppendMedium(mRawList, &newMedium);
   return NS_OK;
 }
 
@@ -156,10 +166,14 @@ MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(Serv
 size_t MediaList::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
   size_t n = 0;
   n += Servo_MediaList_SizeOfIncludingThis(ServoMediaListMallocSizeOf,
                                            ServoMediaListMallocEnclosingSizeOf,
                                            mRawList);
   return n;
 }
 
+bool MediaList::IsReadOnly() const {
+  return mStyleSheet && mStyleSheet->IsReadOnly();
+}
+
 }  // namespace dom
 }  // namespace mozilla
--- a/layout/style/MediaList.h
+++ b/layout/style/MediaList.h
@@ -72,16 +72,18 @@ class MediaList final : public nsISuppor
 
   nsresult Delete(const nsAString& aOldMedium);
   nsresult Append(const nsAString& aNewMedium);
 
   ~MediaList() {
     MOZ_ASSERT(!mStyleSheet, "Backpointer should have been cleared");
   }
 
+  bool IsReadOnly() const;
+
   // not refcounted; sheet will let us know when it goes away
   // mStyleSheet is the sheet that needs to be dirtied when this
   // medialist changes
   StyleSheet* mStyleSheet = nullptr;
 
  private:
   template <typename Func>
   inline nsresult DoMediaChange(Func aCallback);
--- a/layout/style/Rule.cpp
+++ b/layout/style/Rule.cpp
@@ -70,10 +70,18 @@ void Rule::DropSheetReference() { mSheet
 
 void Rule::SetCssText(const nsAString& aCssText) {
   // We used to throw for some rule types, but not all.  Specifically, we did
   // not throw for StyleRule.  Let's just always not throw.
 }
 
 Rule* Rule::GetParentRule() const { return mParentRule; }
 
+bool Rule::IsReadOnly() const {
+  MOZ_ASSERT(!mSheet || !mParentRule ||
+                 mSheet->IsReadOnly() == mParentRule->IsReadOnly(),
+             "a parent rule should be read only iff the owning sheet is "
+             "read only");
+  return mSheet && mSheet->IsReadOnly();
+}
+
 }  // namespace css
 }  // namespace mozilla
--- a/layout/style/Rule.h
+++ b/layout/style/Rule.h
@@ -84,16 +84,19 @@ class Rule : public nsISupports, public 
   void DropReferences() {
     DropSheetReference();
     DropParentRuleReference();
   }
 
   uint32_t GetLineNumber() const { return mLineNumber; }
   uint32_t GetColumnNumber() const { return mColumnNumber; }
 
+  // Whether this a rule in a read only style sheet.
+  bool IsReadOnly() const;
+
   // This is pure virtual because all of Rule's data members are non-owning and
   // thus measured elsewhere.
   virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
       MOZ_MUST_OVERRIDE = 0;
 
   // WebIDL interface
   virtual uint16_t Type() const = 0;
   virtual void GetCssText(nsAString& aCssText) const = 0;
--- a/layout/style/ServoCSSRuleList.cpp
+++ b/layout/style/ServoCSSRuleList.cpp
@@ -149,16 +149,21 @@ void ServoCSSRuleList::DropParentRuleRef
   EnumerateInstantiatedRules(
       [](css::Rule* rule) { rule->DropParentRuleReference(); });
 }
 
 nsresult ServoCSSRuleList::InsertRule(const nsAString& aRule, uint32_t aIndex) {
   MOZ_ASSERT(mStyleSheet,
              "Caller must ensure that "
              "the list is not unlinked from stylesheet");
+
+  if (IsReadOnly()) {
+    return NS_OK;
+  }
+
   NS_ConvertUTF16toUTF8 rule(aRule);
   bool nested = !!mParentRule;
   css::Loader* loader = nullptr;
 
   // TODO(emilio, bug 1535456): Should probably always be able to get a handle
   // to some loader if we're parsing an @import rule, but which one?
   //
   // StyleSheet::ReparseSheet just mints a new loader, but that'd be wrong in
@@ -173,16 +178,20 @@ nsresult ServoCSSRuleList::InsertRule(co
   if (NS_FAILED(rv)) {
     return rv;
   }
   mRules.InsertElementAt(aIndex, type);
   return rv;
 }
 
 nsresult ServoCSSRuleList::DeleteRule(uint32_t aIndex) {
+  if (IsReadOnly()) {
+    return NS_OK;
+  }
+
   nsresult rv = Servo_CssRules_DeleteRule(mRawRules, aIndex);
   if (!NS_FAILED(rv)) {
     uintptr_t rule = mRules[aIndex];
     if (rule > kMaxRuleType) {
       DropRule(already_AddRefed<css::Rule>(CastToPtr(rule)));
     }
     mRules.RemoveElementAt(aIndex);
   }
@@ -198,9 +207,17 @@ uint16_t ServoCSSRuleList::GetDOMCSSRule
 }
 
 ServoCSSRuleList::~ServoCSSRuleList() {
   MOZ_ASSERT(!mStyleSheet, "Backpointer should have been cleared");
   MOZ_ASSERT(!mParentRule, "Backpointer should have been cleared");
   DropAllRules();
 }
 
+bool ServoCSSRuleList::IsReadOnly() const {
+  MOZ_ASSERT(!mStyleSheet || !mParentRule ||
+                 mStyleSheet->IsReadOnly() == mParentRule->IsReadOnly(),
+             "a parent rule should be read only iff the owning sheet is "
+             "read only");
+  return mStyleSheet && mStyleSheet->IsReadOnly();
+}
+
 }  // namespace mozilla
--- a/layout/style/ServoCSSRuleList.h
+++ b/layout/style/ServoCSSRuleList.h
@@ -66,16 +66,18 @@ class ServoCSSRuleList final : public do
     return reinterpret_cast<css::Rule*>(aInt);
   }
 
   template <typename Func>
   void EnumerateInstantiatedRules(Func aCallback);
 
   void DropAllRules();
 
+  bool IsReadOnly() const;
+
   // mStyleSheet may be nullptr when it drops the reference to us.
   StyleSheet* mStyleSheet = nullptr;
   // mParentRule is nullptr if it isn't a nested rule list.
   css::GroupRule* mParentRule = nullptr;
   RefPtr<ServoCssRules> mRawRules;
   // Array stores either a number indicating rule type, or a pointer to
   // css::Rule. If the value is less than kMaxRuleType, the given rule
   // instance has not been constructed, and the value means the type
--- a/layout/style/StyleSheet.cpp
+++ b/layout/style/StyleSheet.cpp
@@ -237,18 +237,17 @@ void StyleSheet::ApplicableStateChanged(
   if (auto* shadow = ShadowRoot::FromNode(node)) {
     shadow->StyleSheetApplicableStateChanged(*this, aApplicable);
   } else {
     node.AsDocument()->SetStyleSheetApplicableState(this, aApplicable);
   }
 }
 
 void StyleSheet::SetDisabled(bool aDisabled) {
-  // Only allow disabling author sheets.
-  if (mParsingMode != css::eAuthorSheetFeatures) {
+  if (IsReadOnly()) {
     return;
   }
 
   if (aDisabled == Disabled()) {
     return;
   }
 
   if (aDisabled) {
@@ -291,19 +290,18 @@ StyleSheetInfo::StyleSheetInfo(CORSMode 
 StyleSheetInfo::StyleSheetInfo(StyleSheetInfo& aCopy, StyleSheet* aPrimarySheet)
     : mSheetURI(aCopy.mSheetURI),
       mOriginalSheetURI(aCopy.mOriginalSheetURI),
       mBaseURI(aCopy.mBaseURI),
       mPrincipal(aCopy.mPrincipal),
       mCORSMode(aCopy.mCORSMode),
       mReferrerPolicy(aCopy.mReferrerPolicy),
       mIntegrity(aCopy.mIntegrity),
-      mFirstChild()  // We don't rebuild the child because we're making a copy
-                     // without children.
-      ,
+      mFirstChild(),  // We don't rebuild the child because we're making a copy
+                      // without children.
       mSourceMapURL(aCopy.mSourceMapURL),
       mSourceMapURLFromComment(aCopy.mSourceMapURLFromComment),
       mSourceURL(aCopy.mSourceURL),
       mContents(Servo_StyleSheet_Clone(aCopy.mContents.get(), aPrimarySheet)
                     .Consume()),
       // Cloning aCopy.mContents will still leave us with some references to
       // data in shared memory (for example, any SelectorList objects will still
       // be shared), so continue to keep it alive.
@@ -407,16 +405,18 @@ void StyleSheet::GetTitle(nsAString& aTi
   if (!mTitle.IsEmpty()) {
     aTitle.Assign(mTitle);
   } else {
     SetDOMStringToNull(aTitle);
   }
 }
 
 void StyleSheet::WillDirty() {
+  MOZ_ASSERT(!IsReadOnly());
+
   if (IsComplete()) {
     EnsureUniqueInner();
   }
 }
 
 void StyleSheet::AddStyleSet(ServoStyleSet* aStyleSet) {
   MOZ_DIAGNOSTIC_ASSERT(!mStyleSets.Contains(aStyleSet),
                         "style set already registered");
@@ -428,16 +428,22 @@ void StyleSheet::DropStyleSet(ServoStyle
   MOZ_DIAGNOSTIC_ASSERT(found, "didn't find style set");
 #ifndef MOZ_DIAGNOSTIC_ASSERT_ENABLED
   Unused << found;
 #endif
 }
 
 void StyleSheet::EnsureUniqueInner() {
   MOZ_ASSERT(mInner->mSheets.Length() != 0, "unexpected number of outers");
+
+  if (IsReadOnly()) {
+    // Sheets that can't be modified don't need a unique inner.
+    return;
+  }
+
   mState |= State::ForcedUniqueInner;
 
   if (HasUniqueInner()) {
     // already unique
     return;
   }
 
   StyleSheetInfo* clone = mInner->CloneFor(this);
@@ -499,24 +505,30 @@ void StyleSheet::SetSourceURL(const nsAS
   mInner->mSourceURL = aSourceURL;
 }
 
 css::Rule* StyleSheet::GetDOMOwnerRule() const { return mOwnerRule; }
 
 uint32_t StyleSheet::InsertRule(const nsAString& aRule, uint32_t aIndex,
                                 nsIPrincipal& aSubjectPrincipal,
                                 ErrorResult& aRv) {
+  if (IsReadOnly()) {
+    return 0;
+  }
   if (!AreRulesAvailable(aSubjectPrincipal, aRv)) {
     return 0;
   }
   return InsertRuleInternal(aRule, aIndex, aRv);
 }
 
 void StyleSheet::DeleteRule(uint32_t aIndex, nsIPrincipal& aSubjectPrincipal,
                             ErrorResult& aRv) {
+  if (IsReadOnly()) {
+    return;
+  }
   if (!AreRulesAvailable(aSubjectPrincipal, aRv)) {
     return;
   }
   return DeleteRuleInternal(aIndex, aRv);
 }
 
 nsresult StyleSheet::DeleteRuleFromGroup(css::GroupRule* aGroup,
                                          uint32_t aIndex) {
@@ -525,16 +537,20 @@ nsresult StyleSheet::DeleteRuleFromGroup
   RefPtr<css::Rule> rule = aGroup->GetStyleRuleAt(aIndex);
   NS_ENSURE_TRUE(rule, NS_ERROR_ILLEGAL_VALUE);
 
   // check that the rule actually belongs to this sheet!
   if (this != rule->GetStyleSheet()) {
     return NS_ERROR_INVALID_ARG;
   }
 
+  if (IsReadOnly()) {
+    return NS_OK;
+  }
+
   WillDirty();
 
   nsresult result = aGroup->DeleteStyleRuleAt(aIndex);
   NS_ENSURE_SUCCESS(result, result);
 
   rule->DropReferences();
 
   RuleRemoved(*rule);
@@ -596,16 +612,20 @@ nsresult StyleSheet::InsertRuleIntoGroup
                                          css::GroupRule* aGroup,
                                          uint32_t aIndex) {
   NS_ASSERTION(IsComplete(), "No inserting into an incomplete sheet!");
   // check that the group actually belongs to this sheet!
   if (this != aGroup->GetStyleSheet()) {
     return NS_ERROR_INVALID_ARG;
   }
 
+  if (IsReadOnly()) {
+    return NS_OK;
+  }
+
   WillDirty();
 
   nsresult result = InsertRuleIntoGroupInternal(aRule, aGroup, aIndex);
   NS_ENSURE_SUCCESS(result, result);
   RuleAdded(*aGroup->GetStyleRuleAt(aIndex));
   return NS_OK;
 }
 
@@ -724,16 +744,17 @@ void StyleSheet::SetAssociatedDocumentOr
 
 void StyleSheet::PrependStyleSheet(StyleSheet* aSheet) {
   WillDirty();
   PrependStyleSheetSilently(aSheet);
 }
 
 void StyleSheet::PrependStyleSheetSilently(StyleSheet* aSheet) {
   MOZ_ASSERT(aSheet);
+  MOZ_ASSERT(!IsReadOnly());
 
   aSheet->mNext = Inner().mFirstChild;
   Inner().mFirstChild = aSheet;
 
   // This is not reference counted. Our parent tells us when
   // it's going away.
   aSheet->mParent = this;
   aSheet->SetAssociatedDocumentOrShadowRoot(mDocumentOrShadowRoot,
@@ -990,18 +1011,18 @@ void StyleSheet::FinishParse() {
 nsresult StyleSheet::ReparseSheet(const nsAString& aInput) {
   if (!IsComplete()) {
     return NS_ERROR_DOM_INVALID_ACCESS_ERR;
   }
 
   // Allowing to modify UA sheets is dangerous (in the sense that C++ code
   // relies on rules in those sheets), plus they're probably going to be shared
   // across processes in which case this is directly a no-go.
-  if (GetOrigin() == StyleOrigin::UserAgent) {
-    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+  if (IsReadOnly()) {
+    return NS_OK;
   }
 
   // Hold strong ref to the CSSLoader in case the document update
   // kills the document
   RefPtr<css::Loader> loader;
   if (Document* doc = GetAssociatedDocument()) {
     loader = doc->CSSLoader();
     NS_ASSERTION(loader, "Document with no CSS loader!");
@@ -1128,16 +1149,18 @@ ServoCSSRuleList* StyleSheet::GetCssRule
     MOZ_ASSERT(rawRules);
     mRuleList = new ServoCSSRuleList(rawRules.forget(), this, nullptr);
   }
   return mRuleList;
 }
 
 uint32_t StyleSheet::InsertRuleInternal(const nsAString& aRule, uint32_t aIndex,
                                         ErrorResult& aRv) {
+  MOZ_ASSERT(!IsReadOnly());
+
   // Ensure mRuleList is constructed.
   GetCssRulesInternal();
 
   aRv = mRuleList->InsertRule(aRule, aIndex);
   if (aRv.Failed()) {
     return 0;
   }
 
@@ -1148,16 +1171,18 @@ uint32_t StyleSheet::InsertRuleInternal(
       !RuleHasPendingChildSheet(rule)) {
     RuleAdded(*rule);
   }
 
   return aIndex;
 }
 
 void StyleSheet::DeleteRuleInternal(uint32_t aIndex, ErrorResult& aRv) {
+  MOZ_ASSERT(!IsReadOnly());
+
   // Ensure mRuleList is constructed.
   GetCssRulesInternal();
   if (aIndex >= mRuleList->Length()) {
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return;
   }
 
   // Hold a strong ref to the rule so it doesn't die when we remove it
@@ -1170,16 +1195,18 @@ void StyleSheet::DeleteRuleInternal(uint
   if (!aRv.Failed()) {
     RuleRemoved(*rule);
   }
 }
 
 nsresult StyleSheet::InsertRuleIntoGroupInternal(const nsAString& aRule,
                                                  css::GroupRule* aGroup,
                                                  uint32_t aIndex) {
+  MOZ_ASSERT(!IsReadOnly());
+
   auto rules = static_cast<ServoCSSRuleList*>(aGroup->CssRules());
   MOZ_ASSERT(rules->GetParentRule() == aGroup);
   return rules->InsertRule(aRule, aIndex);
 }
 
 StyleOrigin StyleSheet::GetOrigin() const {
   return Servo_StyleSheet_GetOrigin(Inner().mContents);
 }
@@ -1210,9 +1237,13 @@ const ServoCssRules* StyleSheet::ToShare
   MOZ_ASSERT(GetReferrerPolicy() == net::RP_Unset);
   MOZ_ASSERT(GetCORSMode() == CORS_NONE);
   MOZ_ASSERT(Inner().mIntegrity.IsEmpty());
   MOZ_ASSERT(nsContentUtils::IsSystemPrincipal(Principal()));
 
   return Servo_SharedMemoryBuilder_AddStylesheet(aBuilder, Inner().mContents);
 }
 
+bool StyleSheet::IsReadOnly() const {
+  return IsComplete() && GetOrigin() == StyleOrigin::UserAgent;
+}
+
 }  // namespace mozilla
--- a/layout/style/StyleSheet.h
+++ b/layout/style/StyleSheet.h
@@ -27,17 +27,16 @@ class nsINode;
 class nsIPrincipal;
 struct nsLayoutStylesheetCacheShm;
 struct RawServoSharedMemoryBuilder;
 
 namespace mozilla {
 
 class ServoCSSRuleList;
 class ServoStyleSet;
-enum class OriginFlags : uint8_t;
 
 typedef MozPromise</* Dummy */ bool,
                    /* Dummy */ bool,
                    /* IsExclusive = */ true>
     StyleSheetParsePromise;
 
 namespace css {
 class GroupRule;
@@ -135,17 +134,17 @@ class StyleSheet final : public nsICSSLo
   // nsICSSLoaderObserver interface
   NS_IMETHOD StyleSheetLoaded(StyleSheet* aSheet, bool aWasAlternate,
                               nsresult aStatus) final;
 
   // Internal GetCssRules methods which do not have security check and
   // completeness check.
   ServoCSSRuleList* GetCssRulesInternal();
 
-  // Returns the stylesheet's Servo origin as an OriginFlags value.
+  // Returns the stylesheet's Servo origin as a StyleOrigin value.
   mozilla::StyleOrigin GetOrigin() const;
 
   /**
    * The different changes that a stylesheet may go through.
    *
    * Used by the StyleSets in order to handle more efficiently some kinds of
    * changes.
    */
@@ -384,16 +383,21 @@ class StyleSheet final : public nsICSSLo
   const ServoCssRules* ToShared(RawServoSharedMemoryBuilder* aBuilder);
 
   // Sets the contents of this style sheet to the specified aSharedRules
   // pointer, which must be a pointer somewhere in the aSharedMemory buffer
   // as previously returned by a ToShared() call.
   void SetSharedContents(nsLayoutStylesheetCacheShm* aSharedMemory,
                          const ServoCssRules* aSharedRules);
 
+  // Whether this style sheet should not allow any modifications.
+  //
+  // This is true for any User Agent sheets once they are complete.
+  bool IsReadOnly() const;
+
  private:
   dom::ShadowRoot* GetContainingShadow() const;
 
   StyleSheetInfo& Inner() {
     MOZ_ASSERT(mInner);
     return *mInner;
   }
 
--- a/layout/style/nsDOMCSSDeclaration.cpp
+++ b/layout/style/nsDOMCSSDeclaration.cpp
@@ -45,16 +45,20 @@ nsresult nsDOMCSSDeclaration::GetPropert
     decl->GetPropertyValueByID(aPropID, aValue);
   }
   return NS_OK;
 }
 
 nsresult nsDOMCSSDeclaration::SetPropertyValue(
     const nsCSSPropertyID aPropID, const nsAString& aValue,
     nsIPrincipal* aSubjectPrincipal) {
+  if (IsReadOnly()) {
+    return NS_OK;
+  }
+
   switch (aPropID) {
     case eCSSProperty_background_position:
     case eCSSProperty_background_position_x:
     case eCSSProperty_background_position_y:
     case eCSSProperty_transform:
     case eCSSProperty_translate:
     case eCSSProperty_rotate:
     case eCSSProperty_scale:
@@ -93,16 +97,20 @@ void nsDOMCSSDeclaration::GetCssText(nsA
   if (decl) {
     decl->ToString(aCssText);
   }
 }
 
 void nsDOMCSSDeclaration::SetCssText(const nsAString& aCssText,
                                      nsIPrincipal* aSubjectPrincipal,
                                      ErrorResult& aRv) {
+  if (IsReadOnly()) {
+    return;
+  }
+
   // We don't need to *do* anything with the old declaration, but we need
   // to ensure that it exists, or else SetCSSDeclaration may crash.
   RefPtr<DeclarationBlock> created;
   DeclarationBlock* olddecl =
       GetOrCreateCSSDeclaration(eOperation_Modify, getter_AddRefs(created));
   if (!olddecl) {
     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
     return;
@@ -174,16 +182,20 @@ void nsDOMCSSDeclaration::GetPropertyPri
   }
 }
 
 NS_IMETHODIMP
 nsDOMCSSDeclaration::SetProperty(const nsAString& aPropertyName,
                                  const nsAString& aValue,
                                  const nsAString& aPriority,
                                  nsIPrincipal* aSubjectPrincipal) {
+  if (IsReadOnly()) {
+    return NS_OK;
+  }
+
   if (aValue.IsEmpty()) {
     // If the new value of the property is an empty string we remove the
     // property.
     // XXX this ignores the priority string, should it?
     return RemovePropertyInternal(aPropertyName);
   }
 
   // In the common (and fast) cases we can use the property id
@@ -207,16 +219,20 @@ nsDOMCSSDeclaration::SetProperty(const n
                                     aSubjectPrincipal);
   }
   return ParsePropertyValue(propID, aValue, important, aSubjectPrincipal);
 }
 
 NS_IMETHODIMP
 nsDOMCSSDeclaration::RemoveProperty(const nsAString& aPropertyName,
                                     nsAString& aReturn) {
+  if (IsReadOnly()) {
+    return NS_OK;
+  }
+
   nsresult rv = GetPropertyValue(aPropertyName, aReturn);
   NS_ENSURE_SUCCESS(rv, rv);
   return RemovePropertyInternal(aPropertyName);
 }
 
 /* static */ nsDOMCSSDeclaration::ParsingEnvironment
 nsDOMCSSDeclaration::GetParsingEnvironmentForRule(const css::Rule* aRule) {
   StyleSheet* sheet = aRule ? aRule->GetStyleSheet() : nullptr;
@@ -274,16 +290,20 @@ nsresult nsDOMCSSDeclaration::ModifyDecl
   return SetCSSDeclaration(decl, aClosureData);
 }
 
 nsresult nsDOMCSSDeclaration::ParsePropertyValue(
     const nsCSSPropertyID aPropID, const nsAString& aPropValue,
     bool aIsImportant, nsIPrincipal* aSubjectPrincipal) {
   AUTO_PROFILER_LABEL_CATEGORY_PAIR(LAYOUT_CSSParsing);
 
+  if (IsReadOnly()) {
+    return NS_OK;
+  }
+
   DeclarationBlockMutationClosure closure = {};
   MutationClosureData closureData;
   GetPropertyChangeClosure(&closure, &closureData);
 
   return ModifyDeclaration(
       aSubjectPrincipal, &closureData,
       [&](DeclarationBlock* decl, ParsingEnvironment& env) {
         NS_ConvertUTF16toUTF8 value(aPropValue);
@@ -293,16 +313,20 @@ nsresult nsDOMCSSDeclaration::ParsePrope
       });
 }
 
 nsresult nsDOMCSSDeclaration::ParseCustomPropertyValue(
     const nsAString& aPropertyName, const nsAString& aPropValue,
     bool aIsImportant, nsIPrincipal* aSubjectPrincipal) {
   MOZ_ASSERT(nsCSSProps::IsCustomPropertyName(aPropertyName));
 
+  if (IsReadOnly()) {
+    return NS_OK;
+  }
+
   DeclarationBlockMutationClosure closure = {};
   MutationClosureData closureData;
   GetPropertyChangeClosure(&closure, &closureData);
 
   return ModifyDeclaration(
       aSubjectPrincipal, &closureData,
       [&](DeclarationBlock* decl, ParsingEnvironment& env) {
         NS_ConvertUTF16toUTF8 property(aPropertyName);
@@ -311,16 +335,20 @@ nsresult nsDOMCSSDeclaration::ParseCusto
             decl->Raw(), &property, &value, aIsImportant, env.mUrlExtraData,
             ParsingMode::Default, env.mCompatMode, env.mLoader, closure);
       });
 }
 
 nsresult nsDOMCSSDeclaration::RemovePropertyInternal(nsCSSPropertyID aPropID) {
   DeclarationBlock* olddecl =
       GetOrCreateCSSDeclaration(eOperation_RemoveProperty, nullptr);
+  if (IsReadOnly()) {
+    return NS_OK;
+  }
+
   if (!olddecl) {
     return NS_OK;  // no decl, so nothing to remove
   }
 
   // For nsDOMCSSAttributeDeclaration, SetCSSDeclaration will lead to
   // Attribute setting code, which leads in turn to BeginUpdate.  We
   // need to start the update now so that the old rule doesn't get used
   // between when we mutate the declaration and when we set the new
@@ -335,16 +363,20 @@ nsresult nsDOMCSSDeclaration::RemoveProp
   if (!decl->RemovePropertyByID(aPropID, closure)) {
     return NS_OK;
   }
   return SetCSSDeclaration(decl, &closureData);
 }
 
 nsresult nsDOMCSSDeclaration::RemovePropertyInternal(
     const nsAString& aPropertyName) {
+  if (IsReadOnly()) {
+    return NS_OK;
+  }
+
   DeclarationBlock* olddecl =
       GetOrCreateCSSDeclaration(eOperation_RemoveProperty, nullptr);
   if (!olddecl) {
     return NS_OK;  // no decl, so nothing to remove
   }
 
   // For nsDOMCSSAttributeDeclaration, SetCSSDeclaration will lead to
   // Attribute setting code, which leads in turn to BeginUpdate.  We
--- a/layout/style/nsICSSDeclaration.cpp
+++ b/layout/style/nsICSSDeclaration.cpp
@@ -15,8 +15,13 @@ using mozilla::dom::DocGroup;
 DocGroup* nsICSSDeclaration::GetDocGroup() {
   nsINode* parentNode = GetParentObject();
   if (!parentNode) {
     return nullptr;
   }
 
   return parentNode->GetDocGroup();
 }
+
+bool nsICSSDeclaration::IsReadOnly() {
+  mozilla::css::Rule* rule = GetParentRule();
+  return rule && rule->IsReadOnly();
+}
--- a/layout/style/nsICSSDeclaration.h
+++ b/layout/style/nsICSSDeclaration.h
@@ -95,16 +95,19 @@ class nsICSSDeclaration : public nsISupp
                    mozilla::ErrorResult& rv) {
     rv = SetProperty(aPropName, aValue, aPriority, aSubjectPrincipal);
   }
   void RemoveProperty(const nsAString& aPropName, nsString& aRetval,
                       mozilla::ErrorResult& rv) {
     rv = RemoveProperty(aPropName, aRetval);
   }
   virtual mozilla::css::Rule* GetParentRule() = 0;
+
+ protected:
+  bool IsReadOnly();
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsICSSDeclaration, NS_ICSSDECLARATION_IID)
 
 #define NS_DECL_NSIDOMCSSSTYLEDECLARATION_HELPER                               \
   void GetCssText(nsAString& aCssText) override;                               \
   void SetCssText(const nsAString& aCssText, nsIPrincipal* aSubjectPrincipal,  \
                   mozilla::ErrorResult& aRv) override;                         \
--- a/layout/svg/svg.css
+++ b/layout/svg/svg.css
@@ -2,20 +2,25 @@
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 @namespace url(http://www.w3.org/2000/svg);
 @namespace xml url(http://www.w3.org/XML/1998/namespace);
 
-style, script, symbol {
+style, script {
  display: none;
 }
 
+/* This is only to be overridden by the rule right below. */
+symbol {
+ display: none !important;
+}
+
 symbol:-moz-use-shadow-tree-root {
   display: inline !important;
 }
 
 switch {
  -moz-binding: none !important;
 }
 
--- a/netwerk/protocol/http/AlternateServices.cpp
+++ b/netwerk/protocol/http/AlternateServices.cpp
@@ -46,18 +46,18 @@ static nsresult SchemeIsHTTPS(const nsAC
 
 bool AltSvcMapping::AcceptableProxy(nsProxyInfo *proxyInfo) {
   return !proxyInfo || proxyInfo->IsDirect() || proxyInfo->IsSOCKS();
 }
 
 void AltSvcMapping::ProcessHeader(
     const nsCString &buf, const nsCString &originScheme,
     const nsCString &originHost, int32_t originPort, const nsACString &username,
-    const nsACString &topWindowOrigin, bool privateBrowsing,
-    nsIInterfaceRequestor *callbacks, nsProxyInfo *proxyInfo, uint32_t caps,
+    bool privateBrowsing, nsIInterfaceRequestor *callbacks,
+    nsProxyInfo *proxyInfo, uint32_t caps,
     const OriginAttributes &originAttributes) {
   MOZ_ASSERT(NS_IsMainThread());
   LOG(("AltSvcMapping::ProcessHeader: %s\n", buf.get()));
   if (!callbacks) {
     return;
   }
 
   if (!AcceptableProxy(proxyInfo)) {
@@ -140,18 +140,18 @@ void AltSvcMapping::ProcessHeader(
           spdyInfo->ProtocolEnabled(spdyIndex))) {
       LOG(("Alt Svc unknown protocol %s, ignoring", npnToken.get()));
       continue;
     }
 
     RefPtr<AltSvcMapping> mapping = new AltSvcMapping(
         gHttpHandler->ConnMgr()->GetStoragePtr(),
         gHttpHandler->ConnMgr()->StorageEpoch(), originScheme, originHost,
-        originPort, username, topWindowOrigin, privateBrowsing,
-        NowInSeconds() + maxage, hostname, portno, npnToken, originAttributes);
+        originPort, username, privateBrowsing, NowInSeconds() + maxage,
+        hostname, portno, npnToken, originAttributes);
     if (mapping->TTL() <= 0) {
       LOG(("Alt Svc invalid map"));
       mapping = nullptr;
       // since this isn't a parse error, let's clear any existing mapping
       // as that would have happened if we had accepted the parameters.
       gHttpHandler->ConnMgr()->ClearHostMapping(originHost, originPort,
                                                 originAttributes);
     } else {
@@ -164,30 +164,28 @@ void AltSvcMapping::ProcessHeader(
     Telemetry::Accumulate(Telemetry::HTTP_ALTSVC_ENTRIES_PER_HEADER,
                           numEntriesInHeader);
   }
 }
 
 AltSvcMapping::AltSvcMapping(DataStorage *storage, int32_t epoch,
                              const nsACString &originScheme,
                              const nsACString &originHost, int32_t originPort,
-                             const nsACString &username,
-                             const nsACString &topWindowOrigin,
-                             bool privateBrowsing, uint32_t expiresAt,
+                             const nsACString &username, bool privateBrowsing,
+                             uint32_t expiresAt,
                              const nsACString &alternateHost,
                              int32_t alternatePort, const nsACString &npnToken,
                              const OriginAttributes &originAttributes)
     : mStorage(storage),
       mStorageEpoch(epoch),
       mAlternateHost(alternateHost),
       mAlternatePort(alternatePort),
       mOriginHost(originHost),
       mOriginPort(originPort),
       mUsername(username),
-      mTopWindowOrigin(topWindowOrigin),
       mPrivate(privateBrowsing),
       mExpiresAt(expiresAt),
       mValidated(false),
       mMixedScheme(false),
       mNPNToken(npnToken),
       mOriginAttributes(originAttributes),
       mSyncOnlyOnSuccess(false) {
   MOZ_ASSERT(NS_IsMainThread());
@@ -307,18 +305,18 @@ bool AltSvcMapping::RouteEquals(AltSvcMa
          (mAlternatePort == map->mAlternatePort) &&
          mNPNToken.Equals(map->mNPNToken);
 }
 
 void AltSvcMapping::GetConnectionInfo(
     nsHttpConnectionInfo **outCI, nsProxyInfo *pi,
     const OriginAttributes &originAttributes) {
   RefPtr<nsHttpConnectionInfo> ci = new nsHttpConnectionInfo(
-      mOriginHost, mOriginPort, mNPNToken, mUsername, mTopWindowOrigin, pi,
-      originAttributes, mAlternateHost, mAlternatePort);
+      mOriginHost, mOriginPort, mNPNToken, mUsername, pi, originAttributes,
+      mAlternateHost, mAlternatePort);
 
   // http:// without the mixed-scheme attribute needs to be segmented in the
   // connection manager connection information hash with this attribute
   if (!mHttps && !mMixedScheme) {
     ci->SetInsecureScheme(true);
   }
   ci->SetPrivate(mPrivate);
   ci.forget(outCI);
@@ -331,18 +329,16 @@ void AltSvcMapping::Serialize(nsCString 
   out.AppendInt(mOriginPort);
   out.Append(':');
   out.Append(mAlternateHost);
   out.Append(':');
   out.AppendInt(mAlternatePort);
   out.Append(':');
   out.Append(mUsername);
   out.Append(':');
-  out.Append(mTopWindowOrigin);
-  out.Append('|');  // Be careful, the top window origin may contain colons!
   out.Append(mPrivate ? 'y' : 'n');
   out.Append(':');
   out.AppendInt(mExpiresAt);
   out.Append(':');
   out.Append(mNPNToken);
   out.Append(':');
   out.Append(mValidated ? 'y' : 'n');
   out.Append(':');
@@ -356,51 +352,44 @@ void AltSvcMapping::Serialize(nsCString 
   out.Append(':');
 }
 
 AltSvcMapping::AltSvcMapping(DataStorage *storage, int32_t epoch,
                              const nsCString &str)
     : mStorage(storage), mStorageEpoch(epoch), mSyncOnlyOnSuccess(false) {
   mValidated = false;
   nsresult code;
-  char separator = ':';
 
   // The the do {} while(0) loop acts like try/catch(e){} with the break in
   // _NS_NEXT_TOKEN
   do {
 #ifdef _NS_NEXT_TOKEN
     COMPILER ERROR
 #endif
-#define _NS_NEXT_TOKEN                  \
-  start = idx + 1;                      \
-  idx = str.FindChar(separator, start); \
+#define _NS_NEXT_TOKEN            \
+  start = idx + 1;                \
+  idx = str.FindChar(':', start); \
   if (idx < 0) break;
         int32_t start = 0;
     int32_t idx;
-    idx = str.FindChar(separator, start);
+    idx = str.FindChar(':', start);
     if (idx < 0) break;
     mHttps = Substring(str, start, idx - start).EqualsLiteral("https");
     _NS_NEXT_TOKEN;
     mOriginHost = Substring(str, start, idx - start);
     _NS_NEXT_TOKEN;
     mOriginPort =
         nsCString(Substring(str, start, idx - start)).ToInteger(&code);
     _NS_NEXT_TOKEN;
     mAlternateHost = Substring(str, start, idx - start);
     _NS_NEXT_TOKEN;
     mAlternatePort =
         nsCString(Substring(str, start, idx - start)).ToInteger(&code);
     _NS_NEXT_TOKEN;
     mUsername = Substring(str, start, idx - start);
-    // The separator after the top window origin is a pipe character since the
-    // origin string can contain colons.
-    separator = '|';
-    _NS_NEXT_TOKEN;
-    mTopWindowOrigin = Substring(str, start, idx - start);
-    separator = ':';
     _NS_NEXT_TOKEN;
     mPrivate = Substring(str, start, idx - start).EqualsLiteral("y");
     _NS_NEXT_TOKEN;
     mExpiresAt = nsCString(Substring(str, start, idx - start)).ToInteger(&code);
     _NS_NEXT_TOKEN;
     mNPNToken = Substring(str, start, idx - start);
     _NS_NEXT_TOKEN;
     mValidated = Substring(str, start, idx - start).EqualsLiteral("y");
--- a/netwerk/protocol/http/AlternateServices.h
+++ b/netwerk/protocol/http/AlternateServices.h
@@ -44,30 +44,28 @@ class WellKnownChecker;
 
 class AltSvcMapping {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AltSvcMapping)
 
  private:  // ctor from ProcessHeader
   AltSvcMapping(DataStorage *storage, int32_t storageEpoch,
                 const nsACString &originScheme, const nsACString &originHost,
                 int32_t originPort, const nsACString &username,
-                const nsACString &topWindowOrigin, bool privateBrowsing,
-                uint32_t expiresAt, const nsACString &alternateHost,
-                int32_t alternatePort, const nsACString &npnToken,
+                bool privateBrowsing, uint32_t expiresAt,
+                const nsACString &alternateHost, int32_t alternatePort,
+                const nsACString &npnToken,
                 const OriginAttributes &originAttributes);
 
  public:
   AltSvcMapping(DataStorage *storage, int32_t storageEpoch,
                 const nsCString &serialized);
 
   static void ProcessHeader(const nsCString &buf, const nsCString &originScheme,
                             const nsCString &originHost, int32_t originPort,
-                            const nsACString &username,
-                            const nsACString &topWindowOrigin,
-                            bool privateBrowsing,
+                            const nsACString &username, bool privateBrowsing,
                             nsIInterfaceRequestor *callbacks,
                             nsProxyInfo *proxyInfo, uint32_t caps,
                             const OriginAttributes &originAttributes);
 
   // AcceptableProxy() decides whether a particular proxy configuration (pi) is
   // suitable for use with Alt-Svc. No proxy (including a null pi) is suitable.
   static bool AcceptableProxy(nsProxyInfo *pi);
 
@@ -112,17 +110,16 @@ class AltSvcMapping {
   // If you change any of these members, update Serialize()
   nsCString mAlternateHost;
   MOZ_INIT_OUTSIDE_CTOR int32_t mAlternatePort;
 
   nsCString mOriginHost;
   MOZ_INIT_OUTSIDE_CTOR int32_t mOriginPort;
 
   nsCString mUsername;
-  nsCString mTopWindowOrigin;
   MOZ_INIT_OUTSIDE_CTOR bool mPrivate;
 
   MOZ_INIT_OUTSIDE_CTOR uint32_t mExpiresAt;  // alt-svc mappping
 
   MOZ_INIT_OUTSIDE_CTOR bool mValidated;
   MOZ_INIT_OUTSIDE_CTOR bool mHttps;  // origin is https://
   MOZ_INIT_OUTSIDE_CTOR bool
       mMixedScheme;  // .wk allows http and https on same con
--- a/netwerk/protocol/http/Http2Session.cpp
+++ b/netwerk/protocol/http/Http2Session.cpp
@@ -2511,20 +2511,20 @@ class UpdateAltSvcEvent : public Runnabl
     if (NS_FAILED(NS_NewURI(getter_AddRefs(uri), mOrigin))) {
       LOG(("UpdateAltSvcEvent origin does not parse %s\n", mOrigin.get()));
       return NS_OK;
     }
     uri->GetScheme(originScheme);
     uri->GetHost(originHost);
     uri->GetPort(&originPort);
 
-    AltSvcMapping::ProcessHeader(
-        mHeader, originScheme, originHost, originPort, mCI->GetUsername(),
-        mCI->GetTopWindowOrigin(), mCI->GetPrivate(), mCallbacks,
-        mCI->ProxyInfo(), 0, mCI->GetOriginAttributes());
+    AltSvcMapping::ProcessHeader(mHeader, originScheme, originHost, originPort,
+                                 mCI->GetUsername(), mCI->GetPrivate(),
+                                 mCallbacks, mCI->ProxyInfo(), 0,
+                                 mCI->GetOriginAttributes());
     return NS_OK;
   }
 
  private:
   nsCString mHeader;
   nsCString mOrigin;
   RefPtr<nsHttpConnectionInfo> mCI;
   nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -329,19 +329,16 @@ nsHttpChannel::nsHttpChannel()
       mHasAutoRedirectVetoNotifier(0),
       mPinCacheContent(0),
       mIsCorsPreflightDone(0),
       mStronglyFramed(false),
       mUsedNetwork(0),
       mAuthConnectionRestartable(0),
       mChannelClassifierCancellationPending(0),
       mAsyncResumePending(0),
-      mHasBeenIsolatedChecked(0),
-      mIsIsolated(0),
-      mTopWindowOriginComputed(0),
       mPushedStream(nullptr),
       mLocalBlocklist(false),
       mOnTailUnblock(nullptr),
       mWarningReporter(nullptr),
       mIsReadingFromCache(false),
       mFirstResponseSource(RESPONSE_PENDING),
       mRaceCacheWithNetwork(false),
       mRaceDelay(0),
@@ -623,20 +620,23 @@ nsresult nsHttpChannel::ContinueOnBefore
   if (mTRR) {
     mCaps |= NS_HTTP_LARGE_KEEPALIVE | NS_HTTP_DISABLE_TRR;
   }
 
   if (mLoadFlags & LOAD_DISABLE_TRR) {
     mCaps |= NS_HTTP_DISABLE_TRR;
   }
 
+  bool isIsolated = mPrivateBrowsing ||
+                    !AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(
+                        this, mURI, nullptr);
+
   // Finalize ConnectionInfo flags before SpeculativeConnect
   mConnectionInfo->SetAnonymous((mLoadFlags & LOAD_ANONYMOUS) != 0);
-  mConnectionInfo->SetPrivate(mPrivateBrowsing);
-  mConnectionInfo->SetIsolated(IsIsolated());
+  mConnectionInfo->SetPrivate(isIsolated);
   mConnectionInfo->SetNoSpdy(mCaps & NS_HTTP_DISALLOW_SPDY);
   mConnectionInfo->SetBeConservative((mCaps & NS_HTTP_BE_CONSERVATIVE) ||
                                      mBeConservative);
   mConnectionInfo->SetTlsFlags(mTlsFlags);
   mConnectionInfo->SetTrrUsed(mTRR);
   mConnectionInfo->SetTrrDisabled(mCaps & NS_HTTP_DISABLE_TRR);
   mConnectionInfo->SetIPv4Disabled(mCaps & NS_HTTP_DISABLE_IPV4);
   mConnectionInfo->SetIPv6Disabled(mCaps & NS_HTTP_DISABLE_IPV6);
@@ -2363,20 +2363,19 @@ void nsHttpChannel::ProcessAltService() 
                                          getter_AddRefs(callbacks));
   if (mProxyInfo) {
     proxyInfo = do_QueryInterface(mProxyInfo);
   }
 
   OriginAttributes originAttributes;
   NS_GetOriginAttributes(this, originAttributes);
 
-  AltSvcMapping::ProcessHeader(altSvc, scheme, originHost, originPort,
-                               mUsername, GetTopWindowOrigin(),
-                               mPrivateBrowsing, callbacks, proxyInfo,
-                               mCaps & NS_HTTP_DISALLOW_SPDY, originAttributes);
+  AltSvcMapping::ProcessHeader(
+      altSvc, scheme, originHost, originPort, mUsername, mPrivateBrowsing,
+      callbacks, proxyInfo, mCaps & NS_HTTP_DISALLOW_SPDY, originAttributes);
 }
 
 nsresult nsHttpChannel::ProcessResponse() {
   uint32_t httpStatus = mResponseHead->Status();
 
   LOG(("nsHttpChannel::ProcessResponse [this=%p httpStatus=%u]\n", this,
        httpStatus));
 
@@ -3891,52 +3890,16 @@ nsresult nsHttpChannel::OpenCacheEntry(b
     if (appCacheContainer) {
       appCacheContainer->GetApplicationCache(getter_AddRefs(mApplicationCache));
     }
   }
 
   return OpenCacheEntryInternal(isHttps, mApplicationCache, true);
 }
 
-bool nsHttpChannel::IsIsolated() {
-  if (mHasBeenIsolatedChecked) {
-    return mIsIsolated;
-  }
-  mIsIsolated = IsThirdPartyTrackingResource() &&
-                !AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(
-                    this, mURI, nullptr);
-  mHasBeenIsolatedChecked = true;
-  return mIsIsolated;
-}
-
-const nsCString &nsHttpChannel::GetTopWindowOrigin() {
-  if (mTopWindowOriginComputed) {
-    return mTopWindowOrigin;
-  }
-
-  nsCOMPtr<nsIURI> topWindowURI;
-  nsresult rv = GetTopWindowURI(getter_AddRefs(topWindowURI));
-  bool isDocument = false;
-  if (NS_FAILED(rv) && NS_SUCCEEDED(GetIsMainDocumentChannel(&isDocument)) &&
-      isDocument) {
-    // For top-level documents, use the document channel's origin to compute
-    // the unique storage space identifier instead of the top Window URI.
-    rv = NS_GetFinalChannelURI(this, getter_AddRefs(topWindowURI));
-    NS_ENSURE_SUCCESS(rv, mTopWindowOrigin);
-  }
-
-  rv = nsContentUtils::GetASCIIOrigin(topWindowURI ? topWindowURI : mURI,
-                                      mTopWindowOrigin);
-  NS_ENSURE_SUCCESS(rv, mTopWindowOrigin);
-
-  mTopWindowOriginComputed = true;
-
-  return mTopWindowOrigin;
-}
-
 nsresult nsHttpChannel::OpenCacheEntryInternal(
     bool isHttps, nsIApplicationCache *applicationCache,
     bool allowApplicationCache) {
   MOZ_ASSERT_IF(!allowApplicationCache, !applicationCache);
 
   nsresult rv;
 
   if (mResuming) {
@@ -4046,24 +4009,37 @@ nsresult nsHttpChannel::OpenCacheEntryIn
 
   if (mPostID) {
     extension.Append(nsPrintfCString("%d", mPostID));
   }
   if (mTRR) {
     extension.Append("TRR");
   }
 
-  if (IsIsolated()) {
-    auto &topWindowOrigin = GetTopWindowOrigin();
-    if (topWindowOrigin.IsEmpty()) {
-      return NS_ERROR_FAILURE;
-    }
+  if (IsThirdPartyTrackingResource() &&
+      !AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(this, mURI,
+                                                               nullptr)) {
+    nsCOMPtr<nsIURI> topWindowURI;
+    rv = GetTopWindowURI(getter_AddRefs(topWindowURI));
+    bool isDocument = false;
+    if (NS_FAILED(rv) && NS_SUCCEEDED(GetIsMainDocumentChannel(&isDocument)) &&
+        isDocument) {
+      // For top-level documents, use the document channel's origin to compute
+      // the unique storage space identifier instead of the top Window URI.
+      rv = NS_GetFinalChannelURI(this, getter_AddRefs(topWindowURI));
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+
+    nsAutoString topWindowOrigin;
+    rv = nsContentUtils::GetUTFOrigin(topWindowURI ? topWindowURI : mURI,
+                                      topWindowOrigin);
+    NS_ENSURE_SUCCESS(rv, rv);
 
     extension.Append("-unique:");
-    extension.Append(topWindowOrigin);
+    extension.Append(NS_ConvertUTF16toUTF8(topWindowOrigin));
   }
 
   mCacheOpenWithPriority = cacheEntryOpenFlags & nsICacheStorage::OPEN_PRIORITY;
   mCacheQueueSizeWhenOpen =
       CacheStorageService::CacheQueueSize(mCacheOpenWithPriority);
 
   if (sRCWNEnabled && maybeRCWN && !mApplicationCacheForWrite) {
     bool hasAltData = false;
@@ -6560,19 +6536,19 @@ nsresult nsHttpChannel::BeginConnect() {
   mRequestHead.SetOrigin(scheme, host, port);
 
   SetOriginHeader();
   SetDoNotTrack();
 
   OriginAttributes originAttributes;
   NS_GetOriginAttributes(this, originAttributes);
 
-  RefPtr<nsHttpConnectionInfo> connInfo = new nsHttpConnectionInfo(
-      host, port, EmptyCString(), mUsername, GetTopWindowOrigin(), proxyInfo,
-      originAttributes, isHttps);
+  RefPtr<nsHttpConnectionInfo> connInfo =
+      new nsHttpConnectionInfo(host, port, EmptyCString(), mUsername, proxyInfo,
+                               originAttributes, isHttps);
   mAllowAltSvc = (mAllowAltSvc && !gHttpHandler->IsSpdyBlacklisted(connInfo));
 
   RefPtr<AltSvcMapping> mapping;
   if (!mConnectionInfo && mAllowAltSvc &&  // per channel
       !(mLoadFlags & LOAD_FRESH_CONNECTION) &&
       AltSvcMapping::AcceptableProxy(proxyInfo) &&
       (scheme.EqualsLiteral("http") || scheme.EqualsLiteral("https")) &&
       (mapping = gHttpHandler->GetAltServiceMapping(
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -541,20 +541,16 @@ class nsHttpChannel final : public HttpB
 
   void MaybeWarnAboutAppCache();
 
   void SetLoadGroupUserAgentOverride();
 
   void SetOriginHeader();
   void SetDoNotTrack();
 
-  bool IsIsolated();
-
-  const nsCString &GetTopWindowOrigin();
-
   already_AddRefed<nsChannelClassifier> GetOrCreateChannelClassifier();
 
   // Start an internal redirect to a new InterceptedHttpChannel which will
   // resolve in firing a ServiceWorker FetchEvent.
   MOZ_MUST_USE nsresult RedirectToInterceptedChannel();
 
   // Determines and sets content type in the cache entry. It's called when
   // writing a new entry. The content type is used in cache internally only.
@@ -717,31 +713,16 @@ class nsHttpChannel final : public HttpB
   // to the safe-browsing classifier rules, but the asynchronous cancellation
   // process hasn't finished yet.
   uint32_t mChannelClassifierCancellationPending : 1;
 
   // True only when we are between Resume and async fire of mCallOnResume.
   // Used to suspend any newly created pumps in mCallOnResume handler.
   uint32_t mAsyncResumePending : 1;
 
-  // True only when we have checked whether this channel has been isolated for
-  // anti-tracking purposes.
-  uint32_t mHasBeenIsolatedChecked : 1;
-  // True only when we have determined this channel should be isolated for
-  // anti-tracking purposes.  Can never ben true unless mHasBeenIsolatedChecked
-  // is true.
-  uint32_t mIsIsolated : 1;
-
-  // True only when we have computed the value of the top window origin.
-  uint32_t mTopWindowOriginComputed : 1;
-
-  // The origin of the top window, only valid when mTopWindowOriginComputed is
-  // true.
-  nsCString mTopWindowOrigin;
-
   nsTArray<nsContinueRedirectionFunc> mRedirectFuncStack;
 
   // Needed for accurate DNS timing
   RefPtr<nsDNSPrefetch> mDNSPrefetch;
 
   Http2PushedStream *mPushedStream;
   // True if the channel's principal was found on a phishing, malware, or
   // tracking (if tracking protection is enabled) blocklist
--- a/netwerk/protocol/http/nsHttpConnectionInfo.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionInfo.cpp
@@ -38,70 +38,48 @@ static nsresult SHA256(const char *aPlai
 }
 
 namespace mozilla {
 namespace net {
 
 nsHttpConnectionInfo::nsHttpConnectionInfo(
     const nsACString &originHost, int32_t originPort,
     const nsACString &npnToken, const nsACString &username,
-    const nsACString &topWindowOrigin, nsProxyInfo *proxyInfo,
-    const OriginAttributes &originAttributes, bool endToEndSSL, bool isolated)
-    : mRoutedPort(443), mIsolated(isolated), mLessThanTls13(false) {
-  Init(originHost, originPort, npnToken, username, topWindowOrigin, proxyInfo,
-       originAttributes, endToEndSSL);
+    nsProxyInfo *proxyInfo, const OriginAttributes &originAttributes,
+    bool endToEndSSL)
+    : mRoutedPort(443), mLessThanTls13(false) {
+  Init(originHost, originPort, npnToken, username, proxyInfo, originAttributes,
+       endToEndSSL);
 }
 
 nsHttpConnectionInfo::nsHttpConnectionInfo(
     const nsACString &originHost, int32_t originPort,
     const nsACString &npnToken, const nsACString &username,
-    const nsACString &topWindowOrigin, nsProxyInfo *proxyInfo,
-    const OriginAttributes &originAttributes, bool endToEndSSL)
-    : nsHttpConnectionInfo(originHost, originPort, npnToken, username,
-                           topWindowOrigin, proxyInfo, originAttributes,
-                           endToEndSSL, false) {}
-
-nsHttpConnectionInfo::nsHttpConnectionInfo(
-    const nsACString &originHost, int32_t originPort,
-    const nsACString &npnToken, const nsACString &username,
-    const nsACString &topWindowOrigin, nsProxyInfo *proxyInfo,
-    const OriginAttributes &originAttributes, const nsACString &routedHost,
-    int32_t routedPort, bool isolated)
-    : mIsolated(isolated), mLessThanTls13(false) {
+    nsProxyInfo *proxyInfo, const OriginAttributes &originAttributes,
+    const nsACString &routedHost, int32_t routedPort)
+    : mLessThanTls13(false) {
   mEndToEndSSL = true;  // so DefaultPort() works
   mRoutedPort = routedPort == -1 ? DefaultPort() : routedPort;
 
   if (!originHost.Equals(routedHost) || (originPort != routedPort)) {
     mRoutedHost = routedHost;
   }
-  Init(originHost, originPort, npnToken, username, topWindowOrigin, proxyInfo,
-       originAttributes, true);
+  Init(originHost, originPort, npnToken, username, proxyInfo, originAttributes,
+       true);
 }
 
-nsHttpConnectionInfo::nsHttpConnectionInfo(
-    const nsACString &originHost, int32_t originPort,
-    const nsACString &npnToken, const nsACString &username,
-    const nsACString &topWindowOrigin, nsProxyInfo *proxyInfo,
-    const OriginAttributes &originAttributes, const nsACString &routedHost,
-    int32_t routedPort)
-    : nsHttpConnectionInfo(originHost, originPort, npnToken, username,
-                           topWindowOrigin, proxyInfo, originAttributes,
-                           routedHost, routedPort, false) {}
-
 void nsHttpConnectionInfo::Init(const nsACString &host, int32_t port,
                                 const nsACString &npnToken,
                                 const nsACString &username,
-                                const nsACString &topWindowOrigin,
                                 nsProxyInfo *proxyInfo,
                                 const OriginAttributes &originAttributes,
                                 bool e2eSSL) {
   LOG(("Init nsHttpConnectionInfo @%p\n", this));
 
   mUsername = username;
-  mTopWindowOrigin = topWindowOrigin;
   mProxyInfo = proxyInfo;
   mEndToEndSSL = e2eSSL;
   mUsingConnect = false;
   mNPNToken = npnToken;
   mOriginAttributes = originAttributes;
   mTlsFlags = 0x0;
   mTrrUsed = false;
   mTrrDisabled = false;
@@ -148,22 +126,18 @@ void nsHttpConnectionInfo::BuildHashKey(
   // The hashkey has 4 fields followed by host connection info
   // byte 0 is P/T/. {P,T} for Plaintext/TLS Proxy over HTTP
   // byte 1 is S/. S is for end to end ssl such as https:// uris
   // byte 2 is A/. A is for an anonymous channel (no cookies, etc..)
   // byte 3 is P/. P is for a private browising channel
   // byte 4 is I/. I is for insecure scheme on TLS for http:// uris
   // byte 5 is X/. X is for disallow_spdy flag
   // byte 6 is C/. C is for be Conservative
-  // byte 7 is i/. i is for isolated
 
-  mHashKey.AssignLiteral("........[tlsflags0x00000000]");
-  if (mIsolated) {
-    mHashKey.SetCharAt('i', 7);
-  }
+  mHashKey.AssignLiteral(".......[tlsflags0x00000000]");
 
   mHashKey.Append(keyHost);
   mHashKey.Append(':');
   mHashKey.AppendInt(keyPort);
   if (!mUsername.IsEmpty()) {
     mHashKey.Append('[');
     mHashKey.Append(mUsername);
     mHashKey.Append(']');
@@ -239,47 +213,39 @@ void nsHttpConnectionInfo::BuildHashKey(
   if (GetIPv4Disabled()) {
     mHashKey.AppendLiteral("[!v4]");
   }
 
   if (GetIPv6Disabled()) {
     mHashKey.AppendLiteral("[!v6]");
   }
 
-  if (mIsolated && !mTopWindowOrigin.IsEmpty()) {
-    mHashKey.Append('{');
-    mHashKey.Append('{');
-    mHashKey.Append(mTopWindowOrigin);
-    mHashKey.Append('}');
-    mHashKey.Append('}');
-  }
-
   nsAutoCString originAttributes;
   mOriginAttributes.CreateSuffix(originAttributes);
   mHashKey.Append(originAttributes);
 }
 
 void nsHttpConnectionInfo::SetOriginServer(const nsACString &host,
                                            int32_t port) {
   mOrigin = host;
   mOriginPort = port == -1 ? DefaultPort() : port;
   BuildHashKey();
 }
 
 already_AddRefed<nsHttpConnectionInfo> nsHttpConnectionInfo::Clone() const {
   RefPtr<nsHttpConnectionInfo> clone;
   if (mRoutedHost.IsEmpty()) {
-    clone = new nsHttpConnectionInfo(
-        mOrigin, mOriginPort, mNPNToken, mUsername, mTopWindowOrigin,
-        mProxyInfo, mOriginAttributes, mEndToEndSSL, mIsolated);
+    clone =
+        new nsHttpConnectionInfo(mOrigin, mOriginPort, mNPNToken, mUsername,
+                                 mProxyInfo, mOriginAttributes, mEndToEndSSL);
   } else {
     MOZ_ASSERT(mEndToEndSSL);
-    clone = new nsHttpConnectionInfo(
-        mOrigin, mOriginPort, mNPNToken, mUsername, mTopWindowOrigin,
-        mProxyInfo, mOriginAttributes, mRoutedHost, mRoutedPort, mIsolated);
+    clone = new nsHttpConnectionInfo(mOrigin, mOriginPort, mNPNToken, mUsername,
+                                     mProxyInfo, mOriginAttributes, mRoutedHost,
+                                     mRoutedPort);
   }
 
   // Make sure the anonymous, insecure-scheme, and private flags are transferred
   clone->SetAnonymous(GetAnonymous());
   clone->SetPrivate(GetPrivate());
   clone->SetInsecureScheme(GetInsecureScheme());
   clone->SetNoSpdy(GetNoSpdy());
   clone->SetBeConservative(GetBeConservative());
@@ -295,19 +261,19 @@ already_AddRefed<nsHttpConnectionInfo> n
 
 void nsHttpConnectionInfo::CloneAsDirectRoute(nsHttpConnectionInfo **outCI) {
   if (mRoutedHost.IsEmpty()) {
     RefPtr<nsHttpConnectionInfo> clone = Clone();
     clone.forget(outCI);
     return;
   }
 
-  RefPtr<nsHttpConnectionInfo> clone = new nsHttpConnectionInfo(
-      mOrigin, mOriginPort, EmptyCString(), mUsername, mTopWindowOrigin,
-      mProxyInfo, mOriginAttributes, mEndToEndSSL, mIsolated);
+  RefPtr<nsHttpConnectionInfo> clone =
+      new nsHttpConnectionInfo(mOrigin, mOriginPort, EmptyCString(), mUsername,
+                               mProxyInfo, mOriginAttributes, mEndToEndSSL);
   // Make sure the anonymous, insecure-scheme, and private flags are transferred
   clone->SetAnonymous(GetAnonymous());
   clone->SetPrivate(GetPrivate());
   clone->SetInsecureScheme(GetInsecureScheme());
   clone->SetNoSpdy(GetNoSpdy());
   clone->SetBeConservative(GetBeConservative());
   clone->SetTlsFlags(GetTlsFlags());
   clone->SetTrrUsed(GetTrrUsed());
@@ -323,19 +289,19 @@ nsresult nsHttpConnectionInfo::CreateWil
   // TS??*:0 (https:proxy.ducksong.com:3128)   [wildcard form]
 
   if (!mUsingHttpsProxy) {
     MOZ_ASSERT(false);
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
   RefPtr<nsHttpConnectionInfo> clone;
-  clone = new nsHttpConnectionInfo(NS_LITERAL_CSTRING("*"), 0, mNPNToken,
-                                   mUsername, mTopWindowOrigin, mProxyInfo,
-                                   mOriginAttributes, true);
+  clone =
+      new nsHttpConnectionInfo(NS_LITERAL_CSTRING("*"), 0, mNPNToken, mUsername,
+                               mProxyInfo, mOriginAttributes, true);
   // Make sure the anonymous and private flags are transferred!
   clone->SetAnonymous(GetAnonymous());
   clone->SetPrivate(GetPrivate());
   clone.forget(outParam);
   return NS_OK;
 }
 
 void nsHttpConnectionInfo::SetTrrDisabled(bool aNoTrr) {
@@ -357,17 +323,17 @@ void nsHttpConnectionInfo::SetIPv6Disabl
     mIPv6Disabled = aNoIPv6;
     BuildHashKey();
   }
 }
 
 void nsHttpConnectionInfo::SetTlsFlags(uint32_t aTlsFlags) {
   mTlsFlags = aTlsFlags;
 
-  mHashKey.Replace(19, 8, nsPrintfCString("%08x", mTlsFlags));
+  mHashKey.Replace(18, 8, nsPrintfCString("%08x", mTlsFlags));
 }
 
 bool nsHttpConnectionInfo::UsingProxy() {
   if (!mProxyInfo) return false;
   return !mProxyInfo->IsDirect();
 }
 
 bool nsHttpConnectionInfo::HostIsLocalIPLiteral() const {
--- a/netwerk/protocol/http/nsHttpConnectionInfo.h
+++ b/netwerk/protocol/http/nsHttpConnectionInfo.h
@@ -33,27 +33,25 @@ namespace mozilla {
 namespace net {
 
 extern LazyLogModule gHttpLog;
 
 class nsHttpConnectionInfo final : public ARefBase {
  public:
   nsHttpConnectionInfo(const nsACString &originHost, int32_t originPort,
                        const nsACString &npnToken, const nsACString &username,
-                       const nsACString &topWindowOrigin,
                        nsProxyInfo *proxyInfo,
                        const OriginAttributes &originAttributes,
                        bool endToEndSSL = false);
 
   // this version must use TLS and you may supply separate
   // connection (aka routing) information than the authenticated
   // origin information
   nsHttpConnectionInfo(const nsACString &originHost, int32_t originPort,
                        const nsACString &npnToken, const nsACString &username,
-                       const nsACString &topWindowOrigin,
                        nsProxyInfo *proxyInfo,
                        const OriginAttributes &originAttributes,
                        const nsACString &routedHost, int32_t routedPort);
 
  private:
   virtual ~nsHttpConnectionInfo() {
     MOZ_LOG(gHttpLog, LogLevel::Debug,
             ("Destroying nsHttpConnectionInfo @%p\n", this));
@@ -119,22 +117,16 @@ class nsHttpConnectionInfo final : publi
   void SetNoSpdy(bool aNoSpdy) { mHashKey.SetCharAt(aNoSpdy ? 'X' : '.', 5); }
   bool GetNoSpdy() const { return mHashKey.CharAt(5) == 'X'; }
 
   void SetBeConservative(bool aBeConservative) {
     mHashKey.SetCharAt(aBeConservative ? 'C' : '.', 6);
   }
   bool GetBeConservative() const { return mHashKey.CharAt(6) == 'C'; }
 
-  void SetIsolated(bool aIsolated) {
-    mIsolated = aIsolated;
-    BuildHashKey();
-  }
-  bool GetIsolated() const { return mIsolated; }
-
   void SetTlsFlags(uint32_t aTlsFlags);
   uint32_t GetTlsFlags() const { return mTlsFlags; }
 
   // TrrUsed means that this connection is used to send TRR requests over
   void SetTrrUsed(bool aUsed) { mTrrUsed = aUsed; }
   bool GetTrrUsed() const { return mTrrUsed; }
 
   // SetTrrDisabled means don't use TRR to resolve host names for this
@@ -145,17 +137,16 @@ class nsHttpConnectionInfo final : publi
   void SetIPv4Disabled(bool aNoIPv4);
   bool GetIPv4Disabled() const { return mIPv4Disabled; }
 
   void SetIPv6Disabled(bool aNoIPv6);
   bool GetIPv6Disabled() const { return mIPv6Disabled; }
 
   const nsCString &GetNPNToken() { return mNPNToken; }
   const nsCString &GetUsername() { return mUsername; }
-  const nsCString &GetTopWindowOrigin() { return mTopWindowOrigin; }
 
   const OriginAttributes &GetOriginAttributes() { return mOriginAttributes; }
 
   // Returns true for any kind of proxy (http, socks, https, etc..)
   bool UsingProxy();
 
   // Returns true when proxying over HTTP or HTTPS
   bool UsingHttpProxy() const { return mUsingHttpProxy || mUsingHttpsProxy; }
@@ -178,55 +169,37 @@ class nsHttpConnectionInfo final : publi
   bool HostIsLocalIPLiteral() const;
 
   bool GetLessThanTls13() const { return mLessThanTls13; }
   void SetLessThanTls13(bool aLessThanTls13) {
     mLessThanTls13 = aLessThanTls13;
   }
 
  private:
-  // These constructor versions are intended to only be used from Clone().
-  nsHttpConnectionInfo(const nsACString &originHost, int32_t originPort,
-                       const nsACString &npnToken, const nsACString &username,
-                       const nsACString &topWindowOrigin,
-                       nsProxyInfo *proxyInfo,
-                       const OriginAttributes &originAttributes,
-                       bool endToEndSSL, bool isolated);
-  nsHttpConnectionInfo(const nsACString &originHost, int32_t originPort,
-                       const nsACString &npnToken, const nsACString &username,
-                       const nsACString &topWindowOrigin,
-                       nsProxyInfo *proxyInfo,
-                       const OriginAttributes &originAttributes,
-                       const nsACString &routedHost, int32_t routedPort,
-                       bool isolated);
-
   void Init(const nsACString &host, int32_t port, const nsACString &npnToken,
-            const nsACString &username, const nsACString &topWindowOrigin,
-            nsProxyInfo *proxyInfo, const OriginAttributes &originAttributes,
-            bool EndToEndSSL);
+            const nsACString &username, nsProxyInfo *proxyInfo,
+            const OriginAttributes &originAttributes, bool EndToEndSSL);
   void SetOriginServer(const nsACString &host, int32_t port);
 
   nsCString mOrigin;
   int32_t mOriginPort;
   nsCString mRoutedHost;
   int32_t mRoutedPort;
 
   nsCString mHashKey;
   nsCString mUsername;
-  nsCString mTopWindowOrigin;
   nsCOMPtr<nsProxyInfo> mProxyInfo;
   bool mUsingHttpProxy;
   bool mUsingHttpsProxy;
   bool mEndToEndSSL;
   bool mUsingConnect;  // if will use CONNECT with http proxy
   nsCString mNPNToken;
   OriginAttributes mOriginAttributes;
 
   uint32_t mTlsFlags;
-  uint16_t mIsolated : 1;
   uint16_t mTrrUsed : 1;
   uint16_t mTrrDisabled : 1;
   uint16_t mIPv4Disabled : 1;
   uint16_t mIPv6Disabled : 1;
 
   bool mLessThanTls13;  // This will be set to true if we negotiate less than
                         // tls1.3. If the tls version is till not know or it
                         // is 1.3 or greater the value will be false.
--- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
@@ -4020,19 +4020,17 @@ nsresult nsHttpConnectionMgr::nsHalfOpen
 
   if (mCaps & NS_HTTP_DISABLE_TRR) {
     tmpFlags = nsISocketTransport::DISABLE_TRR;
   }
 
   if (mCaps & NS_HTTP_LOAD_ANONYMOUS)
     tmpFlags |= nsISocketTransport::ANONYMOUS_CONNECT;
 
-  if (ci->GetPrivate() || ci->GetIsolated()) {
-    tmpFlags |= nsISocketTransport::NO_PERMANENT_STORAGE;
-  }
+  if (ci->GetPrivate()) tmpFlags |= nsISocketTransport::NO_PERMANENT_STORAGE;
 
   if (ci->GetLessThanTls13()) {
     tmpFlags |= nsISocketTransport::DONT_TRY_ESNI;
   }
 
   if ((mCaps & NS_HTTP_BE_CONSERVATIVE) || ci->GetBeConservative()) {
     LOG(("Setting Socket to BE_CONSERVATIVE"));
     tmpFlags |= nsISocketTransport::BE_CONSERVATIVE;
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -2491,23 +2491,19 @@ nsresult nsHttpHandler::SpeculativeConne
 
   int32_t port = -1;
   rv = aURI->GetPort(&port);
   if (NS_FAILED(rv)) return rv;
 
   nsAutoCString username;
   aURI->GetUsername(username);
 
-  // TODO For now pass EmptyCString() for topWindowOrigin for all speculative
-  // connection attempts, but ideally we should pass the accurate top window
-  // origin here.  This would require updating the nsISpeculativeConnect API
-  // and all of its consumers.
-  RefPtr<nsHttpConnectionInfo> ci = new nsHttpConnectionInfo(
-      host, port, EmptyCString(), username, EmptyCString(), nullptr,
-      originAttributes, usingSSL);
+  RefPtr<nsHttpConnectionInfo> ci =
+      new nsHttpConnectionInfo(host, port, EmptyCString(), username, nullptr,
+                               originAttributes, usingSSL);
   ci->SetAnonymous(anonymous);
 
   return SpeculativeConnect(ci, aCallbacks);
 }
 
 NS_IMETHODIMP
 nsHttpHandler::SpeculativeConnect(nsIURI *aURI, nsIPrincipal *aPrincipal,
                                   nsIInterfaceRequestor *aCallbacks) {
--- a/toolkit/components/antitracking/test/browser/browser_tlsSessionTickets.js
+++ b/toolkit/components/antitracking/test/browser/browser_tlsSessionTickets.js
@@ -1,22 +1,8 @@
-function isIsolated(key) {
-  return key.charAt(7) == "i";
-}
-
-function hasTopWindowOrigin(key, origin) {
-  let tokenAtEnd = `{{${origin}}}`;
-  let endPart = key.slice(-tokenAtEnd.length);
-  return endPart == tokenAtEnd;
-}
-
-function hasAnyTopWindowOrigin(key) {
-  return !!key.match(/{{[^}]+}}/);
-}
-
 add_task(async function() {
   info("Starting tlsSessionTickets test");
 
   await SpecialPowers.flushPrefEnv();
   await SpecialPowers.pushPrefEnv({"set": [
     ["browser.cache.disk.enable", false],
     ["browser.cache.memory.enable", false],
     ["browser.contentblocking.allowlist.annotations.enabled", true],
@@ -91,26 +77,16 @@ add_task(async function() {
     // second and third connections are the same.  The reason why this check is
     // done is that the private bit on the connection info object is used to
     // construct the hash key, so when the connection is isolated because it
     // comes from a third-party tracker context, its hash key must be
     // different.
     is(hashKeys.length, 3, "We should have observed 3 loads for " + trackingURL);
     is(hashKeys[1], hashKeys[2], "The second and third hash keys should match");
     isnot(hashKeys[0], hashKeys[1], "The first and second hash keys should not match");
-
-    ok(isIsolated(hashKeys[0]), "The first connection must have been isolated");
-    ok(!isIsolated(hashKeys[1]), "The second connection must not have been isolated");
-    ok(!isIsolated(hashKeys[2]), "The third connection must not have been isolated");
-    ok(hasTopWindowOrigin(hashKeys[0], TEST_DOMAIN.replace(/\/$/, "")),
-       "The first connection must be bound to its top-level window");
-    ok(!hasAnyTopWindowOrigin(hashKeys[1]),
-       "The second connection must not be bound to a top-level window");
-    ok(!hasAnyTopWindowOrigin(hashKeys[2]),
-       "The third connection must not be bound to a top-level window");
   });
 
   info("Removing the tab");
   BrowserTestUtils.removeTab(tab);
 });
 
 add_task(async function() {
   info("Cleaning up.");
--- a/tools/profiler/public/GeckoProfiler.h
+++ b/tools/profiler/public/GeckoProfiler.h
@@ -560,17 +560,17 @@ mozilla::Maybe<ProfilerBufferInfo> profi
 //
 // Note: The use of the Maybe<>s ensures the scopes for the dynamic string and
 // the AutoProfilerLabel are appropriate, while also not incurring the runtime
 // cost of the string assignment unless the profiler is active. Therefore,
 // unlike AUTO_PROFILER_LABEL and AUTO_PROFILER_LABEL_DYNAMIC_CSTR, this macro
 // doesn't push/pop a label when the profiler is inactive.
 #  define AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING(label, categoryPair, nsCStr) \
     mozilla::Maybe<nsAutoCString> autoCStr;                                  \
-    mozilla::Maybe<AutoProfilerLabel> raiiObjectNsCString;                   \
+    mozilla::Maybe<mozilla::AutoProfilerLabel> raiiObjectNsCString;          \
     if (profiler_is_active()) {                                              \
       autoCStr.emplace(nsCStr);                                              \
       raiiObjectNsCString.emplace(label, autoCStr->get(),                    \
                                   JS::ProfilingCategoryPair::categoryPair);  \
     }
 
 // Similar to AUTO_PROFILER_LABEL_DYNAMIC_CSTR, but takes an nsString that is
 // is lossily converted to an ASCII string.
@@ -578,17 +578,17 @@ mozilla::Maybe<ProfilerBufferInfo> profi
 // Note: The use of the Maybe<>s ensures the scopes for the converted dynamic
 // string and the AutoProfilerLabel are appropriate, while also not incurring
 // the runtime cost of the string conversion unless the profiler is active.
 // Therefore, unlike AUTO_PROFILER_LABEL and AUTO_PROFILER_LABEL_DYNAMIC_CSTR,
 // this macro doesn't push/pop a label when the profiler is inactive.
 #  define AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(label, categoryPair,   \
                                                      nsStr)                 \
     mozilla::Maybe<NS_LossyConvertUTF16toASCII> asciiStr;                   \
-    mozilla::Maybe<AutoProfilerLabel> raiiObjectLossyNsString;              \
+    mozilla::Maybe<mozilla::AutoProfilerLabel> raiiObjectLossyNsString;     \
     if (profiler_is_active()) {                                             \
       asciiStr.emplace(nsStr);                                              \
       raiiObjectLossyNsString.emplace(                                      \
           label, asciiStr->get(), JS::ProfilingCategoryPair::categoryPair); \
     }
 
 // Similar to AUTO_PROFILER_LABEL, but accepting a JSContext* parameter, and a
 // no-op if the profiler is disabled.
@@ -649,31 +649,31 @@ void profiler_add_network_marker(
 
 enum TracingKind {
   TRACING_EVENT,
   TRACING_INTERVAL_START,
   TRACING_INTERVAL_END,
 };
 
 // Helper macro to retrieve DocShellId and DocShellHistoryId from docShell
-#  define DECLARE_DOCSHELL_AND_HISTORY_ID(docShell) \
-    mozilla::Maybe<nsID> docShellId;                \
-    mozilla::Maybe<uint32_t> docShellHistoryId;     \
-    if (docShell) {                                 \
-      docShellId = Some(docShell->HistoryID());     \
-      uint32_t id;                                  \
-      nsresult rv = docShell->GetOSHEId(&id);       \
-      if (NS_SUCCEEDED(rv)) {                       \
-        docShellHistoryId = Some(id);               \
-      } else {                                      \
-        docShellHistoryId = Nothing();              \
-      }                                             \
-    } else {                                        \
-      docShellId = Nothing();                       \
-      docShellHistoryId = Nothing();                \
+#  define DECLARE_DOCSHELL_AND_HISTORY_ID(docShell)      \
+    mozilla::Maybe<nsID> docShellId;                     \
+    mozilla::Maybe<uint32_t> docShellHistoryId;          \
+    if (docShell) {                                      \
+      docShellId = mozilla::Some(docShell->HistoryID()); \
+      uint32_t id;                                       \
+      nsresult rv = docShell->GetOSHEId(&id);            \
+      if (NS_SUCCEEDED(rv)) {                            \
+        docShellHistoryId = mozilla::Some(id);           \
+      } else {                                           \
+        docShellHistoryId = mozilla::Nothing();          \
+      }                                                  \
+    } else {                                             \
+      docShellId = mozilla::Nothing();                   \
+      docShellHistoryId = mozilla::Nothing();            \
     }
 
 // Adds a tracing marker to the profile. A no-op if the profiler is inactive or
 // in privacy mode.
 
 #  define PROFILER_TRACING(categoryString, markerName, categoryPair, kind) \
     profiler_tracing(categoryString, markerName,                           \
                      JS::ProfilingCategoryPair::categoryPair, kind)
@@ -752,21 +752,21 @@ class MOZ_RAII AutoProfilerTextMarker {
   nsCString mText;
   const JS::ProfilingCategoryPair mCategoryPair;
   mozilla::TimeStamp mStartTime;
   UniqueProfilerBacktrace mCause;
   const mozilla::Maybe<nsID> mDocShellId;
   const mozilla::Maybe<uint32_t> mDocShellHistoryId;
 };
 
-#  define AUTO_PROFILER_TEXT_MARKER_CAUSE(markerName, text, categoryPair,     \
-                                          cause)                              \
-    AutoProfilerTextMarker PROFILER_RAII(                                     \
-        markerName, text, JS::ProfilingCategoryPair::categoryPair, Nothing(), \
-        Nothing(), cause)
+#  define AUTO_PROFILER_TEXT_MARKER_CAUSE(markerName, text, categoryPair, \
+                                          cause)                          \
+    AutoProfilerTextMarker PROFILER_RAII(                                 \
+        markerName, text, JS::ProfilingCategoryPair::categoryPair,        \
+        mozilla::Nothing(), mozilla::Nothing(), cause)
 
 #  define AUTO_PROFILER_TEXT_MARKER_DOCSHELL(markerName, text, categoryPair,   \
                                              docShell)                         \
     DECLARE_DOCSHELL_AND_HISTORY_ID(docShell);                                 \
     AutoProfilerTextMarker PROFILER_RAII(                                      \
         markerName, text, JS::ProfilingCategoryPair::categoryPair, docShellId, \
         docShellHistoryId)