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/RestyleManager.h"
8 
9 #include "mozilla/AutoRestyleTimelineMarker.h"
10 #include "mozilla/AutoTimelineMarker.h"
11 #include "mozilla/ComputedStyle.h"
12 #include "mozilla/ComputedStyleInlines.h"
13 #include "mozilla/DocumentStyleRootIterator.h"
14 #include "mozilla/EffectSet.h"
15 #include "mozilla/GeckoBindings.h"
16 #include "mozilla/LayerAnimationInfo.h"
17 #include "mozilla/layers/AnimationInfo.h"
18 #include "mozilla/layout/ScrollAnchorContainer.h"
19 #include "mozilla/PresShell.h"
20 #include "mozilla/PresShellInlines.h"
21 #include "mozilla/ProfilerLabels.h"
22 #include "mozilla/ServoBindings.h"
23 #include "mozilla/ServoStyleSetInlines.h"
24 #include "mozilla/StaticPrefs_layout.h"
25 #include "mozilla/SVGIntegrationUtils.h"
26 #include "mozilla/SVGObserverUtils.h"
27 #include "mozilla/SVGTextFrame.h"
28 #include "mozilla/SVGUtils.h"
29 #include "mozilla/Unused.h"
30 #include "mozilla/ViewportFrame.h"
31 #include "mozilla/IntegerRange.h"
32 #include "mozilla/dom/ChildIterator.h"
33 #include "mozilla/dom/DocumentInlines.h"
34 #include "mozilla/dom/ElementInlines.h"
35 #include "mozilla/dom/HTMLBodyElement.h"
36 
37 #include "Layers.h"
38 #include "nsAnimationManager.h"
39 #include "nsBlockFrame.h"
40 #include "nsContentUtils.h"
41 #include "nsCSSFrameConstructor.h"
42 #include "nsCSSRendering.h"
43 #include "nsDocShell.h"
44 #include "nsIFrame.h"
45 #include "nsIFrameInlines.h"
46 #include "nsImageFrame.h"
47 #include "nsPlaceholderFrame.h"
48 #include "nsPrintfCString.h"
49 #include "nsRefreshDriver.h"
50 #include "nsStyleChangeList.h"
51 #include "nsStyleUtil.h"
52 #include "nsTransitionManager.h"
53 #include "StickyScrollContainer.h"
54 #include "ActiveLayerTracker.h"
55 
56 #ifdef ACCESSIBILITY
57 #  include "nsAccessibilityService.h"
58 #endif
59 
60 using mozilla::layers::AnimationInfo;
61 using mozilla::layout::ScrollAnchorContainer;
62 
63 using namespace mozilla::dom;
64 using namespace mozilla::layers;
65 
66 namespace mozilla {
67 
RestyleManager(nsPresContext * aPresContext)68 RestyleManager::RestyleManager(nsPresContext* aPresContext)
69     : mPresContext(aPresContext),
70       mRestyleGeneration(1),
71       mUndisplayedRestyleGeneration(1),
72       mInStyleRefresh(false),
73       mAnimationGeneration(0) {
74   MOZ_ASSERT(mPresContext);
75 }
76 
ContentInserted(nsIContent * aChild)77 void RestyleManager::ContentInserted(nsIContent* aChild) {
78   MOZ_ASSERT(aChild->GetParentNode());
79   RestyleForInsertOrChange(aChild);
80 }
81 
ContentAppended(nsIContent * aFirstNewContent)82 void RestyleManager::ContentAppended(nsIContent* aFirstNewContent) {
83   MOZ_ASSERT(aFirstNewContent->GetParent());
84 
85   // The container cannot be a document, but might be a ShadowRoot.
86   if (!aFirstNewContent->GetParentNode()->IsElement()) {
87     return;
88   }
89   Element* container = aFirstNewContent->GetParentNode()->AsElement();
90 
91 #ifdef DEBUG
92   {
93     for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) {
94       NS_ASSERTION(!cur->IsRootOfNativeAnonymousSubtree(),
95                    "anonymous nodes should not be in child lists");
96     }
97   }
98 #endif
99   uint32_t selectorFlags =
100       container->GetFlags() &
101       (NODE_ALL_SELECTOR_FLAGS & ~NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS);
102   if (selectorFlags == 0) return;
103 
104   if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) {
105     // see whether we need to restyle the container
106     bool wasEmpty = true;  // :empty or :-moz-only-whitespace
107     for (nsIContent* cur = container->GetFirstChild(); cur != aFirstNewContent;
108          cur = cur->GetNextSibling()) {
109       // We don't know whether we're testing :empty or :-moz-only-whitespace,
110       // so be conservative and assume :-moz-only-whitespace (i.e., make
111       // IsSignificantChild less likely to be true, and thus make us more
112       // likely to restyle).
113       if (nsStyleUtil::IsSignificantChild(cur, false)) {
114         wasEmpty = false;
115         break;
116       }
117     }
118     if (wasEmpty) {
119       RestyleForEmptyChange(container);
120       return;
121     }
122   }
123 
124   if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
125     PostRestyleEvent(container, RestyleHint::RestyleSubtree(), nsChangeHint(0));
126     // Restyling the container is the most we can do here, so we're done.
127     return;
128   }
129 
130   if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
131     // restyle the last element child before this node
132     for (nsIContent* cur = aFirstNewContent->GetPreviousSibling(); cur;
133          cur = cur->GetPreviousSibling()) {
134       if (cur->IsElement()) {
135         PostRestyleEvent(cur->AsElement(), RestyleHint::RestyleSubtree(),
136                          nsChangeHint(0));
137         break;
138       }
139     }
140   }
141 }
142 
RestyleSiblingsStartingWith(RestyleManager & aRM,nsIContent * aStartingSibling)143 static void RestyleSiblingsStartingWith(RestyleManager& aRM,
144                                         nsIContent* aStartingSibling) {
145   for (nsIContent* sibling = aStartingSibling; sibling;
146        sibling = sibling->GetNextSibling()) {
147     if (auto* element = Element::FromNode(sibling)) {
148       aRM.PostRestyleEvent(element, RestyleHint::RestyleSubtree(),
149                            nsChangeHint(0));
150     }
151   }
152 }
153 
RestyleForEmptyChange(Element * aContainer)154 void RestyleManager::RestyleForEmptyChange(Element* aContainer) {
155   PostRestyleEvent(aContainer, RestyleHint::RestyleSubtree(), nsChangeHint(0));
156 
157   // In some cases (:empty + E, :empty ~ E), a change in the content of
158   // an element requires restyling its parent's siblings.
159   nsIContent* grandparent = aContainer->GetParent();
160   if (!grandparent ||
161       !(grandparent->GetFlags() & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS)) {
162     return;
163   }
164   RestyleSiblingsStartingWith(*this, aContainer->GetNextSibling());
165 }
166 
MaybeRestyleForEdgeChildChange(Element * aContainer,nsIContent * aChangedChild)167 void RestyleManager::MaybeRestyleForEdgeChildChange(Element* aContainer,
168                                                     nsIContent* aChangedChild) {
169   MOZ_ASSERT(aContainer->GetFlags() & NODE_HAS_EDGE_CHILD_SELECTOR);
170   MOZ_ASSERT(aChangedChild->GetParent() == aContainer);
171   // restyle the previously-first element child if it is after this node
172   bool passedChild = false;
173   for (nsIContent* content = aContainer->GetFirstChild(); content;
174        content = content->GetNextSibling()) {
175     if (content == aChangedChild) {
176       passedChild = true;
177       continue;
178     }
179     if (content->IsElement()) {
180       if (passedChild) {
181         PostRestyleEvent(content->AsElement(), RestyleHint::RestyleSubtree(),
182                          nsChangeHint(0));
183       }
184       break;
185     }
186   }
187   // restyle the previously-last element child if it is before this node
188   passedChild = false;
189   for (nsIContent* content = aContainer->GetLastChild(); content;
190        content = content->GetPreviousSibling()) {
191     if (content == aChangedChild) {
192       passedChild = true;
193       continue;
194     }
195     if (content->IsElement()) {
196       if (passedChild) {
197         PostRestyleEvent(content->AsElement(), RestyleHint::RestyleSubtree(),
198                          nsChangeHint(0));
199       }
200       break;
201     }
202   }
203 }
204 
205 template <typename CharT>
WhitespaceOnly(const CharT * aBuffer,size_t aUpTo)206 bool WhitespaceOnly(const CharT* aBuffer, size_t aUpTo) {
207   for (auto index : IntegerRange(aUpTo)) {
208     if (!dom::IsSpaceCharacter(aBuffer[index])) {
209       return false;
210     }
211   }
212   return true;
213 }
214 
215 template <typename CharT>
WhitespaceOnlyChangedOnAppend(const CharT * aBuffer,size_t aOldLength,size_t aNewLength)216 bool WhitespaceOnlyChangedOnAppend(const CharT* aBuffer, size_t aOldLength,
217                                    size_t aNewLength) {
218   MOZ_ASSERT(aOldLength <= aNewLength);
219   if (!WhitespaceOnly(aBuffer, aOldLength)) {
220     // The old text was already not whitespace-only.
221     return false;
222   }
223 
224   return !WhitespaceOnly(aBuffer + aOldLength, aNewLength - aOldLength);
225 }
226 
HasAnySignificantSibling(Element * aContainer,nsIContent * aChild)227 static bool HasAnySignificantSibling(Element* aContainer, nsIContent* aChild) {
228   MOZ_ASSERT(aChild->GetParent() == aContainer);
229   for (nsIContent* child = aContainer->GetFirstChild(); child;
230        child = child->GetNextSibling()) {
231     if (child == aChild) {
232       continue;
233     }
234     // We don't know whether we're testing :empty or :-moz-only-whitespace,
235     // so be conservative and assume :-moz-only-whitespace (i.e., make
236     // IsSignificantChild less likely to be true, and thus make us more
237     // likely to restyle).
238     if (nsStyleUtil::IsSignificantChild(child, false)) {
239       return true;
240     }
241   }
242 
243   return false;
244 }
245 
CharacterDataChanged(nsIContent * aContent,const CharacterDataChangeInfo & aInfo)246 void RestyleManager::CharacterDataChanged(
247     nsIContent* aContent, const CharacterDataChangeInfo& aInfo) {
248   nsINode* parent = aContent->GetParentNode();
249   MOZ_ASSERT(parent, "How were we notified of a stray node?");
250 
251   uint32_t slowSelectorFlags = parent->GetFlags() & NODE_ALL_SELECTOR_FLAGS;
252   if (!(slowSelectorFlags &
253         (NODE_HAS_EMPTY_SELECTOR | NODE_HAS_EDGE_CHILD_SELECTOR))) {
254     // Nothing to do, no other slow selector can change as a result of this.
255     return;
256   }
257 
258   if (!aContent->IsText()) {
259     // Doesn't matter to styling (could be a processing instruction or a
260     // comment), it can't change whether any selectors match or don't.
261     return;
262   }
263 
264   if (MOZ_UNLIKELY(!parent->IsElement())) {
265     MOZ_ASSERT(parent->IsShadowRoot());
266     return;
267   }
268 
269   if (MOZ_UNLIKELY(aContent->IsRootOfNativeAnonymousSubtree())) {
270     // This is an anonymous node and thus isn't in child lists, so isn't taken
271     // into account for selector matching the relevant selectors here.
272     return;
273   }
274 
275   // Handle appends specially since they're common and we can know both the old
276   // and the new text exactly.
277   //
278   // TODO(emilio): This could be made much more general if :-moz-only-whitespace
279   // / :-moz-first-node and :-moz-last-node didn't exist. In that case we only
280   // need to know whether we went from empty to non-empty, and that's trivial to
281   // know, with CharacterDataChangeInfo...
282   if (!aInfo.mAppend) {
283     // FIXME(emilio): This restyles unnecessarily if the text node is the only
284     // child of the parent element. Fortunately, it's uncommon to have such
285     // nodes and this not being an append.
286     //
287     // See the testcase in bug 1427625 for a test-case that triggers this.
288     RestyleForInsertOrChange(aContent);
289     return;
290   }
291 
292   const nsTextFragment* text = &aContent->AsText()->TextFragment();
293 
294   const size_t oldLength = aInfo.mChangeStart;
295   const size_t newLength = text->GetLength();
296 
297   const bool emptyChanged = !oldLength && newLength;
298 
299   const bool whitespaceOnlyChanged =
300       text->Is2b()
301           ? WhitespaceOnlyChangedOnAppend(text->Get2b(), oldLength, newLength)
302           : WhitespaceOnlyChangedOnAppend(text->Get1b(), oldLength, newLength);
303 
304   if (!emptyChanged && !whitespaceOnlyChanged) {
305     return;
306   }
307 
308   if (slowSelectorFlags & NODE_HAS_EMPTY_SELECTOR) {
309     if (!HasAnySignificantSibling(parent->AsElement(), aContent)) {
310       // We used to be empty, restyle the parent.
311       RestyleForEmptyChange(parent->AsElement());
312       return;
313     }
314   }
315 
316   if (slowSelectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
317     MaybeRestyleForEdgeChildChange(parent->AsElement(), aContent);
318   }
319 }
320 
321 // Restyling for a ContentInserted or CharacterDataChanged notification.
322 // This could be used for ContentRemoved as well if we got the
323 // notification before the removal happened (and sometimes
324 // CharacterDataChanged is more like a removal than an addition).
325 // The comments are written and variables are named in terms of it being
326 // a ContentInserted notification.
RestyleForInsertOrChange(nsIContent * aChild)327 void RestyleManager::RestyleForInsertOrChange(nsIContent* aChild) {
328   nsINode* parentNode = aChild->GetParentNode();
329 
330   MOZ_ASSERT(parentNode);
331   // The container might be a document or a ShadowRoot.
332   if (!parentNode->IsElement()) {
333     return;
334   }
335   Element* container = parentNode->AsElement();
336 
337   NS_ASSERTION(!aChild->IsRootOfNativeAnonymousSubtree(),
338                "anonymous nodes should not be in child lists");
339   uint32_t selectorFlags = container->GetFlags() & NODE_ALL_SELECTOR_FLAGS;
340   if (selectorFlags == 0) return;
341 
342   if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) {
343     // See whether we need to restyle the container due to :empty /
344     // :-moz-only-whitespace.
345     const bool wasEmpty = !HasAnySignificantSibling(container, aChild);
346     if (wasEmpty) {
347       // FIXME(emilio): When coming from CharacterDataChanged this can restyle
348       // unnecessarily. Also can restyle unnecessarily if aChild is not
349       // significant anyway, though that's more unlikely.
350       RestyleForEmptyChange(container);
351       return;
352     }
353   }
354 
355   if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
356     PostRestyleEvent(container, RestyleHint::RestyleSubtree(), nsChangeHint(0));
357     // Restyling the container is the most we can do here, so we're done.
358     return;
359   }
360 
361   if (selectorFlags & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
362     // Restyle all later siblings.
363     RestyleSiblingsStartingWith(*this, aChild->GetNextSibling());
364   }
365 
366   if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
367     MaybeRestyleForEdgeChildChange(container, aChild);
368   }
369 }
370 
ContentRemoved(nsIContent * aOldChild,nsIContent * aFollowingSibling)371 void RestyleManager::ContentRemoved(nsIContent* aOldChild,
372                                     nsIContent* aFollowingSibling) {
373   MOZ_ASSERT(aOldChild->GetParentNode());
374 
375   // Computed style data isn't useful for detached nodes, and we'll need to
376   // recompute it anyway if we ever insert the nodes back into a document.
377   if (aOldChild->IsElement()) {
378     RestyleManager::ClearServoDataFromSubtree(aOldChild->AsElement());
379   }
380 
381   // The container might be a document or a ShadowRoot.
382   if (!aOldChild->GetParentNode()->IsElement()) {
383     return;
384   }
385   Element* container = aOldChild->GetParentNode()->AsElement();
386 
387   if (aOldChild->IsRootOfNativeAnonymousSubtree()) {
388     // This should be an assert, but this is called incorrectly in
389     // HTMLEditor::DeleteRefToAnonymousNode and the assertions were clogging
390     // up the logs.  Make it an assert again when that's fixed.
391     MOZ_ASSERT(aOldChild->GetProperty(nsGkAtoms::restylableAnonymousNode),
392                "anonymous nodes should not be in child lists (bug 439258)");
393   }
394   uint32_t selectorFlags = container->GetFlags() & NODE_ALL_SELECTOR_FLAGS;
395   if (selectorFlags == 0) return;
396 
397   if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) {
398     // see whether we need to restyle the container
399     bool isEmpty = true;  // :empty or :-moz-only-whitespace
400     for (nsIContent* child = container->GetFirstChild(); child;
401          child = child->GetNextSibling()) {
402       // We don't know whether we're testing :empty or :-moz-only-whitespace,
403       // so be conservative and assume :-moz-only-whitespace (i.e., make
404       // IsSignificantChild less likely to be true, and thus make us more
405       // likely to restyle).
406       if (nsStyleUtil::IsSignificantChild(child, false)) {
407         isEmpty = false;
408         break;
409       }
410     }
411     if (isEmpty) {
412       RestyleForEmptyChange(container);
413       return;
414     }
415   }
416 
417   if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
418     PostRestyleEvent(container, RestyleHint::RestyleSubtree(), nsChangeHint(0));
419     // Restyling the container is the most we can do here, so we're done.
420     return;
421   }
422 
423   if (selectorFlags & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
424     // Restyle all later siblings.
425     RestyleSiblingsStartingWith(*this, aFollowingSibling);
426   }
427 
428   if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
429     // restyle the now-first element child if it was after aOldChild
430     bool reachedFollowingSibling = false;
431     for (nsIContent* content = container->GetFirstChild(); content;
432          content = content->GetNextSibling()) {
433       if (content == aFollowingSibling) {
434         reachedFollowingSibling = true;
435         // do NOT continue here; we might want to restyle this node
436       }
437       if (content->IsElement()) {
438         if (reachedFollowingSibling) {
439           PostRestyleEvent(content->AsElement(), RestyleHint::RestyleSubtree(),
440                            nsChangeHint(0));
441         }
442         break;
443       }
444     }
445     // restyle the now-last element child if it was before aOldChild
446     reachedFollowingSibling = (aFollowingSibling == nullptr);
447     for (nsIContent* content = container->GetLastChild(); content;
448          content = content->GetPreviousSibling()) {
449       if (content->IsElement()) {
450         if (reachedFollowingSibling) {
451           PostRestyleEvent(content->AsElement(), RestyleHint::RestyleSubtree(),
452                            nsChangeHint(0));
453         }
454         break;
455       }
456       if (content == aFollowingSibling) {
457         reachedFollowingSibling = true;
458       }
459     }
460   }
461 }
462 
StateChangeMayAffectFrame(const Element & aElement,const nsIFrame & aFrame,EventStates aStates)463 static bool StateChangeMayAffectFrame(const Element& aElement,
464                                       const nsIFrame& aFrame,
465                                       EventStates aStates) {
466   if (aFrame.IsGeneratedContentFrame()) {
467     if (aElement.IsHTMLElement(nsGkAtoms::mozgeneratedcontentimage)) {
468       return aStates.HasState(NS_EVENT_STATE_BROKEN);
469     }
470 
471     // If it's other generated content, ignore LOADING/etc state changes on it.
472     return false;
473   }
474 
475   const bool brokenChanged = aStates.HasState(NS_EVENT_STATE_BROKEN);
476   const bool loadingChanged = aStates.HasState(NS_EVENT_STATE_LOADING);
477 
478   if (!brokenChanged && !loadingChanged) {
479     return false;
480   }
481 
482   if (aElement.IsHTMLElement(nsGkAtoms::img)) {
483     // Loading state doesn't affect <img>, see
484     // `nsImageFrame::ShouldCreateImageFrameFor`.
485     return brokenChanged;
486   }
487 
488   return brokenChanged || loadingChanged;
489 }
490 
491 /**
492  * Calculates the change hint and the restyle hint for a given content state
493  * change.
494  */
ChangeForContentStateChange(const Element & aElement,EventStates aStateMask)495 static nsChangeHint ChangeForContentStateChange(const Element& aElement,
496                                                 EventStates aStateMask) {
497   auto changeHint = nsChangeHint(0);
498 
499   // Any change to a content state that affects which frames we construct
500   // must lead to a frame reconstruct here if we already have a frame.
501   // Note that we never decide through non-CSS means to not create frames
502   // based on content states, so if we already don't have a frame we don't
503   // need to force a reframe -- if it's needed, the HasStateDependentStyle
504   // call will handle things.
505   if (nsIFrame* primaryFrame = aElement.GetPrimaryFrame()) {
506     if (StateChangeMayAffectFrame(aElement, *primaryFrame, aStateMask)) {
507       return nsChangeHint_ReconstructFrame;
508     }
509 
510     StyleAppearance appearance =
511         primaryFrame->StyleDisplay()->EffectiveAppearance();
512     if (appearance != StyleAppearance::None) {
513       nsPresContext* pc = primaryFrame->PresContext();
514       nsITheme* theme = pc->Theme();
515       if (theme->ThemeSupportsWidget(pc, primaryFrame, appearance)) {
516         bool repaint = false;
517         theme->WidgetStateChanged(primaryFrame, appearance, nullptr, &repaint,
518                                   nullptr);
519         if (repaint) {
520           changeHint |= nsChangeHint_RepaintFrame;
521         }
522       }
523     }
524     primaryFrame->ContentStatesChanged(aStateMask);
525   }
526 
527   if (aStateMask.HasState(NS_EVENT_STATE_VISITED)) {
528     // Exposing information to the page about whether the link is
529     // visited or not isn't really something we can worry about here.
530     // FIXME: We could probably do this a bit better.
531     changeHint |= nsChangeHint_RepaintFrame;
532   }
533 
534   return changeHint;
535 }
536 
537 #ifdef DEBUG
538 /* static */
ChangeHintToString(nsChangeHint aHint)539 nsCString RestyleManager::ChangeHintToString(nsChangeHint aHint) {
540   nsCString result;
541   bool any = false;
542   const char* names[] = {"RepaintFrame",
543                          "NeedReflow",
544                          "ClearAncestorIntrinsics",
545                          "ClearDescendantIntrinsics",
546                          "NeedDirtyReflow",
547                          "UpdateCursor",
548                          "UpdateEffects",
549                          "UpdateOpacityLayer",
550                          "UpdateTransformLayer",
551                          "ReconstructFrame",
552                          "UpdateOverflow",
553                          "UpdateSubtreeOverflow",
554                          "UpdatePostTransformOverflow",
555                          "UpdateParentOverflow",
556                          "ChildrenOnlyTransform",
557                          "RecomputePosition",
558                          "UpdateContainingBlock",
559                          "BorderStyleNoneChange",
560                          "SchedulePaint",
561                          "NeutralChange",
562                          "InvalidateRenderingObservers",
563                          "ReflowChangesSizeOrPosition",
564                          "UpdateComputedBSize",
565                          "UpdateUsesOpacity",
566                          "UpdateBackgroundPosition",
567                          "AddOrRemoveTransform",
568                          "ScrollbarChange",
569                          "UpdateTableCellSpans",
570                          "VisibilityChange"};
571   static_assert(nsChangeHint_AllHints ==
572                     static_cast<uint32_t>((1ull << ArrayLength(names)) - 1),
573                 "Name list doesn't match change hints.");
574   uint32_t hint =
575       aHint & static_cast<uint32_t>((1ull << ArrayLength(names)) - 1);
576   uint32_t rest =
577       aHint & ~static_cast<uint32_t>((1ull << ArrayLength(names)) - 1);
578   if ((hint & NS_STYLE_HINT_REFLOW) == NS_STYLE_HINT_REFLOW) {
579     result.AppendLiteral("NS_STYLE_HINT_REFLOW");
580     hint = hint & ~NS_STYLE_HINT_REFLOW;
581     any = true;
582   } else if ((hint & nsChangeHint_AllReflowHints) ==
583              nsChangeHint_AllReflowHints) {
584     result.AppendLiteral("nsChangeHint_AllReflowHints");
585     hint = hint & ~nsChangeHint_AllReflowHints;
586     any = true;
587   } else if ((hint & NS_STYLE_HINT_VISUAL) == NS_STYLE_HINT_VISUAL) {
588     result.AppendLiteral("NS_STYLE_HINT_VISUAL");
589     hint = hint & ~NS_STYLE_HINT_VISUAL;
590     any = true;
591   }
592   for (uint32_t i = 0; i < ArrayLength(names); i++) {
593     if (hint & (1u << i)) {
594       if (any) {
595         result.AppendLiteral(" | ");
596       }
597       result.AppendPrintf("nsChangeHint_%s", names[i]);
598       any = true;
599     }
600   }
601   if (rest) {
602     if (any) {
603       result.AppendLiteral(" | ");
604     }
605     result.AppendPrintf("0x%0x", rest);
606   } else {
607     if (!any) {
608       result.AppendLiteral("nsChangeHint(0)");
609     }
610   }
611   return result;
612 }
613 #endif
614 
615 /**
616  * Frame construction helpers follow.
617  */
618 #ifdef DEBUG
619 static bool gInApplyRenderingChangeToTree = false;
620 #endif
621 
622 /**
623  * Sync views on the frame and all of it's descendants (following placeholders).
624  * The change hint should be some combination of nsChangeHint_RepaintFrame,
625  * nsChangeHint_UpdateOpacityLayer and nsChangeHint_SchedulePaint, nothing else.
626  */
627 static void SyncViewsAndInvalidateDescendants(nsIFrame*, nsChangeHint);
628 
629 static void StyleChangeReflow(nsIFrame* aFrame, nsChangeHint aHint);
630 
631 /**
632  * This helper function is used to find the correct SVG frame to target when we
633  * encounter nsChangeHint_ChildrenOnlyTransform; needed since sometimes we end
634  * up handling that hint while processing hints for one of the SVG frame's
635  * ancestor frames.
636  *
637  * The reason that we sometimes end up trying to process the hint for an
638  * ancestor of the SVG frame that the hint is intended for is due to the way we
639  * process restyle events. ApplyRenderingChangeToTree adjusts the frame from
640  * the restyled element's principle frame to one of its ancestor frames based
641  * on what nsCSSRendering::FindBackground returns, since the background style
642  * may have been propagated up to an ancestor frame. Processing hints using an
643  * ancestor frame is fine in general, but nsChangeHint_ChildrenOnlyTransform is
644  * a special case since it is intended to update a specific frame.
645  */
GetFrameForChildrenOnlyTransformHint(nsIFrame * aFrame)646 static nsIFrame* GetFrameForChildrenOnlyTransformHint(nsIFrame* aFrame) {
647   if (aFrame->IsViewportFrame()) {
648     // This happens if the root-<svg> is fixed positioned, in which case we
649     // can't use aFrame->GetContent() to find the primary frame, since
650     // GetContent() returns nullptr for ViewportFrame.
651     aFrame = aFrame->PrincipalChildList().FirstChild();
652   }
653   // For an nsHTMLScrollFrame, this will get the SVG frame that has the
654   // children-only transforms:
655   aFrame = aFrame->GetContent()->GetPrimaryFrame();
656   if (aFrame->IsSVGOuterSVGFrame()) {
657     aFrame = aFrame->PrincipalChildList().FirstChild();
658     MOZ_ASSERT(aFrame->IsSVGOuterSVGAnonChildFrame(),
659                "Where is the SVGOuterSVGFrame's anon child??");
660   }
661   MOZ_ASSERT(aFrame->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer),
662              "Children-only transforms only expected on SVG frames");
663   return aFrame;
664 }
665 
666 // This function tries to optimize a position style change by either
667 // moving aFrame or ignoring the style change when it's safe to do so.
668 // It returns true when that succeeds, otherwise it posts a reflow request
669 // and returns false.
RecomputePosition(nsIFrame * aFrame)670 static bool RecomputePosition(nsIFrame* aFrame) {
671   // It's pointless to move around frames that have never been reflowed or
672   // are dirty (i.e. they will be reflowed).
673   if (aFrame->HasAnyStateBits(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY)) {
674     return true;
675   }
676 
677   // Don't process position changes on table frames, since we already handle
678   // the dynamic position change on the table wrapper frame, and the
679   // reflow-based fallback code path also ignores positions on inner table
680   // frames.
681   if (aFrame->IsTableFrame()) {
682     return true;
683   }
684 
685   const nsStyleDisplay* display = aFrame->StyleDisplay();
686   // Changes to the offsets of a non-positioned element can safely be ignored.
687   if (display->mPosition == StylePositionProperty::Static) {
688     return true;
689   }
690 
691   // Don't process position changes on frames which have views or the ones which
692   // have a view somewhere in their descendants, because the corresponding view
693   // needs to be repositioned properly as well.
694   if (aFrame->HasView() ||
695       aFrame->HasAnyStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW)) {
696     return false;
697   }
698 
699   if (aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
700     // If the frame has an intrinsic block-size, we resolve its 'auto' margins
701     // after doing layout, since we need to know the frame's block size. See
702     // nsAbsoluteContainingBlock::ResolveAutoMarginsAfterLayout().
703     //
704     // Since the size of the frame doesn't change, we could modify the below
705     // computation to compute the margin correctly without doing a full reflow,
706     // however we decided to try doing a full reflow for now.
707     if (aFrame->HasIntrinsicKeywordForBSize()) {
708       WritingMode wm = aFrame->GetWritingMode();
709       const auto* styleMargin = aFrame->StyleMargin();
710       if (styleMargin->HasBlockAxisAuto(wm)) {
711         return false;
712       }
713     }
714     // Flexbox and Grid layout supports CSS Align and the optimizations below
715     // don't support that yet.
716     nsIFrame* ph = aFrame->GetPlaceholderFrame();
717     if (ph && ph->HasAnyStateBits(PLACEHOLDER_STATICPOS_NEEDS_CSSALIGN)) {
718       return false;
719     }
720   }
721 
722   // If we need to reposition any descendant that depends on our static
723   // position, then we also can't take the optimized path.
724   //
725   // TODO(emilio): It may be worth trying to find them and try to call
726   // RecomputePosition on them too instead of disabling the optimization...
727   if (aFrame->DescendantMayDependOnItsStaticPosition()) {
728     return false;
729   }
730 
731   aFrame->SchedulePaint();
732 
733   // For relative positioning, we can simply update the frame rect
734   if (display->IsRelativelyPositionedStyle()) {
735     if (aFrame->IsGridItem()) {
736       // A grid item's CB is its grid area, not the parent frame content area
737       // as is assumed below.
738       return false;
739     }
740     // Move the frame
741     if (display->mPosition == StylePositionProperty::Sticky) {
742       // Update sticky positioning for an entire element at once, starting with
743       // the first continuation or ib-split sibling.
744       // It's rare that the frame we already have isn't already the first
745       // continuation or ib-split sibling, but it can happen when styles differ
746       // across continuations such as ::first-line or ::first-letter, and in
747       // those cases we will generally (but maybe not always) do the work twice.
748       nsIFrame* firstContinuation =
749           nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
750 
751       StickyScrollContainer::ComputeStickyOffsets(firstContinuation);
752       StickyScrollContainer* ssc =
753           StickyScrollContainer::GetStickyScrollContainerForFrame(
754               firstContinuation);
755       if (ssc) {
756         ssc->PositionContinuations(firstContinuation);
757       }
758     } else {
759       MOZ_ASSERT(StylePositionProperty::Relative == display->mPosition,
760                  "Unexpected type of positioning");
761       for (nsIFrame* cont = aFrame; cont;
762            cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
763         nsIFrame* cb = cont->GetContainingBlock();
764         WritingMode wm = cb->GetWritingMode();
765         const LogicalSize cbSize = cb->ContentSize();
766         const LogicalMargin newLogicalOffsets =
767             ReflowInput::ComputeRelativeOffsets(wm, cont, cbSize);
768         const nsMargin newOffsets = newLogicalOffsets.GetPhysicalMargin(wm);
769 
770         // ReflowInput::ApplyRelativePositioning would work here, but
771         // since we've already checked mPosition and aren't changing the frame's
772         // normal position, go ahead and add the offsets directly.
773         // First, we need to ensure that the normal position is stored though.
774         bool hasProperty;
775         nsPoint normalPosition = cont->GetNormalPosition(&hasProperty);
776         if (!hasProperty) {
777           cont->AddProperty(nsIFrame::NormalPositionProperty(),
778                             new nsPoint(normalPosition));
779         }
780         cont->SetPosition(normalPosition +
781                           nsPoint(newOffsets.left, newOffsets.top));
782       }
783     }
784 
785     if (aFrame->IsInScrollAnchorChain()) {
786       ScrollAnchorContainer* container = ScrollAnchorContainer::FindFor(aFrame);
787       aFrame->PresShell()->PostPendingScrollAnchorAdjustment(container);
788     }
789     return true;
790   }
791 
792   // For the absolute positioning case, set up a fake HTML reflow input for
793   // the frame, and then get the offsets and size from it. If the frame's size
794   // doesn't need to change, we can simply update the frame position. Otherwise
795   // we fall back to a reflow.
796   RefPtr<gfxContext> rc =
797       aFrame->PresShell()->CreateReferenceRenderingContext();
798 
799   // Construct a bogus parent reflow input so that there's a usable
800   // containing block reflow input.
801   nsIFrame* parentFrame = aFrame->GetParent();
802   WritingMode parentWM = parentFrame->GetWritingMode();
803   WritingMode frameWM = aFrame->GetWritingMode();
804   LogicalSize parentSize = parentFrame->GetLogicalSize();
805 
806   nsFrameState savedState = parentFrame->GetStateBits();
807   ReflowInput parentReflowInput(aFrame->PresContext(), parentFrame, rc,
808                                 parentSize);
809   parentFrame->RemoveStateBits(~nsFrameState(0));
810   parentFrame->AddStateBits(savedState);
811 
812   // The bogus parent state here was created with no parent state of its own,
813   // and therefore it won't have an mCBReflowInput set up.
814   // But we may need one (for InitCBReflowInput in a child state), so let's
815   // try to create one here for the cases where it will be needed.
816   Maybe<ReflowInput> cbReflowInput;
817   nsIFrame* cbFrame = parentFrame->GetContainingBlock();
818   if (cbFrame && (aFrame->GetContainingBlock() != parentFrame ||
819                   parentFrame->IsTableFrame())) {
820     const auto cbWM = cbFrame->GetWritingMode();
821     LogicalSize cbSize = cbFrame->GetLogicalSize();
822     cbReflowInput.emplace(cbFrame->PresContext(), cbFrame, rc, cbSize);
823     cbReflowInput->SetComputedLogicalMargin(
824         cbWM, cbFrame->GetLogicalUsedMargin(cbWM));
825     cbReflowInput->SetComputedLogicalPadding(
826         cbWM, cbFrame->GetLogicalUsedPadding(cbWM));
827     cbReflowInput->SetComputedLogicalBorderPadding(
828         cbWM, cbFrame->GetLogicalUsedBorderAndPadding(cbWM));
829     parentReflowInput.mCBReflowInput = cbReflowInput.ptr();
830   }
831 
832   NS_WARNING_ASSERTION(parentSize.ISize(parentWM) != NS_UNCONSTRAINEDSIZE &&
833                            parentSize.BSize(parentWM) != NS_UNCONSTRAINEDSIZE,
834                        "parentSize should be valid");
835   parentReflowInput.SetComputedISize(std::max(parentSize.ISize(parentWM), 0));
836   parentReflowInput.SetComputedBSize(std::max(parentSize.BSize(parentWM), 0));
837   parentReflowInput.SetComputedLogicalMargin(parentWM, LogicalMargin(parentWM));
838 
839   parentReflowInput.SetComputedLogicalPadding(
840       parentWM, parentFrame->GetLogicalUsedPadding(parentWM));
841   parentReflowInput.SetComputedLogicalBorderPadding(
842       parentWM, parentFrame->GetLogicalUsedBorderAndPadding(parentWM));
843   LogicalSize availSize = parentSize.ConvertTo(frameWM, parentWM);
844   availSize.BSize(frameWM) = NS_UNCONSTRAINEDSIZE;
845 
846   ViewportFrame* viewport = do_QueryFrame(parentFrame);
847   nsSize cbSize =
848       viewport
849           ? viewport->AdjustReflowInputAsContainingBlock(&parentReflowInput)
850                 .Size()
851           : aFrame->GetContainingBlock()->GetSize();
852   const nsMargin& parentBorder =
853       parentReflowInput.mStyleBorder->GetComputedBorder();
854   cbSize -= nsSize(parentBorder.LeftRight(), parentBorder.TopBottom());
855   LogicalSize lcbSize(frameWM, cbSize);
856   ReflowInput reflowInput(aFrame->PresContext(), parentReflowInput, aFrame,
857                           availSize, Some(lcbSize));
858   nscoord computedISize = reflowInput.ComputedISize();
859   nscoord computedBSize = reflowInput.ComputedBSize();
860   const auto frameBP = reflowInput.ComputedLogicalBorderPadding(frameWM);
861   computedISize += frameBP.IStartEnd(frameWM);
862   if (computedBSize != NS_UNCONSTRAINEDSIZE) {
863     computedBSize += frameBP.BStartEnd(frameWM);
864   }
865   LogicalSize logicalSize = aFrame->GetLogicalSize(frameWM);
866   nsSize size = aFrame->GetSize();
867   // The RecomputePosition hint is not used if any offset changed between auto
868   // and non-auto. If computedSize.height == NS_UNCONSTRAINEDSIZE then the new
869   // element height will be its intrinsic height, and since 'top' and 'bottom''s
870   // auto-ness hasn't changed, the old height must also be its intrinsic
871   // height, which we can assume hasn't changed (or reflow would have
872   // been triggered).
873   if (computedISize == logicalSize.ISize(frameWM) &&
874       (computedBSize == NS_UNCONSTRAINEDSIZE ||
875        computedBSize == logicalSize.BSize(frameWM))) {
876     // If we're solving for 'left' or 'top', then compute it here, in order to
877     // match the reflow code path.
878     //
879     // TODO(emilio): It'd be nice if this did logical math instead, but it seems
880     // to me the math should work out on vertical writing modes as well. See Bug
881     // 1675861 for some hints.
882     const nsMargin offset = reflowInput.ComputedPhysicalOffsets();
883     const nsMargin margin = reflowInput.ComputedPhysicalMargin();
884 
885     nscoord left = offset.left;
886     if (left == NS_AUTOOFFSET) {
887       left =
888           cbSize.width - offset.right - margin.right - size.width - margin.left;
889     }
890 
891     nscoord top = offset.top;
892     if (top == NS_AUTOOFFSET) {
893       top = cbSize.height - offset.bottom - margin.bottom - size.height -
894             margin.top;
895     }
896 
897     // Move the frame
898     nsPoint pos(parentBorder.left + left + margin.left,
899                 parentBorder.top + top + margin.top);
900     aFrame->SetPosition(pos);
901 
902     if (aFrame->IsInScrollAnchorChain()) {
903       ScrollAnchorContainer* container = ScrollAnchorContainer::FindFor(aFrame);
904       aFrame->PresShell()->PostPendingScrollAnchorAdjustment(container);
905     }
906     return true;
907   }
908 
909   // Fall back to a reflow
910   return false;
911 }
912 
HasBoxAncestor(nsIFrame * aFrame)913 static bool HasBoxAncestor(nsIFrame* aFrame) {
914   for (nsIFrame* f = aFrame; f; f = f->GetParent()) {
915     if (f->IsXULBoxFrame()) {
916       return true;
917     }
918   }
919   return false;
920 }
921 
922 /**
923  * Return true if aFrame's subtree has placeholders for out-of-flow content
924  * that would be affected due to the change to
925  * `aPossiblyChangingContainingBlock` (and thus would need to get reframed).
926  *
927  * In particular, this function returns true if there are placeholders whose OOF
928  * frames may need to be reparented (via reframing) as a result of whatever
929  * change actually happened.
930  *
931  * The `aIs{Abs,Fixed}PosContainingBlock` params represent whether
932  * `aPossiblyChangingContainingBlock` is a containing block for abs pos / fixed
933  * pos stuff, respectively, for the _new_ style that the frame already has, not
934  * the old one.
935  */
ContainingBlockChangeAffectsDescendants(nsIFrame * aPossiblyChangingContainingBlock,nsIFrame * aFrame,bool aIsAbsPosContainingBlock,bool aIsFixedPosContainingBlock)936 static bool ContainingBlockChangeAffectsDescendants(
937     nsIFrame* aPossiblyChangingContainingBlock, nsIFrame* aFrame,
938     bool aIsAbsPosContainingBlock, bool aIsFixedPosContainingBlock) {
939   // All fixed-pos containing blocks should also be abs-pos containing blocks.
940   MOZ_ASSERT_IF(aIsFixedPosContainingBlock, aIsAbsPosContainingBlock);
941 
942   for (const auto& childList : aFrame->ChildLists()) {
943     for (nsIFrame* f : childList.mList) {
944       if (f->IsPlaceholderFrame()) {
945         nsIFrame* outOfFlow = nsPlaceholderFrame::GetRealFrameForPlaceholder(f);
946         // If SVG text frames could appear here, they could confuse us since
947         // they ignore their position style ... but they can't.
948         NS_ASSERTION(!SVGUtils::IsInSVGTextSubtree(outOfFlow),
949                      "SVG text frames can't be out of flow");
950         auto* display = outOfFlow->StyleDisplay();
951         if (display->IsAbsolutelyPositionedStyle()) {
952           const bool isContainingBlock =
953               aIsFixedPosContainingBlock ||
954               (aIsAbsPosContainingBlock &&
955                display->mPosition == StylePositionProperty::Absolute);
956           // NOTE(emilio): aPossiblyChangingContainingBlock is guaranteed to be
957           // a first continuation, see the assertion in the caller.
958           nsIFrame* parent = outOfFlow->GetParent()->FirstContinuation();
959           if (isContainingBlock) {
960             // If we are becoming a containing block, we only need to reframe if
961             // this oof's current containing block is an ancestor of the new
962             // frame.
963             if (parent != aPossiblyChangingContainingBlock &&
964                 nsLayoutUtils::IsProperAncestorFrame(
965                     parent, aPossiblyChangingContainingBlock)) {
966               return true;
967             }
968           } else {
969             // If we are not a containing block anymore, we only need to reframe
970             // if we are the current containing block of the oof frame.
971             if (parent == aPossiblyChangingContainingBlock) {
972               return true;
973             }
974           }
975         }
976       }
977       // NOTE:  It's tempting to check f->IsAbsPosContainingBlock() or
978       // f->IsFixedPosContainingBlock() here.  However, that would only
979       // be testing the *new* style of the frame, which might exclude
980       // descendants that currently have this frame as an abs-pos
981       // containing block.  Taking the codepath where we don't reframe
982       // could lead to an unsafe call to
983       // cont->MarkAsNotAbsoluteContainingBlock() before we've reframed
984       // the descendant and taken it off the absolute list.
985       if (ContainingBlockChangeAffectsDescendants(
986               aPossiblyChangingContainingBlock, f, aIsAbsPosContainingBlock,
987               aIsFixedPosContainingBlock)) {
988         return true;
989       }
990     }
991   }
992   return false;
993 }
994 
NeedToReframeToUpdateContainingBlock(nsIFrame * aFrame)995 static bool NeedToReframeToUpdateContainingBlock(nsIFrame* aFrame) {
996   // NOTE: This looks at the new style.
997   const bool isFixedContainingBlock = aFrame->IsFixedPosContainingBlock();
998   MOZ_ASSERT_IF(isFixedContainingBlock, aFrame->IsAbsPosContainingBlock());
999 
1000   const bool isAbsPosContainingBlock =
1001       isFixedContainingBlock || aFrame->IsAbsPosContainingBlock();
1002 
1003   for (nsIFrame* f = aFrame; f;
1004        f = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(f)) {
1005     if (ContainingBlockChangeAffectsDescendants(
1006             aFrame, f, isAbsPosContainingBlock, isFixedContainingBlock)) {
1007       return true;
1008     }
1009   }
1010   return false;
1011 }
1012 
DoApplyRenderingChangeToTree(nsIFrame * aFrame,nsChangeHint aChange)1013 static void DoApplyRenderingChangeToTree(nsIFrame* aFrame,
1014                                          nsChangeHint aChange) {
1015   MOZ_ASSERT(gInApplyRenderingChangeToTree,
1016              "should only be called within ApplyRenderingChangeToTree");
1017 
1018   for (; aFrame;
1019        aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame)) {
1020     // Invalidate and sync views on all descendant frames, following
1021     // placeholders. We don't need to update transforms in
1022     // SyncViewsAndInvalidateDescendants, because there can't be any
1023     // out-of-flows or popups that need to be transformed; all out-of-flow
1024     // descendants of the transformed element must also be descendants of the
1025     // transformed frame.
1026     SyncViewsAndInvalidateDescendants(
1027         aFrame, nsChangeHint(aChange & (nsChangeHint_RepaintFrame |
1028                                         nsChangeHint_UpdateOpacityLayer |
1029                                         nsChangeHint_SchedulePaint)));
1030     // This must be set to true if the rendering change needs to
1031     // invalidate content.  If it's false, a composite-only paint
1032     // (empty transaction) will be scheduled.
1033     bool needInvalidatingPaint = false;
1034 
1035     // if frame has view, will already be invalidated
1036     if (aChange & nsChangeHint_RepaintFrame) {
1037       // Note that this whole block will be skipped when painting is suppressed
1038       // (due to our caller ApplyRendingChangeToTree() discarding the
1039       // nsChangeHint_RepaintFrame hint).  If you add handling for any other
1040       // hints within this block, be sure that they too should be ignored when
1041       // painting is suppressed.
1042       needInvalidatingPaint = true;
1043       aFrame->InvalidateFrameSubtree();
1044       if ((aChange & nsChangeHint_UpdateEffects) &&
1045           aFrame->IsFrameOfType(nsIFrame::eSVG) &&
1046           !aFrame->IsSVGOuterSVGFrame()) {
1047         // Need to update our overflow rects:
1048         SVGUtils::ScheduleReflowSVG(aFrame);
1049       }
1050 
1051       ActiveLayerTracker::NotifyNeedsRepaint(aFrame);
1052     }
1053     if (aChange & nsChangeHint_UpdateOpacityLayer) {
1054       // FIXME/bug 796697: we can get away with empty transactions for
1055       // opacity updates in many cases.
1056       needInvalidatingPaint = true;
1057 
1058       ActiveLayerTracker::NotifyRestyle(aFrame, eCSSProperty_opacity);
1059       if (SVGIntegrationUtils::UsingEffectsForFrame(aFrame)) {
1060         // SVG effects paints the opacity without using
1061         // nsDisplayOpacity. We need to invalidate manually.
1062         aFrame->InvalidateFrameSubtree();
1063       }
1064     }
1065     if ((aChange & nsChangeHint_UpdateTransformLayer) &&
1066         aFrame->IsTransformed()) {
1067       // Note: All the transform-like properties should map to the same
1068       // layer activity index, so does the restyle count. Therefore, using
1069       // eCSSProperty_transform should be fine.
1070       ActiveLayerTracker::NotifyRestyle(aFrame, eCSSProperty_transform);
1071       // If we're not already going to do an invalidating paint, see
1072       // if we can get away with only updating the transform on a
1073       // layer for this frame, and not scheduling an invalidating
1074       // paint.
1075       if (!needInvalidatingPaint) {
1076         nsDisplayItem::Layer* layer;
1077         needInvalidatingPaint |= !aFrame->TryUpdateTransformOnly(&layer);
1078 
1079         if (!needInvalidatingPaint) {
1080           // Since we're not going to paint, we need to resend animation
1081           // data to the layer.
1082           MOZ_ASSERT(layer, "this can't happen if there's no layer");
1083           nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(
1084               layer, nullptr, nullptr, aFrame, DisplayItemType::TYPE_TRANSFORM);
1085         }
1086       }
1087     }
1088     if (aChange & nsChangeHint_ChildrenOnlyTransform) {
1089       needInvalidatingPaint = true;
1090       nsIFrame* childFrame = GetFrameForChildrenOnlyTransformHint(aFrame)
1091                                  ->PrincipalChildList()
1092                                  .FirstChild();
1093       for (; childFrame; childFrame = childFrame->GetNextSibling()) {
1094         // Note: All the transform-like properties should map to the same
1095         // layer activity index, so does the restyle count. Therefore, using
1096         // eCSSProperty_transform should be fine.
1097         ActiveLayerTracker::NotifyRestyle(childFrame, eCSSProperty_transform);
1098       }
1099     }
1100     if (aChange & nsChangeHint_SchedulePaint) {
1101       needInvalidatingPaint = true;
1102     }
1103     aFrame->SchedulePaint(needInvalidatingPaint
1104                               ? nsIFrame::PAINT_DEFAULT
1105                               : nsIFrame::PAINT_COMPOSITE_ONLY);
1106   }
1107 }
1108 
SyncViewsAndInvalidateDescendants(nsIFrame * aFrame,nsChangeHint aChange)1109 static void SyncViewsAndInvalidateDescendants(nsIFrame* aFrame,
1110                                               nsChangeHint aChange) {
1111   MOZ_ASSERT(gInApplyRenderingChangeToTree,
1112              "should only be called within ApplyRenderingChangeToTree");
1113 
1114   NS_ASSERTION(nsChangeHint_size_t(aChange) ==
1115                    (aChange & (nsChangeHint_RepaintFrame |
1116                                nsChangeHint_UpdateOpacityLayer |
1117                                nsChangeHint_SchedulePaint)),
1118                "Invalid change flag");
1119 
1120   aFrame->SyncFrameViewProperties();
1121 
1122   for (const auto& [list, listID] : aFrame->ChildLists()) {
1123     for (nsIFrame* child : list) {
1124       if (!child->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
1125         // only do frames that don't have placeholders
1126         if (child->IsPlaceholderFrame()) {
1127           // do the out-of-flow frame and its continuations
1128           nsIFrame* outOfFlowFrame =
1129               nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
1130           DoApplyRenderingChangeToTree(outOfFlowFrame, aChange);
1131         } else if (listID == nsIFrame::kPopupList) {
1132           DoApplyRenderingChangeToTree(child, aChange);
1133         } else {  // regular frame
1134           SyncViewsAndInvalidateDescendants(child, aChange);
1135         }
1136       }
1137     }
1138   }
1139 }
1140 
ApplyRenderingChangeToTree(PresShell * aPresShell,nsIFrame * aFrame,nsChangeHint aChange)1141 static void ApplyRenderingChangeToTree(PresShell* aPresShell, nsIFrame* aFrame,
1142                                        nsChangeHint aChange) {
1143   // We check StyleDisplay()->HasTransformStyle() in addition to checking
1144   // IsTransformed() since we can get here for some frames that don't support
1145   // CSS transforms, and table frames, which are their own odd-ball, since the
1146   // transform is handled by their wrapper, which _also_ gets a separate hint.
1147   NS_ASSERTION(!(aChange & nsChangeHint_UpdateTransformLayer) ||
1148                    aFrame->IsTransformed() ||
1149                    aFrame->StyleDisplay()->HasTransformStyle(),
1150                "Unexpected UpdateTransformLayer hint");
1151 
1152   if (aPresShell->IsPaintingSuppressed()) {
1153     // Don't allow synchronous rendering changes when painting is turned off.
1154     aChange &= ~nsChangeHint_RepaintFrame;
1155     if (!aChange) {
1156       return;
1157     }
1158   }
1159 
1160 // Trigger rendering updates by damaging this frame and any
1161 // continuations of this frame.
1162 #ifdef DEBUG
1163   gInApplyRenderingChangeToTree = true;
1164 #endif
1165   if (aChange & nsChangeHint_RepaintFrame) {
1166     // If the frame is the primary frame of either the body element or
1167     // the html element, we propagate the repaint change hint to the
1168     // viewport. This is necessary for background and scrollbar colors
1169     // propagation.
1170     if (aFrame->IsPrimaryFrameOfRootOrBodyElement()) {
1171       nsIFrame* rootFrame = aPresShell->GetRootFrame();
1172       MOZ_ASSERT(rootFrame, "No root frame?");
1173       DoApplyRenderingChangeToTree(rootFrame, nsChangeHint_RepaintFrame);
1174       aChange &= ~nsChangeHint_RepaintFrame;
1175       if (!aChange) {
1176         return;
1177       }
1178     }
1179   }
1180   DoApplyRenderingChangeToTree(aFrame, aChange);
1181 #ifdef DEBUG
1182   gInApplyRenderingChangeToTree = false;
1183 #endif
1184 }
1185 
AddSubtreeToOverflowTracker(nsIFrame * aFrame,OverflowChangedTracker & aOverflowChangedTracker)1186 static void AddSubtreeToOverflowTracker(
1187     nsIFrame* aFrame, OverflowChangedTracker& aOverflowChangedTracker) {
1188   if (aFrame->FrameMaintainsOverflow()) {
1189     aOverflowChangedTracker.AddFrame(aFrame,
1190                                      OverflowChangedTracker::CHILDREN_CHANGED);
1191   }
1192   for (const auto& childList : aFrame->ChildLists()) {
1193     for (nsIFrame* child : childList.mList) {
1194       AddSubtreeToOverflowTracker(child, aOverflowChangedTracker);
1195     }
1196   }
1197 }
1198 
StyleChangeReflow(nsIFrame * aFrame,nsChangeHint aHint)1199 static void StyleChangeReflow(nsIFrame* aFrame, nsChangeHint aHint) {
1200   IntrinsicDirty dirtyType;
1201   if (aHint & nsChangeHint_ClearDescendantIntrinsics) {
1202     NS_ASSERTION(aHint & nsChangeHint_ClearAncestorIntrinsics,
1203                  "Please read the comments in nsChangeHint.h");
1204     NS_ASSERTION(aHint & nsChangeHint_NeedDirtyReflow,
1205                  "ClearDescendantIntrinsics requires NeedDirtyReflow");
1206     dirtyType = IntrinsicDirty::StyleChange;
1207   } else if ((aHint & nsChangeHint_UpdateComputedBSize) &&
1208              aFrame->HasAnyStateBits(
1209                  NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE)) {
1210     dirtyType = IntrinsicDirty::StyleChange;
1211   } else if (aHint & nsChangeHint_ClearAncestorIntrinsics) {
1212     dirtyType = IntrinsicDirty::TreeChange;
1213   } else if ((aHint & nsChangeHint_UpdateComputedBSize) &&
1214              HasBoxAncestor(aFrame)) {
1215     // The frame's computed BSize is changing, and we have a box ancestor
1216     // whose cached intrinsic height may need to be updated.
1217     dirtyType = IntrinsicDirty::TreeChange;
1218   } else {
1219     dirtyType = IntrinsicDirty::Resize;
1220   }
1221 
1222   if (aHint & nsChangeHint_UpdateComputedBSize) {
1223     aFrame->SetHasBSizeChange(true);
1224   }
1225 
1226   nsFrameState dirtyBits;
1227   if (aFrame->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
1228     dirtyBits = nsFrameState(0);
1229   } else if ((aHint & nsChangeHint_NeedDirtyReflow) ||
1230              dirtyType == IntrinsicDirty::StyleChange) {
1231     dirtyBits = NS_FRAME_IS_DIRTY;
1232   } else {
1233     dirtyBits = NS_FRAME_HAS_DIRTY_CHILDREN;
1234   }
1235 
1236   // If we're not going to clear any intrinsic sizes on the frames, and
1237   // there are no dirty bits to set, then there's nothing to do.
1238   if (dirtyType == IntrinsicDirty::Resize && !dirtyBits) return;
1239 
1240   ReflowRootHandling rootHandling;
1241   if (aHint & nsChangeHint_ReflowChangesSizeOrPosition) {
1242     rootHandling = ReflowRootHandling::PositionOrSizeChange;
1243   } else {
1244     rootHandling = ReflowRootHandling::NoPositionOrSizeChange;
1245   }
1246 
1247   do {
1248     aFrame->PresShell()->FrameNeedsReflow(aFrame, dirtyType, dirtyBits,
1249                                           rootHandling);
1250     aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame);
1251   } while (aFrame);
1252 }
1253 
1254 // Get the next sibling which might have a frame.  This only considers siblings
1255 // that stylo post-traversal looks at, so only elements and text.  In
1256 // particular, it ignores comments.
NextSiblingWhichMayHaveFrame(nsIContent * aContent)1257 static nsIContent* NextSiblingWhichMayHaveFrame(nsIContent* aContent) {
1258   for (nsIContent* next = aContent->GetNextSibling(); next;
1259        next = next->GetNextSibling()) {
1260     if (next->IsElement() || next->IsText()) {
1261       return next;
1262     }
1263   }
1264 
1265   return nullptr;
1266 }
1267 
1268 // If |aFrame| is dirty or has dirty children, then we can skip updating
1269 // overflows since that will happen when it's reflowed.
CanSkipOverflowUpdates(const nsIFrame * aFrame)1270 static inline bool CanSkipOverflowUpdates(const nsIFrame* aFrame) {
1271   return aFrame->HasAnyStateBits(NS_FRAME_IS_DIRTY |
1272                                  NS_FRAME_HAS_DIRTY_CHILDREN);
1273 }
1274 
ProcessRestyledFrames(nsStyleChangeList & aChangeList)1275 void RestyleManager::ProcessRestyledFrames(nsStyleChangeList& aChangeList) {
1276   NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
1277                "Someone forgot a script blocker");
1278 
1279   // See bug 1378219 comment 9:
1280   // Recursive calls here are a bit worrying, but apparently do happen in the
1281   // wild (although not currently in any of our automated tests). Try to get a
1282   // stack from Nightly/Dev channel to figure out what's going on and whether
1283   // it's OK.
1284   MOZ_DIAGNOSTIC_ASSERT(!mDestroyedFrames, "ProcessRestyledFrames recursion");
1285 
1286   if (aChangeList.IsEmpty()) {
1287     return;
1288   }
1289 
1290   // If mDestroyedFrames is null, we want to create a new hashtable here
1291   // and destroy it on exit; but if it is already non-null (because we're in
1292   // a recursive call), we will continue to use the existing table to
1293   // accumulate destroyed frames, and NOT clear mDestroyedFrames on exit.
1294   // We use a MaybeClearDestroyedFrames helper to conditionally reset the
1295   // mDestroyedFrames pointer when this method returns.
1296   typedef decltype(mDestroyedFrames) DestroyedFramesT;
1297   class MOZ_RAII MaybeClearDestroyedFrames {
1298    private:
1299     DestroyedFramesT& mDestroyedFramesRef;  // ref to caller's mDestroyedFrames
1300     const bool mResetOnDestruction;
1301 
1302    public:
1303     explicit MaybeClearDestroyedFrames(DestroyedFramesT& aTarget)
1304         : mDestroyedFramesRef(aTarget),
1305           mResetOnDestruction(!aTarget)  // reset only if target starts out null
1306     {}
1307     ~MaybeClearDestroyedFrames() {
1308       if (mResetOnDestruction) {
1309         mDestroyedFramesRef.reset(nullptr);
1310       }
1311     }
1312   };
1313 
1314   MaybeClearDestroyedFrames maybeClear(mDestroyedFrames);
1315   if (!mDestroyedFrames) {
1316     mDestroyedFrames = MakeUnique<nsTHashSet<const nsIFrame*>>();
1317   }
1318 
1319   AUTO_PROFILER_LABEL("RestyleManager::ProcessRestyledFrames", LAYOUT);
1320 
1321   nsPresContext* presContext = PresContext();
1322   nsCSSFrameConstructor* frameConstructor = presContext->FrameConstructor();
1323 
1324   // Handle nsChangeHint_ScrollbarChange, by either updating the
1325   // scrollbars on the viewport, or upgrading the change hint to
1326   // frame-reconstruct.
1327   for (nsStyleChangeData& data : aChangeList) {
1328     if (data.mHint & nsChangeHint_ScrollbarChange) {
1329       data.mHint &= ~nsChangeHint_ScrollbarChange;
1330       bool doReconstruct = true;  // assume the worst
1331 
1332       // Only bother with this if we're html/body, since:
1333       //  (a) It'd be *expensive* to reframe these particular nodes.  They're
1334       //      at the root, so reframing would mean rebuilding the world.
1335       //  (b) It's often *unnecessary* to reframe for "overflow" changes on
1336       //      these particular nodes.  In general, the only reason we reframe
1337       //      for "overflow" changes is so we can construct (or destroy) a
1338       //      scrollframe & scrollbars -- and the html/body nodes often don't
1339       //      need their own scrollframe/scrollbars because they coopt the ones
1340       //      on the viewport (which always exist). So depending on whether
1341       //      that's happening, we can skip the reframe for these nodes.
1342       if (data.mContent->IsAnyOfHTMLElements(nsGkAtoms::body,
1343                                              nsGkAtoms::html)) {
1344         // If the restyled element provided/provides the scrollbar styles for
1345         // the viewport before and/or after this restyle, AND it's not coopting
1346         // that responsibility from some other element (which would need
1347         // reconstruction to make its own scrollframe now), THEN: we don't need
1348         // to reconstruct - we can just reflow, because no scrollframe is being
1349         // added/removed.
1350         nsIContent* prevOverrideNode =
1351             presContext->GetViewportScrollStylesOverrideElement();
1352         nsIContent* newOverrideNode =
1353             presContext->UpdateViewportScrollStylesOverride();
1354 
1355         if (data.mContent == prevOverrideNode ||
1356             data.mContent == newOverrideNode) {
1357           // If we get here, the restyled element provided the scrollbar styles
1358           // for viewport before this restyle, OR it will provide them after.
1359           if (!prevOverrideNode || !newOverrideNode ||
1360               prevOverrideNode == newOverrideNode) {
1361             // If we get here, the restyled element is NOT replacing (or being
1362             // replaced by) some other element as the viewport's
1363             // scrollbar-styles provider. (If it were, we'd potentially need to
1364             // reframe to create a dedicated scrollframe for whichever element
1365             // is being booted from providing viewport scrollbar styles.)
1366             //
1367             // Under these conditions, we're OK to assume that this "overflow"
1368             // change only impacts the root viewport's scrollframe, which
1369             // already exists, so we can simply reflow instead of reframing.
1370             data.mHint |= nsChangeHint_ReflowHintsForScrollbarChange;
1371             doReconstruct = false;
1372           }
1373         }
1374       }
1375       if (doReconstruct) {
1376         data.mHint |= nsChangeHint_ReconstructFrame;
1377       }
1378     }
1379   }
1380 
1381   bool didUpdateCursor = false;
1382 
1383   for (size_t i = 0; i < aChangeList.Length(); ++i) {
1384     // Collect and coalesce adjacent siblings for lazy frame construction.
1385     // Eventually it would be even better to make RecreateFramesForContent
1386     // accept a range and coalesce all adjacent reconstructs (bug 1344139).
1387     size_t lazyRangeStart = i;
1388     while (i < aChangeList.Length() && aChangeList[i].mContent &&
1389            aChangeList[i].mContent->HasFlag(NODE_NEEDS_FRAME) &&
1390            (i == lazyRangeStart ||
1391             NextSiblingWhichMayHaveFrame(aChangeList[i - 1].mContent) ==
1392                 aChangeList[i].mContent)) {
1393       MOZ_ASSERT(aChangeList[i].mHint & nsChangeHint_ReconstructFrame);
1394       MOZ_ASSERT(!aChangeList[i].mFrame);
1395       ++i;
1396     }
1397     if (i != lazyRangeStart) {
1398       nsIContent* start = aChangeList[lazyRangeStart].mContent;
1399       nsIContent* end =
1400           NextSiblingWhichMayHaveFrame(aChangeList[i - 1].mContent);
1401       if (!end) {
1402         frameConstructor->ContentAppended(
1403             start, nsCSSFrameConstructor::InsertionKind::Sync);
1404       } else {
1405         frameConstructor->ContentRangeInserted(
1406             start, end, nsCSSFrameConstructor::InsertionKind::Sync);
1407       }
1408     }
1409     for (size_t j = lazyRangeStart; j < i; ++j) {
1410       MOZ_ASSERT(!aChangeList[j].mContent->GetPrimaryFrame() ||
1411                  !aChangeList[j].mContent->HasFlag(NODE_NEEDS_FRAME));
1412     }
1413     if (i == aChangeList.Length()) {
1414       break;
1415     }
1416 
1417     const nsStyleChangeData& data = aChangeList[i];
1418     nsIFrame* frame = data.mFrame;
1419     nsIContent* content = data.mContent;
1420     nsChangeHint hint = data.mHint;
1421     bool didReflowThisFrame = false;
1422 
1423     NS_ASSERTION(!(hint & nsChangeHint_AllReflowHints) ||
1424                      (hint & nsChangeHint_NeedReflow),
1425                  "Reflow hint bits set without actually asking for a reflow");
1426 
1427     // skip any frame that has been destroyed due to a ripple effect
1428     if (frame && mDestroyedFrames->Contains(frame)) {
1429       continue;
1430     }
1431 
1432     if (frame && frame->GetContent() != content) {
1433       // XXXbz this is due to image maps messing with the primary frame of
1434       // <area>s.  See bug 135040.  Remove this block once that's fixed.
1435       frame = nullptr;
1436       if (!(hint & nsChangeHint_ReconstructFrame)) {
1437         continue;
1438       }
1439     }
1440 
1441     if ((hint & nsChangeHint_UpdateContainingBlock) && frame &&
1442         !(hint & nsChangeHint_ReconstructFrame)) {
1443       if (NeedToReframeToUpdateContainingBlock(frame) ||
1444           frame->IsFieldSetFrame() ||
1445           frame->GetContentInsertionFrame() != frame) {
1446         // The frame has positioned children that need to be reparented, or
1447         // it can't easily be converted to/from being an abs-pos container
1448         // correctly.
1449         hint |= nsChangeHint_ReconstructFrame;
1450       } else {
1451         for (nsIFrame* cont = frame; cont;
1452              cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
1453           // Normally frame construction would set state bits as needed,
1454           // but we're not going to reconstruct the frame so we need to set
1455           // them. It's because we need to set this state on each affected frame
1456           // that we can't coalesce nsChangeHint_UpdateContainingBlock hints up
1457           // to ancestors (i.e. it can't be an change hint that is handled for
1458           // descendants).
1459           if (cont->IsAbsPosContainingBlock()) {
1460             if (!cont->IsAbsoluteContainer() &&
1461                 cont->HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)) {
1462               cont->MarkAsAbsoluteContainingBlock();
1463             }
1464           } else {
1465             if (cont->IsAbsoluteContainer()) {
1466               if (cont->HasAbsolutelyPositionedChildren()) {
1467                 // If |cont| still has absolutely positioned children,
1468                 // we can't call MarkAsNotAbsoluteContainingBlock.  This
1469                 // will remove a frame list that still has children in
1470                 // it that we need to keep track of.
1471                 // The optimization of removing it isn't particularly
1472                 // important, although it does mean we skip some tests.
1473                 NS_WARNING("skipping removal of absolute containing block");
1474               } else {
1475                 cont->MarkAsNotAbsoluteContainingBlock();
1476               }
1477             }
1478           }
1479         }
1480       }
1481     }
1482 
1483     if ((hint & nsChangeHint_AddOrRemoveTransform) && frame &&
1484         !(hint & nsChangeHint_ReconstructFrame)) {
1485       for (nsIFrame* cont = frame; cont;
1486            cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
1487         if (cont->StyleDisplay()->HasTransform(cont)) {
1488           cont->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
1489         }
1490         // Don't remove NS_FRAME_MAY_BE_TRANSFORMED since it may still be
1491         // transformed by other means. It's OK to have the bit even if it's
1492         // not needed.
1493       }
1494     }
1495 
1496     if (hint & nsChangeHint_ReconstructFrame) {
1497       // If we ever start passing true here, be careful of restyles
1498       // that involve a reframe and animations.  In particular, if the
1499       // restyle we're processing here is an animation restyle, but
1500       // the style resolution we will do for the frame construction
1501       // happens async when we're not in an animation restyle already,
1502       // problems could arise.
1503       // We could also have problems with triggering of CSS transitions
1504       // on elements whose frames are reconstructed, since we depend on
1505       // the reconstruction happening synchronously.
1506       frameConstructor->RecreateFramesForContent(
1507           content, nsCSSFrameConstructor::InsertionKind::Sync);
1508       frame = content->GetPrimaryFrame();
1509     } else {
1510       NS_ASSERTION(frame, "This shouldn't happen");
1511 
1512       if (!frame->FrameMaintainsOverflow()) {
1513         // frame does not maintain overflow rects, so avoid calling
1514         // FinishAndStoreOverflow on it:
1515         hint &=
1516             ~(nsChangeHint_UpdateOverflow | nsChangeHint_ChildrenOnlyTransform |
1517               nsChangeHint_UpdatePostTransformOverflow |
1518               nsChangeHint_UpdateParentOverflow |
1519               nsChangeHint_UpdateSubtreeOverflow);
1520       }
1521 
1522       if (!frame->HasAnyStateBits(NS_FRAME_MAY_BE_TRANSFORMED)) {
1523         // Frame can not be transformed, and thus a change in transform will
1524         // have no effect and we should not use either
1525         // nsChangeHint_UpdatePostTransformOverflow or
1526         // nsChangeHint_UpdateTransformLayerhint.
1527         hint &= ~(nsChangeHint_UpdatePostTransformOverflow |
1528                   nsChangeHint_UpdateTransformLayer);
1529       }
1530 
1531       if (hint & nsChangeHint_AddOrRemoveTransform) {
1532         // When dropping a running transform animation we will first add an
1533         // nsChangeHint_UpdateTransformLayer hint as part of the animation-only
1534         // restyle. During the subsequent regular restyle, if the animation was
1535         // the only reason the element had any transform applied, we will add
1536         // nsChangeHint_AddOrRemoveTransform as part of the regular restyle.
1537         //
1538         // With the Gecko backend, these two change hints are processed
1539         // after each restyle but when using the Servo backend they accumulate
1540         // and are processed together after we have already removed the
1541         // transform as part of the regular restyle. Since we don't actually
1542         // need the nsChangeHint_UpdateTransformLayer hint if we already have
1543         // a nsChangeHint_AddOrRemoveTransform hint, and since we
1544         // will fail an assertion in ApplyRenderingChangeToTree if we try
1545         // specify nsChangeHint_UpdateTransformLayer but don't have any
1546         // transform style, we just drop the unneeded hint here.
1547         hint &= ~nsChangeHint_UpdateTransformLayer;
1548       }
1549 
1550       if ((hint & nsChangeHint_UpdateEffects) &&
1551           frame == nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame)) {
1552         SVGObserverUtils::UpdateEffects(frame);
1553       }
1554       if ((hint & nsChangeHint_InvalidateRenderingObservers) ||
1555           ((hint & nsChangeHint_UpdateOpacityLayer) &&
1556            frame->IsFrameOfType(nsIFrame::eSVG) &&
1557            !frame->IsSVGOuterSVGFrame())) {
1558         SVGObserverUtils::InvalidateRenderingObservers(frame);
1559         frame->SchedulePaint();
1560       }
1561       if (hint & nsChangeHint_NeedReflow) {
1562         StyleChangeReflow(frame, hint);
1563         didReflowThisFrame = true;
1564       }
1565 
1566       // Here we need to propagate repaint frame change hint instead of update
1567       // opacity layer change hint when we do opacity optimization for SVG.
1568       // We can't do it in nsStyleEffects::CalcDifference() just like we do
1569       // for the optimization for 0.99 over opacity values since we have no way
1570       // to call SVGUtils::CanOptimizeOpacity() there.
1571       if ((hint & nsChangeHint_UpdateOpacityLayer) &&
1572           SVGUtils::CanOptimizeOpacity(frame)) {
1573         hint &= ~nsChangeHint_UpdateOpacityLayer;
1574         hint |= nsChangeHint_RepaintFrame;
1575       }
1576 
1577       if ((hint & nsChangeHint_UpdateUsesOpacity) &&
1578           frame->IsFrameOfType(nsIFrame::eTablePart)) {
1579         NS_ASSERTION(hint & nsChangeHint_UpdateOpacityLayer,
1580                      "should only return UpdateUsesOpacity hint "
1581                      "when also returning UpdateOpacityLayer hint");
1582         // When an internal table part (including cells) changes between
1583         // having opacity 1 and non-1, it changes whether its
1584         // backgrounds (and those of table parts inside of it) are
1585         // painted as part of the table's nsDisplayTableBorderBackground
1586         // display item, or part of its own display item.  That requires
1587         // invalidation, so change UpdateOpacityLayer to RepaintFrame.
1588         hint &= ~nsChangeHint_UpdateOpacityLayer;
1589         hint |= nsChangeHint_RepaintFrame;
1590       }
1591 
1592       // Opacity disables preserve-3d, so if we toggle it, then we also need
1593       // to update the overflow areas of all potentially affected frames.
1594       if ((hint & nsChangeHint_UpdateUsesOpacity) &&
1595           frame->StyleDisplay()->mTransformStyle ==
1596               StyleTransformStyle::Preserve3d) {
1597         hint |= nsChangeHint_UpdateSubtreeOverflow;
1598       }
1599 
1600       if (hint & nsChangeHint_UpdateBackgroundPosition) {
1601         // For most frame types, DLBI can detect background position changes,
1602         // so we only need to schedule a paint.
1603         hint |= nsChangeHint_SchedulePaint;
1604         if (frame->IsFrameOfType(nsIFrame::eTablePart) ||
1605             frame->IsFrameOfType(nsIFrame::eMathML)) {
1606           // Table parts and MathML frames don't build display items for their
1607           // backgrounds, so DLBI can't detect background-position changes for
1608           // these frames. Repaint the whole frame.
1609           hint |= nsChangeHint_RepaintFrame;
1610         }
1611       }
1612 
1613       if (hint &
1614           (nsChangeHint_RepaintFrame | nsChangeHint_UpdateOpacityLayer |
1615            nsChangeHint_UpdateTransformLayer |
1616            nsChangeHint_ChildrenOnlyTransform | nsChangeHint_SchedulePaint)) {
1617         ApplyRenderingChangeToTree(presContext->PresShell(), frame, hint);
1618       }
1619       if ((hint & nsChangeHint_RecomputePosition) && !didReflowThisFrame) {
1620         ActiveLayerTracker::NotifyOffsetRestyle(frame);
1621         // It is possible for this to fall back to a reflow
1622         if (!RecomputePosition(frame)) {
1623           StyleChangeReflow(frame,
1624                             nsChangeHint_NeedReflow |
1625                                 nsChangeHint_ReflowChangesSizeOrPosition);
1626           didReflowThisFrame = true;
1627         }
1628       }
1629       NS_ASSERTION(!(hint & nsChangeHint_ChildrenOnlyTransform) ||
1630                        (hint & nsChangeHint_UpdateOverflow),
1631                    "nsChangeHint_UpdateOverflow should be passed too");
1632       if (!didReflowThisFrame &&
1633           (hint & (nsChangeHint_UpdateOverflow |
1634                    nsChangeHint_UpdatePostTransformOverflow |
1635                    nsChangeHint_UpdateParentOverflow |
1636                    nsChangeHint_UpdateSubtreeOverflow))) {
1637         if (hint & nsChangeHint_UpdateSubtreeOverflow) {
1638           for (nsIFrame* cont = frame; cont;
1639                cont =
1640                    nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
1641             AddSubtreeToOverflowTracker(cont, mOverflowChangedTracker);
1642           }
1643           // The work we just did in AddSubtreeToOverflowTracker
1644           // subsumes some of the other hints:
1645           hint &= ~(nsChangeHint_UpdateOverflow |
1646                     nsChangeHint_UpdatePostTransformOverflow);
1647         }
1648         if (hint & nsChangeHint_ChildrenOnlyTransform) {
1649           // We need to update overflows. The correct frame(s) to update depends
1650           // on whether the ChangeHint came from an outer or an inner svg.
1651           nsIFrame* hintFrame = GetFrameForChildrenOnlyTransformHint(frame);
1652           NS_ASSERTION(
1653               !nsLayoutUtils::GetNextContinuationOrIBSplitSibling(frame),
1654               "SVG frames should not have continuations "
1655               "or ib-split siblings");
1656           NS_ASSERTION(
1657               !nsLayoutUtils::GetNextContinuationOrIBSplitSibling(hintFrame),
1658               "SVG frames should not have continuations "
1659               "or ib-split siblings");
1660           if (hintFrame->IsSVGOuterSVGAnonChildFrame()) {
1661             // The children only transform of an outer svg frame is applied to
1662             // the outer svg's anonymous child frame (instead of to the
1663             // anonymous child's children).
1664 
1665             if (!CanSkipOverflowUpdates(hintFrame)) {
1666               mOverflowChangedTracker.AddFrame(
1667                   hintFrame, OverflowChangedTracker::CHILDREN_CHANGED);
1668             }
1669           } else {
1670             // The children only transform is applied to the child frames of an
1671             // inner svg frame, so update the child overflows.
1672             nsIFrame* childFrame = hintFrame->PrincipalChildList().FirstChild();
1673             for (; childFrame; childFrame = childFrame->GetNextSibling()) {
1674               MOZ_ASSERT(childFrame->IsFrameOfType(nsIFrame::eSVG),
1675                          "Not expecting non-SVG children");
1676               if (!CanSkipOverflowUpdates(childFrame)) {
1677                 mOverflowChangedTracker.AddFrame(
1678                     childFrame, OverflowChangedTracker::CHILDREN_CHANGED);
1679               }
1680               NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(
1681                                childFrame),
1682                            "SVG frames should not have continuations "
1683                            "or ib-split siblings");
1684               NS_ASSERTION(
1685                   childFrame->GetParent() == hintFrame,
1686                   "SVG child frame not expected to have different parent");
1687             }
1688           }
1689         }
1690         if (!CanSkipOverflowUpdates(frame)) {
1691           if (hint & (nsChangeHint_UpdateOverflow |
1692                       nsChangeHint_UpdatePostTransformOverflow)) {
1693             OverflowChangedTracker::ChangeKind changeKind;
1694             // If we have both nsChangeHint_UpdateOverflow and
1695             // nsChangeHint_UpdatePostTransformOverflow,
1696             // CHILDREN_CHANGED is selected as it is
1697             // strictly stronger.
1698             if (hint & nsChangeHint_UpdateOverflow) {
1699               changeKind = OverflowChangedTracker::CHILDREN_CHANGED;
1700             } else {
1701               changeKind = OverflowChangedTracker::TRANSFORM_CHANGED;
1702             }
1703             for (nsIFrame* cont = frame; cont;
1704                  cont =
1705                      nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
1706               mOverflowChangedTracker.AddFrame(cont, changeKind);
1707             }
1708           }
1709           // UpdateParentOverflow hints need to be processed in addition
1710           // to the above, since if the processing of the above hints
1711           // yields no change, the update will not propagate to the
1712           // parent.
1713           if (hint & nsChangeHint_UpdateParentOverflow) {
1714             MOZ_ASSERT(frame->GetParent(),
1715                        "shouldn't get style hints for the root frame");
1716             for (nsIFrame* cont = frame; cont;
1717                  cont =
1718                      nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
1719               mOverflowChangedTracker.AddFrame(
1720                   cont->GetParent(), OverflowChangedTracker::CHILDREN_CHANGED);
1721             }
1722           }
1723         }
1724       }
1725       if ((hint & nsChangeHint_UpdateCursor) && !didUpdateCursor) {
1726         presContext->PresShell()->SynthesizeMouseMove(false);
1727         didUpdateCursor = true;
1728       }
1729       if (hint & nsChangeHint_UpdateTableCellSpans) {
1730         frameConstructor->UpdateTableCellSpans(content);
1731       }
1732       if (hint & nsChangeHint_VisibilityChange) {
1733         frame->UpdateVisibleDescendantsState();
1734       }
1735     }
1736   }
1737 
1738   aChangeList.Clear();
1739   FlushOverflowChangedTracker();
1740 }
1741 
1742 /* static */
GetAnimationGenerationForFrame(nsIFrame * aStyleFrame)1743 uint64_t RestyleManager::GetAnimationGenerationForFrame(nsIFrame* aStyleFrame) {
1744   EffectSet* effectSet = EffectSet::GetEffectSetForStyleFrame(aStyleFrame);
1745   return effectSet ? effectSet->GetAnimationGeneration() : 0;
1746 }
1747 
IncrementAnimationGeneration()1748 void RestyleManager::IncrementAnimationGeneration() {
1749   // We update the animation generation at start of each call to
1750   // ProcessPendingRestyles so we should ignore any subsequent (redundant)
1751   // calls that occur while we are still processing restyles.
1752   if (!mInStyleRefresh) {
1753     ++mAnimationGeneration;
1754   }
1755 }
1756 
1757 /* static */
AddLayerChangesForAnimation(nsIFrame * aStyleFrame,nsIFrame * aPrimaryFrame,Element * aElement,nsChangeHint aHintForThisFrame,nsStyleChangeList & aChangeListToProcess)1758 void RestyleManager::AddLayerChangesForAnimation(
1759     nsIFrame* aStyleFrame, nsIFrame* aPrimaryFrame, Element* aElement,
1760     nsChangeHint aHintForThisFrame, nsStyleChangeList& aChangeListToProcess) {
1761   MOZ_ASSERT(aElement);
1762   MOZ_ASSERT(!!aStyleFrame == !!aPrimaryFrame);
1763   if (!aStyleFrame) {
1764     return;
1765   }
1766 
1767   uint64_t frameGeneration =
1768       RestyleManager::GetAnimationGenerationForFrame(aStyleFrame);
1769 
1770   Maybe<nsCSSPropertyIDSet> effectiveAnimationProperties;
1771 
1772   nsChangeHint hint = nsChangeHint(0);
1773   auto maybeApplyChangeHint = [&](const Maybe<uint64_t>& aGeneration,
1774                                   DisplayItemType aDisplayItemType) -> bool {
1775     if (aGeneration && frameGeneration != *aGeneration) {
1776       // If we have a transform layer but don't have any transform style, we
1777       // probably just removed the transform but haven't destroyed the layer
1778       // yet. In this case we will typically add the appropriate change hint
1779       // (nsChangeHint_UpdateContainingBlock) when we compare styles so in
1780       // theory we could skip adding any change hint here.
1781       //
1782       // However, sometimes when we compare styles we'll get no change. For
1783       // example, if the transform style was 'none' when we sent the transform
1784       // animation to the compositor and the current transform style is now
1785       // 'none' we'll think nothing changed but actually we still need to
1786       // trigger an update to clear whatever style the transform animation set
1787       // on the compositor. To handle this case we simply set all the change
1788       // hints relevant to removing transform style (since we don't know exactly
1789       // what changes happened while the animation was running on the
1790       // compositor).
1791       //
1792       // Note that we *don't* add nsChangeHint_UpdateTransformLayer since if we
1793       // did, ApplyRenderingChangeToTree would complain that we're updating a
1794       // transform layer without a transform.
1795       if (aDisplayItemType == DisplayItemType::TYPE_TRANSFORM &&
1796           !aStyleFrame->StyleDisplay()->HasTransformStyle()) {
1797         // Add all the hints for a removing a transform if they are not already
1798         // set for this frame.
1799         if (!(NS_IsHintSubset(nsChangeHint_ComprehensiveAddOrRemoveTransform,
1800                               aHintForThisFrame))) {
1801           hint |= nsChangeHint_ComprehensiveAddOrRemoveTransform;
1802         }
1803         return true;
1804       }
1805       hint |= LayerAnimationInfo::GetChangeHintFor(aDisplayItemType);
1806     }
1807 
1808     // We consider it's the first paint for the frame if we have an animation
1809     // for the property but have no layer, for the case of WebRender,  no
1810     // corresponding animation info.
1811     // Note that in case of animations which has properties preventing running
1812     // on the compositor, e.g., width or height, corresponding layer is not
1813     // created at all, but even in such cases, we normally set valid change
1814     // hint for such animations in each tick, i.e. restyles in each tick. As
1815     // a result, we usually do restyles for such animations in every tick on
1816     // the main-thread.  The only animations which will be affected by this
1817     // explicit change hint are animations that have opacity/transform but did
1818     // not have those properies just before. e.g, setting transform by
1819     // setKeyframes or changing target element from other target which prevents
1820     // running on the compositor, etc.
1821     if (!aGeneration) {
1822       nsChangeHint hintForDisplayItem =
1823           LayerAnimationInfo::GetChangeHintFor(aDisplayItemType);
1824       // We don't need to apply the corresponding change hint if we already have
1825       // it.
1826       if (NS_IsHintSubset(hintForDisplayItem, aHintForThisFrame)) {
1827         return true;
1828       }
1829 
1830       if (!effectiveAnimationProperties) {
1831         effectiveAnimationProperties.emplace(
1832             nsLayoutUtils::GetAnimationPropertiesForCompositor(aStyleFrame));
1833       }
1834       const nsCSSPropertyIDSet& propertiesForDisplayItem =
1835           LayerAnimationInfo::GetCSSPropertiesFor(aDisplayItemType);
1836       if (effectiveAnimationProperties->Intersects(propertiesForDisplayItem)) {
1837         hint |= hintForDisplayItem;
1838       }
1839     }
1840     return true;
1841   };
1842 
1843   AnimationInfo::EnumerateGenerationOnFrame(
1844       aStyleFrame, aElement, LayerAnimationInfo::sDisplayItemTypes,
1845       maybeApplyChangeHint);
1846 
1847   if (hint) {
1848     // We apply the hint to the primary frame, not the style frame. Transform
1849     // and opacity hints apply to the table wrapper box, not the table box.
1850     aChangeListToProcess.AppendChange(aPrimaryFrame, aElement, hint);
1851   }
1852 }
1853 
AnimationsWithDestroyedFrame(RestyleManager * aRestyleManager)1854 RestyleManager::AnimationsWithDestroyedFrame::AnimationsWithDestroyedFrame(
1855     RestyleManager* aRestyleManager)
1856     : mRestyleManager(aRestyleManager),
1857       mRestorePointer(mRestyleManager->mAnimationsWithDestroyedFrame) {
1858   MOZ_ASSERT(!mRestyleManager->mAnimationsWithDestroyedFrame,
1859              "shouldn't construct recursively");
1860   mRestyleManager->mAnimationsWithDestroyedFrame = this;
1861 }
1862 
1863 void RestyleManager::AnimationsWithDestroyedFrame ::
StopAnimationsForElementsWithoutFrames()1864     StopAnimationsForElementsWithoutFrames() {
1865   StopAnimationsWithoutFrame(mContents, PseudoStyleType::NotPseudo);
1866   StopAnimationsWithoutFrame(mBeforeContents, PseudoStyleType::before);
1867   StopAnimationsWithoutFrame(mAfterContents, PseudoStyleType::after);
1868   StopAnimationsWithoutFrame(mMarkerContents, PseudoStyleType::marker);
1869 }
1870 
StopAnimationsWithoutFrame(nsTArray<RefPtr<nsIContent>> & aArray,PseudoStyleType aPseudoType)1871 void RestyleManager::AnimationsWithDestroyedFrame ::StopAnimationsWithoutFrame(
1872     nsTArray<RefPtr<nsIContent>>& aArray, PseudoStyleType aPseudoType) {
1873   nsAnimationManager* animationManager =
1874       mRestyleManager->PresContext()->AnimationManager();
1875   nsTransitionManager* transitionManager =
1876       mRestyleManager->PresContext()->TransitionManager();
1877   for (nsIContent* content : aArray) {
1878     if (aPseudoType == PseudoStyleType::NotPseudo) {
1879       if (content->GetPrimaryFrame()) {
1880         continue;
1881       }
1882     } else if (aPseudoType == PseudoStyleType::before) {
1883       if (nsLayoutUtils::GetBeforeFrame(content)) {
1884         continue;
1885       }
1886     } else if (aPseudoType == PseudoStyleType::after) {
1887       if (nsLayoutUtils::GetAfterFrame(content)) {
1888         continue;
1889       }
1890     } else if (aPseudoType == PseudoStyleType::marker) {
1891       if (nsLayoutUtils::GetMarkerFrame(content)) {
1892         continue;
1893       }
1894     }
1895     dom::Element* element = content->AsElement();
1896 
1897     animationManager->StopAnimationsForElement(element, aPseudoType);
1898     transitionManager->StopAnimationsForElement(element, aPseudoType);
1899 
1900     // All other animations should keep running but not running on the
1901     // *compositor* at this point.
1902     EffectSet* effectSet = EffectSet::GetEffectSet(element, aPseudoType);
1903     if (effectSet) {
1904       for (KeyframeEffect* effect : *effectSet) {
1905         effect->ResetIsRunningOnCompositor();
1906       }
1907     }
1908   }
1909 }
1910 
1911 #ifdef DEBUG
IsAnonBox(const nsIFrame * aFrame)1912 static bool IsAnonBox(const nsIFrame* aFrame) {
1913   return aFrame->Style()->IsAnonBox();
1914 }
1915 
FirstContinuationOrPartOfIBSplit(const nsIFrame * aFrame)1916 static const nsIFrame* FirstContinuationOrPartOfIBSplit(
1917     const nsIFrame* aFrame) {
1918   if (!aFrame) {
1919     return nullptr;
1920   }
1921 
1922   return nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
1923 }
1924 
ExpectedOwnerForChild(const nsIFrame * aFrame)1925 static const nsIFrame* ExpectedOwnerForChild(const nsIFrame* aFrame) {
1926   const nsIFrame* parent = aFrame->GetParent();
1927   if (aFrame->IsTableFrame()) {
1928     MOZ_ASSERT(parent->IsTableWrapperFrame());
1929     parent = parent->GetParent();
1930   }
1931 
1932   if (IsAnonBox(aFrame) && !aFrame->IsTextFrame()) {
1933     if (parent->IsLineFrame()) {
1934       parent = parent->GetParent();
1935     }
1936     return parent->IsViewportFrame() ? nullptr
1937                                      : FirstContinuationOrPartOfIBSplit(parent);
1938   }
1939 
1940   if (aFrame->IsLineFrame()) {
1941     // A ::first-line always ends up here via its block, which is therefore the
1942     // right expected owner.  That block can be an
1943     // anonymous box.  For example, we could have a ::first-line on a columnated
1944     // block; the blockframe is the column-content anonymous box in that case.
1945     // So we don't want to end up in the code below, which steps out of anon
1946     // boxes.  Just return the parent of the line frame, which is the block.
1947     return parent;
1948   }
1949 
1950   if (aFrame->IsLetterFrame()) {
1951     // Ditto for ::first-letter. A first-letter always arrives here via its
1952     // direct parent, except when it's parented to a ::first-line.
1953     if (parent->IsLineFrame()) {
1954       parent = parent->GetParent();
1955     }
1956     return FirstContinuationOrPartOfIBSplit(parent);
1957   }
1958 
1959   if (parent->IsLetterFrame()) {
1960     // Things never have ::first-letter as their expected parent.  Go
1961     // on up to the ::first-letter's parent.
1962     parent = parent->GetParent();
1963   }
1964 
1965   parent = FirstContinuationOrPartOfIBSplit(parent);
1966 
1967   // We've handled already anon boxes, so now we're looking at
1968   // a frame of a DOM element or pseudo. Hop through anon and line-boxes
1969   // generated by our DOM parent, and go find the owner frame for it.
1970   while (parent && (IsAnonBox(parent) || parent->IsLineFrame())) {
1971     auto pseudo = parent->Style()->GetPseudoType();
1972     if (pseudo == PseudoStyleType::tableWrapper) {
1973       const nsIFrame* tableFrame = parent->PrincipalChildList().FirstChild();
1974       MOZ_ASSERT(tableFrame->IsTableFrame());
1975       // Handle :-moz-table and :-moz-inline-table.
1976       parent = IsAnonBox(tableFrame) ? parent->GetParent() : tableFrame;
1977     } else {
1978       // We get the in-flow parent here so that we can handle the OOF anonymous
1979       // boxed to get the correct parent.
1980       parent = parent->GetInFlowParent();
1981     }
1982     parent = FirstContinuationOrPartOfIBSplit(parent);
1983   }
1984 
1985   return parent;
1986 }
1987 
1988 // FIXME(emilio, bug 1633685): We should ideally figure out how to properly
1989 // restyle replicated fixed pos frames... We seem to assume everywhere that they
1990 // can't get restyled at the moment...
IsInReplicatedFixedPosTree(const nsIFrame * aFrame)1991 static bool IsInReplicatedFixedPosTree(const nsIFrame* aFrame) {
1992   if (!aFrame->PresContext()->IsPaginated()) {
1993     return false;
1994   }
1995 
1996   for (; aFrame; aFrame = aFrame->GetParent()) {
1997     if (aFrame->StyleDisplay()->mPosition == StylePositionProperty::Fixed &&
1998         !aFrame->FirstContinuation()->IsPrimaryFrame() &&
1999         nsLayoutUtils::IsReallyFixedPos(aFrame)) {
2000       return true;
2001     }
2002   }
2003 
2004   return true;
2005 }
2006 
AssertOwner(const ServoRestyleState & aParent) const2007 void ServoRestyleState::AssertOwner(const ServoRestyleState& aParent) const {
2008   MOZ_ASSERT(mOwner);
2009   MOZ_ASSERT(!mOwner->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW));
2010   MOZ_ASSERT(!mOwner->IsColumnSpanInMulticolSubtree());
2011   // We allow aParent.mOwner to be null, for cases when we're not starting at
2012   // the root of the tree.  We also allow aParent.mOwner to be somewhere up our
2013   // expected owner chain not our immediate owner, which allows us creating long
2014   // chains of ServoRestyleStates in some cases where it's just not worth it.
2015   if (aParent.mOwner) {
2016     const nsIFrame* owner = ExpectedOwnerForChild(mOwner);
2017     if (owner != aParent.mOwner && !IsInReplicatedFixedPosTree(mOwner)) {
2018       MOZ_ASSERT(IsAnonBox(owner),
2019                  "Should only have expected owner weirdness when anon boxes "
2020                  "are involved");
2021       bool found = false;
2022       for (; owner; owner = ExpectedOwnerForChild(owner)) {
2023         if (owner == aParent.mOwner) {
2024           found = true;
2025           break;
2026         }
2027       }
2028       MOZ_ASSERT(found, "Must have aParent.mOwner on our expected owner chain");
2029     }
2030   }
2031 }
2032 
ChangesHandledFor(const nsIFrame * aFrame) const2033 nsChangeHint ServoRestyleState::ChangesHandledFor(
2034     const nsIFrame* aFrame) const {
2035   if (!mOwner) {
2036     MOZ_ASSERT(!mChangesHandled);
2037     return mChangesHandled;
2038   }
2039 
2040   MOZ_ASSERT(mOwner == ExpectedOwnerForChild(aFrame) ||
2041                  IsInReplicatedFixedPosTree(aFrame),
2042              "Missed some frame in the hierarchy?");
2043   return mChangesHandled;
2044 }
2045 #endif
2046 
AddPendingWrapperRestyle(nsIFrame * aWrapperFrame)2047 void ServoRestyleState::AddPendingWrapperRestyle(nsIFrame* aWrapperFrame) {
2048   MOZ_ASSERT(aWrapperFrame->Style()->IsWrapperAnonBox(),
2049              "All our wrappers are anon boxes, and why would we restyle "
2050              "non-inheriting ones?");
2051   MOZ_ASSERT(aWrapperFrame->Style()->IsInheritingAnonBox(),
2052              "All our wrappers are anon boxes, and why would we restyle "
2053              "non-inheriting ones?");
2054   MOZ_ASSERT(
2055       aWrapperFrame->Style()->GetPseudoType() != PseudoStyleType::cellContent,
2056       "Someone should be using TableAwareParentFor");
2057   MOZ_ASSERT(
2058       aWrapperFrame->Style()->GetPseudoType() != PseudoStyleType::tableWrapper,
2059       "Someone should be using TableAwareParentFor");
2060   // Make sure we only add first continuations.
2061   aWrapperFrame = aWrapperFrame->FirstContinuation();
2062   nsIFrame* last = mPendingWrapperRestyles.SafeLastElement(nullptr);
2063   if (last == aWrapperFrame) {
2064     // Already queued up, nothing to do.
2065     return;
2066   }
2067 
2068   // Make sure to queue up parents before children.  But don't queue up
2069   // ancestors of non-anonymous boxes here; those are handled when we traverse
2070   // their non-anonymous kids.
2071   if (aWrapperFrame->ParentIsWrapperAnonBox()) {
2072     AddPendingWrapperRestyle(TableAwareParentFor(aWrapperFrame));
2073   }
2074 
2075   // If the append fails, we'll fail to restyle properly, but that's probably
2076   // better than crashing.
2077   if (mPendingWrapperRestyles.AppendElement(aWrapperFrame, fallible)) {
2078     aWrapperFrame->SetIsWrapperAnonBoxNeedingRestyle(true);
2079   }
2080 }
2081 
ProcessWrapperRestyles(nsIFrame * aParentFrame)2082 void ServoRestyleState::ProcessWrapperRestyles(nsIFrame* aParentFrame) {
2083   size_t i = mPendingWrapperRestyleOffset;
2084   while (i < mPendingWrapperRestyles.Length()) {
2085     i += ProcessMaybeNestedWrapperRestyle(aParentFrame, i);
2086   }
2087 
2088   mPendingWrapperRestyles.TruncateLength(mPendingWrapperRestyleOffset);
2089 }
2090 
ProcessMaybeNestedWrapperRestyle(nsIFrame * aParent,size_t aIndex)2091 size_t ServoRestyleState::ProcessMaybeNestedWrapperRestyle(nsIFrame* aParent,
2092                                                            size_t aIndex) {
2093   // The frame at index aIndex is something we should restyle ourselves, but
2094   // following frames may need separate ServoRestyleStates to restyle.
2095   MOZ_ASSERT(aIndex < mPendingWrapperRestyles.Length());
2096 
2097   nsIFrame* cur = mPendingWrapperRestyles[aIndex];
2098   MOZ_ASSERT(cur->Style()->IsWrapperAnonBox());
2099 
2100   // Where is cur supposed to inherit from?  From its parent frame, except in
2101   // the case when cur is a table, in which case it should be its grandparent.
2102   // Also, not in the case when the resulting frame would be a first-line; in
2103   // that case we should be inheriting from the block, and the first-line will
2104   // do its fixup later if needed.
2105   //
2106   // Note that after we do all that fixup the parent we get might still not be
2107   // aParent; for example aParent could be a scrollframe, in which case we
2108   // should inherit from the scrollcontent frame.  Or the parent might be some
2109   // continuation of aParent.
2110   //
2111   // Try to assert as much as we can about the parent we actually end up using
2112   // without triggering bogus asserts in all those various edge cases.
2113   nsIFrame* parent = cur->GetParent();
2114   if (cur->IsTableFrame()) {
2115     MOZ_ASSERT(parent->IsTableWrapperFrame());
2116     parent = parent->GetParent();
2117   }
2118   if (parent->IsLineFrame()) {
2119     parent = parent->GetParent();
2120   }
2121   MOZ_ASSERT(FirstContinuationOrPartOfIBSplit(parent) == aParent ||
2122              (parent->Style()->IsInheritingAnonBox() &&
2123               parent->GetContent() == aParent->GetContent()));
2124 
2125   // Now "this" is a ServoRestyleState for aParent, so if parent is not a next
2126   // continuation (possibly across ib splits) of aParent we need a new
2127   // ServoRestyleState for the kid.
2128   Maybe<ServoRestyleState> parentRestyleState;
2129   nsIFrame* parentForRestyle =
2130       nsLayoutUtils::FirstContinuationOrIBSplitSibling(parent);
2131   if (parentForRestyle != aParent) {
2132     parentRestyleState.emplace(*parentForRestyle, *this, nsChangeHint_Empty,
2133                                Type::InFlow);
2134   }
2135   ServoRestyleState& curRestyleState =
2136       parentRestyleState ? *parentRestyleState : *this;
2137 
2138   // This frame may already have been restyled.  Even if it has, we can't just
2139   // return, because the next frame may be a kid of it that does need restyling.
2140   if (cur->IsWrapperAnonBoxNeedingRestyle()) {
2141     parentForRestyle->UpdateStyleOfChildAnonBox(cur, curRestyleState);
2142     cur->SetIsWrapperAnonBoxNeedingRestyle(false);
2143   }
2144 
2145   size_t numProcessed = 1;
2146 
2147   // Note: no overflow possible here, since aIndex < length.
2148   if (aIndex + 1 < mPendingWrapperRestyles.Length()) {
2149     nsIFrame* next = mPendingWrapperRestyles[aIndex + 1];
2150     if (TableAwareParentFor(next) == cur &&
2151         next->IsWrapperAnonBoxNeedingRestyle()) {
2152       // It might be nice if we could do better than nsChangeHint_Empty.  On
2153       // the other hand, presumably our mChangesHandled already has the bits
2154       // we really want here so in practice it doesn't matter.
2155       ServoRestyleState childState(*cur, curRestyleState, nsChangeHint_Empty,
2156                                    Type::InFlow,
2157                                    /* aAssertWrapperRestyleLength = */ false);
2158       numProcessed +=
2159           childState.ProcessMaybeNestedWrapperRestyle(cur, aIndex + 1);
2160     }
2161   }
2162 
2163   return numProcessed;
2164 }
2165 
TableAwareParentFor(const nsIFrame * aChild)2166 nsIFrame* ServoRestyleState::TableAwareParentFor(const nsIFrame* aChild) {
2167   // We want to get the anon box parent for aChild. where aChild has
2168   // ParentIsWrapperAnonBox().
2169   //
2170   // For the most part this is pretty straightforward, but there are two
2171   // wrinkles.  First, if aChild is a table, then we really want the parent of
2172   // its table wrapper.
2173   if (aChild->IsTableFrame()) {
2174     aChild = aChild->GetParent();
2175     MOZ_ASSERT(aChild->IsTableWrapperFrame());
2176   }
2177 
2178   nsIFrame* parent = aChild->GetParent();
2179   // Now if parent is a cell-content frame, we actually want the cellframe.
2180   if (parent->Style()->GetPseudoType() == PseudoStyleType::cellContent) {
2181     parent = parent->GetParent();
2182   } else if (parent->IsTableWrapperFrame()) {
2183     // Must be a caption.  In that case we want the table here.
2184     MOZ_ASSERT(aChild->StyleDisplay()->mDisplay == StyleDisplay::TableCaption);
2185     parent = parent->PrincipalChildList().FirstChild();
2186   }
2187   return parent;
2188 }
2189 
PostRestyleEvent(Element * aElement,RestyleHint aRestyleHint,nsChangeHint aMinChangeHint)2190 void RestyleManager::PostRestyleEvent(Element* aElement,
2191                                       RestyleHint aRestyleHint,
2192                                       nsChangeHint aMinChangeHint) {
2193   MOZ_ASSERT(!(aMinChangeHint & nsChangeHint_NeutralChange),
2194              "Didn't expect explicit change hints to be neutral!");
2195   if (MOZ_UNLIKELY(IsDisconnected()) ||
2196       MOZ_UNLIKELY(PresContext()->PresShell()->IsDestroying())) {
2197     return;
2198   }
2199 
2200   // We allow posting restyles from within change hint handling, but not from
2201   // within the restyle algorithm itself.
2202   MOZ_ASSERT(!ServoStyleSet::IsInServoTraversal());
2203 
2204   if (!aRestyleHint && !aMinChangeHint) {
2205     // FIXME(emilio): we should assert against this instead.
2206     return;  // Nothing to do.
2207   }
2208 
2209   // Assuming the restyle hints will invalidate cached style for
2210   // getComputedStyle, since we don't know if any of the restyling that we do
2211   // would affect undisplayed elements.
2212   if (aRestyleHint) {
2213     if (!(aRestyleHint & RestyleHint::ForAnimations())) {
2214       mHaveNonAnimationRestyles = true;
2215     }
2216 
2217     IncrementUndisplayedRestyleGeneration();
2218   }
2219 
2220   // Processing change hints sometimes causes new change hints to be generated,
2221   // and very occasionally, additional restyle hints. We collect the change
2222   // hints manually to avoid re-traversing the DOM to find them.
2223   if (mReentrantChanges && !aRestyleHint) {
2224     mReentrantChanges->AppendElement(ReentrantChange{aElement, aMinChangeHint});
2225     return;
2226   }
2227 
2228   if (aRestyleHint || aMinChangeHint) {
2229     Servo_NoteExplicitHints(aElement, aRestyleHint, aMinChangeHint);
2230   }
2231 }
2232 
PostRestyleEventForAnimations(Element * aElement,PseudoStyleType aPseudoType,RestyleHint aRestyleHint)2233 void RestyleManager::PostRestyleEventForAnimations(Element* aElement,
2234                                                    PseudoStyleType aPseudoType,
2235                                                    RestyleHint aRestyleHint) {
2236   Element* elementToRestyle =
2237       EffectCompositor::GetElementToRestyle(aElement, aPseudoType);
2238 
2239   if (!elementToRestyle) {
2240     // FIXME: Bug 1371107: When reframing happens,
2241     // EffectCompositor::mElementsToRestyle still has unbound old pseudo
2242     // element. We should drop it.
2243     return;
2244   }
2245 
2246   AutoRestyleTimelineMarker marker(mPresContext->GetDocShell(),
2247                                    true /* animation-only */);
2248   Servo_NoteExplicitHints(elementToRestyle, aRestyleHint, nsChangeHint(0));
2249 }
2250 
RebuildAllStyleData(nsChangeHint aExtraHint,RestyleHint aRestyleHint)2251 void RestyleManager::RebuildAllStyleData(nsChangeHint aExtraHint,
2252                                          RestyleHint aRestyleHint) {
2253   // NOTE(emilio): The semantics of these methods are quite funny, in the sense
2254   // that we're not supposed to need to rebuild the actual stylist data.
2255   //
2256   // That's handled as part of the MediumFeaturesChanged stuff, if needed.
2257   //
2258   // Clear the cached style data only if we are guaranteed to process the whole
2259   // DOM tree again.
2260   //
2261   // FIXME(emilio): Decouple this, probably. This probably just wants to reset
2262   // the "uses viewport units / uses rem" bits, and _maybe_ clear cached anon
2263   // box styles and such... But it doesn't really always need to clear the
2264   // initial style of the document and similar...
2265   if (aRestyleHint.DefinitelyRecascadesAllSubtree()) {
2266     StyleSet()->ClearCachedStyleData();
2267   }
2268 
2269   DocumentStyleRootIterator iter(mPresContext->Document());
2270   while (Element* root = iter.GetNextStyleRoot()) {
2271     PostRestyleEvent(root, aRestyleHint, aExtraHint);
2272   }
2273 
2274   // TODO(emilio, bz): Extensions can add/remove stylesheets that can affect
2275   // non-inheriting anon boxes. It's not clear if we want to support that, but
2276   // if we do, we need to re-selector-match them here.
2277 }
2278 
2279 /* static */
ClearServoDataFromSubtree(Element * aElement,IncludeRoot aIncludeRoot)2280 void RestyleManager::ClearServoDataFromSubtree(Element* aElement,
2281                                                IncludeRoot aIncludeRoot) {
2282   if (aElement->HasServoData()) {
2283     StyleChildrenIterator it(aElement);
2284     for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
2285       if (n->IsElement()) {
2286         ClearServoDataFromSubtree(n->AsElement(), IncludeRoot::Yes);
2287       }
2288     }
2289   }
2290 
2291   if (MOZ_LIKELY(aIncludeRoot == IncludeRoot::Yes)) {
2292     aElement->ClearServoData();
2293     MOZ_ASSERT(!aElement->HasAnyOfFlags(Element::kAllServoDescendantBits |
2294                                         NODE_NEEDS_FRAME));
2295     MOZ_ASSERT(aElement != aElement->OwnerDoc()->GetServoRestyleRoot());
2296   }
2297 }
2298 
2299 /* static */
ClearRestyleStateFromSubtree(Element * aElement)2300 void RestyleManager::ClearRestyleStateFromSubtree(Element* aElement) {
2301   if (aElement->HasAnyOfFlags(Element::kAllServoDescendantBits)) {
2302     StyleChildrenIterator it(aElement);
2303     for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
2304       if (n->IsElement()) {
2305         ClearRestyleStateFromSubtree(n->AsElement());
2306       }
2307     }
2308   }
2309 
2310   bool wasRestyled;
2311   Unused << Servo_TakeChangeHint(aElement, &wasRestyled);
2312   aElement->UnsetFlags(Element::kAllServoDescendantBits);
2313 }
2314 
2315 /**
2316  * This struct takes care of encapsulating some common state that text nodes may
2317  * need to track during the post-traversal.
2318  *
2319  * This is currently used to properly compute change hints when the parent
2320  * element of this node is a display: contents node, and also to avoid computing
2321  * the style for text children more than once per element.
2322  */
2323 struct RestyleManager::TextPostTraversalState {
2324  public:
TextPostTraversalStatemozilla::RestyleManager::TextPostTraversalState2325   TextPostTraversalState(Element& aParentElement, ComputedStyle* aParentContext,
2326                          bool aDisplayContentsParentStyleChanged,
2327                          ServoRestyleState& aParentRestyleState)
2328       : mParentElement(aParentElement),
2329         mParentContext(aParentContext),
2330         mParentRestyleState(aParentRestyleState),
2331         mStyle(nullptr),
2332         mShouldPostHints(aDisplayContentsParentStyleChanged),
2333         mShouldComputeHints(aDisplayContentsParentStyleChanged),
2334         mComputedHint(nsChangeHint_Empty) {}
2335 
ChangeListmozilla::RestyleManager::TextPostTraversalState2336   nsStyleChangeList& ChangeList() { return mParentRestyleState.ChangeList(); }
2337 
ComputeStylemozilla::RestyleManager::TextPostTraversalState2338   ComputedStyle& ComputeStyle(nsIContent* aTextNode) {
2339     if (!mStyle) {
2340       mStyle = mParentRestyleState.StyleSet().ResolveStyleForText(
2341           aTextNode, &ParentStyle());
2342     }
2343     MOZ_ASSERT(mStyle);
2344     return *mStyle;
2345   }
2346 
ComputeHintIfNeededmozilla::RestyleManager::TextPostTraversalState2347   void ComputeHintIfNeeded(nsIContent* aContent, nsIFrame* aTextFrame,
2348                            ComputedStyle& aNewStyle) {
2349     MOZ_ASSERT(aTextFrame);
2350     MOZ_ASSERT(aNewStyle.GetPseudoType() == PseudoStyleType::mozText);
2351 
2352     if (MOZ_LIKELY(!mShouldPostHints)) {
2353       return;
2354     }
2355 
2356     ComputedStyle* oldStyle = aTextFrame->Style();
2357     MOZ_ASSERT(oldStyle->GetPseudoType() == PseudoStyleType::mozText);
2358 
2359     // We rely on the fact that all the text children for the same element share
2360     // style to avoid recomputing style differences for all of them.
2361     //
2362     // TODO(emilio): The above may not be true for ::first-{line,letter}, but
2363     // we'll cross that bridge when we support those in stylo.
2364     if (mShouldComputeHints) {
2365       mShouldComputeHints = false;
2366       uint32_t equalStructs;
2367       mComputedHint = oldStyle->CalcStyleDifference(aNewStyle, &equalStructs);
2368       mComputedHint = NS_RemoveSubsumedHints(
2369           mComputedHint, mParentRestyleState.ChangesHandledFor(aTextFrame));
2370     }
2371 
2372     if (mComputedHint) {
2373       mParentRestyleState.ChangeList().AppendChange(aTextFrame, aContent,
2374                                                     mComputedHint);
2375     }
2376   }
2377 
2378  private:
ParentStylemozilla::RestyleManager::TextPostTraversalState2379   ComputedStyle& ParentStyle() {
2380     if (!mParentContext) {
2381       mLazilyResolvedParentContext =
2382           ServoStyleSet::ResolveServoStyle(mParentElement);
2383       mParentContext = mLazilyResolvedParentContext;
2384     }
2385     return *mParentContext;
2386   }
2387 
2388   Element& mParentElement;
2389   ComputedStyle* mParentContext;
2390   RefPtr<ComputedStyle> mLazilyResolvedParentContext;
2391   ServoRestyleState& mParentRestyleState;
2392   RefPtr<ComputedStyle> mStyle;
2393   bool mShouldPostHints;
2394   bool mShouldComputeHints;
2395   nsChangeHint mComputedHint;
2396 };
2397 
UpdateBackdropIfNeeded(nsIFrame * aFrame,ServoStyleSet & aStyleSet,nsStyleChangeList & aChangeList)2398 static void UpdateBackdropIfNeeded(nsIFrame* aFrame, ServoStyleSet& aStyleSet,
2399                                    nsStyleChangeList& aChangeList) {
2400   const nsStyleDisplay* display = aFrame->Style()->StyleDisplay();
2401   if (display->mTopLayer != StyleTopLayer::Top) {
2402     return;
2403   }
2404 
2405   // Elements in the top layer are guaranteed to have absolute or fixed
2406   // position per https://fullscreen.spec.whatwg.org/#new-stacking-layer.
2407   MOZ_ASSERT(display->IsAbsolutelyPositionedStyle());
2408 
2409   nsIFrame* backdropPlaceholder =
2410       aFrame->GetChildList(nsIFrame::kBackdropList).FirstChild();
2411   if (!backdropPlaceholder) {
2412     return;
2413   }
2414 
2415   MOZ_ASSERT(backdropPlaceholder->IsPlaceholderFrame());
2416   nsIFrame* backdropFrame =
2417       nsPlaceholderFrame::GetRealFrameForPlaceholder(backdropPlaceholder);
2418   MOZ_ASSERT(backdropFrame->IsBackdropFrame());
2419   MOZ_ASSERT(backdropFrame->Style()->GetPseudoType() ==
2420              PseudoStyleType::backdrop);
2421 
2422   RefPtr<ComputedStyle> newStyle = aStyleSet.ResolvePseudoElementStyle(
2423       *aFrame->GetContent()->AsElement(), PseudoStyleType::backdrop,
2424       aFrame->Style());
2425 
2426   // NOTE(emilio): We can't use the changes handled for the owner of the
2427   // backdrop frame, since it's out of flow, and parented to the viewport or
2428   // canvas frame (depending on the `position` value).
2429   MOZ_ASSERT(backdropFrame->GetParent()->IsViewportFrame() ||
2430              backdropFrame->GetParent()->IsCanvasFrame());
2431   nsTArray<nsIFrame*> wrappersToRestyle;
2432   nsTArray<RefPtr<Element>> anchorsToSuppress;
2433   ServoRestyleState state(aStyleSet, aChangeList, wrappersToRestyle,
2434                           anchorsToSuppress);
2435   nsIFrame::UpdateStyleOfOwnedChildFrame(backdropFrame, newStyle, state);
2436   MOZ_ASSERT(anchorsToSuppress.IsEmpty());
2437 }
2438 
UpdateFirstLetterIfNeeded(nsIFrame * aFrame,ServoRestyleState & aRestyleState)2439 static void UpdateFirstLetterIfNeeded(nsIFrame* aFrame,
2440                                       ServoRestyleState& aRestyleState) {
2441   MOZ_ASSERT(
2442       !aFrame->IsBlockFrameOrSubclass(),
2443       "You're probably duplicating work with UpdatePseudoElementStyles!");
2444   if (!aFrame->HasFirstLetterChild()) {
2445     return;
2446   }
2447 
2448   // We need to find the block the first-letter is associated with so we can
2449   // find the right element for the first-letter's style resolution.  Might as
2450   // well just delegate the whole thing to that block.
2451   nsIFrame* block = aFrame->GetParent();
2452   while (!block->IsBlockFrameOrSubclass()) {
2453     block = block->GetParent();
2454   }
2455 
2456   static_cast<nsBlockFrame*>(block->FirstContinuation())
2457       ->UpdateFirstLetterStyle(aRestyleState);
2458 }
2459 
UpdateOneAdditionalComputedStyle(nsIFrame * aFrame,uint32_t aIndex,ComputedStyle & aOldContext,ServoRestyleState & aRestyleState)2460 static void UpdateOneAdditionalComputedStyle(nsIFrame* aFrame, uint32_t aIndex,
2461                                              ComputedStyle& aOldContext,
2462                                              ServoRestyleState& aRestyleState) {
2463   auto pseudoType = aOldContext.GetPseudoType();
2464   MOZ_ASSERT(pseudoType != PseudoStyleType::NotPseudo);
2465   MOZ_ASSERT(
2466       !nsCSSPseudoElements::PseudoElementSupportsUserActionState(pseudoType));
2467 
2468   RefPtr<ComputedStyle> newStyle =
2469       aRestyleState.StyleSet().ResolvePseudoElementStyle(
2470           *aFrame->GetContent()->AsElement(), pseudoType, aFrame->Style());
2471 
2472   uint32_t equalStructs;  // Not used, actually.
2473   nsChangeHint childHint =
2474       aOldContext.CalcStyleDifference(*newStyle, &equalStructs);
2475   if (!aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) &&
2476       !aFrame->IsColumnSpanInMulticolSubtree()) {
2477     childHint = NS_RemoveSubsumedHints(childHint,
2478                                        aRestyleState.ChangesHandledFor(aFrame));
2479   }
2480 
2481   if (childHint) {
2482     if (childHint & nsChangeHint_ReconstructFrame) {
2483       // If we generate a reconstruct here, remove any non-reconstruct hints we
2484       // may have already generated for this content.
2485       aRestyleState.ChangeList().PopChangesForContent(aFrame->GetContent());
2486     }
2487     aRestyleState.ChangeList().AppendChange(aFrame, aFrame->GetContent(),
2488                                             childHint);
2489   }
2490 
2491   aFrame->SetAdditionalComputedStyle(aIndex, newStyle);
2492 }
2493 
UpdateAdditionalComputedStyles(nsIFrame * aFrame,ServoRestyleState & aRestyleState)2494 static void UpdateAdditionalComputedStyles(nsIFrame* aFrame,
2495                                            ServoRestyleState& aRestyleState) {
2496   MOZ_ASSERT(aFrame);
2497   MOZ_ASSERT(aFrame->GetContent() && aFrame->GetContent()->IsElement());
2498 
2499   // FIXME(emilio): Consider adding a bit or something to avoid the initial
2500   // virtual call?
2501   uint32_t index = 0;
2502   while (auto* oldStyle = aFrame->GetAdditionalComputedStyle(index)) {
2503     UpdateOneAdditionalComputedStyle(aFrame, index++, *oldStyle, aRestyleState);
2504   }
2505 }
2506 
UpdateFramePseudoElementStyles(nsIFrame * aFrame,ServoRestyleState & aRestyleState)2507 static void UpdateFramePseudoElementStyles(nsIFrame* aFrame,
2508                                            ServoRestyleState& aRestyleState) {
2509   if (nsBlockFrame* blockFrame = do_QueryFrame(aFrame)) {
2510     blockFrame->UpdatePseudoElementStyles(aRestyleState);
2511   } else {
2512     UpdateFirstLetterIfNeeded(aFrame, aRestyleState);
2513   }
2514 
2515   UpdateBackdropIfNeeded(aFrame, aRestyleState.StyleSet(),
2516                          aRestyleState.ChangeList());
2517 }
2518 
2519 enum class ServoPostTraversalFlags : uint32_t {
2520   Empty = 0,
2521   // Whether parent was restyled.
2522   ParentWasRestyled = 1 << 0,
2523   // Skip sending accessibility notifications for all descendants.
2524   SkipA11yNotifications = 1 << 1,
2525   // Always send accessibility notifications if the element is shown.
2526   // The SkipA11yNotifications flag above overrides this flag.
2527   SendA11yNotificationsIfShown = 1 << 2,
2528 };
2529 
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(ServoPostTraversalFlags)2530 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(ServoPostTraversalFlags)
2531 
2532 // Send proper accessibility notifications and return post traversal
2533 // flags for kids.
2534 static ServoPostTraversalFlags SendA11yNotifications(
2535     nsPresContext* aPresContext, Element* aElement,
2536     ComputedStyle* aOldComputedStyle, ComputedStyle* aNewComputedStyle,
2537     ServoPostTraversalFlags aFlags) {
2538   using Flags = ServoPostTraversalFlags;
2539   MOZ_ASSERT(!(aFlags & Flags::SkipA11yNotifications) ||
2540                  !(aFlags & Flags::SendA11yNotificationsIfShown),
2541              "The two a11y flags should never be set together");
2542 
2543 #ifdef ACCESSIBILITY
2544   nsAccessibilityService* accService = GetAccService();
2545   if (!accService) {
2546     // If we don't have accessibility service, accessibility is not
2547     // enabled. Just skip everything.
2548     return Flags::Empty;
2549   }
2550   if (aFlags & Flags::SkipA11yNotifications) {
2551     // Propogate the skipping flag to descendants.
2552     return Flags::SkipA11yNotifications;
2553   }
2554 
2555   bool needsNotify = false;
2556   bool isVisible = aNewComputedStyle->StyleVisibility()->IsVisible();
2557   if (aFlags & Flags::SendA11yNotificationsIfShown) {
2558     if (!isVisible) {
2559       // Propagate the sending-if-shown flag to descendants.
2560       return Flags::SendA11yNotificationsIfShown;
2561     }
2562     // We have asked accessibility service to remove the whole subtree
2563     // of element which becomes invisible from the accessible tree, but
2564     // this element is visible, so we need to add it back.
2565     needsNotify = true;
2566   } else {
2567     // If we shouldn't skip in any case, we need to check whether our
2568     // own visibility has changed.
2569     bool wasVisible = aOldComputedStyle->StyleVisibility()->IsVisible();
2570     needsNotify = wasVisible != isVisible;
2571   }
2572 
2573   if (needsNotify) {
2574     PresShell* presShell = aPresContext->PresShell();
2575     if (isVisible) {
2576       accService->ContentRangeInserted(presShell, aElement,
2577                                        aElement->GetNextSibling());
2578       // We are adding the subtree. Accessibility service would handle
2579       // descendants, so we should just skip them from notifying.
2580       return Flags::SkipA11yNotifications;
2581     }
2582     // Remove the subtree of this invisible element, and ask any shown
2583     // descendant to add themselves back.
2584     accService->ContentRemoved(presShell, aElement);
2585     return Flags::SendA11yNotificationsIfShown;
2586   }
2587 #endif
2588 
2589   return Flags::Empty;
2590 }
2591 
ProcessPostTraversal(Element * aElement,ServoRestyleState & aRestyleState,ServoPostTraversalFlags aFlags)2592 bool RestyleManager::ProcessPostTraversal(Element* aElement,
2593                                           ServoRestyleState& aRestyleState,
2594                                           ServoPostTraversalFlags aFlags) {
2595   nsIFrame* styleFrame = nsLayoutUtils::GetStyleFrame(aElement);
2596   nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
2597 
2598   MOZ_DIAGNOSTIC_ASSERT(aElement->HasServoData(),
2599                         "Element without Servo data on a post-traversal? How?");
2600 
2601   // NOTE(emilio): This is needed because for table frames the bit is set on the
2602   // table wrapper (which is the primary frame), not on the table itself.
2603   const bool isOutOfFlow =
2604       primaryFrame && primaryFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
2605 
2606   // We need this because any column-spanner's parent frame is not its DOM
2607   // parent's primary frame. We need some special check similar to out-of-flow
2608   // frames.
2609   const bool isColumnSpan =
2610       primaryFrame && primaryFrame->IsColumnSpanInMulticolSubtree();
2611 
2612   // Grab the change hint from Servo.
2613   bool wasRestyled;
2614   nsChangeHint changeHint =
2615       static_cast<nsChangeHint>(Servo_TakeChangeHint(aElement, &wasRestyled));
2616 
2617   RefPtr<ComputedStyle> upToDateStyleIfRestyled =
2618       wasRestyled ? ServoStyleSet::ResolveServoStyle(*aElement) : nullptr;
2619 
2620   // We should really fix the weird primary frame mapping for image maps
2621   // (bug 135040)...
2622   if (styleFrame && styleFrame->GetContent() != aElement) {
2623     MOZ_ASSERT(static_cast<nsImageFrame*>(do_QueryFrame(styleFrame)));
2624     styleFrame = nullptr;
2625   }
2626 
2627   // Handle lazy frame construction by posting a reconstruct for any lazily-
2628   // constructed roots.
2629   if (aElement->HasFlag(NODE_NEEDS_FRAME)) {
2630     changeHint |= nsChangeHint_ReconstructFrame;
2631     MOZ_ASSERT(!styleFrame);
2632   }
2633 
2634   if (styleFrame) {
2635     MOZ_ASSERT(primaryFrame);
2636 
2637     nsIFrame* maybeAnonBoxChild;
2638     if (isOutOfFlow) {
2639       maybeAnonBoxChild = primaryFrame->GetPlaceholderFrame();
2640     } else {
2641       maybeAnonBoxChild = primaryFrame;
2642       // Do not subsume change hints for the column-spanner.
2643       if (!isColumnSpan) {
2644         changeHint = NS_RemoveSubsumedHints(
2645             changeHint, aRestyleState.ChangesHandledFor(styleFrame));
2646       }
2647     }
2648 
2649     // If the parent wasn't restyled, the styles of our anon box parents won't
2650     // change either.
2651     if ((aFlags & ServoPostTraversalFlags::ParentWasRestyled) &&
2652         maybeAnonBoxChild->ParentIsWrapperAnonBox()) {
2653       aRestyleState.AddPendingWrapperRestyle(
2654           ServoRestyleState::TableAwareParentFor(maybeAnonBoxChild));
2655     }
2656 
2657     // If we don't have a ::marker pseudo-element, but need it, then
2658     // reconstruct the frame.  (The opposite situation implies 'display'
2659     // changes so doesn't need to be handled explicitly here.)
2660     if (wasRestyled && styleFrame->StyleDisplay()->IsListItem() &&
2661         styleFrame->IsBlockFrameOrSubclass() &&
2662         !nsLayoutUtils::GetMarkerPseudo(aElement)) {
2663       RefPtr<ComputedStyle> pseudoStyle =
2664           aRestyleState.StyleSet().ProbePseudoElementStyle(
2665               *aElement, PseudoStyleType::marker, upToDateStyleIfRestyled);
2666       if (pseudoStyle) {
2667         changeHint |= nsChangeHint_ReconstructFrame;
2668       }
2669     }
2670   }
2671 
2672   // Although we shouldn't generate non-ReconstructFrame hints for elements with
2673   // no frames, we can still get them here if they were explicitly posted by
2674   // PostRestyleEvent, such as a RepaintFrame hint when a :link changes to be
2675   // :visited.  Skip processing these hints if there is no frame.
2676   if ((styleFrame || (changeHint & nsChangeHint_ReconstructFrame)) &&
2677       changeHint) {
2678     aRestyleState.ChangeList().AppendChange(styleFrame, aElement, changeHint);
2679   }
2680 
2681   // If our change hint is reconstruct, we delegate to the frame constructor,
2682   // which consumes the new style and expects the old style to be on the frame.
2683   //
2684   // XXXbholley: We should teach the frame constructor how to clear the dirty
2685   // descendants bit to avoid the traversal here.
2686   if (changeHint & nsChangeHint_ReconstructFrame) {
2687     if (wasRestyled &&
2688         StaticPrefs::layout_css_scroll_anchoring_suppressions_enabled()) {
2689       const bool wasAbsPos =
2690           styleFrame &&
2691           styleFrame->StyleDisplay()->IsAbsolutelyPositionedStyle();
2692       auto* newDisp = upToDateStyleIfRestyled->StyleDisplay();
2693       // https://drafts.csswg.org/css-scroll-anchoring/#suppression-triggers
2694       //
2695       // We need to do the position check here rather than in
2696       // DidSetComputedStyle because changing position reframes.
2697       //
2698       // We suppress adjustments whenever we change from being display: none to
2699       // be an abspos.
2700       //
2701       // Similarly, for other changes from abspos to non-abspos styles.
2702       //
2703       // TODO(emilio): I _think_ chrome won't suppress adjustments whenever
2704       // `display` changes. But that causes some infinite loops in cases like
2705       // bug 1568778.
2706       if (wasAbsPos != newDisp->IsAbsolutelyPositionedStyle()) {
2707         aRestyleState.AddPendingScrollAnchorSuppression(aElement);
2708       }
2709     }
2710     ClearRestyleStateFromSubtree(aElement);
2711     return true;
2712   }
2713 
2714   // TODO(emilio): We could avoid some refcount traffic here, specially in the
2715   // ComputedStyle case, which uses atomic refcounting.
2716   //
2717   // Hold the ComputedStyle alive, because it could become a dangling pointer
2718   // during the replacement. In practice it's not a huge deal, but better not
2719   // playing with dangling pointers if not needed.
2720   //
2721   // NOTE(emilio): We could keep around the old computed style for display:
2722   // contents elements too, but we don't really need it right now.
2723   RefPtr<ComputedStyle> oldOrDisplayContentsStyle =
2724       styleFrame ? styleFrame->Style() : nullptr;
2725 
2726   MOZ_ASSERT(!(styleFrame && Servo_Element_IsDisplayContents(aElement)),
2727              "display: contents node has a frame, yet we didn't reframe it"
2728              " above?");
2729   const bool isDisplayContents = !styleFrame && aElement->HasServoData() &&
2730                                  Servo_Element_IsDisplayContents(aElement);
2731   if (isDisplayContents) {
2732     oldOrDisplayContentsStyle = ServoStyleSet::ResolveServoStyle(*aElement);
2733   }
2734 
2735   Maybe<ServoRestyleState> thisFrameRestyleState;
2736   if (styleFrame) {
2737     auto type = isOutOfFlow || isColumnSpan ? ServoRestyleState::Type::OutOfFlow
2738                                             : ServoRestyleState::Type::InFlow;
2739 
2740     thisFrameRestyleState.emplace(*styleFrame, aRestyleState, changeHint, type);
2741   }
2742 
2743   // We can't really assume as used changes from display: contents elements (or
2744   // other elements without frames).
2745   ServoRestyleState& childrenRestyleState =
2746       thisFrameRestyleState ? *thisFrameRestyleState : aRestyleState;
2747 
2748   ComputedStyle* upToDateStyle =
2749       wasRestyled ? upToDateStyleIfRestyled : oldOrDisplayContentsStyle;
2750 
2751   ServoPostTraversalFlags childrenFlags =
2752       wasRestyled ? ServoPostTraversalFlags::ParentWasRestyled
2753                   : ServoPostTraversalFlags::Empty;
2754 
2755   if (wasRestyled && oldOrDisplayContentsStyle) {
2756     MOZ_ASSERT(styleFrame || isDisplayContents);
2757 
2758     // We want to walk all the continuations here, even the ones with different
2759     // styles.  In practice, the only reason we get continuations with different
2760     // styles here is ::first-line (::first-letter never affects element
2761     // styles).  But in that case, newStyle is the right context for the
2762     // _later_ continuations anyway (the ones not affected by ::first-line), not
2763     // the earlier ones, so there is no point stopping right at the point when
2764     // we'd actually be setting the right ComputedStyle.
2765     //
2766     // This does mean that we may be setting the wrong ComputedStyle on our
2767     // initial continuations; ::first-line fixes that up after the fact.
2768     for (nsIFrame* f = styleFrame; f; f = f->GetNextContinuation()) {
2769       MOZ_ASSERT_IF(f != styleFrame, !f->GetAdditionalComputedStyle(0));
2770       f->SetComputedStyle(upToDateStyle);
2771     }
2772 
2773     if (styleFrame) {
2774       UpdateAdditionalComputedStyles(styleFrame, aRestyleState);
2775     }
2776 
2777     if (!aElement->GetParent()) {
2778       // This is the root.  Update styles on the viewport as needed.
2779       ViewportFrame* viewport =
2780           do_QueryFrame(mPresContext->PresShell()->GetRootFrame());
2781       if (viewport) {
2782         // NB: The root restyle state, not the one for our children!
2783         viewport->UpdateStyle(aRestyleState);
2784       }
2785     }
2786 
2787     // Some changes to animations don't affect the computed style and yet still
2788     // require the layer to be updated. For example, pausing an animation via
2789     // the Web Animations API won't affect an element's style but still
2790     // requires to update the animation on the layer.
2791     //
2792     // We can sometimes reach this when the animated style is being removed.
2793     // Since AddLayerChangesForAnimation checks if |styleFrame| has a transform
2794     // style or not, we need to call it *after* setting |newStyle| to
2795     // |styleFrame| to ensure the animated transform has been removed first.
2796     AddLayerChangesForAnimation(styleFrame, primaryFrame, aElement, changeHint,
2797                                 aRestyleState.ChangeList());
2798 
2799     childrenFlags |=
2800         SendA11yNotifications(mPresContext, aElement, oldOrDisplayContentsStyle,
2801                               upToDateStyle, aFlags);
2802   }
2803 
2804   const bool traverseElementChildren =
2805       aElement->HasAnyOfFlags(Element::kAllServoDescendantBits);
2806   const bool traverseTextChildren =
2807       wasRestyled || aElement->HasFlag(NODE_DESCENDANTS_NEED_FRAMES);
2808   bool recreatedAnyContext = wasRestyled;
2809   if (traverseElementChildren || traverseTextChildren) {
2810     StyleChildrenIterator it(aElement);
2811     TextPostTraversalState textState(*aElement, upToDateStyle,
2812                                      isDisplayContents && wasRestyled,
2813                                      childrenRestyleState);
2814     for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
2815       if (traverseElementChildren && n->IsElement()) {
2816         recreatedAnyContext |= ProcessPostTraversal(
2817             n->AsElement(), childrenRestyleState, childrenFlags);
2818       } else if (traverseTextChildren && n->IsText()) {
2819         recreatedAnyContext |= ProcessPostTraversalForText(
2820             n, textState, childrenRestyleState, childrenFlags);
2821       }
2822     }
2823   }
2824 
2825   // We want to update frame pseudo-element styles after we've traversed our
2826   // kids, because some of those updates (::first-line/::first-letter) need to
2827   // modify the styles of the kids, and the child traversal above would just
2828   // clobber those modifications.
2829   if (styleFrame) {
2830     if (wasRestyled) {
2831       // Make sure to update anon boxes and pseudo bits after updating text,
2832       // otherwise ProcessPostTraversalForText could clobber first-letter
2833       // styles, for example.
2834       styleFrame->UpdateStyleOfOwnedAnonBoxes(childrenRestyleState);
2835     }
2836     // Process anon box wrapper frames before ::first-line bits, but _after_
2837     // owned anon boxes, since the children wrapper anon boxes could be
2838     // inheriting from our own owned anon boxes.
2839     childrenRestyleState.ProcessWrapperRestyles(styleFrame);
2840     if (wasRestyled) {
2841       UpdateFramePseudoElementStyles(styleFrame, childrenRestyleState);
2842     } else if (traverseElementChildren &&
2843                styleFrame->IsBlockFrameOrSubclass()) {
2844       // Even if we were not restyled, if we're a block with a first-line and
2845       // one of our descendant elements which is on the first line was restyled,
2846       // we need to update the styles of things on the first line, because
2847       // they're wrong now.
2848       //
2849       // FIXME(bz) Could we do better here?  For example, could we keep track of
2850       // frames that are "block with a ::first-line so we could avoid
2851       // IsFrameOfType() and digging about for the first-line frame if not?
2852       // Could we keep track of whether the element children we actually restyle
2853       // are affected by first-line?  Something else?  Bug 1385443 tracks making
2854       // this better.
2855       nsIFrame* firstLineFrame =
2856           static_cast<nsBlockFrame*>(styleFrame)->GetFirstLineFrame();
2857       if (firstLineFrame) {
2858         for (nsIFrame* kid : firstLineFrame->PrincipalChildList()) {
2859           ReparentComputedStyleForFirstLine(kid);
2860         }
2861       }
2862     }
2863   }
2864 
2865   aElement->UnsetFlags(Element::kAllServoDescendantBits);
2866   return recreatedAnyContext;
2867 }
2868 
ProcessPostTraversalForText(nsIContent * aTextNode,TextPostTraversalState & aPostTraversalState,ServoRestyleState & aRestyleState,ServoPostTraversalFlags aFlags)2869 bool RestyleManager::ProcessPostTraversalForText(
2870     nsIContent* aTextNode, TextPostTraversalState& aPostTraversalState,
2871     ServoRestyleState& aRestyleState, ServoPostTraversalFlags aFlags) {
2872   // Handle lazy frame construction.
2873   if (aTextNode->HasFlag(NODE_NEEDS_FRAME)) {
2874     aPostTraversalState.ChangeList().AppendChange(
2875         nullptr, aTextNode, nsChangeHint_ReconstructFrame);
2876     return true;
2877   }
2878 
2879   // Handle restyle.
2880   nsIFrame* primaryFrame = aTextNode->GetPrimaryFrame();
2881   if (!primaryFrame) {
2882     return false;
2883   }
2884 
2885   // If the parent wasn't restyled, the styles of our anon box parents won't
2886   // change either.
2887   if ((aFlags & ServoPostTraversalFlags::ParentWasRestyled) &&
2888       primaryFrame->ParentIsWrapperAnonBox()) {
2889     aRestyleState.AddPendingWrapperRestyle(
2890         ServoRestyleState::TableAwareParentFor(primaryFrame));
2891   }
2892 
2893   ComputedStyle& newStyle = aPostTraversalState.ComputeStyle(aTextNode);
2894   aPostTraversalState.ComputeHintIfNeeded(aTextNode, primaryFrame, newStyle);
2895 
2896   // We want to walk all the continuations here, even the ones with different
2897   // styles.  In practice, the only reasons we get continuations with different
2898   // styles are ::first-line and ::first-letter.  But in those cases,
2899   // newStyle is the right context for the _later_ continuations anyway (the
2900   // ones not affected by ::first-line/::first-letter), not the earlier ones,
2901   // so there is no point stopping right at the point when we'd actually be
2902   // setting the right ComputedStyle.
2903   //
2904   // This does mean that we may be setting the wrong ComputedStyle on our
2905   // initial continuations; ::first-line/::first-letter fix that up after the
2906   // fact.
2907   for (nsIFrame* f = primaryFrame; f; f = f->GetNextContinuation()) {
2908     f->SetComputedStyle(&newStyle);
2909   }
2910 
2911   return true;
2912 }
2913 
ClearSnapshots()2914 void RestyleManager::ClearSnapshots() {
2915   for (auto iter = mSnapshots.Iter(); !iter.Done(); iter.Next()) {
2916     iter.Key()->UnsetFlags(ELEMENT_HAS_SNAPSHOT | ELEMENT_HANDLED_SNAPSHOT);
2917     iter.Remove();
2918   }
2919 }
2920 
SnapshotFor(Element & aElement)2921 ServoElementSnapshot& RestyleManager::SnapshotFor(Element& aElement) {
2922   MOZ_DIAGNOSTIC_ASSERT(!mInStyleRefresh);
2923 
2924   // NOTE(emilio): We can handle snapshots from a one-off restyle of those that
2925   // we do to restyle stuff for reconstruction, for example.
2926   //
2927   // It seems to be the case that we always flush in between that happens and
2928   // the next attribute change, so we can assert that we haven't handled the
2929   // snapshot here yet. If this assertion didn't hold, we'd need to unset that
2930   // flag from here too.
2931   //
2932   // Can't wait to make ProcessPendingRestyles the only entry-point for styling,
2933   // so this becomes much easier to reason about. Today is not that day though.
2934   MOZ_ASSERT(aElement.HasServoData());
2935   MOZ_ASSERT(!aElement.HasFlag(ELEMENT_HANDLED_SNAPSHOT));
2936 
2937   ServoElementSnapshot* snapshot =
2938       mSnapshots.GetOrInsertNew(&aElement, aElement);
2939   aElement.SetFlags(ELEMENT_HAS_SNAPSHOT);
2940 
2941   // Now that we have a snapshot, make sure a restyle is triggered.
2942   aElement.NoteDirtyForServo();
2943   return *snapshot;
2944 }
2945 
DoProcessPendingRestyles(ServoTraversalFlags aFlags)2946 void RestyleManager::DoProcessPendingRestyles(ServoTraversalFlags aFlags) {
2947   nsPresContext* presContext = PresContext();
2948   PresShell* presShell = presContext->PresShell();
2949 
2950   MOZ_ASSERT(presContext->Document(), "No document?  Pshaw!");
2951   // FIXME(emilio): In the "flush animations" case, ideally, we should only
2952   // recascade animation styles running on the compositor, so we shouldn't care
2953   // about other styles, or new rules that apply to the page...
2954   //
2955   // However, that's not true as of right now, see bug 1388031 and bug 1388692.
2956   MOZ_ASSERT((aFlags & ServoTraversalFlags::FlushThrottledAnimations) ||
2957                  !presContext->HasPendingMediaQueryUpdates(),
2958              "Someone forgot to update media queries?");
2959   MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(), "Missing a script blocker!");
2960   MOZ_RELEASE_ASSERT(!mInStyleRefresh, "Reentrant call?");
2961 
2962   if (MOZ_UNLIKELY(!presShell->DidInitialize())) {
2963     // PresShell::FlushPendingNotifications doesn't early-return in the case
2964     // where the PresShell hasn't yet been initialized (and therefore we haven't
2965     // yet done the initial style traversal of the DOM tree). We should arguably
2966     // fix up the callers and assert against this case, but we just detect and
2967     // handle it for now.
2968     return;
2969   }
2970 
2971   // It'd be bad!
2972   PresShell::AutoAssertNoFlush noReentrantFlush(*presShell);
2973 
2974   // Create a AnimationsWithDestroyedFrame during restyling process to
2975   // stop animations and transitions on elements that have no frame at the end
2976   // of the restyling process.
2977   AnimationsWithDestroyedFrame animationsWithDestroyedFrame(this);
2978 
2979   ServoStyleSet* styleSet = StyleSet();
2980   Document* doc = presContext->Document();
2981 
2982   // Ensure the refresh driver is active during traversal to avoid mutating
2983   // mActiveTimer and mMostRecentRefresh time.
2984   presContext->RefreshDriver()->MostRecentRefresh();
2985 
2986   // Perform the Servo traversal, and the post-traversal if required. We do this
2987   // in a loop because certain rare paths in the frame constructor can trigger
2988   // additional style invalidations.
2989   //
2990   // FIXME(emilio): Confirm whether that's still true now that XBL is gone.
2991   mInStyleRefresh = true;
2992   if (mHaveNonAnimationRestyles) {
2993     ++mAnimationGeneration;
2994   }
2995 
2996   if (mRestyleForCSSRuleChanges) {
2997     aFlags |= ServoTraversalFlags::ForCSSRuleChanges;
2998   }
2999 
3000   while (styleSet->StyleDocument(aFlags)) {
3001     ClearSnapshots();
3002 
3003     // Select scroll anchors for frames that have been scrolled. Do this
3004     // before processing restyled frames so that anchor nodes are correctly
3005     // marked when directly moving frames with RecomputePosition.
3006     presContext->PresShell()->FlushPendingScrollAnchorSelections();
3007 
3008     nsStyleChangeList currentChanges;
3009     bool anyStyleChanged = false;
3010 
3011     // Recreate styles , and queue up change hints (which also handle lazy frame
3012     // construction).
3013     nsTArray<RefPtr<Element>> anchorsToSuppress;
3014 
3015     {
3016       AutoRestyleTimelineMarker marker(presContext->GetDocShell(), false);
3017       DocumentStyleRootIterator iter(doc->GetServoRestyleRoot());
3018       while (Element* root = iter.GetNextStyleRoot()) {
3019         nsTArray<nsIFrame*> wrappersToRestyle;
3020         ServoRestyleState state(*styleSet, currentChanges, wrappersToRestyle,
3021                                 anchorsToSuppress);
3022         ServoPostTraversalFlags flags = ServoPostTraversalFlags::Empty;
3023         anyStyleChanged |= ProcessPostTraversal(root, state, flags);
3024       }
3025 
3026       // We want to suppress adjustments the current (before-change) scroll
3027       // anchor container now, and save a reference to the content node so that
3028       // we can suppress them in the after-change scroll anchor .
3029       for (Element* element : anchorsToSuppress) {
3030         if (nsIFrame* frame = element->GetPrimaryFrame()) {
3031           if (auto* container = ScrollAnchorContainer::FindFor(frame)) {
3032             container->SuppressAdjustments();
3033           }
3034         }
3035       }
3036     }
3037 
3038     doc->ClearServoRestyleRoot();
3039 
3040     // Process the change hints.
3041     //
3042     // Unfortunately, the frame constructor can generate new change hints while
3043     // processing existing ones. We redirect those into a secondary queue and
3044     // iterate until there's nothing left.
3045     {
3046       AutoTimelineMarker marker(presContext->GetDocShell(),
3047                                 "StylesApplyChanges");
3048       ReentrantChangeList newChanges;
3049       mReentrantChanges = &newChanges;
3050       while (!currentChanges.IsEmpty()) {
3051         ProcessRestyledFrames(currentChanges);
3052         MOZ_ASSERT(currentChanges.IsEmpty());
3053         for (ReentrantChange& change : newChanges) {
3054           if (!(change.mHint & nsChangeHint_ReconstructFrame) &&
3055               !change.mContent->GetPrimaryFrame()) {
3056             // SVG Elements post change hints without ensuring that the primary
3057             // frame will be there after that (see bug 1366142).
3058             //
3059             // Just ignore those, since we can't really process them.
3060             continue;
3061           }
3062           currentChanges.AppendChange(change.mContent->GetPrimaryFrame(),
3063                                       change.mContent, change.mHint);
3064         }
3065         newChanges.Clear();
3066       }
3067       mReentrantChanges = nullptr;
3068     }
3069 
3070     // Suppress adjustments in the after-change scroll anchors if needed, now
3071     // that we're done reframing everything.
3072     for (Element* element : anchorsToSuppress) {
3073       if (nsIFrame* frame = element->GetPrimaryFrame()) {
3074         if (auto* container = ScrollAnchorContainer::FindFor(frame)) {
3075           container->SuppressAdjustments();
3076         }
3077       }
3078     }
3079 
3080     if (anyStyleChanged) {
3081       // Maybe no styles changed when:
3082       //
3083       //  * Only explicit change hints were posted in the first place.
3084       //  * When an attribute or state change in the content happens not to need
3085       //    a restyle after all.
3086       //
3087       // In any case, we don't need to increment the restyle generation in that
3088       // case.
3089       IncrementRestyleGeneration();
3090     }
3091   }
3092 
3093   doc->ClearServoRestyleRoot();
3094 
3095   ClearSnapshots();
3096   styleSet->AssertTreeIsClean();
3097   mHaveNonAnimationRestyles = false;
3098   mRestyleForCSSRuleChanges = false;
3099   mInStyleRefresh = false;
3100 
3101   // Now that everything has settled, see if we have enough free rule nodes in
3102   // the tree to warrant sweeping them.
3103   styleSet->MaybeGCRuleTree();
3104 
3105   // Note: We are in the scope of |animationsWithDestroyedFrame|, so
3106   //       |mAnimationsWithDestroyedFrame| is still valid.
3107   MOZ_ASSERT(mAnimationsWithDestroyedFrame);
3108   mAnimationsWithDestroyedFrame->StopAnimationsForElementsWithoutFrames();
3109 }
3110 
3111 #ifdef DEBUG
VerifyFlatTree(const nsIContent & aContent)3112 static void VerifyFlatTree(const nsIContent& aContent) {
3113   StyleChildrenIterator iter(&aContent);
3114 
3115   for (auto* content = iter.GetNextChild(); content;
3116        content = iter.GetNextChild()) {
3117     MOZ_ASSERT(content->GetFlattenedTreeParentNodeForStyle() == &aContent);
3118     VerifyFlatTree(*content);
3119   }
3120 }
3121 #endif
3122 
ProcessPendingRestyles()3123 void RestyleManager::ProcessPendingRestyles() {
3124 #ifdef DEBUG
3125   if (auto* root = mPresContext->Document()->GetRootElement()) {
3126     VerifyFlatTree(*root);
3127   }
3128 #endif
3129 
3130   DoProcessPendingRestyles(ServoTraversalFlags::Empty);
3131 }
3132 
ProcessAllPendingAttributeAndStateInvalidations()3133 void RestyleManager::ProcessAllPendingAttributeAndStateInvalidations() {
3134   if (mSnapshots.IsEmpty()) {
3135     return;
3136   }
3137   for (const auto& key : mSnapshots.Keys()) {
3138     // Servo data for the element might have been dropped. (e.g. by removing
3139     // from its document)
3140     if (key->HasFlag(ELEMENT_HAS_SNAPSHOT)) {
3141       Servo_ProcessInvalidations(StyleSet()->RawSet(), key, &mSnapshots);
3142     }
3143   }
3144   ClearSnapshots();
3145 }
3146 
UpdateOnlyAnimationStyles()3147 void RestyleManager::UpdateOnlyAnimationStyles() {
3148   bool doCSS = PresContext()->EffectCompositor()->HasPendingStyleUpdates();
3149   if (!doCSS) {
3150     return;
3151   }
3152 
3153   DoProcessPendingRestyles(ServoTraversalFlags::FlushThrottledAnimations);
3154 }
3155 
ContentStateChanged(nsIContent * aContent,EventStates aChangedBits)3156 void RestyleManager::ContentStateChanged(nsIContent* aContent,
3157                                          EventStates aChangedBits) {
3158   MOZ_DIAGNOSTIC_ASSERT(!mInStyleRefresh);
3159 
3160   if (!aContent->IsElement()) {
3161     return;
3162   }
3163 
3164   Element& element = *aContent->AsElement();
3165   if (!element.HasServoData()) {
3166     return;
3167   }
3168 
3169   const EventStates kVisitedAndUnvisited =
3170       NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED;
3171 
3172   // When visited links are disabled, they cannot influence style for obvious
3173   // reasons.
3174   //
3175   // When layout.css.always-repaint-on-unvisited is true, we'll restyle when the
3176   // relevant visited query finishes, regardless of the style (see
3177   // Link::VisitedQueryFinished). So there's no need to do anything as a result
3178   // of this state change just yet.
3179   //
3180   // Note that this check checks for _both_ bits: This is only true when visited
3181   // changes to unvisited or vice-versa, but not when we start or stop being a
3182   // link itself.
3183   if (aChangedBits.HasAllStates(kVisitedAndUnvisited)) {
3184     if (!Gecko_VisitedStylesEnabled(element.OwnerDoc()) ||
3185         StaticPrefs::layout_css_always_repaint_on_unvisited()) {
3186       aChangedBits &= ~kVisitedAndUnvisited;
3187       if (aChangedBits.IsEmpty()) {
3188         return;
3189       }
3190     }
3191   }
3192 
3193   if (auto changeHint = ChangeForContentStateChange(element, aChangedBits)) {
3194     Servo_NoteExplicitHints(&element, RestyleHint{0}, changeHint);
3195   }
3196 
3197   // Don't bother taking a snapshot if no rules depend on these state bits.
3198   //
3199   // We always take a snapshot for the LTR/RTL event states, since Servo doesn't
3200   // track those bits in the same way, and we know that :dir() rules are always
3201   // present in UA style sheets.
3202   if (!aChangedBits.HasAtLeastOneOfStates(DIRECTION_STATES) &&
3203       !StyleSet()->HasStateDependency(element, aChangedBits)) {
3204     return;
3205   }
3206 
3207   ServoElementSnapshot& snapshot = SnapshotFor(element);
3208   EventStates previousState = element.StyleState() ^ aChangedBits;
3209   snapshot.AddState(previousState);
3210 
3211   // Assuming we need to invalidate cached style in getComputedStyle for
3212   // undisplayed elements, since we don't know if it is needed.
3213   IncrementUndisplayedRestyleGeneration();
3214 }
3215 
AttributeInfluencesOtherPseudoClassState(const Element & aElement,const nsAtom * aAttribute)3216 static inline bool AttributeInfluencesOtherPseudoClassState(
3217     const Element& aElement, const nsAtom* aAttribute) {
3218   // We must record some state for :-moz-browser-frame,
3219   // :-moz-table-border-nonzero, and :-moz-select-list-box.
3220   if (aAttribute == nsGkAtoms::mozbrowser) {
3221     return aElement.IsAnyOfHTMLElements(nsGkAtoms::iframe, nsGkAtoms::frame);
3222   }
3223 
3224   if (aAttribute == nsGkAtoms::border) {
3225     return aElement.IsHTMLElement(nsGkAtoms::table);
3226   }
3227 
3228   if (aAttribute == nsGkAtoms::multiple || aAttribute == nsGkAtoms::size) {
3229     return aElement.IsHTMLElement(nsGkAtoms::select);
3230   }
3231 
3232   return false;
3233 }
3234 
NeedToRecordAttrChange(const ServoStyleSet & aStyleSet,const Element & aElement,int32_t aNameSpaceID,nsAtom * aAttribute,bool * aInfluencesOtherPseudoClassState)3235 static inline bool NeedToRecordAttrChange(
3236     const ServoStyleSet& aStyleSet, const Element& aElement,
3237     int32_t aNameSpaceID, nsAtom* aAttribute,
3238     bool* aInfluencesOtherPseudoClassState) {
3239   *aInfluencesOtherPseudoClassState =
3240       AttributeInfluencesOtherPseudoClassState(aElement, aAttribute);
3241 
3242   // If the attribute influences one of the pseudo-classes that are backed by
3243   // attributes, we just record it.
3244   if (*aInfluencesOtherPseudoClassState) {
3245     return true;
3246   }
3247 
3248   // We assume that id and class attributes are used in class/id selectors, and
3249   // thus record them.
3250   //
3251   // TODO(emilio): We keep a filter of the ids in use somewhere in the StyleSet,
3252   // presumably we could try to filter the old and new id, but it's not clear
3253   // it's worth it.
3254   if (aNameSpaceID == kNameSpaceID_None &&
3255       (aAttribute == nsGkAtoms::id || aAttribute == nsGkAtoms::_class)) {
3256     return true;
3257   }
3258 
3259   // We always record lang="", even though we force a subtree restyle when it
3260   // changes, since it can change how its siblings match :lang(..) due to
3261   // selectors like :lang(..) + div.
3262   if (aAttribute == nsGkAtoms::lang) {
3263     return true;
3264   }
3265 
3266   // Otherwise, just record the attribute change if a selector in the page may
3267   // reference it from an attribute selector.
3268   return aStyleSet.MightHaveAttributeDependency(aElement, aAttribute);
3269 }
3270 
AttributeWillChange(Element * aElement,int32_t aNameSpaceID,nsAtom * aAttribute,int32_t aModType)3271 void RestyleManager::AttributeWillChange(Element* aElement,
3272                                          int32_t aNameSpaceID,
3273                                          nsAtom* aAttribute, int32_t aModType) {
3274   TakeSnapshotForAttributeChange(*aElement, aNameSpaceID, aAttribute);
3275 }
3276 
ClassAttributeWillBeChangedBySMIL(Element * aElement)3277 void RestyleManager::ClassAttributeWillBeChangedBySMIL(Element* aElement) {
3278   TakeSnapshotForAttributeChange(*aElement, kNameSpaceID_None,
3279                                  nsGkAtoms::_class);
3280 }
3281 
TakeSnapshotForAttributeChange(Element & aElement,int32_t aNameSpaceID,nsAtom * aAttribute)3282 void RestyleManager::TakeSnapshotForAttributeChange(Element& aElement,
3283                                                     int32_t aNameSpaceID,
3284                                                     nsAtom* aAttribute) {
3285   MOZ_DIAGNOSTIC_ASSERT(!mInStyleRefresh);
3286 
3287   if (!aElement.HasServoData()) {
3288     return;
3289   }
3290 
3291   bool influencesOtherPseudoClassState;
3292   if (!NeedToRecordAttrChange(*StyleSet(), aElement, aNameSpaceID, aAttribute,
3293                               &influencesOtherPseudoClassState)) {
3294     return;
3295   }
3296 
3297   // We cannot tell if the attribute change will affect the styles of
3298   // undisplayed elements, because we don't actually restyle those elements
3299   // during the restyle traversal. So just assume that the attribute change can
3300   // cause the style to change.
3301   IncrementUndisplayedRestyleGeneration();
3302 
3303   // Some other random attribute changes may also affect the transitions,
3304   // so we also set this true here.
3305   mHaveNonAnimationRestyles = true;
3306 
3307   ServoElementSnapshot& snapshot = SnapshotFor(aElement);
3308   snapshot.AddAttrs(aElement, aNameSpaceID, aAttribute);
3309 
3310   if (influencesOtherPseudoClassState) {
3311     snapshot.AddOtherPseudoClassState(aElement);
3312   }
3313 }
3314 
3315 // For some attribute changes we must restyle the whole subtree:
3316 //
3317 // * <td> is affected by the cellpadding on its ancestor table
3318 // * lwtheme and lwthemetextcolor on root element of XUL document
3319 //   affects all descendants due to :-moz-lwtheme* pseudo-classes
3320 // * lang="" and xml:lang="" can affect all descendants due to :lang()
3321 // * exportparts can affect all descendant parts. We could certainly integrate
3322 //   it better in the invalidation machinery if it was necessary.
AttributeChangeRequiresSubtreeRestyle(const Element & aElement,nsAtom * aAttr)3323 static inline bool AttributeChangeRequiresSubtreeRestyle(
3324     const Element& aElement, nsAtom* aAttr) {
3325   if (aAttr == nsGkAtoms::cellpadding) {
3326     return aElement.IsHTMLElement(nsGkAtoms::table);
3327   }
3328   if (aAttr == nsGkAtoms::lwtheme || aAttr == nsGkAtoms::lwthemetextcolor) {
3329     Document* doc = aElement.OwnerDoc();
3330     return doc->IsInChromeDocShell() && &aElement == doc->GetRootElement();
3331   }
3332   // TODO(emilio, bug 1598094): Maybe finer-grained invalidation for exportparts
3333   // attribute changes?
3334   if (aAttr == nsGkAtoms::exportparts) {
3335     return !!aElement.GetShadowRoot();
3336   }
3337   return aAttr == nsGkAtoms::lang;
3338 }
3339 
AttributeChanged(Element * aElement,int32_t aNameSpaceID,nsAtom * aAttribute,int32_t aModType,const nsAttrValue * aOldValue)3340 void RestyleManager::AttributeChanged(Element* aElement, int32_t aNameSpaceID,
3341                                       nsAtom* aAttribute, int32_t aModType,
3342                                       const nsAttrValue* aOldValue) {
3343   MOZ_ASSERT(!mInStyleRefresh);
3344 
3345   auto changeHint = nsChangeHint(0);
3346   auto restyleHint = RestyleHint{0};
3347 
3348   changeHint |= aElement->GetAttributeChangeHint(aAttribute, aModType);
3349 
3350   if (aAttribute == nsGkAtoms::style) {
3351     restyleHint |= RestyleHint::RESTYLE_STYLE_ATTRIBUTE;
3352   } else if (AttributeChangeRequiresSubtreeRestyle(*aElement, aAttribute)) {
3353     restyleHint |= RestyleHint::RestyleSubtree();
3354   } else if (aElement->IsAttributeMapped(aAttribute)) {
3355     // FIXME(emilio): Does this really need to re-selector-match?
3356     restyleHint |= RestyleHint::RESTYLE_SELF;
3357   } else if (aElement->IsInShadowTree() && aAttribute == nsGkAtoms::part) {
3358     // TODO(emilio, bug 1598094): Maybe finer-grained invalidation for part
3359     // attribute changes?
3360     restyleHint |= RestyleHint::RESTYLE_SELF;
3361   }
3362 
3363   if (nsIFrame* primaryFrame = aElement->GetPrimaryFrame()) {
3364     // See if we have appearance information for a theme.
3365     StyleAppearance appearance =
3366         primaryFrame->StyleDisplay()->EffectiveAppearance();
3367     if (appearance != StyleAppearance::None) {
3368       nsITheme* theme = PresContext()->Theme();
3369       if (theme->ThemeSupportsWidget(PresContext(), primaryFrame, appearance)) {
3370         bool repaint = false;
3371         theme->WidgetStateChanged(primaryFrame, appearance, aAttribute,
3372                                   &repaint, aOldValue);
3373         if (repaint) {
3374           changeHint |= nsChangeHint_RepaintFrame;
3375         }
3376       }
3377     }
3378 
3379     primaryFrame->AttributeChanged(aNameSpaceID, aAttribute, aModType);
3380   }
3381 
3382   if (restyleHint || changeHint) {
3383     Servo_NoteExplicitHints(aElement, restyleHint, changeHint);
3384   }
3385 
3386   if (restyleHint) {
3387     // Assuming we need to invalidate cached style in getComputedStyle for
3388     // undisplayed elements, since we don't know if it is needed.
3389     IncrementUndisplayedRestyleGeneration();
3390 
3391     // If we change attributes, we have to mark this to be true, so we will
3392     // increase the animation generation for the new created transition if any.
3393     mHaveNonAnimationRestyles = true;
3394   }
3395 }
3396 
ReparentComputedStyleForFirstLine(nsIFrame * aFrame)3397 void RestyleManager::ReparentComputedStyleForFirstLine(nsIFrame* aFrame) {
3398   // This is only called when moving frames in or out of the first-line
3399   // pseudo-element (or one of its descendants).  We can't say much about
3400   // aFrame's ancestors, unfortunately (e.g. during a dynamic insert into
3401   // something inside an inline-block on the first line the ancestors could be
3402   // totally arbitrary), but we will definitely find a line frame on the
3403   // ancestor chain.  Note that the lineframe may not actually be the one that
3404   // corresponds to ::first-line; when we're moving _out_ of the ::first-line it
3405   // will be one of the continuations instead.
3406 #ifdef DEBUG
3407   {
3408     nsIFrame* f = aFrame->GetParent();
3409     while (f && !f->IsLineFrame()) {
3410       f = f->GetParent();
3411     }
3412     MOZ_ASSERT(f, "Must have found a first-line frame");
3413   }
3414 #endif
3415 
3416   DoReparentComputedStyleForFirstLine(aFrame, *StyleSet());
3417 }
3418 
DoReparentComputedStyleForFirstLine(nsIFrame * aFrame,ServoStyleSet & aStyleSet)3419 void RestyleManager::DoReparentComputedStyleForFirstLine(
3420     nsIFrame* aFrame, ServoStyleSet& aStyleSet) {
3421   if (aFrame->IsBackdropFrame()) {
3422     // Style context of backdrop frame has no parent style, and thus we do not
3423     // need to reparent it.
3424     return;
3425   }
3426 
3427   if (aFrame->IsPlaceholderFrame()) {
3428     // Also reparent the out-of-flow and all its continuations.  We're doing
3429     // this to match Gecko for now, but it's not clear that this behavior is
3430     // correct per spec.  It's certainly pretty odd for out-of-flows whose
3431     // containing block is not within the first line.
3432     //
3433     // Right now we're somewhat inconsistent in this testcase:
3434     //
3435     //  <style>
3436     //    div { color: orange; clear: left; }
3437     //    div::first-line { color: blue; }
3438     //  </style>
3439     //  <div>
3440     //    <span style="float: left">What color is this text?</span>
3441     //  </div>
3442     //  <div>
3443     //    <span><span style="float: left">What color is this text?</span></span>
3444     //  </div>
3445     //
3446     // We make the first float orange and the second float blue.  On the other
3447     // hand, if the float were within an inline-block that was on the first
3448     // line, arguably it _should_ inherit from the ::first-line...
3449     nsIFrame* outOfFlow =
3450         nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame);
3451     MOZ_ASSERT(outOfFlow, "no out-of-flow frame");
3452     for (; outOfFlow; outOfFlow = outOfFlow->GetNextContinuation()) {
3453       DoReparentComputedStyleForFirstLine(outOfFlow, aStyleSet);
3454     }
3455   }
3456 
3457   // FIXME(emilio): This is the only caller of GetParentComputedStyle, let's try
3458   // to remove it?
3459   nsIFrame* providerFrame;
3460   ComputedStyle* newParentStyle =
3461       aFrame->GetParentComputedStyle(&providerFrame);
3462   // If our provider is our child, we want to reparent it first, because we
3463   // inherit style from it.
3464   bool isChild = providerFrame && providerFrame->GetParent() == aFrame;
3465   nsIFrame* providerChild = nullptr;
3466   if (isChild) {
3467     DoReparentComputedStyleForFirstLine(providerFrame, aStyleSet);
3468     // Get the style again after ReparentComputedStyle() which might have
3469     // changed it.
3470     newParentStyle = providerFrame->Style();
3471     providerChild = providerFrame;
3472     MOZ_ASSERT(!providerFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW),
3473                "Out of flow provider?");
3474   }
3475 
3476   if (!newParentStyle) {
3477     // No need to do anything here for this frame, but we should still reparent
3478     // its descendants, because those may have styles that inherit from the
3479     // parent of this frame (e.g. non-anonymous columns in an anonymous
3480     // colgroup).
3481     MOZ_ASSERT(aFrame->Style()->IsNonInheritingAnonBox(),
3482                "Why did this frame not end up with a parent context?");
3483     ReparentFrameDescendants(aFrame, providerChild, aStyleSet);
3484     return;
3485   }
3486 
3487   bool isElement = aFrame->GetContent()->IsElement();
3488 
3489   // We probably don't want to initiate transitions from ReparentComputedStyle,
3490   // since we call it during frame construction rather than in response to
3491   // dynamic changes.
3492   // Also see the comment at the start of
3493   // nsTransitionManager::ConsiderInitiatingTransition.
3494   //
3495   // We don't try to do the fancy copying from previous continuations that
3496   // GeckoRestyleManager does here, because that relies on knowing the parents
3497   // of ComputedStyles, and we don't know those.
3498   ComputedStyle* oldStyle = aFrame->Style();
3499   Element* ourElement =
3500       oldStyle->GetPseudoType() == PseudoStyleType::NotPseudo && isElement
3501           ? aFrame->GetContent()->AsElement()
3502           : nullptr;
3503   ComputedStyle* newParent = newParentStyle;
3504 
3505   ComputedStyle* newParentIgnoringFirstLine;
3506   if (newParent->GetPseudoType() == PseudoStyleType::firstLine) {
3507     MOZ_ASSERT(
3508         providerFrame && providerFrame->GetParent()->IsBlockFrameOrSubclass(),
3509         "How could we get a ::first-line parent style without having "
3510         "a ::first-line provider frame?");
3511     // If newParent is a ::first-line style, get the parent blockframe, and then
3512     // correct it for our pseudo as needed (e.g. stepping out of anon boxes).
3513     // Use the resulting style for the "parent style ignoring ::first-line".
3514     nsIFrame* blockFrame = providerFrame->GetParent();
3515     nsIFrame* correctedFrame = nsIFrame::CorrectStyleParentFrame(
3516         blockFrame, oldStyle->GetPseudoType());
3517     newParentIgnoringFirstLine = correctedFrame->Style();
3518   } else {
3519     newParentIgnoringFirstLine = newParent;
3520   }
3521 
3522   if (!providerFrame) {
3523     // No providerFrame means we inherited from a display:contents thing.  Our
3524     // layout parent style is the style of our nearest ancestor frame.  But we
3525     // have to be careful to do that with our placeholder, not with us, if we're
3526     // out of flow.
3527     if (aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
3528       aFrame->FirstContinuation()
3529           ->GetPlaceholderFrame()
3530           ->GetLayoutParentStyleForOutOfFlow(&providerFrame);
3531     } else {
3532       providerFrame = nsIFrame::CorrectStyleParentFrame(
3533           aFrame->GetParent(), oldStyle->GetPseudoType());
3534     }
3535   }
3536   ComputedStyle* layoutParent = providerFrame->Style();
3537 
3538   RefPtr<ComputedStyle> newStyle = aStyleSet.ReparentComputedStyle(
3539       oldStyle, newParent, newParentIgnoringFirstLine, layoutParent,
3540       ourElement);
3541   aFrame->SetComputedStyle(newStyle);
3542 
3543   // This logic somewhat mirrors the logic in
3544   // RestyleManager::ProcessPostTraversal.
3545   if (isElement) {
3546     // We can't use UpdateAdditionalComputedStyles as-is because it needs a
3547     // ServoRestyleState and maintaining one of those during a _frametree_
3548     // traversal is basically impossible.
3549     uint32_t index = 0;
3550     while (auto* oldAdditionalStyle =
3551                aFrame->GetAdditionalComputedStyle(index)) {
3552       RefPtr<ComputedStyle> newAdditionalContext =
3553           aStyleSet.ReparentComputedStyle(oldAdditionalStyle, newStyle,
3554                                           newStyle, newStyle, nullptr);
3555       aFrame->SetAdditionalComputedStyle(index, newAdditionalContext);
3556       ++index;
3557     }
3558   }
3559 
3560   // Generally, owned anon boxes are our descendants.  The only exceptions are
3561   // tables (for the table wrapper) and inline frames (for the block part of the
3562   // block-in-inline split).  We're going to update our descendants when looping
3563   // over kids, and we don't want to update the block part of a block-in-inline
3564   // split if the inline is on the first line but the block is not (and if the
3565   // block is, it's the child of something else on the first line and will get
3566   // updated as a child).  And given how this method ends up getting called, if
3567   // we reach here for a table frame, we are already in the middle of
3568   // reparenting the table wrapper frame.  So no need to
3569   // UpdateStyleOfOwnedAnonBoxes() here.
3570 
3571   ReparentFrameDescendants(aFrame, providerChild, aStyleSet);
3572 
3573   // We do not need to do the equivalent of UpdateFramePseudoElementStyles,
3574   // because those are handled by our descendant walk.
3575 }
3576 
ReparentFrameDescendants(nsIFrame * aFrame,nsIFrame * aProviderChild,ServoStyleSet & aStyleSet)3577 void RestyleManager::ReparentFrameDescendants(nsIFrame* aFrame,
3578                                               nsIFrame* aProviderChild,
3579                                               ServoStyleSet& aStyleSet) {
3580   if (aFrame->GetContent()->IsElement() &&
3581       !aFrame->GetContent()->AsElement()->HasServoData()) {
3582     // We're getting into a display: none subtree, avoid reparenting into stuff
3583     // that is going to go away anyway in seconds.
3584     return;
3585   }
3586   for (const auto& childList : aFrame->ChildLists()) {
3587     for (nsIFrame* child : childList.mList) {
3588       // only do frames that are in flow
3589       if (!child->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) &&
3590           child != aProviderChild) {
3591         DoReparentComputedStyleForFirstLine(child, aStyleSet);
3592       }
3593     }
3594   }
3595 }
3596 
3597 }  // namespace mozilla
3598