Bug 1525134 - Move image loads out of the style struct accessors. r=heycam
authorEmilio Cobos Álvarez <emilio@crisal.io>
Tue, 05 Feb 2019 19:47:29 +0000
changeset 515191 6b71e3d434c232998629b8e18a983212e9d3fc9d
parent 515190 d52e6ae0212c784f23aa473380f1a52fd72c22f4
child 515192 d5758ae506b2ff287b7dfec79fd73eb174e43bce
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheycam
bugs1525134
milestone67.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
Bug 1525134 - Move image loads out of the style struct accessors. r=heycam After this I can pass the document from the caller to ResolveSameStructsAs, and get rid of the pres context pointer. Differential Revision: https://phabricator.services.mozilla.com/D18600
dom/animation/KeyframeEffect.cpp
layout/base/RestyleManager.cpp
layout/base/nsCSSFrameConstructor.cpp
layout/generic/ViewportFrame.cpp
layout/generic/nsBulletFrame.cpp
layout/generic/nsFrame.cpp
layout/style/ComputedStyle.cpp
layout/style/ComputedStyle.h
layout/style/ComputedStyleInlines.h
layout/style/GeckoBindings.cpp
layout/style/nsStyleStruct.h
layout/style/nsStyleStructInlines.h
layout/svg/SVGGeometryFrame.cpp
layout/tables/nsTableFrame.cpp
layout/xul/nsTextBoxFrame.cpp
layout/xul/tree/nsTreeStyleCache.cpp
testing/web-platform/tests/css/css-lists/inheritance.html
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -1532,17 +1532,17 @@ void KeyframeEffect::CalculateCumulative
           property.mProperty, segment.mToValue, presContext, aComputedStyle);
       if (!toContext) {
         mCumulativeChangeHint = ~nsChangeHint_Hints_CanIgnoreIfNotVisible;
         return;
       }
 
       uint32_t equalStructs = 0;
       nsChangeHint changeHint =
