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