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