Line data Source code
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/Unused.h"
15 : #include "mozilla/ViewportFrame.h"
16 : #include "mozilla/dom/ChildIterator.h"
17 : #include "mozilla/dom/ElementInlines.h"
18 : #include "nsBlockFrame.h"
19 : #include "nsBulletFrame.h"
20 : #include "nsPlaceholderFrame.h"
21 : #include "nsContentUtils.h"
22 : #include "nsCSSFrameConstructor.h"
23 : #include "nsPrintfCString.h"
24 : #include "nsRefreshDriver.h"
25 : #include "nsStyleChangeList.h"
26 :
27 : using namespace mozilla::dom;
28 :
29 : namespace mozilla {
30 :
31 : #ifdef DEBUG
32 : static bool
33 0 : IsAnonBox(const nsIFrame& aFrame)
34 : {
35 0 : return aFrame.StyleContext()->IsAnonBox();
36 : }
37 :
38 : static const nsIFrame*
39 0 : FirstContinuationOrPartOfIBSplit(const nsIFrame* aFrame)
40 : {
41 0 : if (!aFrame) {
42 0 : return nullptr;
43 : }
44 :
45 0 : return nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
46 : }
47 :
48 : static const nsIFrame*
49 0 : ExpectedOwnerForChild(const nsIFrame& aFrame)
50 : {
51 0 : if (IsAnonBox(aFrame)) {
52 0 : return aFrame.GetParent()->IsViewportFrame() ? nullptr : aFrame.GetParent();
53 : }
54 :
55 0 : if (aFrame.IsBulletFrame()) {
56 0 : return aFrame.GetParent();
57 : }
58 :
59 0 : const nsIFrame* parent = FirstContinuationOrPartOfIBSplit(aFrame.GetParent());
60 :
61 0 : if (aFrame.IsTableFrame()) {
62 0 : MOZ_ASSERT(parent->IsTableWrapperFrame());
63 0 : parent = FirstContinuationOrPartOfIBSplit(parent->GetParent());
64 : }
65 :
66 : // We've handled already anon boxes and bullet frames, so now we're looking at
67 : // a frame of a DOM element or pseudo. Hop through anon and line-boxes
68 : // generated by our DOM parent, and go find the owner frame for it.
69 0 : while (parent && (IsAnonBox(*parent) || parent->IsLineFrame())) {
70 0 : auto* pseudo = parent->StyleContext()->GetPseudo();
71 0 : if (pseudo == nsCSSAnonBoxes::tableWrapper) {
72 0 : const nsIFrame* tableFrame = parent->PrincipalChildList().FirstChild();
73 0 : MOZ_ASSERT(tableFrame->IsTableFrame());
74 : // Handle :-moz-table and :-moz-inline-table.
75 0 : parent = IsAnonBox(*tableFrame) ? parent->GetParent() : tableFrame;
76 : } else {
77 0 : parent = parent->GetParent();
78 : }
79 0 : parent = FirstContinuationOrPartOfIBSplit(parent);
80 : }
81 :
82 0 : return parent;
83 : }
84 :
85 : void
86 0 : ServoRestyleState::AssertOwner(const ServoRestyleState& aParent) const
87 : {
88 0 : MOZ_ASSERT(mOwner);
89 0 : MOZ_ASSERT(!mOwner->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW));
90 0 : MOZ_ASSERT(ExpectedOwnerForChild(*mOwner) == aParent.mOwner);
91 0 : }
92 :
93 : nsChangeHint
94 0 : ServoRestyleState::ChangesHandledFor(const nsIFrame& aFrame) const
95 : {
96 0 : if (!mOwner) {
97 0 : MOZ_ASSERT(!mChangesHandled);
98 0 : return mChangesHandled;
99 : }
100 :
101 0 : MOZ_ASSERT(mOwner == ExpectedOwnerForChild(aFrame),
102 : "Missed some frame in the hierarchy?");
103 0 : return mChangesHandled;
104 : }
105 : #endif
106 :
107 0 : ServoRestyleManager::ServoRestyleManager(nsPresContext* aPresContext)
108 : : RestyleManager(StyleBackendType::Servo, aPresContext)
109 0 : , mReentrantChanges(nullptr)
110 : {
111 0 : }
112 :
113 : void
114 0 : ServoRestyleManager::PostRestyleEvent(Element* aElement,
115 : nsRestyleHint aRestyleHint,
116 : nsChangeHint aMinChangeHint)
117 : {
118 0 : MOZ_ASSERT(!(aMinChangeHint & nsChangeHint_NeutralChange),
119 : "Didn't expect explicit change hints to be neutral!");
120 0 : if (MOZ_UNLIKELY(IsDisconnected()) ||
121 0 : MOZ_UNLIKELY(PresContext()->PresShell()->IsDestroying())) {
122 0 : return;
123 : }
124 :
125 : // We allow posting restyles from within change hint handling, but not from
126 : // within the restyle algorithm itself.
127 0 : MOZ_ASSERT(!ServoStyleSet::IsInServoTraversal());
128 :
129 0 : if (aRestyleHint == 0 && !aMinChangeHint) {
130 0 : return; // Nothing to do.
131 : }
132 :
133 : // Assuming the restyle hints will invalidate cached style for
134 : // getComputedStyle, since we don't know if any of the restyling that we do
135 : // would affect undisplayed elements.
136 0 : if (aRestyleHint) {
137 0 : IncrementUndisplayedRestyleGeneration();
138 : }
139 :
140 : // Processing change hints sometimes causes new change hints to be generated,
141 : // and very occasionally, additional restyle hints. We collect the change
142 : // hints manually to avoid re-traversing the DOM to find them.
143 0 : if (mReentrantChanges && !aRestyleHint) {
144 0 : mReentrantChanges->AppendElement(ReentrantChange { aElement, aMinChangeHint });
145 0 : return;
146 : }
147 :
148 0 : if (aRestyleHint & ~eRestyle_AllHintsWithAnimations) {
149 0 : mHaveNonAnimationRestyles = true;
150 : }
151 :
152 0 : if (aRestyleHint & eRestyle_LaterSiblings) {
153 0 : aRestyleHint &= ~eRestyle_LaterSiblings;
154 :
155 0 : nsRestyleHint siblingHint = eRestyle_Subtree;
156 0 : Element* current = aElement->GetNextElementSibling();
157 0 : while (current) {
158 0 : Servo_NoteExplicitHints(current, siblingHint, nsChangeHint(0));
159 0 : current = current->GetNextElementSibling();
160 : }
161 : }
162 :
163 0 : if (aRestyleHint || aMinChangeHint) {
164 0 : Servo_NoteExplicitHints(aElement, aRestyleHint, aMinChangeHint);
165 : }
166 : }
167 :
168 : void
169 0 : ServoRestyleManager::PostRestyleEventForCSSRuleChanges()
170 : {
171 0 : mRestyleForCSSRuleChanges = true;
172 0 : mPresContext->PresShell()->EnsureStyleFlush();
173 0 : }
174 :
175 : /* static */ void
176 0 : ServoRestyleManager::PostRestyleEventForAnimations(
177 : Element* aElement,
178 : CSSPseudoElementType aPseudoType,
179 : nsRestyleHint aRestyleHint)
180 : {
181 : Element* elementToRestyle =
182 0 : EffectCompositor::GetElementToRestyle(aElement, aPseudoType);
183 :
184 0 : if (!elementToRestyle) {
185 : // FIXME: Bug 1371107: When reframing happens,
186 : // EffectCompositor::mElementsToRestyle still has unbinded old pseudo
187 : // element. We should drop it.
188 0 : return;
189 : }
190 :
191 0 : Servo_NoteExplicitHints(elementToRestyle, aRestyleHint, nsChangeHint(0));
192 : }
193 :
194 : void
195 0 : ServoRestyleManager::RebuildAllStyleData(nsChangeHint aExtraHint,
196 : nsRestyleHint aRestyleHint)
197 : {
198 : // NOTE(emilio): GeckoRestlyeManager does a sync style flush, which seems not
199 : // to be needed in my testing.
200 0 : PostRebuildAllStyleDataEvent(aExtraHint, aRestyleHint);
201 0 : }
202 :
203 : void
204 0 : ServoRestyleManager::PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint,
205 : nsRestyleHint aRestyleHint)
206 : {
207 0 : StyleSet()->ClearDataAndMarkDeviceDirty();
208 :
209 0 : DocumentStyleRootIterator iter(mPresContext->Document());
210 0 : while (Element* root = iter.GetNextStyleRoot()) {
211 0 : PostRestyleEvent(root, aRestyleHint, aExtraHint);
212 0 : }
213 :
214 : // TODO(emilio, bz): Extensions can add/remove stylesheets that can affect
215 : // non-inheriting anon boxes. It's not clear if we want to support that, but
216 : // if we do, we need to re-selector-match them here.
217 0 : }
218 :
219 : /* static */ void
220 0 : ServoRestyleManager::ClearServoDataFromSubtree(Element* aElement)
221 : {
222 0 : if (!aElement->HasServoData()) {
223 0 : MOZ_ASSERT(!aElement->HasDirtyDescendantsForServo());
224 0 : MOZ_ASSERT(!aElement->HasAnimationOnlyDirtyDescendantsForServo());
225 0 : return;
226 : }
227 :
228 0 : StyleChildrenIterator it(aElement);
229 0 : for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
230 0 : if (n->IsElement()) {
231 0 : ClearServoDataFromSubtree(n->AsElement());
232 : }
233 : }
234 :
235 0 : aElement->ClearServoData();
236 0 : aElement->UnsetHasDirtyDescendantsForServo();
237 0 : aElement->UnsetHasAnimationOnlyDirtyDescendantsForServo();
238 : }
239 :
240 : /* static */ void
241 0 : ServoRestyleManager::ClearRestyleStateFromSubtree(Element* aElement)
242 : {
243 0 : if (aElement->HasDirtyDescendantsForServo() ||
244 0 : aElement->HasAnimationOnlyDirtyDescendantsForServo()) {
245 0 : StyleChildrenIterator it(aElement);
246 0 : for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
247 0 : if (n->IsElement()) {
248 0 : ClearRestyleStateFromSubtree(n->AsElement());
249 : }
250 : }
251 : }
252 :
253 0 : Unused << Servo_TakeChangeHint(aElement);
254 0 : aElement->UnsetHasDirtyDescendantsForServo();
255 0 : aElement->UnsetHasAnimationOnlyDirtyDescendantsForServo();
256 0 : aElement->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES);
257 0 : }
258 :
259 : /**
260 : * This struct takes care of encapsulating some common state that text nodes may
261 : * need to track during the post-traversal.
262 : *
263 : * This is currently used to properly compute change hints when the parent
264 : * element of this node is a display: contents node, and also to avoid computing
265 : * the style for text children more than once per element.
266 : */
267 0 : struct ServoRestyleManager::TextPostTraversalState
268 : {
269 : public:
270 0 : TextPostTraversalState(nsStyleContext& aParentContext,
271 : bool aDisplayContentsParentStyleChanged,
272 : ServoRestyleState& aParentRestyleState)
273 0 : : mParentContext(aParentContext)
274 : , mParentRestyleState(aParentRestyleState)
275 : , mStyle(nullptr)
276 : , mShouldPostHints(aDisplayContentsParentStyleChanged)
277 : , mShouldComputeHints(aDisplayContentsParentStyleChanged)
278 0 : , mComputedHint(nsChangeHint_Empty)
279 0 : {}
280 :
281 0 : nsStyleChangeList& ChangeList() { return mParentRestyleState.ChangeList(); }
282 :
283 0 : nsStyleContext& ComputeStyle(nsIContent* aTextNode)
284 : {
285 0 : if (!mStyle) {
286 0 : mStyle = mParentRestyleState.StyleSet().ResolveStyleForText(
287 0 : aTextNode, &mParentContext);
288 : }
289 0 : MOZ_ASSERT(mStyle);
290 0 : return *mStyle;
291 : }
292 :
293 0 : void ComputeHintIfNeeded(nsIContent* aContent,
294 : nsIFrame* aTextFrame,
295 : nsStyleContext& aNewContext)
296 : {
297 0 : MOZ_ASSERT(aTextFrame);
298 0 : MOZ_ASSERT(aNewContext.GetPseudo() == nsCSSAnonBoxes::mozText);
299 :
300 0 : if (MOZ_LIKELY(!mShouldPostHints)) {
301 0 : return;
302 : }
303 :
304 0 : nsStyleContext* oldContext = aTextFrame->StyleContext();
305 0 : MOZ_ASSERT(oldContext->GetPseudo() == nsCSSAnonBoxes::mozText);
306 :
307 : // We rely on the fact that all the text children for the same element share
308 : // style to avoid recomputing style differences for all of them.
309 : //
310 : // TODO(emilio): The above may not be true for ::first-{line,letter}, but
311 : // we'll cross that bridge when we support those in stylo.
312 0 : if (mShouldComputeHints) {
313 0 : mShouldComputeHints = false;
314 : uint32_t equalStructs, samePointerStructs;
315 0 : mComputedHint =
316 0 : oldContext->CalcStyleDifference(&aNewContext,
317 : &equalStructs,
318 : &samePointerStructs);
319 0 : mComputedHint = NS_RemoveSubsumedHints(
320 0 : mComputedHint, mParentRestyleState.ChangesHandledFor(*aTextFrame));
321 : }
322 :
323 0 : if (mComputedHint) {
324 0 : mParentRestyleState.ChangeList().AppendChange(
325 0 : aTextFrame, aContent, mComputedHint);
326 : }
327 : }
328 :
329 : private:
330 : nsStyleContext& mParentContext;
331 : ServoRestyleState& mParentRestyleState;
332 : RefPtr<nsStyleContext> mStyle;
333 : bool mShouldPostHints;
334 : bool mShouldComputeHints;
335 : nsChangeHint mComputedHint;
336 : };
337 :
338 : // Find the first-letter frame for the given element, if any. Returns null to
339 : // indicate there isn't one.
340 : static nsIFrame*
341 0 : FindFirstLetterFrameForElement(const Element* aElement)
342 : {
343 0 : nsIFrame* frame = aElement->GetPrimaryFrame();
344 0 : if (!frame) {
345 0 : return nullptr;
346 : }
347 : // The first-letter frame will always be inside the content insertion frame,
348 : // which will always be a block if we have a first-letter frame at all.
349 0 : frame = frame->GetContentInsertionFrame();
350 0 : if (!frame) {
351 : // We're a leaf; certainly no first-letter frame.
352 0 : return nullptr;
353 : }
354 :
355 0 : if (!frame->IsFrameOfType(nsIFrame::eBlockFrame)) {
356 0 : return nullptr;
357 : }
358 :
359 0 : return static_cast<nsBlockFrame*>(frame)->GetFirstLetter();
360 : }
361 :
362 : static void
363 0 : UpdateBackdropIfNeeded(nsIFrame* aFrame,
364 : ServoStyleSet& aStyleSet,
365 : nsStyleChangeList& aChangeList)
366 : {
367 0 : const nsStyleDisplay* display = aFrame->StyleContext()->StyleDisplay();
368 0 : if (display->mTopLayer != NS_STYLE_TOP_LAYER_TOP) {
369 0 : return;
370 : }
371 :
372 : // Elements in the top layer are guaranteed to have absolute or fixed
373 : // position per https://fullscreen.spec.whatwg.org/#new-stacking-layer.
374 0 : MOZ_ASSERT(display->IsAbsolutelyPositionedStyle());
375 :
376 : nsIFrame* backdropPlaceholder =
377 0 : aFrame->GetChildList(nsIFrame::kBackdropList).FirstChild();
378 0 : if (!backdropPlaceholder) {
379 0 : return;
380 : }
381 :
382 0 : MOZ_ASSERT(backdropPlaceholder->IsPlaceholderFrame());
383 : nsIFrame* backdropFrame =
384 0 : nsPlaceholderFrame::GetRealFrameForPlaceholder(backdropPlaceholder);
385 0 : MOZ_ASSERT(backdropFrame->IsBackdropFrame());
386 0 : MOZ_ASSERT(backdropFrame->StyleContext()->GetPseudoType() ==
387 : CSSPseudoElementType::backdrop);
388 :
389 : RefPtr<nsStyleContext> newContext =
390 0 : aStyleSet.ResolvePseudoElementStyle(aFrame->GetContent()->AsElement(),
391 : CSSPseudoElementType::backdrop,
392 : aFrame->StyleContext(),
393 0 : /* aPseudoElement = */ nullptr);
394 :
395 : // NOTE(emilio): We can't use the changes handled for the owner of the
396 : // backdrop frame, since it's out of flow, and parented to the viewport frame.
397 0 : MOZ_ASSERT(backdropFrame->GetParent()->IsViewportFrame());
398 0 : ServoRestyleState state(aStyleSet, aChangeList);
399 0 : aFrame->UpdateStyleOfOwnedChildFrame(backdropFrame, newContext, state);
400 : }
401 :
402 : static void
403 0 : UpdateFirstLetterIfNeeded(nsIFrame* aFrame, ServoRestyleState& aRestyleState)
404 : {
405 0 : if (!aFrame->HasFirstLetterChild()) {
406 0 : return;
407 : }
408 :
409 : // We need to find the block the first-letter is associated with so we can
410 : // find the right element for the first-letter's style resolution. Might as
411 : // well just delegate the whole thing to that block.
412 0 : nsIFrame* block = aFrame;
413 0 : while (!block->IsFrameOfType(nsIFrame::eBlockFrame)) {
414 0 : block = block->GetParent();
415 : }
416 0 : static_cast<nsBlockFrame*>(block->FirstContinuation())->
417 0 : UpdateFirstLetterStyle(aRestyleState);
418 : }
419 :
420 : static void
421 0 : UpdateFramePseudoElementStyles(nsIFrame* aFrame,
422 : ServoRestyleState& aRestyleState)
423 : {
424 : // first-letter needs to be updated before first-line, because first-line can
425 : // change the style of the first-letter.
426 0 : UpdateFirstLetterIfNeeded(aFrame, aRestyleState);
427 :
428 0 : if (aFrame->IsFrameOfType(nsIFrame::eBlockFrame)) {
429 0 : static_cast<nsBlockFrame*>(aFrame)->UpdatePseudoElementStyles(aRestyleState);
430 : }
431 :
432 0 : UpdateBackdropIfNeeded(
433 0 : aFrame, aRestyleState.StyleSet(), aRestyleState.ChangeList());
434 0 : }
435 :
436 : bool
437 0 : ServoRestyleManager::ProcessPostTraversal(Element* aElement,
438 : nsStyleContext* aParentContext,
439 : ServoRestyleState& aRestyleState)
440 : {
441 0 : nsIFrame* styleFrame = nsLayoutUtils::GetStyleFrame(aElement);
442 :
443 : // NOTE(emilio): This is needed because for table frames the bit is set on the
444 : // table wrapper (which is the primary frame), not on the table itself.
445 : const bool isOutOfFlow =
446 0 : aElement->GetPrimaryFrame() &&
447 0 : aElement->GetPrimaryFrame()->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
448 :
449 : // Grab the change hint from Servo.
450 0 : nsChangeHint changeHint = Servo_TakeChangeHint(aElement);
451 :
452 : // We should really fix the weird primary frame mapping for image maps
453 : // (bug 135040)...
454 0 : if (styleFrame && styleFrame->GetContent() != aElement) {
455 0 : MOZ_ASSERT(styleFrame->IsImageFrame());
456 0 : styleFrame = nullptr;
457 : }
458 :
459 : // Handle lazy frame construction by posting a reconstruct for any lazily-
460 : // constructed roots.
461 0 : if (aElement->HasFlag(NODE_NEEDS_FRAME)) {
462 0 : changeHint |= nsChangeHint_ReconstructFrame;
463 0 : MOZ_ASSERT(!styleFrame);
464 : }
465 :
466 0 : if (styleFrame && !isOutOfFlow) {
467 0 : changeHint = NS_RemoveSubsumedHints(
468 : changeHint, aRestyleState.ChangesHandledFor(*styleFrame));
469 : }
470 :
471 : // Although we shouldn't generate non-ReconstructFrame hints for elements with
472 : // no frames, we can still get them here if they were explicitly posted by
473 : // PostRestyleEvent, such as a RepaintFrame hint when a :link changes to be
474 : // :visited. Skip processing these hints if there is no frame.
475 0 : if ((styleFrame || (changeHint & nsChangeHint_ReconstructFrame)) && changeHint) {
476 0 : aRestyleState.ChangeList().AppendChange(styleFrame, aElement, changeHint);
477 : }
478 :
479 : // If our change hint is reconstruct, we delegate to the frame constructor,
480 : // which consumes the new style and expects the old style to be on the frame.
481 : //
482 : // XXXbholley: We should teach the frame constructor how to clear the dirty
483 : // descendants bit to avoid the traversal here.
484 0 : if (changeHint & nsChangeHint_ReconstructFrame) {
485 0 : ClearRestyleStateFromSubtree(aElement);
486 0 : return true;
487 : }
488 :
489 : // TODO(emilio): We could avoid some refcount traffic here, specially in the
490 : // ServoComputedValues case, which uses atomic refcounting.
491 : //
492 : // Hold the old style context alive, because it could become a dangling
493 : // pointer during the replacement. In practice it's not a huge deal, but
494 : // better not playing with dangling pointers if not needed.
495 : RefPtr<ServoStyleContext> oldStyleContext =
496 0 : styleFrame ? styleFrame->StyleContext()->AsServo() : nullptr;
497 :
498 0 : UndisplayedNode* displayContentsNode = nullptr;
499 : // FIXME(emilio, bug 1303605): This can be simpler for Servo.
500 : // Note that we intentionally don't check for display: none content.
501 0 : if (!oldStyleContext) {
502 : displayContentsNode =
503 0 : PresContext()->FrameConstructor()->GetDisplayContentsNodeFor(aElement);
504 0 : if (displayContentsNode) {
505 0 : oldStyleContext = displayContentsNode->mStyle->AsServo();
506 : }
507 : }
508 :
509 : RefPtr<ServoComputedValues> computedValues =
510 0 : aRestyleState.StyleSet().ResolveServoStyle(aElement);
511 :
512 : // Note that we rely in the fact that we don't cascade pseudo-element styles
513 : // separately right now (that is, if a pseudo style changes, the normal style
514 : // changes too).
515 : //
516 : // Otherwise we should probably encode that information somehow to avoid
517 : // expensive checks in the common case.
518 : //
519 : // Also, we're going to need to check for pseudos of display: contents
520 : // elements, though that is buggy right now even in non-stylo mode, see
521 : // bug 1251799.
522 0 : const bool recreateContext = oldStyleContext &&
523 0 : oldStyleContext->ComputedValues() != computedValues;
524 :
525 0 : Maybe<ServoRestyleState> thisFrameRestyleState;
526 0 : if (styleFrame) {
527 : auto type = isOutOfFlow
528 0 : ? ServoRestyleState::Type::OutOfFlow
529 0 : : ServoRestyleState::Type::InFlow;
530 :
531 0 : thisFrameRestyleState.emplace(*styleFrame, aRestyleState, changeHint, type);
532 : }
533 :
534 : // We can't really assume as used changes from display: contents elements (or
535 : // other elements without frames).
536 : ServoRestyleState& childrenRestyleState =
537 0 : thisFrameRestyleState ? *thisFrameRestyleState : aRestyleState;
538 :
539 0 : RefPtr<ServoStyleContext> newContext = nullptr;
540 0 : if (recreateContext) {
541 0 : MOZ_ASSERT(styleFrame || displayContentsNode);
542 :
543 0 : auto pseudo = aElement->GetPseudoElementType();
544 : nsIAtom* pseudoTag = pseudo == CSSPseudoElementType::NotPseudo
545 0 : ? nullptr : nsCSSPseudoElements::GetPseudoAtom(pseudo);
546 :
547 0 : newContext = aRestyleState.StyleSet().GetContext(
548 0 : computedValues.forget(), aParentContext, pseudoTag, pseudo, aElement);
549 :
550 0 : newContext->ResolveSameStructsAs(PresContext(), oldStyleContext);
551 :
552 : // We want to walk all the continuations here, even the ones with different
553 : // styles. In practice, the only reason we get continuations with different
554 : // styles here is ::first-line (::first-letter never affects element
555 : // styles). But in that case, newContext is the right context for the
556 : // _later_ continuations anyway (the ones not affected by ::first-line), not
557 : // the earlier ones, so there is no point stopping right at the point when
558 : // we'd actually be setting the right style context.
559 : //
560 : // This does mean that we may be setting the wrong style context on our
561 : // initial continuations; ::first-line fixes that up after the fact.
562 0 : for (nsIFrame* f = styleFrame; f; f = f->GetNextContinuation()) {
563 0 : f->SetStyleContext(newContext);
564 : }
565 :
566 0 : if (MOZ_UNLIKELY(displayContentsNode)) {
567 0 : MOZ_ASSERT(!styleFrame);
568 0 : displayContentsNode->mStyle = newContext;
569 : }
570 :
571 0 : if (styleFrame) {
572 0 : styleFrame->UpdateStyleOfOwnedAnonBoxes(childrenRestyleState);
573 : }
574 :
575 0 : if (!aElement->GetParent()) {
576 : // This is the root. Update styles on the viewport as needed.
577 : ViewportFrame* viewport =
578 0 : do_QueryFrame(mPresContext->PresShell()->GetRootFrame());
579 0 : if (viewport) {
580 : // NB: The root restyle state, not the one for our children!
581 0 : viewport->UpdateStyle(aRestyleState);
582 : }
583 : }
584 :
585 : // Some changes to animations don't affect the computed style and yet still
586 : // require the layer to be updated. For example, pausing an animation via
587 : // the Web Animations API won't affect an element's style but still
588 : // requires to update the animation on the layer.
589 : //
590 : // We can sometimes reach this when the animated style is being removed.
591 : // Since AddLayerChangesForAnimation checks if |styleFrame| has a transform
592 : // style or not, we need to call it *after* setting |newContext| to
593 : // |styleFrame| to ensure the animated transform has been removed first.
594 0 : AddLayerChangesForAnimation(
595 0 : styleFrame, aElement, aRestyleState.ChangeList());
596 : }
597 :
598 : const bool descendantsNeedFrames =
599 0 : aElement->HasFlag(NODE_DESCENDANTS_NEED_FRAMES);
600 : const bool traverseElementChildren =
601 0 : aElement->HasDirtyDescendantsForServo() ||
602 0 : aElement->HasAnimationOnlyDirtyDescendantsForServo() ||
603 0 : descendantsNeedFrames;
604 0 : const bool traverseTextChildren = recreateContext || descendantsNeedFrames;
605 0 : bool recreatedAnyContext = recreateContext;
606 0 : if (traverseElementChildren || traverseTextChildren) {
607 : nsStyleContext* upToDateContext =
608 0 : recreateContext ? newContext : oldStyleContext;
609 :
610 0 : StyleChildrenIterator it(aElement);
611 : TextPostTraversalState textState(*upToDateContext,
612 0 : displayContentsNode && recreateContext,
613 0 : childrenRestyleState);
614 0 : for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
615 0 : if (traverseElementChildren && n->IsElement()) {
616 0 : recreatedAnyContext |= ProcessPostTraversal(
617 : n->AsElement(), upToDateContext, childrenRestyleState);
618 0 : } else if (traverseTextChildren && n->IsNodeOfType(nsINode::eTEXT)) {
619 0 : recreatedAnyContext |= ProcessPostTraversalForText(n, textState);
620 : }
621 : }
622 : }
623 :
624 : // We want to update frame pseudo-element styles after we've traversed our
625 : // kids, because some of those updates (::first-line/::first-letter) need to
626 : // modify the styles of the kids, and the child traversal above would just
627 : // clobber those modifications.
628 0 : if (recreateContext && styleFrame) {
629 0 : UpdateFramePseudoElementStyles(styleFrame, childrenRestyleState);
630 : }
631 :
632 0 : aElement->UnsetHasDirtyDescendantsForServo();
633 0 : aElement->UnsetHasAnimationOnlyDirtyDescendantsForServo();
634 0 : aElement->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES);
635 0 : return recreatedAnyContext;
636 : }
637 :
638 : bool
639 0 : ServoRestyleManager::ProcessPostTraversalForText(
640 : nsIContent* aTextNode,
641 : TextPostTraversalState& aPostTraversalState)
642 : {
643 : // Handle lazy frame construction.
644 0 : if (aTextNode->HasFlag(NODE_NEEDS_FRAME)) {
645 0 : aPostTraversalState.ChangeList().AppendChange(
646 0 : nullptr, aTextNode, nsChangeHint_ReconstructFrame);
647 0 : return true;
648 : }
649 :
650 : // Handle restyle.
651 0 : nsIFrame* primaryFrame = aTextNode->GetPrimaryFrame();
652 0 : if (!primaryFrame) {
653 0 : return false;
654 : }
655 :
656 0 : nsStyleContext& newContext = aPostTraversalState.ComputeStyle(aTextNode);
657 0 : aPostTraversalState.ComputeHintIfNeeded(aTextNode, primaryFrame, newContext);
658 :
659 : // We want to walk all the continuations here, even the ones with different
660 : // styles. In practice, the only reasons we get continuations with different
661 : // styles are ::first-line and ::first-letter. But in those cases,
662 : // newContext is the right context for the _later_ continuations anyway (the
663 : // ones not affected by ::first-line/::first-letter), not the earlier ones,
664 : // so there is no point stopping right at the point when we'd actually be
665 : // setting the right style context.
666 : //
667 : // This does mean that we may be setting the wrong style context on our
668 : // initial continuations; ::first-line/::first-letter fix that up after the
669 : // fact.
670 0 : for (nsIFrame* f = primaryFrame; f; f = f->GetNextContinuation()) {
671 0 : f->SetStyleContext(&newContext);
672 : }
673 :
674 0 : return true;
675 : }
676 :
677 : void
678 0 : ServoRestyleManager::ClearSnapshots()
679 : {
680 0 : for (auto iter = mSnapshots.Iter(); !iter.Done(); iter.Next()) {
681 0 : iter.Key()->UnsetFlags(ELEMENT_HAS_SNAPSHOT | ELEMENT_HANDLED_SNAPSHOT);
682 0 : iter.Remove();
683 : }
684 0 : }
685 :
686 : ServoElementSnapshot&
687 0 : ServoRestyleManager::SnapshotFor(Element* aElement)
688 : {
689 0 : MOZ_ASSERT(!mInStyleRefresh);
690 :
691 : // NOTE(emilio): We can handle snapshots from a one-off restyle of those that
692 : // we do to restyle stuff for reconstruction, for example.
693 : //
694 : // It seems to be the case that we always flush in between that happens and
695 : // the next attribute change, so we can assert that we haven't handled the
696 : // snapshot here yet. If this assertion didn't hold, we'd need to unset that
697 : // flag from here too.
698 : //
699 : // Can't wait to make ProcessPendingRestyles the only entry-point for styling,
700 : // so this becomes much easier to reason about. Today is not that day though.
701 0 : MOZ_ASSERT(aElement->HasServoData());
702 0 : MOZ_ASSERT(!aElement->HasFlag(ELEMENT_HANDLED_SNAPSHOT));
703 :
704 0 : ServoElementSnapshot* snapshot = mSnapshots.LookupOrAdd(aElement, aElement);
705 0 : aElement->SetFlags(ELEMENT_HAS_SNAPSHOT);
706 :
707 0 : nsIPresShell* presShell = mPresContext->PresShell();
708 0 : presShell->EnsureStyleFlush();
709 :
710 0 : return *snapshot;
711 : }
712 :
713 : /* static */ nsIFrame*
714 0 : ServoRestyleManager::FrameForPseudoElement(const Element* aElement,
715 : nsIAtom* aPseudoTagOrNull)
716 : {
717 0 : if (!aPseudoTagOrNull) {
718 0 : return nsLayoutUtils::GetStyleFrame(aElement);
719 : }
720 :
721 0 : if (aPseudoTagOrNull == nsCSSPseudoElements::before) {
722 0 : Element* pseudoElement = nsLayoutUtils::GetBeforePseudo(aElement);
723 0 : return pseudoElement ? nsLayoutUtils::GetStyleFrame(pseudoElement) : nullptr;
724 : }
725 :
726 0 : if (aPseudoTagOrNull == nsCSSPseudoElements::after) {
727 0 : Element* pseudoElement = nsLayoutUtils::GetAfterPseudo(aElement);
728 0 : return pseudoElement ? nsLayoutUtils::GetStyleFrame(pseudoElement) : nullptr;
729 : }
730 :
731 0 : if (aPseudoTagOrNull == nsCSSPseudoElements::firstLetter) {
732 0 : return FindFirstLetterFrameForElement(aElement);
733 : }
734 :
735 0 : if (aPseudoTagOrNull == nsCSSPseudoElements::firstLine) {
736 : // TODO(emilio, bz): Figure out the best way to diff these styles.
737 0 : return nullptr;
738 : }
739 :
740 0 : MOZ_CRASH("Unkown pseudo-element given to "
741 : "ServoRestyleManager::FrameForPseudoElement");
742 : return nullptr;
743 : }
744 :
745 : void
746 0 : ServoRestyleManager::DoProcessPendingRestyles(TraversalRestyleBehavior
747 : aRestyleBehavior)
748 : {
749 0 : MOZ_ASSERT(PresContext()->Document(), "No document? Pshaw!");
750 0 : MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(), "Missing a script blocker!");
751 0 : MOZ_ASSERT(!mInStyleRefresh, "Reentrant call?");
752 :
753 0 : if (MOZ_UNLIKELY(!PresContext()->PresShell()->DidInitialize())) {
754 : // PresShell::FlushPendingNotifications doesn't early-return in the case
755 : // where the PreShell hasn't yet been initialized (and therefore we haven't
756 : // yet done the initial style traversal of the DOM tree). We should arguably
757 : // fix up the callers and assert against this case, but we just detect and
758 : // handle it for now.
759 0 : return;
760 : }
761 :
762 : // Create a AnimationsWithDestroyedFrame during restyling process to
763 : // stop animations and transitions on elements that have no frame at the end
764 : // of the restyling process.
765 0 : AnimationsWithDestroyedFrame animationsWithDestroyedFrame(this);
766 :
767 0 : ServoStyleSet* styleSet = StyleSet();
768 0 : nsIDocument* doc = PresContext()->Document();
769 : bool animationOnly = aRestyleBehavior ==
770 0 : TraversalRestyleBehavior::ForAnimationOnly;
771 :
772 : // Ensure the refresh driver is active during traversal to avoid mutating
773 : // mActiveTimer and mMostRecentRefresh time.
774 0 : PresContext()->RefreshDriver()->MostRecentRefresh();
775 :
776 :
777 : // Perform the Servo traversal, and the post-traversal if required. We do this
778 : // in a loop because certain rare paths in the frame constructor (like
779 : // uninstalling XBL bindings) can trigger additional style validations.
780 0 : mInStyleRefresh = true;
781 0 : if (mHaveNonAnimationRestyles && !animationOnly) {
782 0 : ++mAnimationGeneration;
783 : }
784 :
785 0 : TraversalRestyleBehavior restyleBehavior = mRestyleForCSSRuleChanges
786 0 : ? TraversalRestyleBehavior::ForCSSRuleChanges
787 0 : : TraversalRestyleBehavior::Normal;
788 0 : while (animationOnly ? styleSet->StyleDocumentForAnimationOnly()
789 : : styleSet->StyleDocument(restyleBehavior)) {
790 0 : if (!animationOnly) {
791 0 : ClearSnapshots();
792 : }
793 :
794 0 : nsStyleChangeList currentChanges(StyleBackendType::Servo);
795 0 : bool anyStyleChanged = false;
796 :
797 : // Recreate style contexts, and queue up change hints (which also handle
798 : // lazy frame construction).
799 : {
800 : AutoRestyleTimelineMarker marker(
801 0 : mPresContext->GetDocShell(), animationOnly);
802 0 : DocumentStyleRootIterator iter(doc);
803 0 : while (Element* root = iter.GetNextStyleRoot()) {
804 0 : ServoRestyleState state(*styleSet, currentChanges);
805 0 : anyStyleChanged |= ProcessPostTraversal(root, nullptr, state);
806 0 : }
807 : }
808 :
809 : // Process the change hints.
810 : //
811 : // Unfortunately, the frame constructor can generate new change hints while
812 : // processing existing ones. We redirect those into a secondary queue and
813 : // iterate until there's nothing left.
814 : {
815 : AutoTimelineMarker marker(
816 0 : mPresContext->GetDocShell(), "StylesApplyChanges");
817 0 : ReentrantChangeList newChanges;
818 0 : mReentrantChanges = &newChanges;
819 0 : while (!currentChanges.IsEmpty()) {
820 0 : ProcessRestyledFrames(currentChanges);
821 0 : MOZ_ASSERT(currentChanges.IsEmpty());
822 0 : for (ReentrantChange& change: newChanges) {
823 0 : if (!(change.mHint & nsChangeHint_ReconstructFrame) &&
824 0 : !change.mContent->GetPrimaryFrame()) {
825 : // SVG Elements post change hints without ensuring that the primary
826 : // frame will be there after that (see bug 1366142).
827 : //
828 : // Just ignore those, since we can't really process them.
829 0 : continue;
830 : }
831 0 : currentChanges.AppendChange(change.mContent->GetPrimaryFrame(),
832 0 : change.mContent, change.mHint);
833 : }
834 0 : newChanges.Clear();
835 : }
836 0 : mReentrantChanges = nullptr;
837 : }
838 :
839 0 : if (anyStyleChanged) {
840 : // Maybe no styles changed when:
841 : //
842 : // * Only explicit change hints were posted in the first place.
843 : // * When an attribute or state change in the content happens not to need
844 : // a restyle after all.
845 : //
846 : // In any case, we don't need to increment the restyle generation in that
847 : // case.
848 0 : IncrementRestyleGeneration();
849 : }
850 : }
851 :
852 0 : FlushOverflowChangedTracker();
853 :
854 0 : if (!animationOnly) {
855 0 : ClearSnapshots();
856 0 : styleSet->AssertTreeIsClean();
857 0 : mHaveNonAnimationRestyles = false;
858 : }
859 0 : mRestyleForCSSRuleChanges = false;
860 0 : mInStyleRefresh = false;
861 :
862 : // Now that everything has settled, see if we have enough free rule nodes in
863 : // the tree to warrant sweeping them.
864 0 : styleSet->MaybeGCRuleTree();
865 :
866 : // Note: We are in the scope of |animationsWithDestroyedFrame|, so
867 : // |mAnimationsWithDestroyedFrame| is still valid.
868 0 : MOZ_ASSERT(mAnimationsWithDestroyedFrame);
869 0 : mAnimationsWithDestroyedFrame->StopAnimationsForElementsWithoutFrames();
870 : }
871 :
872 : void
873 0 : ServoRestyleManager::ProcessPendingRestyles()
874 : {
875 0 : DoProcessPendingRestyles(TraversalRestyleBehavior::Normal);
876 0 : }
877 :
878 : void
879 0 : ServoRestyleManager::UpdateOnlyAnimationStyles()
880 : {
881 : // Bug 1365855: We also need to implement this for SMIL.
882 0 : bool doCSS = PresContext()->EffectCompositor()->HasPendingStyleUpdates();
883 0 : if (!doCSS) {
884 0 : return;
885 : }
886 :
887 0 : DoProcessPendingRestyles(TraversalRestyleBehavior::ForAnimationOnly);
888 : }
889 :
890 : void
891 0 : ServoRestyleManager::ContentStateChanged(nsIContent* aContent,
892 : EventStates aChangedBits)
893 : {
894 0 : MOZ_ASSERT(!mInStyleRefresh);
895 :
896 0 : if (!aContent->IsElement()) {
897 0 : return;
898 : }
899 :
900 0 : Element* aElement = aContent->AsElement();
901 : nsChangeHint changeHint;
902 : nsRestyleHint restyleHint;
903 :
904 0 : if (!aElement->HasServoData()) {
905 0 : return;
906 : }
907 :
908 : // NOTE: restyleHint here is effectively always 0, since that's what
909 : // ServoStyleSet::HasStateDependentStyle returns. Servo computes on
910 : // ProcessPendingRestyles using the ElementSnapshot, but in theory could
911 : // compute it sequentially easily.
912 : //
913 : // Determine what's the best way to do it, and how much work do we save
914 : // processing the restyle hint early (i.e., computing the style hint here
915 : // sequentially, potentially saving the snapshot), vs lazily (snapshot
916 : // approach).
917 : //
918 : // If we take the sequential approach we need to specialize Servo's restyle
919 : // hints system a bit more, and mesure whether we save something storing the
920 : // restyle hint in the table and deferring the dirtiness setting until
921 : // ProcessPendingRestyles (that's a requirement if we store snapshots though),
922 : // vs processing the restyle hint in-place, dirtying the nodes on
923 : // PostRestyleEvent.
924 : //
925 : // If we definitely take the snapshot approach, we should take rid of
926 : // HasStateDependentStyle, etc (though right now they're no-ops).
927 0 : ContentStateChangedInternal(aElement, aChangedBits, &changeHint,
928 0 : &restyleHint);
929 :
930 : // Don't bother taking a snapshot if no rules depend on these state bits.
931 : //
932 : // We always take a snapshot for the LTR/RTL event states, since Servo doesn't
933 : // track those bits in the same way, and we know that :dir() rules are always
934 : // present in UA style sheets.
935 0 : if (!aChangedBits.HasAtLeastOneOfStates(DIRECTION_STATES) &&
936 0 : !StyleSet()->HasStateDependency(*aElement, aChangedBits)) {
937 0 : return;
938 : }
939 :
940 0 : ServoElementSnapshot& snapshot = SnapshotFor(aElement);
941 0 : EventStates previousState = aElement->StyleState() ^ aChangedBits;
942 0 : snapshot.AddState(previousState);
943 :
944 0 : if (Element* parent = aElement->GetFlattenedTreeParentElementForStyle()) {
945 0 : parent->NoteDirtyDescendantsForServo();
946 : }
947 :
948 0 : if (restyleHint || changeHint) {
949 0 : Servo_NoteExplicitHints(aElement, restyleHint, changeHint);
950 : }
951 :
952 : // Assuming we need to invalidate cached style in getComputedStyle for
953 : // undisplayed elements, since we don't know if it is needed.
954 0 : IncrementUndisplayedRestyleGeneration();
955 : }
956 :
957 : static inline bool
958 0 : AttributeInfluencesOtherPseudoClassState(const Element& aElement,
959 : const nsIAtom* aAttribute)
960 : {
961 : // We must record some state for :-moz-browser-frame and
962 : // :-moz-table-border-nonzero.
963 0 : if (aAttribute == nsGkAtoms::mozbrowser) {
964 0 : return aElement.IsAnyOfHTMLElements(nsGkAtoms::iframe, nsGkAtoms::frame);
965 : }
966 :
967 0 : if (aAttribute == nsGkAtoms::border) {
968 0 : return aElement.IsHTMLElement(nsGkAtoms::table);
969 : }
970 :
971 0 : return false;
972 : }
973 :
974 : static inline bool
975 0 : NeedToRecordAttrChange(const ServoStyleSet& aStyleSet,
976 : const Element& aElement,
977 : int32_t aNameSpaceID,
978 : nsIAtom* aAttribute,
979 : bool* aInfluencesOtherPseudoClassState)
980 : {
981 0 : *aInfluencesOtherPseudoClassState =
982 0 : AttributeInfluencesOtherPseudoClassState(aElement, aAttribute);
983 :
984 : // If the attribute influences one of the pseudo-classes that are backed by
985 : // attributes, we just record it.
986 0 : if (*aInfluencesOtherPseudoClassState) {
987 0 : return true;
988 : }
989 :
990 : // We assume that id and class attributes are used in class/id selectors, and
991 : // thus record them.
992 : //
993 : // TODO(emilio): We keep a filter of the ids in use somewhere in the StyleSet,
994 : // presumably we could try to filter the old and new id, but it's not clear
995 : // it's worth it.
996 0 : if (aNameSpaceID == kNameSpaceID_None &&
997 0 : (aAttribute == nsGkAtoms::id || aAttribute == nsGkAtoms::_class)) {
998 0 : return true;
999 : }
1000 :
1001 : // We always record lang="", even though we force a subtree restyle when it
1002 : // changes, since it can change how its siblings match :lang(..) due to
1003 : // selectors like :lang(..) + div.
1004 0 : if (aAttribute == nsGkAtoms::lang) {
1005 0 : return true;
1006 : }
1007 :
1008 : // Otherwise, just record the attribute change if a selector in the page may
1009 : // reference it from an attribute selector.
1010 0 : return aStyleSet.MightHaveAttributeDependency(aElement, aAttribute);
1011 : }
1012 :
1013 : void
1014 0 : ServoRestyleManager::AttributeWillChange(Element* aElement,
1015 : int32_t aNameSpaceID,
1016 : nsIAtom* aAttribute, int32_t aModType,
1017 : const nsAttrValue* aNewValue)
1018 : {
1019 0 : TakeSnapshotForAttributeChange(aElement, aNameSpaceID, aAttribute);
1020 0 : }
1021 :
1022 : void
1023 0 : ServoRestyleManager::ClassAttributeWillBeChangedBySMIL(Element* aElement)
1024 : {
1025 : TakeSnapshotForAttributeChange(aElement, kNameSpaceID_None,
1026 0 : nsGkAtoms::_class);
1027 0 : }
1028 :
1029 : void
1030 0 : ServoRestyleManager::TakeSnapshotForAttributeChange(Element* aElement,
1031 : int32_t aNameSpaceID,
1032 : nsIAtom* aAttribute)
1033 : {
1034 0 : MOZ_ASSERT(!mInStyleRefresh);
1035 :
1036 0 : if (!aElement->HasServoData()) {
1037 0 : return;
1038 : }
1039 :
1040 : bool influencesOtherPseudoClassState;
1041 0 : if (!NeedToRecordAttrChange(*StyleSet(),
1042 : *aElement,
1043 : aNameSpaceID,
1044 : aAttribute,
1045 : &influencesOtherPseudoClassState)) {
1046 0 : return;
1047 : }
1048 :
1049 : // We cannot tell if the attribute change will affect the styles of
1050 : // undisplayed elements, because we don't actually restyle those elements
1051 : // during the restyle traversal. So just assume that the attribute change can
1052 : // cause the style to change.
1053 0 : IncrementUndisplayedRestyleGeneration();
1054 :
1055 0 : ServoElementSnapshot& snapshot = SnapshotFor(aElement);
1056 0 : snapshot.AddAttrs(aElement, aNameSpaceID, aAttribute);
1057 :
1058 0 : if (influencesOtherPseudoClassState) {
1059 0 : snapshot.AddOtherPseudoClassState(aElement);
1060 : }
1061 :
1062 0 : if (Element* parent = aElement->GetFlattenedTreeParentElementForStyle()) {
1063 0 : parent->NoteDirtyDescendantsForServo();
1064 : }
1065 : }
1066 :
1067 : // For some attribute changes we must restyle the whole subtree:
1068 : //
1069 : // * <td> is affected by the cellpadding on its ancestor table
1070 : // * lang="" and xml:lang="" can affect all descendants due to :lang()
1071 : //
1072 : static inline bool
1073 0 : AttributeChangeRequiresSubtreeRestyle(const Element& aElement, nsIAtom* aAttr)
1074 : {
1075 0 : if (aAttr == nsGkAtoms::cellpadding) {
1076 0 : return aElement.IsHTMLElement(nsGkAtoms::table);
1077 : }
1078 :
1079 0 : return aAttr == nsGkAtoms::lang;
1080 : }
1081 :
1082 : void
1083 0 : ServoRestyleManager::AttributeChanged(Element* aElement, int32_t aNameSpaceID,
1084 : nsIAtom* aAttribute, int32_t aModType,
1085 : const nsAttrValue* aOldValue)
1086 : {
1087 0 : MOZ_ASSERT(!mInStyleRefresh);
1088 :
1089 0 : if (nsIFrame* primaryFrame = aElement->GetPrimaryFrame()) {
1090 0 : primaryFrame->AttributeChanged(aNameSpaceID, aAttribute, aModType);
1091 : }
1092 :
1093 0 : auto changeHint = nsChangeHint(0);
1094 0 : auto restyleHint = nsRestyleHint(0);
1095 :
1096 0 : changeHint |= aElement->GetAttributeChangeHint(aAttribute, aModType);
1097 :
1098 0 : if (aAttribute == nsGkAtoms::style) {
1099 0 : restyleHint |= eRestyle_StyleAttribute;
1100 0 : } else if (AttributeChangeRequiresSubtreeRestyle(*aElement, aAttribute)) {
1101 0 : restyleHint |= eRestyle_Subtree;
1102 0 : } else if (aElement->IsAttributeMapped(aAttribute)) {
1103 0 : restyleHint |= eRestyle_Self;
1104 : }
1105 :
1106 0 : if (restyleHint || changeHint) {
1107 0 : Servo_NoteExplicitHints(aElement, restyleHint, changeHint);
1108 : }
1109 :
1110 0 : if (restyleHint) {
1111 : // Assuming we need to invalidate cached style in getComputedStyle for
1112 : // undisplayed elements, since we don't know if it is needed.
1113 0 : IncrementUndisplayedRestyleGeneration();
1114 : }
1115 0 : }
1116 :
1117 : nsresult
1118 0 : ServoRestyleManager::ReparentStyleContext(nsIFrame* aFrame)
1119 : {
1120 0 : NS_WARNING("stylo: ServoRestyleManager::ReparentStyleContext not implemented");
1121 0 : return NS_OK;
1122 : }
1123 :
1124 : } // namespace mozilla
|