1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "mozilla/ServoRestyleManager.h"
8 
9 #include "mozilla/AutoRestyleTimelineMarker.h"
10 #include "mozilla/AutoTimelineMarker.h"
11 #include "mozilla/DocumentStyleRootIterator.h"
12 #include "mozilla/ServoBindings.h"
13 #include "mozilla/ServoStyleSet.h"
14 #include "mozilla/ServoStyleContext.h"
15 #include "mozilla/ServoStyleContextInlines.h"
16 #include "mozilla/Unused.h"
17 #include "mozilla/ViewportFrame.h"
18 #include "mozilla/dom/ChildIterator.h"
19 #include "mozilla/dom/ElementInlines.h"
20 #include "nsBlockFrame.h"
21 #include "nsBulletFrame.h"
22 #include "nsIFrameInlines.h"
23 #include "nsImageFrame.h"
24 #include "nsPlaceholderFrame.h"
25 #include "nsContentUtils.h"
26 #include "nsCSSFrameConstructor.h"
27 #include "nsPrintfCString.h"
28 #include "nsRefreshDriver.h"
29 #include "nsStyleChangeList.h"
30 
31 #ifdef ACCESSIBILITY
32 #include "nsAccessibilityService.h"
33 #endif
34 
35 using namespace mozilla::dom;
36 
37 namespace mozilla {
38 
39 #ifdef DEBUG
IsAnonBox(const nsIFrame & aFrame)40 static bool IsAnonBox(const nsIFrame& aFrame) {
41   return aFrame.StyleContext()->IsAnonBox();
42 }
43 
FirstContinuationOrPartOfIBSplit(const nsIFrame * aFrame)44 static const nsIFrame* FirstContinuationOrPartOfIBSplit(
45     const nsIFrame* aFrame) {
46   if (!aFrame) {
47     return nullptr;
48   }
49 
50   return nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
51 }
52 
ExpectedOwnerForChild(const nsIFrame & aFrame)53 static const nsIFrame* ExpectedOwnerForChild(const nsIFrame& aFrame) {
54   const nsIFrame* parent = aFrame.GetParent();
55   if (aFrame.IsTableFrame()) {
56     MOZ_ASSERT(parent->IsTableWrapperFrame());
57     parent = parent->GetParent();
58   }
59 
60   if (IsAnonBox(aFrame) && !aFrame.IsTextFrame()) {
61     if (parent->IsLineFrame()) {
62       parent = parent->GetParent();
63     }
64     return parent->IsViewportFrame() ? nullptr
65                                      : FirstContinuationOrPartOfIBSplit(parent);
66   }
67 
68   if (aFrame.IsBulletFrame()) {
69     return FirstContinuationOrPartOfIBSplit(parent);
70   }
71 
72   if (aFrame.IsLineFrame()) {
73     // A ::first-line always ends up here via its block, which is therefore the
74     // right expected owner.  That block can be an
75     // anonymous box.  For example, we could have a ::first-line on a columnated
76     // block; the blockframe is the column-content anonymous box in that case.
77     // So we don't want to end up in the code below, which steps out of anon
78     // boxes.  Just return the parent of the line frame, which is the block.
79     return parent;
80   }
81 
82   if (aFrame.IsLetterFrame()) {
83     // Ditto for ::first-letter. A first-letter always arrives here via its
84     // direct parent, except when it's parented to a ::first-line.
85     if (parent->IsLineFrame()) {
86       parent = parent->GetParent();
87     }
88     return FirstContinuationOrPartOfIBSplit(parent);
89   }
90 
91   if (parent->IsLetterFrame()) {
92     // Things never have ::first-letter as their expected parent.  Go
93     // on up to the ::first-letter's parent.
94     parent = parent->GetParent();
95   }
96 
97   parent = FirstContinuationOrPartOfIBSplit(parent);
98 
99   // We've handled already anon boxes and bullet frames, so now we're looking at
100   // a frame of a DOM element or pseudo. Hop through anon and line-boxes
101   // generated by our DOM parent, and go find the owner frame for it.
102   while (parent && (IsAnonBox(*parent) || parent->IsLineFrame())) {
103     auto* pseudo = parent->StyleContext()->GetPseudo();
104     if (pseudo == nsCSSAnonBoxes::tableWrapper) {
105       const nsIFrame* tableFrame = parent->PrincipalChildList().FirstChild();
106       MOZ_ASSERT(tableFrame->IsTableFrame());
107       // Handle :-moz-table and :-moz-inline-table.
108       parent = IsAnonBox(*tableFrame) ? parent->GetParent() : tableFrame;
109     } else {
110       // We get the in-flow parent here so that we can handle the OOF anonymous
111       // boxed to get the correct parent.
112       parent = parent->GetInFlowParent();
113     }
114     parent = FirstContinuationOrPartOfIBSplit(parent);
115   }
116 
117   return parent;
118 }
119 
AssertOwner(const ServoRestyleState & aParent) const120 void ServoRestyleState::AssertOwner(const ServoRestyleState& aParent) const {
121   MOZ_ASSERT(mOwner);
122   MOZ_ASSERT(!mOwner->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW));
123   // We allow aParent.mOwner to be null, for cases when we're not starting at
124   // the root of the tree.  We also allow aParent.mOwner to be somewhere up our
125   // expected owner chain not our immediate owner, which allows us creating long
126   // chains of ServoRestyleStates in some cases where it's just not worth it.
127 #ifdef DEBUG
128   if (aParent.mOwner) {
129     const nsIFrame* owner = ExpectedOwnerForChild(*mOwner);
130     if (owner != aParent.mOwner) {
131       MOZ_ASSERT(IsAnonBox(*owner),
132                  "Should only have expected owner weirdness when anon boxes "
133                  "are involved");
134       bool found = false;
135       for (; owner; owner = ExpectedOwnerForChild(*owner)) {
136         if (owner == aParent.mOwner) {
137           found = true;
138           break;
139         }
140       }
141       MOZ_ASSERT(found, "Must have aParent.mOwner on our expected owner chain");
142     }
143   }
144 #endif
145 }
146 
ChangesHandledFor(const nsIFrame & aFrame) const147 nsChangeHint ServoRestyleState::ChangesHandledFor(
148     const nsIFrame& aFrame) const {
149   if (!mOwner) {
150     MOZ_ASSERT(!mChangesHandled);
151     return mChangesHandled;
152   }
153 
154   MOZ_ASSERT(mOwner == ExpectedOwnerForChild(aFrame),
155              "Missed some frame in the hierarchy?");
156   return mChangesHandled;
157 }
158 #endif
159 
AddPendingWrapperRestyle(nsIFrame * aWrapperFrame)160 void ServoRestyleState::AddPendingWrapperRestyle(nsIFrame* aWrapperFrame) {
161   MOZ_ASSERT(aWrapperFrame->StyleContext()->IsWrapperAnonBox(),
162              "All our wrappers are anon boxes, and why would we restyle "
163              "non-inheriting ones?");
164   MOZ_ASSERT(aWrapperFrame->StyleContext()->IsInheritingAnonBox(),
165              "All our wrappers are anon boxes, and why would we restyle "
166              "non-inheriting ones?");
167   MOZ_ASSERT(
168       aWrapperFrame->StyleContext()->GetPseudo() != nsCSSAnonBoxes::cellContent,
169       "Someone should be using TableAwareParentFor");
170   MOZ_ASSERT(aWrapperFrame->StyleContext()->GetPseudo() !=
171                  nsCSSAnonBoxes::tableWrapper,
172              "Someone should be using TableAwareParentFor");
173   // Make sure we only add first continuations.
174   aWrapperFrame = aWrapperFrame->FirstContinuation();
175   nsIFrame* last = mPendingWrapperRestyles.SafeLastElement(nullptr);
176   if (last == aWrapperFrame) {
177     // Already queued up, nothing to do.
178     return;
179   }
180 
181   // Make sure to queue up parents before children.  But don't queue up
182   // ancestors of non-anonymous boxes here; those are handled when we traverse
183   // their non-anonymous kids.
184   if (aWrapperFrame->ParentIsWrapperAnonBox()) {
185     AddPendingWrapperRestyle(TableAwareParentFor(aWrapperFrame));
186   }
187 
188   // If the append fails, we'll fail to restyle properly, but that's probably
189   // better than crashing.
190   if (mPendingWrapperRestyles.AppendElement(aWrapperFrame, fallible)) {
191     aWrapperFrame->SetIsWrapperAnonBoxNeedingRestyle(true);
192   }
193 }
194 
ProcessWrapperRestyles(nsIFrame * aParentFrame)195 void ServoRestyleState::ProcessWrapperRestyles(nsIFrame* aParentFrame) {
196   size_t i = mPendingWrapperRestyleOffset;
197   while (i < mPendingWrapperRestyles.Length()) {
198     i += ProcessMaybeNestedWrapperRestyle(aParentFrame, i);
199   }
200 
201   mPendingWrapperRestyles.TruncateLength(mPendingWrapperRestyleOffset);
202 }
203 
ProcessMaybeNestedWrapperRestyle(nsIFrame * aParent,size_t aIndex)204 size_t ServoRestyleState::ProcessMaybeNestedWrapperRestyle(nsIFrame* aParent,
205                                                            size_t aIndex) {
206   // The frame at index aIndex is something we should restyle ourselves, but
207   // following frames may need separate ServoRestyleStates to restyle.
208   MOZ_ASSERT(aIndex < mPendingWrapperRestyles.Length());
209 
210   nsIFrame* cur = mPendingWrapperRestyles[aIndex];
211   MOZ_ASSERT(cur->StyleContext()->IsWrapperAnonBox());
212 
213   // Where is cur supposed to inherit from?  From its parent frame, except in
214   // the case when cur is a table, in which case it should be its grandparent.
215   // Also, not in the case when the resulting frame would be a first-line; in
216   // that case we should be inheriting from the block, and the first-line will
217   // do its fixup later if needed.
218   //
219   // Note that after we do all that fixup the parent we get might still not be
220   // aParent; for example aParent could be a scrollframe, in which case we
221   // should inherit from the scrollcontent frame.  Or the parent might be some
222   // continuation of aParent.
223   //
224   // Try to assert as much as we can about the parent we actually end up using
225   // without triggering bogus asserts in all those various edge cases.
226   nsIFrame* parent = cur->GetParent();
227   if (cur->IsTableFrame()) {
228     MOZ_ASSERT(parent->IsTableWrapperFrame());
229     parent = parent->GetParent();
230   }
231   if (parent->IsLineFrame()) {
232     parent = parent->GetParent();
233   }
234   MOZ_ASSERT(FirstContinuationOrPartOfIBSplit(parent) == aParent ||
235              (parent->StyleContext()->IsInheritingAnonBox() &&
236               parent->GetContent() == aParent->GetContent()));
237 
238   // Now "this" is a ServoRestyleState for aParent, so if parent is not a next
239   // continuation (possibly across ib splits) of aParent we need a new
240   // ServoRestyleState for the kid.
241   Maybe<ServoRestyleState> parentRestyleState;
242   nsIFrame* parentForRestyle =
243       nsLayoutUtils::FirstContinuationOrIBSplitSibling(parent);
244   if (parentForRestyle != aParent) {
245     parentRestyleState.emplace(*parentForRestyle, *this, nsChangeHint_Empty,
246                                Type::InFlow);
247   }
248   ServoRestyleState& curRestyleState =
249       parentRestyleState ? *parentRestyleState : *this;
250 
251   // This frame may already have been restyled.  Even if it has, we can't just
252   // return, because the next frame may be a kid of it that does need restyling.
253   if (cur->IsWrapperAnonBoxNeedingRestyle()) {
254     parentForRestyle->UpdateStyleOfChildAnonBox(cur, curRestyleState);
255     cur->SetIsWrapperAnonBoxNeedingRestyle(false);
256   }
257 
258   size_t numProcessed = 1;
259 
260   // Note: no overflow possible here, since aIndex < length.
261   if (aIndex + 1 < mPendingWrapperRestyles.Length()) {
262     nsIFrame* next = mPendingWrapperRestyles[aIndex + 1];
263     if (TableAwareParentFor(next) == cur &&
264         next->IsWrapperAnonBoxNeedingRestyle()) {
265       // It might be nice if we could do better than nsChangeHint_Empty.  On
266       // the other hand, presumably our mChangesHandled already has the bits
267       // we really want here so in practice it doesn't matter.
268       ServoRestyleState childState(*cur, curRestyleState, nsChangeHint_Empty,
269                                    Type::InFlow,
270                                    /* aAssertWrapperRestyleLength = */ false);
271       numProcessed +=
272           childState.ProcessMaybeNestedWrapperRestyle(cur, aIndex + 1);
273     }
274   }
275 
276   return numProcessed;
277 }
278 
TableAwareParentFor(const nsIFrame * aChild)279 nsIFrame* ServoRestyleState::TableAwareParentFor(const nsIFrame* aChild) {
280   // We want to get the anon box parent for aChild. where aChild has
281   // ParentIsWrapperAnonBox().
282   //
283   // For the most part this is pretty straightforward, but there are two
284   // wrinkles.  First, if aChild is a table, then we really want the parent of
285   // its table wrapper.
286   if (aChild->IsTableFrame()) {
287     aChild = aChild->GetParent();
288     MOZ_ASSERT(aChild->IsTableWrapperFrame());
289   }
290 
291   nsIFrame* parent = aChild->GetParent();
292   // Now if parent is a cell-content frame, we actually want the cellframe.
293   if (parent->StyleContext()->GetPseudo() == nsCSSAnonBoxes::cellContent) {
294     parent = parent->GetParent();
295   } else if (parent->IsTableWrapperFrame()) {
296     // Must be a caption.  In that case we want the table here.
297     MOZ_ASSERT(aChild->StyleDisplay()->mDisplay == StyleDisplay::TableCaption);
298     parent = parent->PrincipalChildList().FirstChild();
299   }
300   return parent;
301 }
302 
ServoRestyleManager(nsPresContext * aPresContext)303 ServoRestyleManager::ServoRestyleManager(nsPresContext* aPresContext)
304     : RestyleManager(StyleBackendType::Servo, aPresContext),
305       mReentrantChanges(nullptr) {}
306 
PostRestyleEvent(Element * aElement,nsRestyleHint aRestyleHint,nsChangeHint aMinChangeHint)307 void ServoRestyleManager::PostRestyleEvent(Element* aElement,
308                                            nsRestyleHint aRestyleHint,
309                                            nsChangeHint aMinChangeHint) {
310   MOZ_ASSERT(!(aMinChangeHint & nsChangeHint_NeutralChange),
311              "Didn't expect explicit change hints to be neutral!");
312   if (MOZ_UNLIKELY(IsDisconnected()) ||
313       MOZ_UNLIKELY(PresContext()->PresShell()->IsDestroying())) {
314     return;
315   }
316 
317   // We allow posting restyles from within change hint handling, but not from
318   // within the restyle algorithm itself.
319   MOZ_ASSERT(!ServoStyleSet::IsInServoTraversal());
320 
321   if (aRestyleHint == 0 && !aMinChangeHint) {
322     return;  // Nothing to do.
323   }
324 
325   // Assuming the restyle hints will invalidate cached style for
326   // getComputedStyle, since we don't know if any of the restyling that we do
327   // would affect undisplayed elements.
328   if (aRestyleHint) {
329     IncrementUndisplayedRestyleGeneration();
330   }
331 
332   // Processing change hints sometimes causes new change hints to be generated,
333   // and very occasionally, additional restyle hints. We collect the change
334   // hints manually to avoid re-traversing the DOM to find them.
335   if (mReentrantChanges && !aRestyleHint) {
336     mReentrantChanges->AppendElement(ReentrantChange{aElement, aMinChangeHint});
337     return;
338   }
339 
340   if (aRestyleHint & ~eRestyle_AllHintsWithAnimations) {
341     mHaveNonAnimationRestyles = true;
342   }
343 
344   if (aRestyleHint & eRestyle_LaterSiblings) {
345     aRestyleHint &= ~eRestyle_LaterSiblings;
346 
347     nsRestyleHint siblingHint = eRestyle_Subtree;
348     Element* current = aElement->GetNextElementSibling();
349     while (current) {
350       Servo_NoteExplicitHints(current, siblingHint, nsChangeHint(0));
351       current = current->GetNextElementSibling();
352     }
353   }
354 
355   if (aRestyleHint || aMinChangeHint) {
356     Servo_NoteExplicitHints(aElement, aRestyleHint, aMinChangeHint);
357   }
358 }
359 
PostRestyleEventForCSSRuleChanges()360 void ServoRestyleManager::PostRestyleEventForCSSRuleChanges() {
361   mRestyleForCSSRuleChanges = true;
362   mPresContext->PresShell()->EnsureStyleFlush();
363 }
364 
PostRestyleEventForAnimations(Element * aElement,CSSPseudoElementType aPseudoType,nsRestyleHint aRestyleHint)365 void ServoRestyleManager::PostRestyleEventForAnimations(
366     Element* aElement, CSSPseudoElementType aPseudoType,
367     nsRestyleHint aRestyleHint) {
368   Element* elementToRestyle =
369       EffectCompositor::GetElementToRestyle(aElement, aPseudoType);
370 
371   if (!elementToRestyle) {
372     // FIXME: Bug 1371107: When reframing happens,
373     // EffectCompositor::mElementsToRestyle still has unbinded old pseudo
374     // element. We should drop it.
375     return;
376   }
377 
378   AutoRestyleTimelineMarker marker(mPresContext->GetDocShell(),
379                                    true /* animation-only */);
380   Servo_NoteExplicitHints(elementToRestyle, aRestyleHint, nsChangeHint(0));
381 }
382 
RebuildAllStyleData(nsChangeHint aExtraHint,nsRestyleHint aRestyleHint)383 void ServoRestyleManager::RebuildAllStyleData(nsChangeHint aExtraHint,
384                                               nsRestyleHint aRestyleHint) {
385   // NOTE(emilio): GeckoRestlyeManager does a sync style flush, which seems not
386   // to be needed in my testing.
387   PostRebuildAllStyleDataEvent(aExtraHint, aRestyleHint);
388 }
389 
PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint,nsRestyleHint aRestyleHint)390 void ServoRestyleManager::PostRebuildAllStyleDataEvent(
391     nsChangeHint aExtraHint, nsRestyleHint aRestyleHint) {
392   // NOTE(emilio): The semantics of these methods are quite funny, in the sense
393   // that we're not supposed to need to rebuild the actual stylist data.
394   //
395   // That's handled as part of the MediumFeaturesChanged stuff, if needed.
396   StyleSet()->ClearCachedStyleData();
397 
398   DocumentStyleRootIterator iter(mPresContext->Document());
399   while (Element* root = iter.GetNextStyleRoot()) {
400     PostRestyleEvent(root, aRestyleHint, aExtraHint);
401   }
402 
403   // TODO(emilio, bz): Extensions can add/remove stylesheets that can affect
404   // non-inheriting anon boxes. It's not clear if we want to support that, but
405   // if we do, we need to re-selector-match them here.
406 }
407 
ClearServoDataFromSubtree(Element * aElement,IncludeRoot aIncludeRoot)408 /* static */ void ServoRestyleManager::ClearServoDataFromSubtree(
409     Element* aElement, IncludeRoot aIncludeRoot) {
410   if (aElement->HasServoData()) {
411     StyleChildrenIterator it(aElement);
412     for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
413       if (n->IsElement()) {
414         ClearServoDataFromSubtree(n->AsElement(), IncludeRoot::Yes);
415       }
416     }
417   }
418 
419   if (MOZ_LIKELY(aIncludeRoot == IncludeRoot::Yes)) {
420     aElement->ClearServoData();
421     MOZ_ASSERT(!aElement->HasAnyOfFlags(Element::kAllServoDescendantBits |
422                                         NODE_NEEDS_FRAME));
423     MOZ_ASSERT(aElement != aElement->OwnerDoc()->GetServoRestyleRoot());
424   }
425 }
426 
ClearRestyleStateFromSubtree(Element * aElement)427 /* static */ void ServoRestyleManager::ClearRestyleStateFromSubtree(
428     Element* aElement) {
429   if (aElement->HasAnyOfFlags(Element::kAllServoDescendantBits)) {
430     StyleChildrenIterator it(aElement);
431     for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
432       if (n->IsElement()) {
433         ClearRestyleStateFromSubtree(n->AsElement());
434       }
435     }
436   }
437 
438   bool wasRestyled;
439   Unused << Servo_TakeChangeHint(aElement, &wasRestyled);
440   aElement->UnsetFlags(Element::kAllServoDescendantBits);
441 }
442 
443 /**
444  * This struct takes care of encapsulating some common state that text nodes may
445  * need to track during the post-traversal.
446  *
447  * This is currently used to properly compute change hints when the parent
448  * element of this node is a display: contents node, and also to avoid computing
449  * the style for text children more than once per element.
450  */
451 struct ServoRestyleManager::TextPostTraversalState {
452  public:
TextPostTraversalStatemozilla::ServoRestyleManager::TextPostTraversalState453   TextPostTraversalState(Element& aParentElement,
454                          ServoStyleContext* aParentContext,
455                          bool aDisplayContentsParentStyleChanged,
456                          ServoRestyleState& aParentRestyleState)
457       : mParentElement(aParentElement),
458         mParentContext(aParentContext),
459         mParentRestyleState(aParentRestyleState),
460         mStyle(nullptr),
461         mShouldPostHints(aDisplayContentsParentStyleChanged),
462         mShouldComputeHints(aDisplayContentsParentStyleChanged),
463         mComputedHint(nsChangeHint_Empty) {}
464 
ChangeListmozilla::ServoRestyleManager::TextPostTraversalState465   nsStyleChangeList& ChangeList() { return mParentRestyleState.ChangeList(); }
466 
ComputeStylemozilla::ServoRestyleManager::TextPostTraversalState467   nsStyleContext& ComputeStyle(nsIContent* aTextNode) {
468     if (!mStyle) {
469       mStyle = mParentRestyleState.StyleSet().ResolveStyleForText(
470           aTextNode, &ParentStyle());
471     }
472     MOZ_ASSERT(mStyle);
473     return *mStyle;
474   }
475 
ComputeHintIfNeededmozilla::ServoRestyleManager::TextPostTraversalState476   void ComputeHintIfNeeded(nsIContent* aContent, nsIFrame* aTextFrame,
477                            nsStyleContext& aNewContext) {
478     MOZ_ASSERT(aTextFrame);
479     MOZ_ASSERT(aNewContext.GetPseudo() == nsCSSAnonBoxes::mozText);
480 
481     if (MOZ_LIKELY(!mShouldPostHints)) {
482       return;
483     }
484 
485     ServoStyleContext* oldContext = aTextFrame->StyleContext()->AsServo();
486     MOZ_ASSERT(oldContext->GetPseudo() == nsCSSAnonBoxes::mozText);
487 
488     // We rely on the fact that all the text children for the same element share
489     // style to avoid recomputing style differences for all of them.
490     //
491     // TODO(emilio): The above may not be true for ::first-{line,letter}, but
492     // we'll cross that bridge when we support those in stylo.
493     if (mShouldComputeHints) {
494       mShouldComputeHints = false;
495       uint32_t equalStructs, samePointerStructs;
496       mComputedHint = oldContext->CalcStyleDifference(
497           &aNewContext, &equalStructs, &samePointerStructs);
498       mComputedHint = NS_RemoveSubsumedHints(
499           mComputedHint, mParentRestyleState.ChangesHandledFor(*aTextFrame));
500     }
501 
502     if (mComputedHint) {
503       mParentRestyleState.ChangeList().AppendChange(aTextFrame, aContent,
504                                                     mComputedHint);
505     }
506   }
507 
508  private:
ParentStylemozilla::ServoRestyleManager::TextPostTraversalState509   ServoStyleContext& ParentStyle() {
510     if (!mParentContext) {
511       mLazilyResolvedParentContext =
512           mParentRestyleState.StyleSet().ResolveServoStyle(&mParentElement);
513       mParentContext = mLazilyResolvedParentContext;
514     }
515     return *mParentContext;
516   }
517 
518   Element& mParentElement;
519   ServoStyleContext* mParentContext;
520   RefPtr<ServoStyleContext> mLazilyResolvedParentContext;
521   ServoRestyleState& mParentRestyleState;
522   RefPtr<nsStyleContext> mStyle;
523   bool mShouldPostHints;
524   bool mShouldComputeHints;
525   nsChangeHint mComputedHint;
526 };
527 
UpdateBackdropIfNeeded(nsIFrame * aFrame,ServoStyleSet & aStyleSet,nsStyleChangeList & aChangeList)528 static void UpdateBackdropIfNeeded(nsIFrame* aFrame, ServoStyleSet& aStyleSet,
529                                    nsStyleChangeList& aChangeList) {
530   const nsStyleDisplay* display = aFrame->StyleContext()->StyleDisplay();
531   if (display->mTopLayer != NS_STYLE_TOP_LAYER_TOP) {
532     return;
533   }
534 
535   // Elements in the top layer are guaranteed to have absolute or fixed
536   // position per https://fullscreen.spec.whatwg.org/#new-stacking-layer.
537   MOZ_ASSERT(display->IsAbsolutelyPositionedStyle());
538 
539   nsIFrame* backdropPlaceholder =
540       aFrame->GetChildList(nsIFrame::kBackdropList).FirstChild();
541   if (!backdropPlaceholder) {
542     return;
543   }
544 
545   MOZ_ASSERT(backdropPlaceholder->IsPlaceholderFrame());
546   nsIFrame* backdropFrame =
547       nsPlaceholderFrame::GetRealFrameForPlaceholder(backdropPlaceholder);
548   MOZ_ASSERT(backdropFrame->IsBackdropFrame());
549   MOZ_ASSERT(backdropFrame->StyleContext()->GetPseudoType() ==
550              CSSPseudoElementType::backdrop);
551 
552   RefPtr<nsStyleContext> newContext = aStyleSet.ResolvePseudoElementStyle(
553       aFrame->GetContent()->AsElement(), CSSPseudoElementType::backdrop,
554       aFrame->StyleContext()->AsServo(),
555       /* aPseudoElement = */ nullptr);
556 
557   // NOTE(emilio): We can't use the changes handled for the owner of the
558   // backdrop frame, since it's out of flow, and parented to the viewport or
559   // canvas frame (depending on the `position` value).
560   MOZ_ASSERT(backdropFrame->GetParent()->IsViewportFrame() ||
561              backdropFrame->GetParent()->IsCanvasFrame());
562   nsTArray<nsIFrame*> wrappersToRestyle;
563   ServoRestyleState state(aStyleSet, aChangeList, wrappersToRestyle);
564   aFrame->UpdateStyleOfOwnedChildFrame(backdropFrame, newContext, state);
565 }
566 
UpdateFirstLetterIfNeeded(nsIFrame * aFrame,ServoRestyleState & aRestyleState)567 static void UpdateFirstLetterIfNeeded(nsIFrame* aFrame,
568                                       ServoRestyleState& aRestyleState) {
569   MOZ_ASSERT(
570       !aFrame->IsFrameOfType(nsIFrame::eBlockFrame),
571       "You're probably duplicating work with UpdatePseudoElementStyles!");
572   if (!aFrame->HasFirstLetterChild()) {
573     return;
574   }
575 
576   // We need to find the block the first-letter is associated with so we can
577   // find the right element for the first-letter's style resolution.  Might as
578   // well just delegate the whole thing to that block.
579   nsIFrame* block = aFrame->GetParent();
580   while (!block->IsFrameOfType(nsIFrame::eBlockFrame)) {
581     block = block->GetParent();
582   }
583 
584   static_cast<nsBlockFrame*>(block->FirstContinuation())
585       ->UpdateFirstLetterStyle(aRestyleState);
586 }
587 
UpdateOneAdditionalStyleContext(nsIFrame * aFrame,uint32_t aIndex,ServoStyleContext & aOldContext,ServoRestyleState & aRestyleState)588 static void UpdateOneAdditionalStyleContext(nsIFrame* aFrame, uint32_t aIndex,
589                                             ServoStyleContext& aOldContext,
590                                             ServoRestyleState& aRestyleState) {
591   auto pseudoType = aOldContext.GetPseudoType();
592   MOZ_ASSERT(pseudoType != CSSPseudoElementType::NotPseudo);
593   MOZ_ASSERT(
594       !nsCSSPseudoElements::PseudoElementSupportsUserActionState(pseudoType));
595 
596   RefPtr<ServoStyleContext> newContext =
597       aRestyleState.StyleSet().ResolvePseudoElementStyle(
598           aFrame->GetContent()->AsElement(), pseudoType,
599           aFrame->StyleContext()->AsServo(),
600           /* aPseudoElement = */ nullptr);
601 
602   uint32_t equalStructs, samePointerStructs;  // Not used, actually.
603   nsChangeHint childHint = aOldContext.CalcStyleDifference(
604       newContext, &equalStructs, &samePointerStructs);
605   if (!aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
606     childHint = NS_RemoveSubsumedHints(
607         childHint, aRestyleState.ChangesHandledFor(*aFrame));
608   }
609 
610   if (childHint) {
611     if (childHint & nsChangeHint_ReconstructFrame) {
612       // If we generate a reconstruct here, remove any non-reconstruct hints we
613       // may have already generated for this content.
614       aRestyleState.ChangeList().PopChangesForContent(aFrame->GetContent());
615     }
616     aRestyleState.ChangeList().AppendChange(aFrame, aFrame->GetContent(),
617                                             childHint);
618   }
619 
620   aFrame->SetAdditionalStyleContext(aIndex, newContext);
621 }
622 
UpdateAdditionalStyleContexts(nsIFrame * aFrame,ServoRestyleState & aRestyleState)623 static void UpdateAdditionalStyleContexts(nsIFrame* aFrame,
624                                           ServoRestyleState& aRestyleState) {
625   MOZ_ASSERT(aFrame);
626   MOZ_ASSERT(aFrame->GetContent() && aFrame->GetContent()->IsElement());
627 
628   // FIXME(emilio): Consider adding a bit or something to avoid the initial
629   // virtual call?
630   uint32_t index = 0;
631   while (auto* oldContext = aFrame->GetAdditionalStyleContext(index)) {
632     UpdateOneAdditionalStyleContext(aFrame, index++, *oldContext->AsServo(),
633                                     aRestyleState);
634   }
635 }
636 
UpdateFramePseudoElementStyles(nsIFrame * aFrame,ServoRestyleState & aRestyleState)637 static void UpdateFramePseudoElementStyles(nsIFrame* aFrame,
638                                            ServoRestyleState& aRestyleState) {
639   if (aFrame->IsFrameOfType(nsIFrame::eBlockFrame)) {
640     static_cast<nsBlockFrame*>(aFrame)->UpdatePseudoElementStyles(
641         aRestyleState);
642   } else {
643     UpdateFirstLetterIfNeeded(aFrame, aRestyleState);
644   }
645 
646   UpdateBackdropIfNeeded(aFrame, aRestyleState.StyleSet(),
647                          aRestyleState.ChangeList());
648 }
649 
650 enum class ServoPostTraversalFlags : uint32_t {
651   Empty = 0,
652   // Whether parent was restyled.
653   ParentWasRestyled = 1 << 0,
654   // Skip sending accessibility notifications for all descendants.
655   SkipA11yNotifications = 1 << 1,
656   // Always send accessibility notifications if the element is shown.
657   // The SkipA11yNotifications flag above overrides this flag.
658   SendA11yNotificationsIfShown = 1 << 2,
659 };
660 
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(ServoPostTraversalFlags)661 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(ServoPostTraversalFlags)
662 
663 // Send proper accessibility notifications and return post traversal
664 // flags for kids.
665 static ServoPostTraversalFlags SendA11yNotifications(
666     nsPresContext* aPresContext, Element* aElement,
667     nsStyleContext* aOldStyleContext, nsStyleContext* aNewStyleContext,
668     ServoPostTraversalFlags aFlags) {
669   using Flags = ServoPostTraversalFlags;
670   MOZ_ASSERT(!(aFlags & Flags::SkipA11yNotifications) ||
671                  !(aFlags & Flags::SendA11yNotificationsIfShown),
672              "The two a11y flags should never be set together");
673 
674 #ifdef ACCESSIBILITY
675   nsAccessibilityService* accService = GetAccService();
676   if (!accService) {
677     // If we don't have accessibility service, accessibility is not
678     // enabled. Just skip everything.
679     return Flags::Empty;
680   }
681   if (aFlags & Flags::SkipA11yNotifications) {
682     // Propogate the skipping flag to descendants.
683     return Flags::SkipA11yNotifications;
684   }
685 
686   bool needsNotify = false;
687   bool isVisible = aNewStyleContext->StyleVisibility()->IsVisible();
688   if (aFlags & Flags::SendA11yNotificationsIfShown) {
689     if (!isVisible) {
690       // Propagate the sending-if-shown flag to descendants.
691       return Flags::SendA11yNotificationsIfShown;
692     }
693     // We have asked accessibility service to remove the whole subtree
694     // of element which becomes invisible from the accessible tree, but
695     // this element is visible, so we need to add it back.
696     needsNotify = true;
697   } else {
698     // If we shouldn't skip in any case, we need to check whether our
699     // own visibility has changed.
700     bool wasVisible = aOldStyleContext->StyleVisibility()->IsVisible();
701     needsNotify = wasVisible != isVisible;
702   }
703 
704   if (needsNotify) {
705     nsIPresShell* presShell = aPresContext->PresShell();
706     if (isVisible) {
707       accService->ContentRangeInserted(presShell, aElement->GetParent(),
708                                        aElement, aElement->GetNextSibling());
709       // We are adding the subtree. Accessibility service would handle
710       // descendants, so we should just skip them from notifying.
711       return Flags::SkipA11yNotifications;
712     }
713     // Remove the subtree of this invisible element, and ask any shown
714     // descendant to add themselves back.
715     accService->ContentRemoved(presShell, aElement);
716     return Flags::SendA11yNotificationsIfShown;
717   }
718 #endif
719 
720   return Flags::Empty;
721 }
722 
ProcessPostTraversal(Element * aElement,ServoStyleContext * aParentContext,ServoRestyleState & aRestyleState,ServoPostTraversalFlags aFlags)723 bool ServoRestyleManager::ProcessPostTraversal(
724     Element* aElement, ServoStyleContext* aParentContext,
725     ServoRestyleState& aRestyleState, ServoPostTraversalFlags aFlags) {
726   nsIFrame* styleFrame = nsLayoutUtils::GetStyleFrame(aElement);
727   nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
728 
729   // NOTE(emilio): This is needed because for table frames the bit is set on the
730   // table wrapper (which is the primary frame), not on the table itself.
731   const bool isOutOfFlow =
732       primaryFrame && primaryFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
733 
734   // Grab the change hint from Servo.
735   bool wasRestyled;
736   nsChangeHint changeHint =
737       static_cast<nsChangeHint>(Servo_TakeChangeHint(aElement, &wasRestyled));
738 
739   // We should really fix the weird primary frame mapping for image maps
740   // (bug 135040)...
741   if (styleFrame && styleFrame->GetContent() != aElement) {
742     MOZ_ASSERT(static_cast<nsImageFrame*>(do_QueryFrame(styleFrame)));
743     styleFrame = nullptr;
744   }
745 
746   // Handle lazy frame construction by posting a reconstruct for any lazily-
747   // constructed roots.
748   if (aElement->HasFlag(NODE_NEEDS_FRAME)) {
749     changeHint |= nsChangeHint_ReconstructFrame;
750     MOZ_ASSERT(!styleFrame);
751   }
752 
753   if (styleFrame) {
754     MOZ_ASSERT(primaryFrame);
755 
756     nsIFrame* maybeAnonBoxChild;
757     if (isOutOfFlow) {
758       maybeAnonBoxChild = primaryFrame->GetPlaceholderFrame();
759     } else {
760       maybeAnonBoxChild = primaryFrame;
761       changeHint = NS_RemoveSubsumedHints(
762           changeHint, aRestyleState.ChangesHandledFor(*styleFrame));
763     }
764 
765     // If the parent wasn't restyled, the styles of our anon box parents won't
766     // change either.
767     if ((aFlags & ServoPostTraversalFlags::ParentWasRestyled) &&
768         maybeAnonBoxChild->ParentIsWrapperAnonBox()) {
769       aRestyleState.AddPendingWrapperRestyle(
770           ServoRestyleState::TableAwareParentFor(maybeAnonBoxChild));
771     }
772   }
773 
774   // Although we shouldn't generate non-ReconstructFrame hints for elements with
775   // no frames, we can still get them here if they were explicitly posted by
776   // PostRestyleEvent, such as a RepaintFrame hint when a :link changes to be
777   // :visited.  Skip processing these hints if there is no frame.
778   if ((styleFrame || (changeHint & nsChangeHint_ReconstructFrame)) &&
779       changeHint) {
780     aRestyleState.ChangeList().AppendChange(styleFrame, aElement, changeHint);
781   }
782 
783   // If our change hint is reconstruct, we delegate to the frame constructor,
784   // which consumes the new style and expects the old style to be on the frame.
785   //
786   // XXXbholley: We should teach the frame constructor how to clear the dirty
787   // descendants bit to avoid the traversal here.
788   if (changeHint & nsChangeHint_ReconstructFrame) {
789     ClearRestyleStateFromSubtree(aElement);
790     return true;
791   }
792 
793   // TODO(emilio): We could avoid some refcount traffic here, specially in the
794   // ServoStyleContext case, which uses atomic refcounting.
795   //
796   // Hold the old style context alive, because it could become a dangling
797   // pointer during the replacement. In practice it's not a huge deal, but
798   // better not playing with dangling pointers if not needed.
799   RefPtr<ServoStyleContext> oldStyleContext =
800       styleFrame ? styleFrame->StyleContext()->AsServo() : nullptr;
801 
802   nsStyleContext* displayContentsStyle = nullptr;
803   // FIXME(emilio, bug 1303605): This can be simpler for Servo.
804   // Note that we intentionally don't check for display: none content.
805   if (!oldStyleContext) {
806     displayContentsStyle =
807         PresContext()->FrameConstructor()->GetDisplayContentsStyleFor(aElement);
808     if (displayContentsStyle) {
809       oldStyleContext = displayContentsStyle->AsServo();
810     }
811   }
812 
813   Maybe<ServoRestyleState> thisFrameRestyleState;
814   if (styleFrame) {
815     auto type = isOutOfFlow ? ServoRestyleState::Type::OutOfFlow
816                             : ServoRestyleState::Type::InFlow;
817 
818     thisFrameRestyleState.emplace(*styleFrame, aRestyleState, changeHint, type);
819   }
820 
821   // We can't really assume as used changes from display: contents elements (or
822   // other elements without frames).
823   ServoRestyleState& childrenRestyleState =
824       thisFrameRestyleState ? *thisFrameRestyleState : aRestyleState;
825 
826   RefPtr<ServoStyleContext> upToDateContext =
827       wasRestyled ? aRestyleState.StyleSet().ResolveServoStyle(aElement)
828                   : oldStyleContext;
829 
830   ServoPostTraversalFlags childrenFlags =
831       wasRestyled ? ServoPostTraversalFlags::ParentWasRestyled
832                   : ServoPostTraversalFlags::Empty;
833 
834   if (wasRestyled && oldStyleContext) {
835     MOZ_ASSERT(styleFrame || displayContentsStyle);
836     MOZ_ASSERT(oldStyleContext->ComputedData() !=
837                upToDateContext->ComputedData());
838 
839     upToDateContext->ResolveSameStructsAs(oldStyleContext);
840 
841     // We want to walk all the continuations here, even the ones with different
842     // styles.  In practice, the only reason we get continuations with different
843     // styles here is ::first-line (::first-letter never affects element
844     // styles).  But in that case, newContext is the right context for the
845     // _later_ continuations anyway (the ones not affected by ::first-line), not
846     // the earlier ones, so there is no point stopping right at the point when
847     // we'd actually be setting the right style context.
848     //
849     // This does mean that we may be setting the wrong style context on our
850     // initial continuations; ::first-line fixes that up after the fact.
851     for (nsIFrame* f = styleFrame; f; f = f->GetNextContinuation()) {
852       MOZ_ASSERT_IF(f != styleFrame, !f->GetAdditionalStyleContext(0));
853       f->SetStyleContext(upToDateContext);
854     }
855 
856     if (MOZ_UNLIKELY(displayContentsStyle)) {
857       MOZ_ASSERT(!styleFrame);
858       PresContext()
859           ->FrameConstructor()
860           ->ChangeRegisteredDisplayContentsStyleFor(aElement, upToDateContext);
861     }
862 
863     if (styleFrame) {
864       UpdateAdditionalStyleContexts(styleFrame, aRestyleState);
865     }
866 
867     if (!aElement->GetParent()) {
868       // This is the root.  Update styles on the viewport as needed.
869       ViewportFrame* viewport =
870           do_QueryFrame(mPresContext->PresShell()->GetRootFrame());
871       if (viewport) {
872         // NB: The root restyle state, not the one for our children!
873         viewport->UpdateStyle(aRestyleState);
874       }
875     }
876 
877     // Some changes to animations don't affect the computed style and yet still
878     // require the layer to be updated. For example, pausing an animation via
879     // the Web Animations API won't affect an element's style but still
880     // requires to update the animation on the layer.
881     //
882     // We can sometimes reach this when the animated style is being removed.
883     // Since AddLayerChangesForAnimation checks if |styleFrame| has a transform
884     // style or not, we need to call it *after* setting |newContext| to
885     // |styleFrame| to ensure the animated transform has been removed first.
886     AddLayerChangesForAnimation(styleFrame, aElement,
887                                 aRestyleState.ChangeList());
888 
889     childrenFlags |= SendA11yNotifications(
890         mPresContext, aElement, oldStyleContext, upToDateContext, aFlags);
891   }
892 
893   const bool traverseElementChildren =
894       aElement->HasAnyOfFlags(Element::kAllServoDescendantBits);
895   const bool traverseTextChildren =
896       wasRestyled || aElement->HasFlag(NODE_DESCENDANTS_NEED_FRAMES);
897   bool recreatedAnyContext = wasRestyled;
898   if (traverseElementChildren || traverseTextChildren) {
899     StyleChildrenIterator it(aElement);
900     TextPostTraversalState textState(*aElement, upToDateContext,
901                                      displayContentsStyle && wasRestyled,
902                                      childrenRestyleState);
903     for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
904       if (traverseElementChildren && n->IsElement()) {
905         recreatedAnyContext |=
906             ProcessPostTraversal(n->AsElement(), upToDateContext,
907                                  childrenRestyleState, childrenFlags);
908       } else if (traverseTextChildren && n->IsNodeOfType(nsINode::eTEXT)) {
909         recreatedAnyContext |= ProcessPostTraversalForText(
910             n, textState, childrenRestyleState, childrenFlags);
911       }
912     }
913   }
914 
915   // We want to update frame pseudo-element styles after we've traversed our
916   // kids, because some of those updates (::first-line/::first-letter) need to
917   // modify the styles of the kids, and the child traversal above would just
918   // clobber those modifications.
919   if (styleFrame) {
920     if (wasRestyled) {
921       // Make sure to update anon boxes and pseudo bits after updating text,
922       // otherwise ProcessPostTraversalForText could clobber first-letter
923       // styles, for example.
924       styleFrame->UpdateStyleOfOwnedAnonBoxes(childrenRestyleState);
925     }
926     // Process anon box wrapper frames before ::first-line bits, but _after_
927     // owned anon boxes, since the children wrapper anon boxes could be
928     // inheriting from our own owned anon boxes.
929     childrenRestyleState.ProcessWrapperRestyles(styleFrame);
930     if (wasRestyled) {
931       UpdateFramePseudoElementStyles(styleFrame, childrenRestyleState);
932     } else if (traverseElementChildren &&
933                styleFrame->IsFrameOfType(nsIFrame::eBlockFrame)) {
934       // Even if we were not restyled, if we're a block with a first-line and
935       // one of our descendant elements which is on the first line was restyled,
936       // we need to update the styles of things on the first line, because
937       // they're wrong now.
938       //
939       // FIXME(bz) Could we do better here?  For example, could we keep track of
940       // frames that are "block with a ::first-line so we could avoid
941       // IsFrameOfType() and digging about for the first-line frame if not?
942       // Could we keep track of whether the element children we actually restyle
943       // are affected by first-line?  Something else?  Bug 1385443 tracks making
944       // this better.
945       nsIFrame* firstLineFrame =
946           static_cast<nsBlockFrame*>(styleFrame)->GetFirstLineFrame();
947       if (firstLineFrame) {
948         for (nsIFrame* kid : firstLineFrame->PrincipalChildList()) {
949           ReparentStyleContext(kid);
950         }
951       }
952     }
953   }
954 
955   aElement->UnsetFlags(Element::kAllServoDescendantBits);
956   return recreatedAnyContext;
957 }
958 
ProcessPostTraversalForText(nsIContent * aTextNode,TextPostTraversalState & aPostTraversalState,ServoRestyleState & aRestyleState,ServoPostTraversalFlags aFlags)959 bool ServoRestyleManager::ProcessPostTraversalForText(
960     nsIContent* aTextNode, TextPostTraversalState& aPostTraversalState,
961     ServoRestyleState& aRestyleState, ServoPostTraversalFlags aFlags) {
962   // Handle lazy frame construction.
963   if (aTextNode->HasFlag(NODE_NEEDS_FRAME)) {
964     aPostTraversalState.ChangeList().AppendChange(
965         nullptr, aTextNode, nsChangeHint_ReconstructFrame);
966     return true;
967   }
968 
969   // Handle restyle.
970   nsIFrame* primaryFrame = aTextNode->GetPrimaryFrame();
971   if (!primaryFrame) {
972     return false;
973   }
974 
975   // If the parent wasn't restyled, the styles of our anon box parents won't
976   // change either.
977   if ((aFlags & ServoPostTraversalFlags::ParentWasRestyled) &&
978       primaryFrame->ParentIsWrapperAnonBox()) {
979     aRestyleState.AddPendingWrapperRestyle(
980         ServoRestyleState::TableAwareParentFor(primaryFrame));
981   }
982 
983   nsStyleContext& newContext = aPostTraversalState.ComputeStyle(aTextNode);
984   aPostTraversalState.ComputeHintIfNeeded(aTextNode, primaryFrame, newContext);
985 
986   // We want to walk all the continuations here, even the ones with different
987   // styles.  In practice, the only reasons we get continuations with different
988   // styles are ::first-line and ::first-letter.  But in those cases,
989   // newContext is the right context for the _later_ continuations anyway (the
990   // ones not affected by ::first-line/::first-letter), not the earlier ones,
991   // so there is no point stopping right at the point when we'd actually be
992   // setting the right style context.
993   //
994   // This does mean that we may be setting the wrong style context on our
995   // initial continuations; ::first-line/::first-letter fix that up after the
996   // fact.
997   for (nsIFrame* f = primaryFrame; f; f = f->GetNextContinuation()) {
998     f->SetStyleContext(&newContext);
999   }
1000 
1001   return true;
1002 }
1003 
ClearSnapshots()1004 void ServoRestyleManager::ClearSnapshots() {
1005   for (auto iter = mSnapshots.Iter(); !iter.Done(); iter.Next()) {
1006     iter.Key()->UnsetFlags(ELEMENT_HAS_SNAPSHOT | ELEMENT_HANDLED_SNAPSHOT);
1007     iter.Remove();
1008   }
1009 }
1010 
SnapshotFor(Element * aElement)1011 ServoElementSnapshot& ServoRestyleManager::SnapshotFor(Element* aElement) {
1012   MOZ_ASSERT(!mInStyleRefresh);
1013 
1014   // NOTE(emilio): We can handle snapshots from a one-off restyle of those that
1015   // we do to restyle stuff for reconstruction, for example.
1016   //
1017   // It seems to be the case that we always flush in between that happens and
1018   // the next attribute change, so we can assert that we haven't handled the
1019   // snapshot here yet. If this assertion didn't hold, we'd need to unset that
1020   // flag from here too.
1021   //
1022   // Can't wait to make ProcessPendingRestyles the only entry-point for styling,
1023   // so this becomes much easier to reason about. Today is not that day though.
1024   MOZ_ASSERT(aElement->HasServoData());
1025   MOZ_ASSERT(!aElement->HasFlag(ELEMENT_HANDLED_SNAPSHOT));
1026 
1027   ServoElementSnapshot* snapshot = mSnapshots.LookupOrAdd(aElement, aElement);
1028   aElement->SetFlags(ELEMENT_HAS_SNAPSHOT);
1029 
1030   // Now that we have a snapshot, make sure a restyle is triggered.
1031   aElement->NoteDirtyForServo();
1032   return *snapshot;
1033 }
1034 
DoProcessPendingRestyles(ServoTraversalFlags aFlags)1035 void ServoRestyleManager::DoProcessPendingRestyles(ServoTraversalFlags aFlags) {
1036   nsPresContext* presContext = PresContext();
1037 
1038   MOZ_ASSERT(presContext->Document(), "No document?  Pshaw!");
1039   // FIXME(emilio): In the "flush animations" case, ideally, we should only
1040   // recascade animation styles running on the compositor, so we shouldn't care
1041   // about other styles, or new rules that apply to the page...
1042   //
1043   // However, that's not true as of right now, see bug 1388031 and bug 1388692.
1044   MOZ_ASSERT((aFlags & ServoTraversalFlags::FlushThrottledAnimations) ||
1045                  !presContext->HasPendingMediaQueryUpdates(),
1046              "Someone forgot to update media queries?");
1047   MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(), "Missing a script blocker!");
1048   MOZ_ASSERT(!mInStyleRefresh, "Reentrant call?");
1049 
1050   if (MOZ_UNLIKELY(!presContext->PresShell()->DidInitialize())) {
1051     // PresShell::FlushPendingNotifications doesn't early-return in the case
1052     // where the PresShell hasn't yet been initialized (and therefore we haven't
1053     // yet done the initial style traversal of the DOM tree). We should arguably
1054     // fix up the callers and assert against this case, but we just detect and
1055     // handle it for now.
1056     return;
1057   }
1058 
1059   // Create a AnimationsWithDestroyedFrame during restyling process to
1060   // stop animations and transitions on elements that have no frame at the end
1061   // of the restyling process.
1062   AnimationsWithDestroyedFrame animationsWithDestroyedFrame(this);
1063 
1064   ServoStyleSet* styleSet = StyleSet();
1065   nsIDocument* doc = presContext->Document();
1066 
1067   // Ensure the refresh driver is active during traversal to avoid mutating
1068   // mActiveTimer and mMostRecentRefresh time.
1069   presContext->RefreshDriver()->MostRecentRefresh();
1070 
1071   // Perform the Servo traversal, and the post-traversal if required. We do this
1072   // in a loop because certain rare paths in the frame constructor (like
1073   // uninstalling XBL bindings) can trigger additional style validations.
1074   mInStyleRefresh = true;
1075   if (mHaveNonAnimationRestyles) {
1076     ++mAnimationGeneration;
1077   }
1078 
1079   if (mRestyleForCSSRuleChanges) {
1080     aFlags |= ServoTraversalFlags::ForCSSRuleChanges;
1081   }
1082 
1083   while (styleSet->StyleDocument(aFlags)) {
1084     ClearSnapshots();
1085 
1086     nsStyleChangeList currentChanges(StyleBackendType::Servo);
1087     bool anyStyleChanged = false;
1088 
1089     // Recreate style contexts, and queue up change hints (which also handle
1090     // lazy frame construction).
1091     {
1092       AutoRestyleTimelineMarker marker(presContext->GetDocShell(), false);
1093       DocumentStyleRootIterator iter(doc->GetServoRestyleRoot());
1094       while (Element* root = iter.GetNextStyleRoot()) {
1095         nsTArray<nsIFrame*> wrappersToRestyle;
1096         ServoRestyleState state(*styleSet, currentChanges, wrappersToRestyle);
1097         ServoPostTraversalFlags flags = ServoPostTraversalFlags::Empty;
1098         anyStyleChanged |= ProcessPostTraversal(root, nullptr, state, flags);
1099       }
1100     }
1101 
1102     doc->ClearServoRestyleRoot();
1103 
1104     // Process the change hints.
1105     //
1106     // Unfortunately, the frame constructor can generate new change hints while
1107     // processing existing ones. We redirect those into a secondary queue and
1108     // iterate until there's nothing left.
1109     {
1110       AutoTimelineMarker marker(presContext->GetDocShell(),
1111                                 "StylesApplyChanges");
1112       ReentrantChangeList newChanges;
1113       mReentrantChanges = &newChanges;
1114       while (!currentChanges.IsEmpty()) {
1115         ProcessRestyledFrames(currentChanges);
1116         MOZ_ASSERT(currentChanges.IsEmpty());
1117         for (ReentrantChange& change : newChanges) {
1118           if (!(change.mHint & nsChangeHint_ReconstructFrame) &&
1119               !change.mContent->GetPrimaryFrame()) {
1120             // SVG Elements post change hints without ensuring that the primary
1121             // frame will be there after that (see bug 1366142).
1122             //
1123             // Just ignore those, since we can't really process them.
1124             continue;
1125           }
1126           currentChanges.AppendChange(change.mContent->GetPrimaryFrame(),
1127                                       change.mContent, change.mHint);
1128         }
1129         newChanges.Clear();
1130       }
1131       mReentrantChanges = nullptr;
1132     }
1133 
1134     if (anyStyleChanged) {
1135       // Maybe no styles changed when:
1136       //
1137       //  * Only explicit change hints were posted in the first place.
1138       //  * When an attribute or state change in the content happens not to need
1139       //    a restyle after all.
1140       //
1141       // In any case, we don't need to increment the restyle generation in that
1142       // case.
1143       IncrementRestyleGeneration();
1144     }
1145   }
1146 
1147   doc->ClearServoRestyleRoot();
1148 
1149   FlushOverflowChangedTracker();
1150 
1151   ClearSnapshots();
1152   styleSet->AssertTreeIsClean();
1153   mHaveNonAnimationRestyles = false;
1154   mRestyleForCSSRuleChanges = false;
1155   mInStyleRefresh = false;
1156 
1157   // Now that everything has settled, see if we have enough free rule nodes in
1158   // the tree to warrant sweeping them.
1159   styleSet->MaybeGCRuleTree();
1160 
1161   // Note: We are in the scope of |animationsWithDestroyedFrame|, so
1162   //       |mAnimationsWithDestroyedFrame| is still valid.
1163   MOZ_ASSERT(mAnimationsWithDestroyedFrame);
1164   mAnimationsWithDestroyedFrame->StopAnimationsForElementsWithoutFrames();
1165 }
1166 
1167 #ifdef DEBUG
VerifyFlatTree(const nsIContent & aContent)1168 static void VerifyFlatTree(const nsIContent& aContent) {
1169   StyleChildrenIterator iter(&aContent);
1170 
1171   for (auto* content = iter.GetNextChild(); content;
1172        content = iter.GetNextChild()) {
1173     MOZ_ASSERT(content->GetFlattenedTreeParentNodeForStyle() == &aContent);
1174     VerifyFlatTree(*content);
1175   }
1176 }
1177 #endif
1178 
ProcessPendingRestyles()1179 void ServoRestyleManager::ProcessPendingRestyles() {
1180 #ifdef DEBUG
1181   if (auto* root = mPresContext->Document()->GetRootElement()) {
1182     VerifyFlatTree(*root);
1183   }
1184 #endif
1185 
1186   DoProcessPendingRestyles(ServoTraversalFlags::Empty);
1187 }
1188 
ProcessAllPendingAttributeAndStateInvalidations()1189 void ServoRestyleManager::ProcessAllPendingAttributeAndStateInvalidations() {
1190   if (mSnapshots.IsEmpty()) {
1191     return;
1192   }
1193   for (auto iter = mSnapshots.Iter(); !iter.Done(); iter.Next()) {
1194     // Servo data for the element might have been dropped. (e.g. by removing
1195     // from its document)
1196     if (iter.Key()->HasFlag(ELEMENT_HAS_SNAPSHOT)) {
1197       Servo_ProcessInvalidations(StyleSet()->RawSet(), iter.Key(), &mSnapshots);
1198     }
1199   }
1200   ClearSnapshots();
1201 }
1202 
HasPendingRestyleAncestor(Element * aElement) const1203 bool ServoRestyleManager::HasPendingRestyleAncestor(Element* aElement) const {
1204   return Servo_HasPendingRestyleAncestor(aElement);
1205 }
1206 
UpdateOnlyAnimationStyles()1207 void ServoRestyleManager::UpdateOnlyAnimationStyles() {
1208   bool doCSS = PresContext()->EffectCompositor()->HasPendingStyleUpdates();
1209   if (!doCSS) {
1210     return;
1211   }
1212 
1213   DoProcessPendingRestyles(ServoTraversalFlags::FlushThrottledAnimations);
1214 }
1215 
ContentStateChanged(nsIContent * aContent,EventStates aChangedBits)1216 void ServoRestyleManager::ContentStateChanged(nsIContent* aContent,
1217                                               EventStates aChangedBits) {
1218   MOZ_ASSERT(!mInStyleRefresh);
1219 
1220   if (!aContent->IsElement()) {
1221     return;
1222   }
1223 
1224   Element* aElement = aContent->AsElement();
1225   if (!aElement->HasServoData()) {
1226     return;
1227   }
1228 
1229   const EventStates kVisitedAndUnvisited =
1230       NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED;
1231   if (aChangedBits.HasAllStates(kVisitedAndUnvisited) &&
1232       !Gecko_VisitedStylesEnabled(aElement->OwnerDoc())) {
1233     aChangedBits &= ~kVisitedAndUnvisited;
1234     if (aChangedBits.IsEmpty()) {
1235       return;
1236     }
1237   }
1238 
1239   nsChangeHint changeHint;
1240   ContentStateChangedInternal(aElement, aChangedBits, &changeHint);
1241 
1242   // Don't bother taking a snapshot if no rules depend on these state bits.
1243   //
1244   // We always take a snapshot for the LTR/RTL event states, since Servo doesn't
1245   // track those bits in the same way, and we know that :dir() rules are always
1246   // present in UA style sheets.
1247   if (!aChangedBits.HasAtLeastOneOfStates(DIRECTION_STATES) &&
1248       !StyleSet()->HasStateDependency(*aElement, aChangedBits)) {
1249     return;
1250   }
1251 
1252   ServoElementSnapshot& snapshot = SnapshotFor(aElement);
1253   EventStates previousState = aElement->StyleState() ^ aChangedBits;
1254   snapshot.AddState(previousState);
1255 
1256   if (changeHint) {
1257     Servo_NoteExplicitHints(aElement, nsRestyleHint(0), changeHint);
1258   }
1259 
1260   // Assuming we need to invalidate cached style in getComputedStyle for
1261   // undisplayed elements, since we don't know if it is needed.
1262   IncrementUndisplayedRestyleGeneration();
1263 }
1264 
AttributeInfluencesOtherPseudoClassState(const Element & aElement,const nsAtom * aAttribute)1265 static inline bool AttributeInfluencesOtherPseudoClassState(
1266     const Element& aElement, const nsAtom* aAttribute) {
1267   // We must record some state for :-moz-browser-frame and
1268   // :-moz-table-border-nonzero.
1269   if (aAttribute == nsGkAtoms::mozbrowser) {
1270     return aElement.IsAnyOfHTMLElements(nsGkAtoms::iframe, nsGkAtoms::frame);
1271   }
1272 
1273   if (aAttribute == nsGkAtoms::border) {
1274     return aElement.IsHTMLElement(nsGkAtoms::table);
1275   }
1276 
1277   return false;
1278 }
1279 
NeedToRecordAttrChange(const ServoStyleSet & aStyleSet,const Element & aElement,int32_t aNameSpaceID,nsAtom * aAttribute,bool * aInfluencesOtherPseudoClassState)1280 static inline bool NeedToRecordAttrChange(
1281     const ServoStyleSet& aStyleSet, const Element& aElement,
1282     int32_t aNameSpaceID, nsAtom* aAttribute,
1283     bool* aInfluencesOtherPseudoClassState) {
1284   *aInfluencesOtherPseudoClassState =
1285       AttributeInfluencesOtherPseudoClassState(aElement, aAttribute);
1286 
1287   // If the attribute influences one of the pseudo-classes that are backed by
1288   // attributes, we just record it.
1289   if (*aInfluencesOtherPseudoClassState) {
1290     return true;
1291   }
1292 
1293   // We assume that id and class attributes are used in class/id selectors, and
1294   // thus record them.
1295   //
1296   // TODO(emilio): We keep a filter of the ids in use somewhere in the StyleSet,
1297   // presumably we could try to filter the old and new id, but it's not clear
1298   // it's worth it.
1299   if (aNameSpaceID == kNameSpaceID_None &&
1300       (aAttribute == nsGkAtoms::id || aAttribute == nsGkAtoms::_class)) {
1301     return true;
1302   }
1303 
1304   // We always record lang="", even though we force a subtree restyle when it
1305   // changes, since it can change how its siblings match :lang(..) due to
1306   // selectors like :lang(..) + div.
1307   if (aAttribute == nsGkAtoms::lang) {
1308     return true;
1309   }
1310 
1311   // Otherwise, just record the attribute change if a selector in the page may
1312   // reference it from an attribute selector.
1313   return aStyleSet.MightHaveAttributeDependency(aElement, aAttribute);
1314 }
1315 
AttributeWillChange(Element * aElement,int32_t aNameSpaceID,nsAtom * aAttribute,int32_t aModType,const nsAttrValue * aNewValue)1316 void ServoRestyleManager::AttributeWillChange(Element* aElement,
1317                                               int32_t aNameSpaceID,
1318                                               nsAtom* aAttribute,
1319                                               int32_t aModType,
1320                                               const nsAttrValue* aNewValue) {
1321   TakeSnapshotForAttributeChange(aElement, aNameSpaceID, aAttribute);
1322 }
1323 
ClassAttributeWillBeChangedBySMIL(Element * aElement)1324 void ServoRestyleManager::ClassAttributeWillBeChangedBySMIL(Element* aElement) {
1325   TakeSnapshotForAttributeChange(aElement, kNameSpaceID_None,
1326                                  nsGkAtoms::_class);
1327 }
1328 
TakeSnapshotForAttributeChange(Element * aElement,int32_t aNameSpaceID,nsAtom * aAttribute)1329 void ServoRestyleManager::TakeSnapshotForAttributeChange(Element* aElement,
1330                                                          int32_t aNameSpaceID,
1331                                                          nsAtom* aAttribute) {
1332   MOZ_ASSERT(!mInStyleRefresh);
1333 
1334   if (!aElement->HasServoData()) {
1335     return;
1336   }
1337 
1338   bool influencesOtherPseudoClassState;
1339   if (!NeedToRecordAttrChange(*StyleSet(), *aElement, aNameSpaceID, aAttribute,
1340                               &influencesOtherPseudoClassState)) {
1341     return;
1342   }
1343 
1344   // We cannot tell if the attribute change will affect the styles of
1345   // undisplayed elements, because we don't actually restyle those elements
1346   // during the restyle traversal. So just assume that the attribute change can
1347   // cause the style to change.
1348   IncrementUndisplayedRestyleGeneration();
1349 
1350   // Some other random attribute changes may also affect the transitions,
1351   // so we also set this true here.
1352   mHaveNonAnimationRestyles = true;
1353 
1354   ServoElementSnapshot& snapshot = SnapshotFor(aElement);
1355   snapshot.AddAttrs(aElement, aNameSpaceID, aAttribute);
1356 
1357   if (influencesOtherPseudoClassState) {
1358     snapshot.AddOtherPseudoClassState(aElement);
1359   }
1360 }
1361 
1362 // For some attribute changes we must restyle the whole subtree:
1363 //
1364 // * <td> is affected by the cellpadding on its ancestor table
1365 // * lwtheme and lwthemetextcolor on root element of XUL document
1366 //   affects all descendants due to :-moz-lwtheme* pseudo-classes
1367 // * lang="" and xml:lang="" can affect all descendants due to :lang()
1368 //
AttributeChangeRequiresSubtreeRestyle(const Element & aElement,nsAtom * aAttr)1369 static inline bool AttributeChangeRequiresSubtreeRestyle(
1370     const Element& aElement, nsAtom* aAttr) {
1371   if (aAttr == nsGkAtoms::cellpadding) {
1372     return aElement.IsHTMLElement(nsGkAtoms::table);
1373   }
1374   if (aAttr == nsGkAtoms::lwtheme || aAttr == nsGkAtoms::lwthemetextcolor) {
1375     return aElement.GetNameSpaceID() == kNameSpaceID_XUL &&
1376            &aElement == aElement.OwnerDoc()->GetRootElement();
1377   }
1378 
1379   return aAttr == nsGkAtoms::lang;
1380 }
1381 
AttributeChanged(Element * aElement,int32_t aNameSpaceID,nsAtom * aAttribute,int32_t aModType,const nsAttrValue * aOldValue)1382 void ServoRestyleManager::AttributeChanged(Element* aElement,
1383                                            int32_t aNameSpaceID,
1384                                            nsAtom* aAttribute, int32_t aModType,
1385                                            const nsAttrValue* aOldValue) {
1386   MOZ_ASSERT(!mInStyleRefresh);
1387 
1388   auto changeHint = nsChangeHint(0);
1389   auto restyleHint = nsRestyleHint(0);
1390 
1391   changeHint |= aElement->GetAttributeChangeHint(aAttribute, aModType);
1392 
1393   if (aAttribute == nsGkAtoms::style) {
1394     restyleHint |= eRestyle_StyleAttribute;
1395   } else if (AttributeChangeRequiresSubtreeRestyle(*aElement, aAttribute)) {
1396     restyleHint |= eRestyle_Subtree;
1397   } else if (aElement->IsAttributeMapped(aAttribute)) {
1398     restyleHint |= eRestyle_Self;
1399   }
1400 
1401   if (nsIFrame* primaryFrame = aElement->GetPrimaryFrame()) {
1402     // See if we have appearance information for a theme.
1403     const nsStyleDisplay* disp = primaryFrame->StyleDisplay();
1404     if (disp->mAppearance) {
1405       nsITheme* theme = PresContext()->GetTheme();
1406       if (theme && theme->ThemeSupportsWidget(PresContext(), primaryFrame,
1407                                               disp->mAppearance)) {
1408         bool repaint = false;
1409         theme->WidgetStateChanged(primaryFrame, disp->mAppearance, aAttribute,
1410                                   &repaint, aOldValue);
1411         if (repaint) {
1412           changeHint |= nsChangeHint_RepaintFrame;
1413         }
1414       }
1415     }
1416 
1417     primaryFrame->AttributeChanged(aNameSpaceID, aAttribute, aModType);
1418   }
1419 
1420   if (restyleHint || changeHint) {
1421     Servo_NoteExplicitHints(aElement, restyleHint, changeHint);
1422   }
1423 
1424   if (restyleHint) {
1425     // Assuming we need to invalidate cached style in getComputedStyle for
1426     // undisplayed elements, since we don't know if it is needed.
1427     IncrementUndisplayedRestyleGeneration();
1428 
1429     // If we change attributes, we have to mark this to be true, so we will
1430     // increase the animation generation for the new created transition if any.
1431     mHaveNonAnimationRestyles = true;
1432   }
1433 }
1434 
ReparentStyleContext(nsIFrame * aFrame)1435 nsresult ServoRestyleManager::ReparentStyleContext(nsIFrame* aFrame) {
1436 // This is only called when moving frames in or out of the first-line
1437 // pseudo-element (or one of its descendants).  We can't say much about
1438 // aFrame's ancestors, unfortunately (e.g. during a dynamic insert into
1439 // something inside an inline-block on the first line the ancestors could be
1440 // totally arbitrary), but we will definitely find a line frame on the
1441 // ancestor chain.  Note that the lineframe may not actually be the one that
1442 // corresponds to ::first-line; when we're moving _out_ of the ::first-line it
1443 // will be one of the continuations instead.
1444 #ifdef DEBUG
1445   {
1446     nsIFrame* f = aFrame->GetParent();
1447     while (f && !f->IsLineFrame()) {
1448       f = f->GetParent();
1449     }
1450     MOZ_ASSERT(f, "Must have found a first-line frame");
1451   }
1452 #endif
1453 
1454   DoReparentStyleContext(aFrame, *StyleSet());
1455 
1456   return NS_OK;
1457 }
1458 
DoReparentStyleContext(nsIFrame * aFrame,ServoStyleSet & aStyleSet)1459 void ServoRestyleManager::DoReparentStyleContext(nsIFrame* aFrame,
1460                                                  ServoStyleSet& aStyleSet) {
1461   if (aFrame->IsBackdropFrame()) {
1462     // Style context of backdrop frame has no parent style context, and
1463     // thus we do not need to reparent it.
1464     return;
1465   }
1466 
1467   if (aFrame->IsPlaceholderFrame()) {
1468     // Also reparent the out-of-flow and all its continuations.  We're doing
1469     // this to match Gecko for now, but it's not clear that this behavior is
1470     // correct per spec.  It's certainly pretty odd for out-of-flows whose
1471     // containing block is not within the first line.
1472     //
1473     // Right now we're somewhat inconsistent in this testcase:
1474     //
1475     //  <style>
1476     //    div { color: orange; clear: left; }
1477     //    div::first-line { color: blue; }
1478     //  </style>
1479     //  <div>
1480     //    <span style="float: left">What color is this text?</span>
1481     //  </div>
1482     //  <div>
1483     //    <span><span style="float: left">What color is this text?</span></span>
1484     //  </div>
1485     //
1486     // We make the first float orange and the second float blue.  On the other
1487     // hand, if the float were within an inline-block that was on the first
1488     // line, arguably it _should_ inherit from the ::first-line...
1489     nsIFrame* outOfFlow =
1490         nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame);
1491     MOZ_ASSERT(outOfFlow, "no out-of-flow frame");
1492     for (; outOfFlow; outOfFlow = outOfFlow->GetNextContinuation()) {
1493       DoReparentStyleContext(outOfFlow, aStyleSet);
1494     }
1495   }
1496 
1497   nsIFrame* providerFrame;
1498   nsStyleContext* newParentContext =
1499       aFrame->GetParentStyleContext(&providerFrame);
1500   // If our provider is our child, we want to reparent it first, because we
1501   // inherit style from it.
1502   bool isChild = providerFrame && providerFrame->GetParent() == aFrame;
1503   nsIFrame* providerChild = nullptr;
1504   if (isChild) {
1505     DoReparentStyleContext(providerFrame, aStyleSet);
1506     // Get the style context again after ReparentStyleContext() which might have
1507     // changed it.
1508     newParentContext = providerFrame->StyleContext();
1509     providerChild = providerFrame;
1510     MOZ_ASSERT(!providerFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW),
1511                "Out of flow provider?");
1512   }
1513 
1514   if (!newParentContext) {
1515     // No need to do anything here for this frame, but we should still reparent
1516     // its descendants, because those may have styles that inherit from the
1517     // parent of this frame (e.g. non-anonymous columns in an anonymous
1518     // colgroup).
1519     MOZ_ASSERT(aFrame->StyleContext()->IsNonInheritingAnonBox(),
1520                "Why did this frame not end up with a parent context?");
1521     ReparentFrameDescendants(aFrame, providerChild, aStyleSet);
1522     return;
1523   }
1524 
1525   bool isElement = aFrame->GetContent()->IsElement();
1526 
1527   // We probably don't want to initiate transitions from
1528   // ReparentStyleContext, since we call it during frame
1529   // construction rather than in response to dynamic changes.
1530   // Also see the comment at the start of
1531   // nsTransitionManager::ConsiderInitiatingTransition.
1532   //
1533   // We don't try to do the fancy copying from previous continuations that
1534   // GeckoRestyleManager does here, because that relies on knowing the parents
1535   // of style contexts, and we don't know those.
1536   ServoStyleContext* oldContext = aFrame->StyleContext()->AsServo();
1537   Element* ourElement =
1538       oldContext->GetPseudoType() == CSSPseudoElementType::NotPseudo &&
1539               isElement
1540           ? aFrame->GetContent()->AsElement()
1541           : nullptr;
1542   ServoStyleContext* newParent = newParentContext->AsServo();
1543 
1544   ServoStyleContext* newParentIgnoringFirstLine;
1545   if (newParent->GetPseudoType() == CSSPseudoElementType::firstLine) {
1546     MOZ_ASSERT(providerFrame && providerFrame->GetParent()->IsFrameOfType(
1547                                     nsIFrame::eBlockFrame),
1548                "How could we get a ::first-line parent style without having "
1549                "a ::first-line provider frame?");
1550     // If newParent is a ::first-line style, get the parent blockframe, and then
1551     // correct it for our pseudo as needed (e.g. stepping out of anon boxes).
1552     // Use the resulting style for the "parent style ignoring ::first-line".
1553     nsIFrame* blockFrame = providerFrame->GetParent();
1554     nsIFrame* correctedFrame =
1555         nsFrame::CorrectStyleParentFrame(blockFrame, oldContext->GetPseudo());
1556     newParentIgnoringFirstLine = correctedFrame->StyleContext()->AsServo();
1557   } else {
1558     newParentIgnoringFirstLine = newParent;
1559   }
1560 
1561   if (!providerFrame) {
1562     // No providerFrame means we inherited from a display:contents thing.  Our
1563     // layout parent style is the style of our nearest ancestor frame.  But we
1564     // have to be careful to do that with our placeholder, not with us, if we're
1565     // out of flow.
1566     if (aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
1567       aFrame->GetPlaceholderFrame()->GetLayoutParentStyleForOutOfFlow(
1568           &providerFrame);
1569     } else {
1570       providerFrame = nsFrame::CorrectStyleParentFrame(aFrame->GetParent(),
1571                                                        oldContext->GetPseudo());
1572     }
1573   }
1574   ServoStyleContext* layoutParent = providerFrame->StyleContext()->AsServo();
1575 
1576   RefPtr<ServoStyleContext> newContext = aStyleSet.ReparentStyleContext(
1577       oldContext, newParent, newParentIgnoringFirstLine, layoutParent,
1578       ourElement);
1579   aFrame->SetStyleContext(newContext);
1580 
1581   // This logic somewhat mirrors the logic in
1582   // ServoRestyleManager::ProcessPostTraversal.
1583   if (isElement) {
1584     // We can't use UpdateAdditionalStyleContexts as-is because it needs a
1585     // ServoRestyleState and maintaining one of those during a _frametree_
1586     // traversal is basically impossible.
1587     uint32_t index = 0;
1588     while (nsStyleContext* oldAdditionalContext =
1589                aFrame->GetAdditionalStyleContext(index)) {
1590       RefPtr<ServoStyleContext> newAdditionalContext =
1591           aStyleSet.ReparentStyleContext(oldAdditionalContext->AsServo(),
1592                                          newContext, newContext, newContext,
1593                                          nullptr);
1594       aFrame->SetAdditionalStyleContext(index, newAdditionalContext);
1595       ++index;
1596     }
1597   }
1598 
1599   // Generally, owned anon boxes are our descendants.  The only exceptions are
1600   // tables (for the table wrapper) and inline frames (for the block part of the
1601   // block-in-inline split).  We're going to update our descendants when looping
1602   // over kids, and we don't want to update the block part of a block-in-inline
1603   // split if the inline is on the first line but the block is not (and if the
1604   // block is, it's the child of something else on the first line and will get
1605   // updated as a child).  And given how this method ends up getting called, if
1606   // we reach here for a table frame, we are already in the middle of
1607   // reparenting the table wrapper frame.  So no need to
1608   // UpdateStyleOfOwnedAnonBoxes() here.
1609 
1610   ReparentFrameDescendants(aFrame, providerChild, aStyleSet);
1611 
1612   // We do not need to do the equivalent of UpdateFramePseudoElementStyles,
1613   // because those are hadled by our descendant walk.
1614 }
1615 
ReparentFrameDescendants(nsIFrame * aFrame,nsIFrame * aProviderChild,ServoStyleSet & aStyleSet)1616 void ServoRestyleManager::ReparentFrameDescendants(nsIFrame* aFrame,
1617                                                    nsIFrame* aProviderChild,
1618                                                    ServoStyleSet& aStyleSet) {
1619   nsIFrame::ChildListIterator lists(aFrame);
1620   for (; !lists.IsDone(); lists.Next()) {
1621     for (nsIFrame* child : lists.CurrentList()) {
1622       // only do frames that are in flow
1623       if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
1624           child != aProviderChild) {
1625         DoReparentStyleContext(child, aStyleSet);
1626       }
1627     }
1628   }
1629 }
1630 
1631 }  // namespace mozilla
1632