-          fromContext->CalcStyleDifference(toContext, &equalStructs);
+          fromContext->CalcStyleDifference(*toContext, &equalStructs);
 
       mCumulativeChangeHint |= changeHint;
     }
   }
 }
 
 void KeyframeEffect::SetAnimation(Animation* aAnimation) {
   if (mAnimation == aAnimation) {
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -49,16 +49,17 @@
 #include "ActiveLayerTracker.h"
 #include "nsSVGIntegrationUtils.h"
 
 #ifdef ACCESSIBILITY
 #  include "nsAccessibilityService.h"
 #endif
 
 using mozilla::layers::AnimationInfo;
+using mozilla::layout::ScrollAnchorContainer;
 
 using namespace mozilla::dom;
 using namespace mozilla::layers;
 
 namespace mozilla {
 
 RestyleManager::RestyleManager(nsPresContext* aPresContext)
     : mPresContext(aPresContext),
@@ -2434,17 +2435,17 @@ struct RestyleManager::TextPostTraversal
     // We rely on the fact that all the text children for the same element share
     // style to avoid recomputing style differences for all of them.
     //
     // TODO(emilio): The above may not be true for ::first-{line,letter}, but
     // we'll cross that bridge when we support those in stylo.
     if (mShouldComputeHints) {
       mShouldComputeHints = false;
       uint32_t equalStructs;
-      mComputedHint = oldStyle->CalcStyleDifference(&aNewStyle, &equalStructs);
+      mComputedHint = oldStyle->CalcStyleDifference(aNewStyle, &equalStructs);
       mComputedHint = NS_RemoveSubsumedHints(
           mComputedHint, mParentRestyleState.ChangesHandledFor(aTextFrame));
     }
 
     if (mComputedHint) {
       mParentRestyleState.ChangeList().AppendChange(aTextFrame, aContent,
                                                     mComputedHint);
     }
@@ -2540,17 +2541,17 @@ static void UpdateOneAdditionalComputedS
 
   RefPtr<ComputedStyle> newStyle =
       aRestyleState.StyleSet().ResolvePseudoElementStyle(
           aFrame->GetContent()->AsElement(), pseudoType, aFrame->Style(),
           /* aPseudoElement = */ nullptr);
 
   uint32_t equalStructs;  // Not used, actually.
   nsChangeHint childHint =
-      aOldContext.CalcStyleDifference(newStyle, &equalStructs);
+      aOldContext.CalcStyleDifference(*newStyle, &equalStructs);
   if (!aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
     childHint = NS_RemoveSubsumedHints(childHint,
                                        aRestyleState.ChangesHandledFor(aFrame));
   }
 
   if (childHint) {
     if (childHint & nsChangeHint_ReconstructFrame) {
       // If we generate a reconstruct here, remove any non-reconstruct hints we
@@ -2791,17 +2792,18 @@ bool RestyleManager::ProcessPostTraversa
 
   if (wasRestyled && oldOrDisplayContentsStyle) {
     MOZ_ASSERT(styleFrame || isDisplayContents);
 
     // Note that upToDateContext could be the same as oldOrDisplayContentsStyle,
     // but it doesn't matter, since the only point of it is calling
     // TriggerImageLoads on the relevant structs, and those don't matter for
     // display: contents.
-    upToDateContext->ResolveSameStructsAs(oldOrDisplayContentsStyle);
+    upToDateContext->StartImageLoads(*mPresContext->Document(),
+                                     oldOrDisplayContentsStyle);
 
     // We want to walk all the continuations here, even the ones with different
     // styles.  In practice, the only reason we get continuations with different
     // styles here is ::first-line (::first-letter never affects element
     // styles).  But in that case, newStyle is the right context for the
     // _later_ continuations anyway (the ones not affected by ::first-line), not
     // the earlier ones, so there is no point stopping right at the point when
     // we'd actually be setting the right ComputedStyle.
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -2331,19 +2331,16 @@ nsIFrame* nsCSSFrameConstructor::Constru
                    state.mPresContext->IsPaginated() ||
                    propagatedScrollFrom == aDocElement,
                "Scrollbars should have been propagated to the viewport");
 
   if (MOZ_UNLIKELY(display->mDisplay == StyleDisplay::None)) {
     return nullptr;
   }
 
-  // Make sure to start any background image loads for the root element now.
-  computedStyle->StartBackgroundImageLoads();
-
   nsFrameConstructorSaveState docElementContainingBlockAbsoluteSaveState;
   if (mHasRootAbsPosContainingBlock) {
     // Push the absolute containing block now so we can absolutely position
     // the root element
     mDocElementContainingBlock->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
     state.PushAbsoluteContainingBlock(
         mDocElementContainingBlock, mDocElementContainingBlock,
         docElementContainingBlockAbsoluteSaveState);
@@ -5645,20 +5642,16 @@ void nsCSSFrameConstructor::ConstructFra
         !mAlwaysCreateFramesForIgnorableWhitespace && item.IsWhitespace(aState))
       return;
 
     ConstructTextFrame(item.mFCData, aState, item.mContent, adjParentFrame,
                        computedStyle, aFrameItems);
     return;
   }
 
-  // Start background loads during frame construction so that we're
-  // guaranteed that they will be started before onload fires.
-  computedStyle->StartBackgroundImageLoads();
-
   AutoRestore<nsFrameState> savedStateBits(aState.mAdditionalStateBits);
   if (item.mIsGeneratedContent) {
     // Ensure that frames created here are all tagged with
     // NS_FRAME_GENERATED_CONTENT.
     aState.mAdditionalStateBits |= NS_FRAME_GENERATED_CONTENT;
   }
 
   // XXXbz maybe just inline ConstructFrameFromItemInternal here or something?
--- a/layout/generic/ViewportFrame.cpp
+++ b/layout/generic/ViewportFrame.cpp
@@ -377,22 +377,16 @@ void ViewportFrame::Reflow(nsPresContext
 }
 
 void ViewportFrame::UpdateStyle(ServoRestyleState& aRestyleState) {
   nsAtom* pseudo = Style()->GetPseudo();
   RefPtr<ComputedStyle> newStyle =
       aRestyleState.StyleSet().ResolveInheritingAnonymousBoxStyle(pseudo,
                                                                   nullptr);
 
-  // We're special because we have a null GetContent(), so don't call things
-  // like UpdateStyleOfOwnedChildFrame that try to append changes for the
-  // content to the change list.  Nor do we computed a changehint, since we have
-  // no way to apply it anyway.
-  newStyle->ResolveSameStructsAs(Style());
-
   MOZ_ASSERT(!GetNextContinuation(), "Viewport has continuations?");
   SetComputedStyle(newStyle);
 
   UpdateStyleOfOwnedAnonBoxes(aRestyleState);
 }
 
 void ViewportFrame::AppendDirectlyOwnedAnonBoxes(
     nsTArray<OwnedAnonBox>& aResult) {
--- a/layout/generic/nsBulletFrame.cpp
+++ b/layout/generic/nsBulletFrame.cpp
@@ -145,29 +145,27 @@ bool nsBulletFrame::IsSelfEmpty() {
   }
 
 #ifdef ACCESSIBILITY
   // Update the list bullet accessible. If old style list isn't available then
   // no need to update the accessible tree because it's not created yet.
   if (aOldComputedStyle) {
     nsAccessibilityService* accService = nsIPresShell::AccService();
     if (accService) {
-      const nsStyleList* oldStyleList = aOldComputedStyle->PeekStyleList();
-      if (oldStyleList) {
-        bool hadBullet = oldStyleList->GetListStyleImage() ||
-                         !oldStyleList->mCounterStyle.IsNone();
+      const nsStyleList* oldStyleList = aOldComputedStyle->StyleList();
+      bool hadBullet = oldStyleList->GetListStyleImage() ||
+                       !oldStyleList->mCounterStyle.IsNone();
 
-        const nsStyleList* newStyleList = StyleList();
-        bool hasBullet = newStyleList->GetListStyleImage() ||
-                         !newStyleList->mCounterStyle.IsNone();
+      const nsStyleList* newStyleList = StyleList();
+      bool hasBullet = newStyleList->GetListStyleImage() ||
+                       !newStyleList->mCounterStyle.IsNone();
 
-        if (hadBullet != hasBullet) {
-          accService->UpdateListBullet(PresContext()->GetPresShell(), mContent,
-                                       hasBullet);
-        }
+      if (hadBullet != hasBullet) {
+        accService->UpdateListBullet(PresContext()->GetPresShell(), mContent,
+                                     hasBullet);
       }
     }
   }
 #endif
 }
 
 class nsDisplayBulletGeometry
     : public nsDisplayItemGenericGeometry,
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -584,16 +584,17 @@ void nsFrame::Init(nsIContent* aContent,
     nsFrameState state = aPrevInFlow->GetStateBits();
 
     // Make bits that are currently off (see constructor) the same:
     AddStateBits(state &
                  (NS_FRAME_INDEPENDENT_SELECTION | NS_FRAME_PART_OF_IBSPLIT |
                   NS_FRAME_MAY_BE_TRANSFORMED |
                   NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN));
   } else {
+    mComputedStyle->StartImageLoads(*PresContext()->Document());
     PresContext()->ConstructedFrame();
   }
   if (GetParent()) {
     nsFrameState state = GetParent()->GetStateBits();
 
     // Make bits that are currently off (see constructor) the same:
     AddStateBits(state & (NS_FRAME_INDEPENDENT_SELECTION |
                           NS_FRAME_GENERATED_CONTENT | NS_FRAME_IS_SVG_TEXT |
@@ -1061,73 +1062,68 @@ void nsIFrame::MarkNeedsDisplayItemRebui
 
     // If we detect a change on margin, padding or border, we store the old
     // values on the frame itself between now and reflow, so if someone
     // calls GetUsed(Margin|Border|Padding)() before the next reflow, we
     // can give an accurate answer.
     // We don't want to set the property if one already exists.
     nsMargin oldValue(0, 0, 0, 0);
     nsMargin newValue(0, 0, 0, 0);
-    const nsStyleMargin* oldMargin = aOldComputedStyle->PeekStyleMargin();
-    if (oldMargin && oldMargin->GetMargin(oldValue)) {
+    const nsStyleMargin* oldMargin = aOldComputedStyle->StyleMargin();
+    if (oldMargin->GetMargin(oldValue)) {
       if (!StyleMargin()->GetMargin(newValue) || oldValue != newValue) {
         if (!HasProperty(UsedMarginProperty())) {
           AddProperty(UsedMarginProperty(), new nsMargin(oldValue));
         }
         needAnchorSuppression = true;
       }
     }
 
-    const nsStylePadding* oldPadding = aOldComputedStyle->PeekStylePadding();
-    if (oldPadding && oldPadding->GetPadding(oldValue)) {
+    const nsStylePadding* oldPadding = aOldComputedStyle->StylePadding();
+    if (oldPadding->GetPadding(oldValue)) {
       if (!StylePadding()->GetPadding(newValue) || oldValue != newValue) {
         if (!HasProperty(UsedPaddingProperty())) {
           AddProperty(UsedPaddingProperty(), new nsMargin(oldValue));
         }
         needAnchorSuppression = true;
       }
     }
 
-    const nsStyleBorder* oldBorder = aOldComputedStyle->PeekStyleBorder();
-    if (oldBorder) {
-      oldValue = oldBorder->GetComputedBorder();
-      newValue = StyleBorder()->GetComputedBorder();
-      if (oldValue != newValue && !HasProperty(UsedBorderProperty())) {
-        AddProperty(UsedBorderProperty(), new nsMargin(oldValue));
-      }
-    }
-
-    const nsStyleDisplay* oldDisp = aOldComputedStyle->PeekStyleDisplay();
-    if (oldDisp &&
-        (oldDisp->mOverflowAnchor != StyleDisplay()->mOverflowAnchor)) {
-      if (ScrollAnchorContainer* container =
-              ScrollAnchorContainer::FindFor(this)) {
+    const nsStyleBorder* oldBorder = aOldComputedStyle->StyleBorder();
+    oldValue = oldBorder->GetComputedBorder();
+    newValue = StyleBorder()->GetComputedBorder();
+    if (oldValue != newValue && !HasProperty(UsedBorderProperty())) {
+      AddProperty(UsedBorderProperty(), new nsMargin(oldValue));
+    }
+
+    const nsStyleDisplay* oldDisp = aOldComputedStyle->StyleDisplay();
+    if (oldDisp->mOverflowAnchor != StyleDisplay()->mOverflowAnchor) {
+      if (auto* container = ScrollAnchorContainer::FindFor(this)) {
         container->InvalidateAnchor();
       }
       if (nsIScrollableFrame* scrollableFrame = do_QueryFrame(this)) {
         scrollableFrame->GetAnchor()->InvalidateAnchor();
       }
     }
 
     if (mInScrollAnchorChain) {
       const nsStylePosition* oldPosition =
-          aOldComputedStyle->PeekStylePosition();
-      if (oldPosition &&
-          (oldPosition->mOffset != StylePosition()->mOffset ||
-           oldPosition->mWidth != StylePosition()->mWidth ||
-           oldPosition->mMinWidth != StylePosition()->mMinWidth ||
-           oldPosition->mMaxWidth != StylePosition()->mMaxWidth ||
-           oldPosition->mHeight != StylePosition()->mHeight ||
-           oldPosition->mMinHeight != StylePosition()->mMinHeight ||
-           oldPosition->mMaxHeight != StylePosition()->mMaxHeight)) {
+          aOldComputedStyle->StylePosition();
+      if (oldPosition->mOffset != StylePosition()->mOffset ||
+          oldPosition->mWidth != StylePosition()->mWidth ||
+          oldPosition->mMinWidth != StylePosition()->mMinWidth ||
+          oldPosition->mMaxWidth != StylePosition()->mMaxWidth ||
+          oldPosition->mHeight != StylePosition()->mHeight ||
+          oldPosition->mMinHeight != StylePosition()->mMinHeight ||
+          oldPosition->mMaxHeight != StylePosition()->mMaxHeight) {
         needAnchorSuppression = true;
       }
 
-      if (oldDisp && (oldDisp->mPosition != StyleDisplay()->mPosition ||
-                      oldDisp->TransformChanged(*StyleDisplay()))) {
+      if (oldDisp->mPosition != StyleDisplay()->mPosition ||
+          oldDisp->TransformChanged(*StyleDisplay())) {
         needAnchorSuppression = true;
       }
     }
 
     if (mInScrollAnchorChain && needAnchorSuppression) {
       ScrollAnchorContainer::FindFor(this)->SuppressAdjustments();
     }
   }
@@ -10243,24 +10239,17 @@ void nsIFrame::UpdateStyleOfChildAnonBox
   //    us, it's possible that no one ever asked us for those style structs and
   //    hence changes to them aren't reflected in the changes handled at all.
   //
   // 2) Content can change stylesheets that change the styles of pseudos, and
   //    extensions can add/remove stylesheets that change the styles of
   //    anonymous boxes directly.
   uint32_t equalStructs;  // Not used, actually.
   nsChangeHint childHint = aChildFrame->Style()->CalcStyleDifference(
-      aNewComputedStyle, &equalStructs);
-
-  // CalcStyleDifference will handle caching structs on the new style, but only
-  // if we're not on a style worker thread.
-  MOZ_ASSERT(!ServoStyleSet::IsInServoTraversal(),
-             "if we can get in here from style worker threads, then we need "
-             "a ResolveSameStructsAs call to ensure structs are cached on "
-             "aNewComputedStyle");
+      *aNewComputedStyle, &equalStructs);
 
   // If aChildFrame is out of flow, then aRestyleState's "changes handled by the
   // parent" doesn't apply to it, because it may have some other parent in the
   // frame tree.
   if (!aChildFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
     childHint = NS_RemoveSubsumedHints(
         childHint, aRestyleState.ChangesHandledFor(aChildFrame));
   }
--- a/layout/style/ComputedStyle.cpp
+++ b/layout/style/ComputedStyle.cpp
@@ -49,19 +49,18 @@ ComputedStyle::ComputedStyle(nsPresConte
     : mPresContext(aPresContext),
       mSource(aComputedValues),
       mPseudoTag(aPseudoTag),
       mBits(static_cast<Bit>(Servo_ComputedValues_GetStyleBits(this))),
       mPseudoType(aPseudoType) {
   MOZ_ASSERT(ComputedData());
 }
 
-nsChangeHint ComputedStyle::CalcStyleDifference(ComputedStyle* aNewContext,
-                                                uint32_t* aEqualStructs) {
-  MOZ_ASSERT(aNewContext);
+nsChangeHint ComputedStyle::CalcStyleDifference(const ComputedStyle& aNewStyle,
+                                                uint32_t* aEqualStructs) const {
   AUTO_PROFILER_LABEL("ComputedStyle::CalcStyleDifference", LAYOUT);
   static_assert(StyleStructConstants::kStyleStructCount <= 32,
                 "aEqualStructs is not big enough");
 
   *aEqualStructs = 0;
 
   nsChangeHint hint = nsChangeHint(0);
   // We must always ensure that we populate the structs on the new style
@@ -86,40 +85,35 @@ nsChangeHint ComputedStyle::CalcStyleDif
   // FIXME(emilio): Reintroduce that optimization either for all kind of structs
   // after bug 1368290 with a weak parent pointer from text, or just for reset
   // structs.
 #define STYLE_STRUCT_BIT(name_) \
   StyleStructConstants::BitFor(StyleStructID::name_)
 #define PEEK(struct_) ComputedData()->GetStyle##struct_()
 
 #define EXPAND(...) __VA_ARGS__
-#define DO_STRUCT_DIFFERENCE_WITH_ARGS(struct_, extra_args_)                 \
-  PR_BEGIN_MACRO                                                             \
-  const nsStyle##struct_* this##struct_ = PEEK(struct_);                     \
-  if (this##struct_) {                                                       \
-    structsFound |= STYLE_STRUCT_BIT(struct_);                               \
-                                                                             \
-    const nsStyle##struct_* other##struct_ =                                 \
-        aNewContext->ThreadsafeStyle##struct_();                             \
-    if (this##struct_ == other##struct_) {                                   \
-      /* The very same struct, so we know that there will be no */           \
-      /* differences.                                           */           \
-      *aEqualStructs |= STYLE_STRUCT_BIT(struct_);                           \
-    } else {                                                                 \
-      nsChangeHint difference =                                              \
-          this##struct_->CalcDifference(*other##struct_ EXPAND extra_args_); \
-      hint |= difference;                                                    \
-      if (!difference) {                                                     \
-        *aEqualStructs |= STYLE_STRUCT_BIT(struct_);                         \
-      }                                                                      \
-    }                                                                        \
-  } else {                                                                   \
-    *aEqualStructs |= STYLE_STRUCT_BIT(struct_);                             \
-  }                                                                          \
-  styleStructCount++;                                                        \
+#define DO_STRUCT_DIFFERENCE_WITH_ARGS(struct_, extra_args_)               \
+  PR_BEGIN_MACRO                                                           \
+  const nsStyle##struct_* this##struct_ = Style##struct_();                \
+  structsFound |= STYLE_STRUCT_BIT(struct_);                               \
+                                                                           \
+  const nsStyle##struct_* other##struct_ = aNewStyle.Style##struct_();     \
+  if (this##struct_ == other##struct_) {                                   \
+    /* The very same struct, so we know that there will be no */           \
+    /* differences.                                           */           \
+    *aEqualStructs |= STYLE_STRUCT_BIT(struct_);                           \
+  } else {                                                                 \
+    nsChangeHint difference =                                              \
+        this##struct_->CalcDifference(*other##struct_ EXPAND extra_args_); \
+    hint |= difference;                                                    \
+    if (!difference) {                                                     \
+      *aEqualStructs |= STYLE_STRUCT_BIT(struct_);                         \
+    }                                                                      \
+  }                                                                        \
+  styleStructCount++;                                                      \
   PR_END_MACRO
 #define DO_STRUCT_DIFFERENCE(struct_) \
   DO_STRUCT_DIFFERENCE_WITH_ARGS(struct_, ())
 
   // FIXME: The order of these DO_STRUCT_DIFFERENCE calls is no longer
   // significant.  With a small amount of effort, we could replace them with a
   // #include "nsStyleStructList.h".
   DO_STRUCT_DIFFERENCE(Display);
@@ -174,18 +168,18 @@ nsChangeHint ComputedStyle::CalcStyleDif
   // the page to measure.
   // However, we do need to compute the larger of the changes that can
   // happen depending on whether the link is visited or unvisited, since
   // doing only the one that's currently appropriate would expose which
   // links are in history to easy performance measurement.  Therefore,
   // here, we add nsChangeHint_RepaintFrame hints (the maximum for
   // things that can depend on :visited) for the properties on which we
   // call GetVisitedDependentColor.
-  ComputedStyle* thisVis = GetStyleIfVisited();
-  ComputedStyle* otherVis = aNewContext->GetStyleIfVisited();
+  const ComputedStyle* thisVis = GetStyleIfVisited();
+  const ComputedStyle* otherVis = aNewStyle.GetStyleIfVisited();
   if (!thisVis != !otherVis) {
     // One style has a style-if-visited and the other doesn't.
     // Presume a difference.
 #define STYLE_STRUCT(name_, fields_) *aEqualStructs &= ~STYLE_STRUCT_BIT(name_);
 #include "nsCSSVisitedDependentPropList.h"
 #undef STYLE_STRUCT
     hint |= nsChangeHint_RepaintFrame;
   } else if (thisVis) {
@@ -193,25 +187,24 @@ nsChangeHint ComputedStyle::CalcStyleDif
     bool change = false;
 
     // NB: Calling Peek on |this|, not |thisVis|, since callers may look
     // at a struct on |this| without looking at the same struct on
     // |thisVis| (including this function if we skip one of these checks
     // due to change being true already or due to the old style not having a
     // style-if-visited), but not the other way around.
 #define STYLE_FIELD(name_) thisVisStruct->name_ != otherVisStruct->name_
-#define STYLE_STRUCT(name_, fields_)                                           \
-  if (PEEK(name_)) {                                                           \
-    const nsStyle##name_* thisVisStruct = thisVis->ThreadsafeStyle##name_();   \
-    const nsStyle##name_* otherVisStruct = otherVis->ThreadsafeStyle##name_(); \
-    if (MOZ_FOR_EACH_SEPARATED(STYLE_FIELD, (||), (), fields_)) {              \
-      *aEqualStructs &= ~STYLE_STRUCT_BIT(name_);                              \
-      change = true;                                                           \
-    }                                                                          \
-  }
+#define STYLE_STRUCT(name_, fields_) {                                       \
+  const nsStyle##name_* thisVisStruct = thisVis->Style##name_();             \
+  const nsStyle##name_* otherVisStruct = otherVis->Style##name_();           \
+  if (MOZ_FOR_EACH_SEPARATED(STYLE_FIELD, (||), (), fields_)) {              \
+    *aEqualStructs &= ~STYLE_STRUCT_BIT(name_);                              \
+    change = true;                                                           \
+  }                                                                          \
+}
 #include "nsCSSVisitedDependentPropList.h"
 #undef STYLE_STRUCT
 #undef STYLE_FIELD
 #undef STYLE_STRUCT_BIT
 
     if (change) {
       hint |= nsChangeHint_RepaintFrame;
     }
@@ -226,25 +219,24 @@ nsChangeHint ComputedStyle::CalcStyleDif
 
     // This depends on data in nsStyleDisplay and nsStyleEffects, so we do it
     // here
 
     // Note that it's perhaps good for this test to be last because it
     // doesn't use Peek* functions to get the structs on the old
     // context.  But this isn't a big concern because these struct
     // getters should be called during frame construction anyway.
-    const nsStyleDisplay* oldDisp = ThreadsafeStyleDisplay();
-    const nsStyleDisplay* newDisp = aNewContext->ThreadsafeStyleDisplay();
+    const nsStyleDisplay* oldDisp = StyleDisplay();
+    const nsStyleDisplay* newDisp = aNewStyle.StyleDisplay();
     bool isFixedCB;
     if (oldDisp->IsAbsPosContainingBlockForNonSVGTextFrames() ==
             newDisp->IsAbsPosContainingBlockForNonSVGTextFrames() &&
         (isFixedCB =
              oldDisp->IsFixedPosContainingBlockForNonSVGTextFrames(*this)) ==
-            newDisp->IsFixedPosContainingBlockForNonSVGTextFrames(
-                *aNewContext) &&
+            newDisp->IsFixedPosContainingBlockForNonSVGTextFrames(aNewStyle) &&
         // transform-supporting frames are a subcategory of non-SVG-text
         // frames, so no need to test this if isFixedCB is true (both
         // before and after the change)
         (isFixedCB ||
          oldDisp->IsFixedPosContainingBlockForTransformSupportingFrames() ==
              newDisp
                  ->IsFixedPosContainingBlockForTransformSupportingFrames()) &&
         // contain-layout-and-paint-supporting frames are a subset of
--- a/layout/style/ComputedStyle.h
+++ b/layout/style/ComputedStyle.h
@@ -37,16 +37,20 @@ class nsWindowSizes;
 extern "C" {
 void Servo_ComputedStyle_AddRef(const mozilla::ComputedStyle* aStyle);
 void Servo_ComputedStyle_Release(const mozilla::ComputedStyle* aStyle);
 void Gecko_ComputedStyle_Destroy(mozilla::ComputedStyle*);
 }
 
 namespace mozilla {
 
+namespace dom {
+class Document;
+}
+
 enum class CSSPseudoElementType : uint8_t;
 class ComputedStyle;
 
 /**
  * A ComputedStyle represents the computed style data for an element.
  *
  * The computed style data are stored in a set of reference counted structs
  * (see nsStyleStruct.h) that are stored directly on the ComputedStyle.
@@ -214,48 +218,20 @@ class ComputedStyle {
     if (nsCSSPseudoElements::PseudoElementSupportsUserActionState(
             aStyle->GetPseudoType())) {
       return;
     }
 
     mCachedInheritingStyles.Insert(aStyle);
   }
 
-/**
- * Define typesafe getter functions for each style struct by
- * preprocessing the list of style structs.  These functions are the
- * preferred way to get style data.  The macro creates functions like:
- *   const nsStyleBorder* StyleBorder();
- *   const nsStyleColor* StyleColor();
- */
-#define STYLE_STRUCT(name_) \
-  inline const nsStyle##name_* Style##name_() MOZ_NONNULL_RETURN;
-#include "nsStyleStructList.h"
-#undef STYLE_STRUCT
-
-  /**
-   * Equivalent to StyleFoo(), except that we skip the cache write during the
-   * servo traversal. This can cause incorrect behavior if used improperly,
-   * since we won't record that layout potentially depends on the values in
-   * this style struct. Use with care.
-   */
-
-#define STYLE_STRUCT(name_) \
-  inline const nsStyle##name_* ThreadsafeStyle##name_();
-#include "nsStyleStructList.h"
-#undef STYLE_STRUCT
-
-/**
- * PeekStyle* is like Style* but doesn't trigger style
- * computation if the data is not cached on either the ComputedStyle
- * or the rule node.
- *
- * Perhaps this shouldn't be a public ComputedStyle API.
- */
-#define STYLE_STRUCT(name_) inline const nsStyle##name_* PeekStyle##name_();
+#define STYLE_STRUCT(name_)                                              \
+  inline const nsStyle##name_* Style##name_() const MOZ_NONNULL_RETURN { \
+    return mSource.GetStyle##name_();                                    \
+  }
 #include "nsStyleStructList.h"
 #undef STYLE_STRUCT
 
   /**
    * Compute the style changes needed during restyling when this style
    * context is being replaced by aNewContext.  (This is nonsymmetric since
    * we optimize by skipping comparison for styles that have never been
    * requested.)
@@ -266,18 +242,18 @@ class ComputedStyle {
    * hints) also apply to all descendants.
    *
    * aEqualStructs must not be null.  Into it will be stored a bitfield
    * representing which structs were compared to be non-equal.
    *
    * CSS Variables are not compared here. Instead, the caller is responsible for
    * that when needed (basically only for elements).
    */
-  nsChangeHint CalcStyleDifference(ComputedStyle* aNewContext,
-                                   uint32_t* aEqualStructs);
+  nsChangeHint CalcStyleDifference(const ComputedStyle& aNewContext,
+                                   uint32_t* aEqualStructs) const;
 
  public:
   /**
    * Get a color that depends on link-visitedness using this and
    * this->GetStyleIfVisited().
    *
    * @param aField A pointer to a member variable in a style struct.
    *               The member variable and its style struct must have
@@ -291,76 +267,53 @@ class ComputedStyle {
    * color is the unvisited color and the second is the visited color.
    *
    * Combine the R, G, and B components of whichever of aColors should
    * be used based on aLinkIsVisited with the A component of aColors[0].
    */
   static nscolor CombineVisitedColors(nscolor* aColors, bool aLinkIsVisited);
 
   /**
-   * Start the background image loads for this ComputedStyle.
+   * Start image loads for this style.
+   *
+   * The Document is used to get a hand on the image loader. The old style is a
+   * hack for bug 1439285.
    */
-  inline void StartBackgroundImageLoads();
+  inline void StartImageLoads(dom::Document&,
+                              const ComputedStyle* aOldStyle = nullptr);
 
 #ifdef DEBUG
   void List(FILE* out, int32_t aIndent);
   static const char* StructName(StyleStructID aSID);
   static Maybe<StyleStructID> LookupStruct(const nsACString& aName);
 #endif
 
-  /**
-   * Makes this context match |aOther| in terms of which style structs have
-   * been resolved.
-   */
-  inline void ResolveSameStructsAs(const ComputedStyle* aOther);
-
   // The |aCVsSize| outparam on this function is where the actual CVs size
   // value is added. It's done that way because the callers know which value
   // the size should be added to.
   void AddSizeOfIncludingThis(nsWindowSizes& aSizes, size_t* aCVsSize) const;
 
  protected:
-  bool HasRequestedStruct(StyleStructID aID) const {
-    return mRequestedStructs & StyleStructConstants::BitFor(aID);
-  }
-
-  void SetRequestedStruct(StyleStructID aID) {
-    mRequestedStructs |= StyleStructConstants::BitFor(aID);
-  }
 
   // Needs to be friend so that it can call the destructor without making it
   // public.
   friend void ::Gecko_ComputedStyle_Destroy(ComputedStyle*);
 
   ~ComputedStyle() = default;
 
   nsPresContext* const mPresContext;
 
   ServoComputedData mSource;
 
   // A cache of anonymous box and lazy pseudo styles inheriting from this style.
   CachedInheritingStyles mCachedInheritingStyles;
 
-// Helper functions for GetStyle* and PeekStyle*
-#define STYLE_STRUCT_INHERITED(name_) \
-  template <bool aComputeData>        \
-  const nsStyle##name_* DoGetStyle##name_();
-#define STYLE_STRUCT_RESET(name_) \
-  template <bool aComputeData>    \
-  const nsStyle##name_* DoGetStyle##name_();
-
-#include "nsStyleStructList.h"
-#undef STYLE_STRUCT_RESET
-#undef STYLE_STRUCT_INHERITED
-
   // If this ComputedStyle is for a pseudo-element or anonymous box,
   // the relevant atom.
   const RefPtr<nsAtom> mPseudoTag;
 
-  // A bitfield with the structs that have been requested so far.
-  uint32_t mRequestedStructs = 0;
   const Bit mBits;
   const CSSPseudoElementType mPseudoType;
 };
 
 }  // namespace mozilla
 
 #endif
--- a/layout/style/ComputedStyleInlines.h
+++ b/layout/style/ComputedStyleInlines.h
@@ -16,74 +16,24 @@
 
 #include "mozilla/ComputedStyle.h"
 #include "mozilla/ServoComputedDataInlines.h"
 #include "mozilla/ServoUtils.h"
 #include "nsPresContext.h"
 
 namespace mozilla {
 
-#define STYLE_STRUCT(name_)                                       \
-  const nsStyle##name_* ComputedStyle::Style##name_() {           \
-    return DoGetStyle##name_<true>();                             \
-  }                                                               \
-  const nsStyle##name_* ComputedStyle::ThreadsafeStyle##name_() { \
-    if (mozilla::IsInServoTraversal()) {                          \
-      return ComputedData()->GetStyle##name_();                   \
-    }                                                             \
-    return Style##name_();                                        \
-  }                                                               \
-  const nsStyle##name_* ComputedStyle::PeekStyle##name_() {       \
-    return DoGetStyle##name_<false>();                            \
+void ComputedStyle::StartImageLoads(dom::Document& aDocument,
+                                    const ComputedStyle* aOldStyle) {
+  MOZ_ASSERT(NS_IsMainThread());
+#define STYLE_STRUCT(name_)                                      \
+  if (nsStyle##name_::kHasTriggerImageLoads) {                   \
+    auto* old = aOldStyle ? aOldStyle->Style##name_() : nullptr; \
+    auto* current = const_cast<nsStyle##name_*>(Style##name_()); \
+    current->TriggerImageLoads(aDocument, old);                  \
   }
 #include "nsStyleStructList.h"
 #undef STYLE_STRUCT
-
-// Helper functions for GetStyle* and PeekStyle*
-#define STYLE_STRUCT(name_)                                         \
-  template <bool aComputeData>                                      \
-  const nsStyle##name_* ComputedStyle::DoGetStyle##name_() {        \
-    const auto kStructID = StyleStructID::name_;                    \
-    const bool needToCompute = !HasRequestedStruct(kStructID);      \
-    if (!aComputeData && needToCompute) {                           \
-      return nullptr;                                               \
-    }                                                               \
-    const nsStyle##name_* data = ComputedData()->GetStyle##name_(); \
-    /* perform any remaining main thread work on the struct */      \
-    if (needToCompute) {                                            \
-      MOZ_ASSERT(NS_IsMainThread());                                \
-      MOZ_ASSERT(!mozilla::IsInServoTraversal());                   \
-      const_cast<nsStyle##name_*>(data)->TriggerImageLoads(         \
-          *mPresContext->Document(), nullptr);                      \
-      /* the ComputedStyle owns the struct */                       \
-      SetRequestedStruct(kStructID);                                \
-    }                                                               \
-    return data;                                                    \
-  }
-#include "nsStyleStructList.h"
-#undef STYLE_STRUCT
-
-void ComputedStyle::StartBackgroundImageLoads() {
-  // Just get our background struct; that should do the trick
-  StyleBackground();
-}
-
-void ComputedStyle::ResolveSameStructsAs(const ComputedStyle* aOther) {
-  // Only resolve structs that are not already resolved in this struct.
-  auto newBits = aOther->mRequestedStructs & ~mRequestedStructs;
-
-#define STYLE_STRUCT(name_)                                                    \
-  if (nsStyle##name_::kHasTriggerImageLoads &&                                 \
-      (newBits & StyleStructConstants::BitFor(StyleStructID::name_))) {        \
-    const nsStyle##name_* data = ComputedData()->GetStyle##name_();            \
-    const nsStyle##name_* oldData = aOther->ComputedData()->GetStyle##name_(); \
-    const_cast<nsStyle##name_*>(data)->TriggerImageLoads(                      \
-        *mPresContext->Document(), oldData);                                   \
-  }
-#include "nsStyleStructList.h"
-#undef STYLE_STRUCT
-
-  mRequestedStructs |= newBits;
 }
 
 }  // namespace mozilla
 
 #endif  // ComputedStyleInlines_h
--- a/layout/style/GeckoBindings.cpp
+++ b/layout/style/GeckoBindings.cpp
@@ -322,18 +322,17 @@ uint32_t Gecko_CalcStyleDifference(Compu
                                    ComputedStyleBorrowed aNewStyle,
                                    bool* aAnyStyleStructChanged,
                                    bool* aOnlyResetStructsChanged) {
   MOZ_ASSERT(aOldStyle);
   MOZ_ASSERT(aNewStyle);
 
   uint32_t equalStructs;
   nsChangeHint result =
-      const_cast<ComputedStyle*>(aOldStyle)->CalcStyleDifference(
-          const_cast<ComputedStyle*>(aNewStyle), &equalStructs);
+      aOldStyle->CalcStyleDifference(*aNewStyle, &equalStructs);
 
   *aAnyStyleStructChanged =
       equalStructs != StyleStructConstants::kAllStructsMask;
 
   const auto kInheritedStructsMask =
       StyleStructConstants::kInheritedStructsMask;
   *aOnlyResetStructsChanged =
       (equalStructs & kInheritedStructsMask) == kInheritedStructsMask;
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -2289,17 +2289,17 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
    *  - frames that support CSS contain:layout and contain:paint and are not
    *    SVG text frames.
    *  - frames that support CSS transforms and are not SVG text frames.
    *
    * This should be used only when the caller has the style but not the
    * frame (i.e., when calculating style changes).
    */
   inline bool IsFixedPosContainingBlockForNonSVGTextFrames(
-      mozilla::ComputedStyle&) const;
+      const mozilla::ComputedStyle&) const;
   inline bool
   IsFixedPosContainingBlockForContainLayoutAndPaintSupportingFrames() const;
   inline bool IsFixedPosContainingBlockForTransformSupportingFrames() const;
 
   /**
    * Returns the final combined individual transform.
    **/
   already_AddRefed<nsCSSValueSharedList> GetCombinedTransform() const {
--- a/layout/style/nsStyleStructInlines.h
+++ b/layout/style/nsStyleStructInlines.h
@@ -125,26 +125,26 @@ bool nsStyleDisplay::HasTransform(const 
 
 bool nsStyleDisplay::HasPerspective(const nsIFrame* aContextFrame) const {
   MOZ_ASSERT(aContextFrame->StyleDisplay() == this, "unexpected aContextFrame");
   return HasPerspectiveStyle() &&
          aContextFrame->IsFrameOfType(nsIFrame::eSupportsCSSTransforms);
 }
 
 bool nsStyleDisplay::IsFixedPosContainingBlockForNonSVGTextFrames(
-    mozilla::ComputedStyle& aStyle) const {
+    const mozilla::ComputedStyle& aStyle) const {
   // NOTE: Any CSS properties that influence the output of this function
   // should have the FIXPOS_CB flag set on them.
-  NS_ASSERTION(aStyle.ThreadsafeStyleDisplay() == this, "unexpected aStyle");
+  NS_ASSERTION(aStyle.StyleDisplay() == this, "unexpected aStyle");
 
   if (mWillChangeBitField & NS_STYLE_WILL_CHANGE_FIXPOS_CB) {
     return true;
   }
 
-  return aStyle.ThreadsafeStyleEffects()->HasFilters();
+  return aStyle.StyleEffects()->HasFilters();
 }
 
 bool nsStyleDisplay::
     IsFixedPosContainingBlockForContainLayoutAndPaintSupportingFrames() const {
   return IsContainPaint() || IsContainLayout();
 }
 
 bool nsStyleDisplay::IsFixedPosContainingBlockForTransformSupportingFrames()
@@ -152,18 +152,17 @@ bool nsStyleDisplay::IsFixedPosContainin
   // NOTE: Any CSS properties that influence the output of this function
   // should have the FIXPOS_CB flag set on them.
   return HasTransformStyle() || HasPerspectiveStyle();
 }
 
 bool nsStyleDisplay::IsFixedPosContainingBlock(
     const nsIFrame* aContextFrame) const {
   mozilla::ComputedStyle* style = aContextFrame->Style();
-  NS_ASSERTION(style->ThreadsafeStyleDisplay() == this,
-               "unexpected aContextFrame");
+  NS_ASSERTION(style->StyleDisplay() == this, "unexpected aContextFrame");
   // NOTE: Any CSS properties that influence the output of this function
   // should have the FIXPOS_CB flag set on them.
   if (!IsFixedPosContainingBlockForNonSVGTextFrames(*style) &&
       (!IsFixedPosContainingBlockForContainLayoutAndPaintSupportingFrames() ||
        !aContextFrame->IsFrameOfType(
            nsIFrame::eSupportsContainLayoutAndPaint)) &&
       (!IsFixedPosContainingBlockForTransformSupportingFrames() ||
        !aContextFrame->IsFrameOfType(nsIFrame::eSupportsCSSTransforms))) {
@@ -182,18 +181,17 @@ bool nsStyleDisplay::IsAbsPosContainingB
   // should have the ABSPOS_CB set on them.
   return IsAbsolutelyPositionedStyle() || IsRelativelyPositionedStyle() ||
          (mWillChangeBitField & NS_STYLE_WILL_CHANGE_ABSPOS_CB);
 }
 
 bool nsStyleDisplay::IsAbsPosContainingBlock(
     const nsIFrame* aContextFrame) const {
   mozilla::ComputedStyle* style = aContextFrame->Style();
-  NS_ASSERTION(style->ThreadsafeStyleDisplay() == this,
-               "unexpected aContextFrame");
+  NS_ASSERTION(style->StyleDisplay() == this, "unexpected aContextFrame");
   // NOTE: Any CSS properties that influence the output of this function
   // should have the ABSPOS_CB set on them.
   if (!IsAbsPosContainingBlockForNonSVGTextFrames() &&
       !IsFixedPosContainingBlockForNonSVGTextFrames(*style) &&
       (!IsFixedPosContainingBlockForContainLayoutAndPaintSupportingFrames() ||
        !aContextFrame->IsFrameOfType(
            nsIFrame::eSupportsContainLayoutAndPaint)) &&
       (!IsFixedPosContainingBlockForTransformSupportingFrames() ||
--- a/layout/svg/SVGGeometryFrame.cpp
+++ b/layout/svg/SVGGeometryFrame.cpp
@@ -171,18 +171,18 @@ nsresult SVGGeometryFrame::AttributeChan
 /* virtual */ void SVGGeometryFrame::DidSetComputedStyle(
     ComputedStyle* aOldComputedStyle) {
   nsFrame::DidSetComputedStyle(aOldComputedStyle);
 
   if (aOldComputedStyle) {
     SVGGeometryElement* element =
         static_cast<SVGGeometryElement*>(GetContent());
 
-    auto oldStyleSVG = aOldComputedStyle->PeekStyleSVG();
-    if (oldStyleSVG && !SVGContentUtils::ShapeTypeHasNoCorners(GetContent())) {
+    auto* oldStyleSVG = aOldComputedStyle->StyleSVG();
+    if (!SVGContentUtils::ShapeTypeHasNoCorners(GetContent())) {
       if (StyleSVG()->mStrokeLinecap != oldStyleSVG->mStrokeLinecap &&
           element->IsSVGElement(nsGkAtoms::path)) {
         // If the stroke-linecap changes to or from "butt" then our element
         // needs to update its cached Moz2D Path, since SVGPathData::BuildPath
         // decides whether or not to insert little lines into the path for zero
         // length subpaths base on that property.
         element->ClearAnyCachedPath();
       } else if (GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD) {
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -4751,19 +4751,17 @@ class nsDelayedCalcBCBorders : public Ru
 };
 
 bool nsTableFrame::BCRecalcNeeded(ComputedStyle* aOldComputedStyle,
                                   ComputedStyle* aNewComputedStyle) {
   // Attention: the old ComputedStyle is the one we're forgetting,
   // and hence possibly completely bogus for GetStyle* purposes.
   // We use PeekStyleData instead.
 
-  const nsStyleBorder* oldStyleData = aOldComputedStyle->PeekStyleBorder();
-  if (!oldStyleData) return false;
-
+  const nsStyleBorder* oldStyleData = aOldComputedStyle->StyleBorder();
   const nsStyleBorder* newStyleData = aNewComputedStyle->StyleBorder();
   nsChangeHint change = newStyleData->CalcDifference(*oldStyleData);
   if (!change) return false;
   if (change & nsChangeHint_NeedReflow)
     return true;  // the caller only needs to mark the bc damage area
   if (change & nsChangeHint_RepaintFrame) {
     // we need to recompute the borders and the caller needs to mark
     // the bc damage area
@@ -7693,24 +7691,17 @@ void nsTableFrame::AppendDirectlyOwnedAn
   // of anonymous boxes directly, so we need to handle that potential change
   // here.
   //
   // NOTE(emilio): We can't use the ChangesHandledFor optimization (and we
   // assert against that), because the table wrapper is up in the frame tree
   // compared to the owner frame.
   uint32_t equalStructs;  // Not used, actually.
   nsChangeHint wrapperHint =
-      aWrapperFrame->Style()->CalcStyleDifference(newStyle, &equalStructs);
-
-  // CalcStyleDifference will handle caching structs on the new ComputedStyle,
-  // but only if we're not on a style worker thread.
-  MOZ_ASSERT(!ServoStyleSet::IsInServoTraversal(),
-             "if we can get in here from style worker threads, then we need "
-             "a ResolveSameStructsAs call to ensure structs are cached on "
-             "aNewComputedStyle");
+      aWrapperFrame->Style()->CalcStyleDifference(*newStyle, &equalStructs);
 
   if (wrapperHint) {
     aRestyleState.ChangeList().AppendChange(
         aWrapperFrame, aWrapperFrame->GetContent(), wrapperHint);
   }
 
   for (nsIFrame* cur = aWrapperFrame; cur; cur = cur->GetNextContinuation()) {
     cur->SetComputedStyle(newStyle);
--- a/layout/xul/nsTextBoxFrame.cpp
+++ b/layout/xul/nsTextBoxFrame.cpp
@@ -887,22 +887,18 @@ void nsTextBoxFrame::RecomputeTitle() {
 }
 
 void nsTextBoxFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
   if (!aOldComputedStyle) {
     // We're just being initialized
     return;
   }
 
-  const nsStyleText* oldTextStyle = aOldComputedStyle->PeekStyleText();
-  // We should really have oldTextStyle here, since we asked for our
-  // nsStyleText during Init(), but if it's not there for some reason
-  // just assume the worst and recompute mTitle.
-  if (!oldTextStyle ||
-      oldTextStyle->mTextTransform != StyleText()->mTextTransform) {
+  const nsStyleText* oldTextStyle = aOldComputedStyle->StyleText();
+  if (oldTextStyle->mTextTransform != StyleText()->mTextTransform) {
     RecomputeTitle();
     UpdateAccessTitle();
   }
 }
 
 NS_IMETHODIMP
 nsTextBoxFrame::DoXULLayout(nsBoxLayoutState& aBoxLayoutState) {
   if (mNeedsReflowCallback) {
--- a/layout/xul/tree/nsTreeStyleCache.cpp
+++ b/layout/xul/tree/nsTreeStyleCache.cpp
@@ -69,16 +69,21 @@ ComputedStyle* nsTreeStyleCache::GetComp
     result = mCache->GetWeak(currState);
   }
   if (!result) {
     // We missed the cache. Resolve this pseudo-style.
     RefPtr<ComputedStyle> newResult =
         aPresContext->StyleSet()->ResolveXULTreePseudoStyle(
             aContent->AsElement(), aPseudoElement, aStyle, aInputWord);
 
+    // Normally we rely on nsFrame::Init / RestyleManager to call this, but
+    // these are weird and don't use a frame, yet ::-moz-tree-twisty definitely
+    // pokes at list-style-image.
+    newResult->StartImageLoads(*aPresContext->Document());
+
     // Put the ComputedStyle in our table, transferring the owning reference to
     // the table.
     if (!mCache) {
       mCache = new ComputedStyleCache();
     }
     result = newResult.get();
     mCache->Put(currState, newResult.forget());
   }
--- a/testing/web-platform/tests/css/css-lists/inheritance.html
+++ b/testing/web-platform/tests/css/css-lists/inheritance.html
@@ -12,14 +12,14 @@
 </head>
 <body>
 <div id="container">
   <div id="target"></div>
 </div>
 <script>
 assert_not_inherited('counter-increment', 'none', 'foo 123');
 assert_not_inherited('counter-reset', 'none', 'foo 123');
-assert_inherited('list-style-image', 'none', 'url("https://example.com/")');
+assert_inherited('list-style-image', 'none', 'url("data:,a")');
 assert_inherited('list-style-position', 'outside', 'inside');
 assert_inherited('list-style-type', 'disc', 'square');
 </script>
 </body>
 </html>