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 : #include "mozilla/RestyleManagerInlines.h"
9 :
10 : #include "Layers.h"
11 : #include "LayerAnimationInfo.h" // For LayerAnimationInfo::sRecords
12 : #include "mozilla/StyleSetHandleInlines.h"
13 : #include "nsIFrame.h"
14 : #include "nsIPresShellInlines.h"
15 :
16 :
17 : namespace mozilla {
18 :
19 28 : RestyleManager::RestyleManager(StyleBackendType aType,
20 28 : nsPresContext* aPresContext)
21 : : mPresContext(aPresContext)
22 : , mRestyleGeneration(1)
23 : , mUndisplayedRestyleGeneration(1)
24 : , mHoverGeneration(0)
25 : , mType(aType)
26 : , mInStyleRefresh(false)
27 28 : , mAnimationGeneration(0)
28 : {
29 28 : MOZ_ASSERT(mPresContext);
30 28 : }
31 :
32 : void
33 19 : RestyleManager::ContentInserted(nsINode* aContainer, nsIContent* aChild)
34 : {
35 19 : RestyleForInsertOrChange(aContainer, aChild);
36 19 : }
37 :
38 : void
39 56 : RestyleManager::ContentAppended(nsIContent* aContainer, nsIContent* aFirstNewContent)
40 : {
41 56 : RestyleForAppend(aContainer, aFirstNewContent);
42 56 : }
43 :
44 : void
45 0 : RestyleManager::RestyleForEmptyChange(Element* aContainer)
46 : {
47 : // In some cases (:empty + E, :empty ~ E), a change in the content of
48 : // an element requires restyling its parent's siblings.
49 0 : nsRestyleHint hint = eRestyle_Subtree;
50 0 : nsIContent* grandparent = aContainer->GetParent();
51 0 : if (grandparent &&
52 0 : (grandparent->GetFlags() & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS)) {
53 0 : hint = nsRestyleHint(hint | eRestyle_LaterSiblings);
54 : }
55 0 : PostRestyleEvent(aContainer, hint, nsChangeHint(0));
56 0 : }
57 :
58 : void
59 56 : RestyleManager::RestyleForAppend(nsIContent* aContainer,
60 : nsIContent* aFirstNewContent)
61 : {
62 : // The container cannot be a document, but might be a ShadowRoot.
63 56 : if (!aContainer->IsElement()) {
64 0 : return;
65 : }
66 56 : Element* container = aContainer->AsElement();
67 :
68 : #ifdef DEBUG
69 : {
70 213 : for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) {
71 157 : NS_ASSERTION(!cur->IsRootOfAnonymousSubtree(),
72 : "anonymous nodes should not be in child lists");
73 : }
74 : }
75 : #endif
76 : uint32_t selectorFlags =
77 56 : container->GetFlags() & (NODE_ALL_SELECTOR_FLAGS &
78 56 : ~NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS);
79 56 : if (selectorFlags == 0)
80 56 : return;
81 :
82 0 : if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) {
83 : // see whether we need to restyle the container
84 0 : bool wasEmpty = true; // :empty or :-moz-only-whitespace
85 0 : for (nsIContent* cur = container->GetFirstChild();
86 0 : cur != aFirstNewContent;
87 0 : cur = cur->GetNextSibling()) {
88 : // We don't know whether we're testing :empty or :-moz-only-whitespace,
89 : // so be conservative and assume :-moz-only-whitespace (i.e., make
90 : // IsSignificantChild less likely to be true, and thus make us more
91 : // likely to restyle).
92 0 : if (nsStyleUtil::IsSignificantChild(cur, true, false)) {
93 0 : wasEmpty = false;
94 0 : break;
95 : }
96 : }
97 0 : if (wasEmpty) {
98 0 : RestyleForEmptyChange(container);
99 0 : return;
100 : }
101 : }
102 :
103 0 : if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
104 0 : PostRestyleEvent(container, eRestyle_Subtree, nsChangeHint(0));
105 : // Restyling the container is the most we can do here, so we're done.
106 0 : return;
107 : }
108 :
109 0 : if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
110 : // restyle the last element child before this node
111 0 : for (nsIContent* cur = aFirstNewContent->GetPreviousSibling();
112 0 : cur;
113 0 : cur = cur->GetPreviousSibling()) {
114 0 : if (cur->IsElement()) {
115 0 : PostRestyleEvent(cur->AsElement(), eRestyle_Subtree, nsChangeHint(0));
116 0 : break;
117 : }
118 : }
119 : }
120 : }
121 :
122 : // Needed since we can't use PostRestyleEvent on non-elements (with
123 : // eRestyle_LaterSiblings or nsRestyleHint(eRestyle_Subtree |
124 : // eRestyle_LaterSiblings) as appropriate).
125 : static void
126 6 : RestyleSiblingsStartingWith(RestyleManager* aRestyleManager,
127 : nsIContent* aStartingSibling /* may be null */)
128 : {
129 6 : for (nsIContent* sibling = aStartingSibling; sibling;
130 0 : sibling = sibling->GetNextSibling()) {
131 5 : if (sibling->IsElement()) {
132 : aRestyleManager->
133 5 : PostRestyleEvent(sibling->AsElement(),
134 : nsRestyleHint(eRestyle_Subtree | eRestyle_LaterSiblings),
135 5 : nsChangeHint(0));
136 5 : break;
137 : }
138 : }
139 6 : }
140 :
141 : // Restyling for a ContentInserted or CharacterDataChanged notification.
142 : // This could be used for ContentRemoved as well if we got the
143 : // notification before the removal happened (and sometimes
144 : // CharacterDataChanged is more like a removal than an addition).
145 : // The comments are written and variables are named in terms of it being
146 : // a ContentInserted notification.
147 : void
148 19 : RestyleManager::RestyleForInsertOrChange(nsINode* aContainer,
149 : nsIContent* aChild)
150 : {
151 : // The container might be a document or a ShadowRoot.
152 19 : if (!aContainer->IsElement()) {
153 0 : return;
154 : }
155 19 : Element* container = aContainer->AsElement();
156 :
157 19 : NS_ASSERTION(!aChild->IsRootOfAnonymousSubtree(),
158 : "anonymous nodes should not be in child lists");
159 19 : uint32_t selectorFlags = container->GetFlags() & NODE_ALL_SELECTOR_FLAGS;
160 19 : if (selectorFlags == 0)
161 14 : return;
162 :
163 5 : if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) {
164 : // see whether we need to restyle the container
165 0 : bool wasEmpty = true; // :empty or :-moz-only-whitespace
166 0 : for (nsIContent* child = container->GetFirstChild();
167 0 : child;
168 0 : child = child->GetNextSibling()) {
169 0 : if (child == aChild)
170 0 : continue;
171 : // We don't know whether we're testing :empty or :-moz-only-whitespace,
172 : // so be conservative and assume :-moz-only-whitespace (i.e., make
173 : // IsSignificantChild less likely to be true, and thus make us more
174 : // likely to restyle).
175 0 : if (nsStyleUtil::IsSignificantChild(child, true, false)) {
176 0 : wasEmpty = false;
177 0 : break;
178 : }
179 : }
180 0 : if (wasEmpty) {
181 0 : RestyleForEmptyChange(container);
182 0 : return;
183 : }
184 : }
185 :
186 5 : if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
187 0 : PostRestyleEvent(container, eRestyle_Subtree, nsChangeHint(0));
188 : // Restyling the container is the most we can do here, so we're done.
189 0 : return;
190 : }
191 :
192 5 : if (selectorFlags & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
193 : // Restyle all later siblings.
194 5 : RestyleSiblingsStartingWith(this, aChild->GetNextSibling());
195 : }
196 :
197 5 : if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
198 : // restyle the previously-first element child if it is after this node
199 0 : bool passedChild = false;
200 0 : for (nsIContent* content = container->GetFirstChild();
201 0 : content;
202 0 : content = content->GetNextSibling()) {
203 0 : if (content == aChild) {
204 0 : passedChild = true;
205 0 : continue;
206 : }
207 0 : if (content->IsElement()) {
208 0 : if (passedChild) {
209 0 : PostRestyleEvent(content->AsElement(), eRestyle_Subtree,
210 0 : nsChangeHint(0));
211 : }
212 0 : break;
213 : }
214 : }
215 : // restyle the previously-last element child if it is before this node
216 0 : passedChild = false;
217 0 : for (nsIContent* content = container->GetLastChild();
218 0 : content;
219 0 : content = content->GetPreviousSibling()) {
220 0 : if (content == aChild) {
221 0 : passedChild = true;
222 0 : continue;
223 : }
224 0 : if (content->IsElement()) {
225 0 : if (passedChild) {
226 0 : PostRestyleEvent(content->AsElement(), eRestyle_Subtree,
227 0 : nsChangeHint(0));
228 : }
229 0 : break;
230 : }
231 : }
232 : }
233 : }
234 :
235 : void
236 11 : RestyleManager::ContentRemoved(nsINode* aContainer,
237 : nsIContent* aOldChild,
238 : nsIContent* aFollowingSibling)
239 : {
240 : // The container might be a document or a ShadowRoot.
241 11 : if (!aContainer->IsElement()) {
242 0 : return;
243 : }
244 11 : Element* container = aContainer->AsElement();
245 :
246 11 : if (aOldChild->IsRootOfAnonymousSubtree()) {
247 : // This should be an assert, but this is called incorrectly in
248 : // HTMLEditor::DeleteRefToAnonymousNode and the assertions were clogging
249 : // up the logs. Make it an assert again when that's fixed.
250 0 : MOZ_ASSERT(aOldChild->GetProperty(nsGkAtoms::restylableAnonymousNode),
251 : "anonymous nodes should not be in child lists (bug 439258)");
252 : }
253 11 : uint32_t selectorFlags = container->GetFlags() & NODE_ALL_SELECTOR_FLAGS;
254 11 : if (selectorFlags == 0)
255 10 : return;
256 :
257 1 : if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) {
258 : // see whether we need to restyle the container
259 0 : bool isEmpty = true; // :empty or :-moz-only-whitespace
260 0 : for (nsIContent* child = container->GetFirstChild();
261 0 : child;
262 0 : child = child->GetNextSibling()) {
263 : // We don't know whether we're testing :empty or :-moz-only-whitespace,
264 : // so be conservative and assume :-moz-only-whitespace (i.e., make
265 : // IsSignificantChild less likely to be true, and thus make us more
266 : // likely to restyle).
267 0 : if (nsStyleUtil::IsSignificantChild(child, true, false)) {
268 0 : isEmpty = false;
269 0 : break;
270 : }
271 : }
272 0 : if (isEmpty) {
273 0 : RestyleForEmptyChange(container);
274 0 : return;
275 : }
276 : }
277 :
278 1 : if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
279 0 : PostRestyleEvent(container, eRestyle_Subtree, nsChangeHint(0));
280 : // Restyling the container is the most we can do here, so we're done.
281 0 : return;
282 : }
283 :
284 1 : if (selectorFlags & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
285 : // Restyle all later siblings.
286 1 : RestyleSiblingsStartingWith(this, aFollowingSibling);
287 : }
288 :
289 1 : if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
290 : // restyle the now-first element child if it was after aOldChild
291 0 : bool reachedFollowingSibling = false;
292 0 : for (nsIContent* content = container->GetFirstChild();
293 0 : content;
294 0 : content = content->GetNextSibling()) {
295 0 : if (content == aFollowingSibling) {
296 0 : reachedFollowingSibling = true;
297 : // do NOT continue here; we might want to restyle this node
298 : }
299 0 : if (content->IsElement()) {
300 0 : if (reachedFollowingSibling) {
301 0 : PostRestyleEvent(content->AsElement(), eRestyle_Subtree,
302 0 : nsChangeHint(0));
303 : }
304 0 : break;
305 : }
306 : }
307 : // restyle the now-last element child if it was before aOldChild
308 0 : reachedFollowingSibling = (aFollowingSibling == nullptr);
309 0 : for (nsIContent* content = container->GetLastChild();
310 0 : content;
311 0 : content = content->GetPreviousSibling()) {
312 0 : if (content->IsElement()) {
313 0 : if (reachedFollowingSibling) {
314 0 : PostRestyleEvent(content->AsElement(), eRestyle_Subtree, nsChangeHint(0));
315 : }
316 0 : break;
317 : }
318 0 : if (content == aFollowingSibling) {
319 0 : reachedFollowingSibling = true;
320 : }
321 : }
322 : }
323 : }
324 :
325 : /**
326 : * Calculates the change hint and the restyle hint for a given content state
327 : * change.
328 : *
329 : * This is called from both Restyle managers.
330 : */
331 : void
332 51 : RestyleManager::ContentStateChangedInternal(Element* aElement,
333 : EventStates aStateMask,
334 : nsChangeHint* aOutChangeHint,
335 : nsRestyleHint* aOutRestyleHint)
336 : {
337 51 : MOZ_ASSERT(!mInStyleRefresh);
338 51 : MOZ_ASSERT(aOutChangeHint);
339 51 : MOZ_ASSERT(aOutRestyleHint);
340 :
341 51 : StyleSetHandle styleSet = PresContext()->StyleSet();
342 51 : NS_ASSERTION(styleSet, "couldn't get style set");
343 :
344 51 : *aOutChangeHint = nsChangeHint(0);
345 : // Any change to a content state that affects which frames we construct
346 : // must lead to a frame reconstruct here if we already have a frame.
347 : // Note that we never decide through non-CSS means to not create frames
348 : // based on content states, so if we already don't have a frame we don't
349 : // need to force a reframe -- if it's needed, the HasStateDependentStyle
350 : // call will handle things.
351 51 : nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
352 51 : CSSPseudoElementType pseudoType = CSSPseudoElementType::NotPseudo;
353 51 : if (primaryFrame) {
354 : // If it's generated content, ignore LOADING/etc state changes on it.
355 225 : if (!primaryFrame->IsGeneratedContentFrame() &&
356 180 : aStateMask.HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN |
357 225 : NS_EVENT_STATE_USERDISABLED |
358 90 : NS_EVENT_STATE_SUPPRESSED |
359 135 : NS_EVENT_STATE_LOADING)) {
360 0 : *aOutChangeHint = nsChangeHint_ReconstructFrame;
361 : } else {
362 45 : uint8_t app = primaryFrame->StyleDisplay()->mAppearance;
363 45 : if (app) {
364 3 : nsITheme* theme = PresContext()->GetTheme();
365 6 : if (theme &&
366 3 : theme->ThemeSupportsWidget(PresContext(), primaryFrame, app)) {
367 3 : bool repaint = false;
368 3 : theme->WidgetStateChanged(primaryFrame, app, nullptr, &repaint,
369 6 : nullptr);
370 3 : if (repaint) {
371 0 : *aOutChangeHint |= nsChangeHint_RepaintFrame;
372 : }
373 : }
374 : }
375 : }
376 :
377 45 : pseudoType = primaryFrame->StyleContext()->GetPseudoType();
378 :
379 45 : primaryFrame->ContentStatesChanged(aStateMask);
380 : }
381 :
382 51 : if (pseudoType >= CSSPseudoElementType::Count) {
383 51 : *aOutRestyleHint = styleSet->HasStateDependentStyle(aElement, aStateMask);
384 0 : } else if (nsCSSPseudoElements::PseudoElementSupportsUserActionState(
385 : pseudoType)) {
386 : // If aElement is a pseudo-element, we want to check to see whether there
387 : // are any state-dependent rules applying to that pseudo.
388 : Element* ancestor =
389 0 : ElementForStyleContext(nullptr, primaryFrame, pseudoType);
390 0 : *aOutRestyleHint = styleSet->HasStateDependentStyle(ancestor, pseudoType,
391 : aElement, aStateMask);
392 : } else {
393 0 : *aOutRestyleHint = nsRestyleHint(0);
394 : }
395 :
396 51 : if (aStateMask.HasState(NS_EVENT_STATE_HOVER) && *aOutRestyleHint != 0) {
397 0 : IncrementHoverGeneration();
398 : }
399 :
400 51 : if (aStateMask.HasState(NS_EVENT_STATE_VISITED)) {
401 : // Exposing information to the page about whether the link is
402 : // visited or not isn't really something we can worry about here.
403 : // FIXME: We could probably do this a bit better.
404 0 : *aOutChangeHint |= nsChangeHint_RepaintFrame;
405 : }
406 51 : }
407 :
408 : /* static */ nsCString
409 0 : RestyleManager::RestyleHintToString(nsRestyleHint aHint)
410 : {
411 0 : nsCString result;
412 0 : bool any = false;
413 : const char* names[] = {
414 : "Self", "SomeDescendants", "Subtree", "LaterSiblings", "CSSTransitions",
415 : "CSSAnimations", "StyleAttribute", "StyleAttribute_Animations",
416 : "Force", "ForceDescendants"
417 0 : };
418 0 : uint32_t hint = aHint & ((1 << ArrayLength(names)) - 1);
419 0 : uint32_t rest = aHint & ~((1 << ArrayLength(names)) - 1);
420 0 : for (uint32_t i = 0; i < ArrayLength(names); i++) {
421 0 : if (hint & (1 << i)) {
422 0 : if (any) {
423 0 : result.AppendLiteral(" | ");
424 : }
425 0 : result.AppendPrintf("eRestyle_%s", names[i]);
426 0 : any = true;
427 : }
428 : }
429 0 : if (rest) {
430 0 : if (any) {
431 0 : result.AppendLiteral(" | ");
432 : }
433 0 : result.AppendPrintf("0x%0x", rest);
434 : } else {
435 0 : if (!any) {
436 0 : result.AppendLiteral("0");
437 : }
438 : }
439 0 : return result;
440 : }
441 :
442 : #ifdef DEBUG
443 : /* static */ nsCString
444 0 : RestyleManager::ChangeHintToString(nsChangeHint aHint)
445 : {
446 0 : nsCString result;
447 0 : bool any = false;
448 : const char* names[] = {
449 : "RepaintFrame", "NeedReflow", "ClearAncestorIntrinsics",
450 : "ClearDescendantIntrinsics", "NeedDirtyReflow", "SyncFrameView",
451 : "UpdateCursor", "UpdateEffects", "UpdateOpacityLayer",
452 : "UpdateTransformLayer", "ReconstructFrame", "UpdateOverflow",
453 : "UpdateSubtreeOverflow", "UpdatePostTransformOverflow",
454 : "UpdateParentOverflow",
455 : "ChildrenOnlyTransform", "RecomputePosition", "UpdateContainingBlock",
456 : "BorderStyleNoneChange", "UpdateTextPath", "SchedulePaint",
457 : "NeutralChange", "InvalidateRenderingObservers",
458 : "ReflowChangesSizeOrPosition", "UpdateComputedBSize",
459 : "UpdateUsesOpacity", "UpdateBackgroundPosition",
460 : "AddOrRemoveTransform", "CSSOverflowChange",
461 : "UpdateWidgetProperties"
462 0 : };
463 : static_assert(nsChangeHint_AllHints == (1 << ArrayLength(names)) - 1,
464 : "Name list doesn't match change hints.");
465 0 : uint32_t hint = aHint & ((1 << ArrayLength(names)) - 1);
466 0 : uint32_t rest = aHint & ~((1 << ArrayLength(names)) - 1);
467 0 : if ((hint & NS_STYLE_HINT_REFLOW) == NS_STYLE_HINT_REFLOW) {
468 0 : result.AppendLiteral("NS_STYLE_HINT_REFLOW");
469 0 : hint = hint & ~NS_STYLE_HINT_REFLOW;
470 0 : any = true;
471 0 : } else if ((hint & nsChangeHint_AllReflowHints) == nsChangeHint_AllReflowHints) {
472 0 : result.AppendLiteral("nsChangeHint_AllReflowHints");
473 0 : hint = hint & ~nsChangeHint_AllReflowHints;
474 0 : any = true;
475 0 : } else if ((hint & NS_STYLE_HINT_VISUAL) == NS_STYLE_HINT_VISUAL) {
476 0 : result.AppendLiteral("NS_STYLE_HINT_VISUAL");
477 0 : hint = hint & ~NS_STYLE_HINT_VISUAL;
478 0 : any = true;
479 : }
480 0 : for (uint32_t i = 0; i < ArrayLength(names); i++) {
481 0 : if (hint & (1 << i)) {
482 0 : if (any) {
483 0 : result.AppendLiteral(" | ");
484 : }
485 0 : result.AppendPrintf("nsChangeHint_%s", names[i]);
486 0 : any = true;
487 : }
488 : }
489 0 : if (rest) {
490 0 : if (any) {
491 0 : result.AppendLiteral(" | ");
492 : }
493 0 : result.AppendPrintf("0x%0x", rest);
494 : } else {
495 0 : if (!any) {
496 0 : result.AppendLiteral("nsChangeHint(0)");
497 : }
498 : }
499 0 : return result;
500 : }
501 : #endif
502 :
503 : /**
504 : * Frame construction helpers follow.
505 : */
506 : #ifdef DEBUG
507 : static bool gInApplyRenderingChangeToTree = false;
508 : #endif
509 :
510 : #ifdef DEBUG
511 : static void
512 0 : DumpContext(nsIFrame* aFrame, nsStyleContext* aContext)
513 : {
514 0 : if (aFrame) {
515 0 : fputs("frame: ", stdout);
516 0 : nsAutoString name;
517 0 : aFrame->GetFrameName(name);
518 0 : fputs(NS_LossyConvertUTF16toASCII(name).get(), stdout);
519 0 : fprintf(stdout, " (%p)", static_cast<void*>(aFrame));
520 : }
521 0 : if (aContext) {
522 0 : fprintf(stdout, " style: %p ", static_cast<void*>(aContext));
523 :
524 0 : nsIAtom* pseudoTag = aContext->GetPseudo();
525 0 : if (pseudoTag) {
526 0 : nsAutoString buffer;
527 0 : pseudoTag->ToString(buffer);
528 0 : fputs(NS_LossyConvertUTF16toASCII(buffer).get(), stdout);
529 0 : fputs(" ", stdout);
530 : }
531 0 : fputs("{}\n", stdout);
532 : }
533 0 : }
534 :
535 : static void
536 0 : VerifySameTree(nsStyleContext* aContext1, nsStyleContext* aContext2)
537 : {
538 0 : nsStyleContext* top1 = aContext1;
539 0 : nsStyleContext* top2 = aContext2;
540 : nsStyleContext* parent;
541 : for (;;) {
542 0 : parent = top1->GetParent();
543 0 : if (!parent)
544 0 : break;
545 0 : top1 = parent;
546 : }
547 : for (;;) {
548 0 : parent = top2->GetParent();
549 0 : if (!parent)
550 0 : break;
551 0 : top2 = parent;
552 : }
553 0 : NS_ASSERTION(top1 == top2,
554 : "Style contexts are not in the same style context tree");
555 0 : }
556 :
557 : static void
558 427 : VerifyContextParent(nsIFrame* aFrame, nsStyleContext* aContext,
559 : nsStyleContext* aParentContext)
560 : {
561 : // get the contexts not provided
562 427 : if (!aContext) {
563 13 : aContext = aFrame->StyleContext();
564 : }
565 :
566 427 : if (!aParentContext) {
567 : nsIFrame* providerFrame;
568 427 : aParentContext = aFrame->GetParentStyleContext(&providerFrame);
569 : // aParentContext could still be null
570 : }
571 :
572 427 : NS_ASSERTION(aContext, "Failure to get required contexts");
573 427 : nsStyleContext* actualParentContext = aContext->GetParent();
574 :
575 427 : if (aParentContext) {
576 414 : if (aParentContext != actualParentContext) {
577 0 : DumpContext(aFrame, aContext);
578 0 : if (aContext == aParentContext) {
579 0 : NS_ERROR("Using parent's style context");
580 : } else {
581 0 : NS_ERROR("Wrong parent style context");
582 0 : fputs("Wrong parent style context: ", stdout);
583 0 : DumpContext(nullptr, actualParentContext);
584 0 : fputs("should be using: ", stdout);
585 0 : DumpContext(nullptr, aParentContext);
586 0 : VerifySameTree(actualParentContext, aParentContext);
587 0 : fputs("\n", stdout);
588 : }
589 : }
590 :
591 : } else {
592 13 : if (actualParentContext) {
593 0 : NS_ERROR("Have parent context and shouldn't");
594 0 : DumpContext(aFrame, aContext);
595 0 : fputs("Has parent context: ", stdout);
596 0 : DumpContext(nullptr, actualParentContext);
597 0 : fputs("Should be null\n\n", stdout);
598 : }
599 : }
600 :
601 427 : nsStyleContext* childStyleIfVisited = aContext->GetStyleIfVisited();
602 : // Either childStyleIfVisited has aContext->GetParent()->GetStyleIfVisited()
603 : // as the parent or it has a different rulenode from aContext _and_ has
604 : // aContext->GetParent() as the parent.
605 427 : if (childStyleIfVisited &&
606 0 : !((childStyleIfVisited->RuleNode() != aContext->RuleNode() &&
607 0 : childStyleIfVisited->GetParent() == aContext->GetParent()) ||
608 0 : childStyleIfVisited->GetParent() ==
609 0 : aContext->GetParent()->GetStyleIfVisited())) {
610 0 : NS_ERROR("Visited style has wrong parent");
611 0 : DumpContext(aFrame, aContext);
612 0 : fputs("\n", stdout);
613 : }
614 427 : }
615 :
616 : static void
617 414 : VerifyStyleTree(nsIFrame* aFrame)
618 : {
619 414 : nsStyleContext* context = aFrame->StyleContext();
620 414 : VerifyContextParent(aFrame, context, nullptr);
621 :
622 828 : nsIFrame::ChildListIterator lists(aFrame);
623 864 : for (; !lists.IsDone(); lists.Next()) {
624 547 : for (nsIFrame* child : lists.CurrentList()) {
625 322 : if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
626 : // only do frames that are in flow
627 322 : if (child->IsPlaceholderFrame()) {
628 : // placeholder: first recurse and verify the out of flow frame,
629 : // then verify the placeholder's context
630 : nsIFrame* outOfFlowFrame =
631 13 : nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
632 :
633 : // recurse to out of flow frame, letting the parent context get resolved
634 13 : do {
635 13 : VerifyStyleTree(outOfFlowFrame);
636 13 : } while ((outOfFlowFrame = outOfFlowFrame->GetNextContinuation()));
637 :
638 : // verify placeholder using the parent frame's context as
639 : // parent context
640 13 : VerifyContextParent(child, nullptr, nullptr);
641 : } else { // regular frame
642 309 : VerifyStyleTree(child);
643 : }
644 : }
645 : }
646 : }
647 :
648 : // do additional contexts
649 414 : int32_t contextIndex = 0;
650 414 : for (nsStyleContext* extraContext;
651 414 : (extraContext = aFrame->GetAdditionalStyleContext(contextIndex));
652 : ++contextIndex) {
653 0 : VerifyContextParent(aFrame, extraContext, context);
654 : }
655 414 : }
656 :
657 : void
658 92 : RestyleManager::DebugVerifyStyleTree(nsIFrame* aFrame)
659 : {
660 92 : if (IsServo()) {
661 : // XXXheycam For now, we know that we don't use the same inheritance
662 : // hierarchy for certain cases, so just skip these assertions until
663 : // we work out what we want to assert (bug 1322570).
664 0 : return;
665 : }
666 92 : if (aFrame) {
667 92 : VerifyStyleTree(aFrame);
668 : }
669 : }
670 :
671 : #endif // DEBUG
672 :
673 : /**
674 : * Sync views on aFrame and all of aFrame's descendants (following placeholders),
675 : * if aChange has nsChangeHint_SyncFrameView.
676 : * Calls DoApplyRenderingChangeToTree on all aFrame's out-of-flow descendants
677 : * (following placeholders), if aChange has nsChangeHint_RepaintFrame.
678 : * aFrame should be some combination of nsChangeHint_SyncFrameView,
679 : * nsChangeHint_RepaintFrame, nsChangeHint_UpdateOpacityLayer and
680 : * nsChangeHint_SchedulePaint, nothing else.
681 : */
682 : static void SyncViewsAndInvalidateDescendants(nsIFrame* aFrame,
683 : nsChangeHint aChange);
684 :
685 : static void StyleChangeReflow(nsIFrame* aFrame, nsChangeHint aHint);
686 :
687 : /**
688 : * To handle nsChangeHint_ChildrenOnlyTransform we must iterate over the child
689 : * frames of the SVG frame concerned. This helper function is used to find that
690 : * SVG frame when we encounter nsChangeHint_ChildrenOnlyTransform to ensure
691 : * that we iterate over the intended children, since sometimes we end up
692 : * handling that hint while processing hints for one of the SVG frame's
693 : * ancestor frames.
694 : *
695 : * The reason that we sometimes end up trying to process the hint for an
696 : * ancestor of the SVG frame that the hint is intended for is due to the way we
697 : * process restyle events. ApplyRenderingChangeToTree adjusts the frame from
698 : * the restyled element's principle frame to one of its ancestor frames based
699 : * on what nsCSSRendering::FindBackground returns, since the background style
700 : * may have been propagated up to an ancestor frame. Processing hints using an
701 : * ancestor frame is fine in general, but nsChangeHint_ChildrenOnlyTransform is
702 : * a special case since it is intended to update the children of a specific
703 : * frame.
704 : */
705 : static nsIFrame*
706 0 : GetFrameForChildrenOnlyTransformHint(nsIFrame* aFrame)
707 : {
708 0 : if (aFrame->IsViewportFrame()) {
709 : // This happens if the root-<svg> is fixed positioned, in which case we
710 : // can't use aFrame->GetContent() to find the primary frame, since
711 : // GetContent() returns nullptr for ViewportFrame.
712 0 : aFrame = aFrame->PrincipalChildList().FirstChild();
713 : }
714 : // For an nsHTMLScrollFrame, this will get the SVG frame that has the
715 : // children-only transforms:
716 0 : aFrame = aFrame->GetContent()->GetPrimaryFrame();
717 0 : if (aFrame->IsSVGOuterSVGFrame()) {
718 0 : aFrame = aFrame->PrincipalChildList().FirstChild();
719 0 : MOZ_ASSERT(aFrame->IsSVGOuterSVGAnonChildFrame(),
720 : "Where is the nsSVGOuterSVGFrame's anon child??");
721 : }
722 0 : MOZ_ASSERT(aFrame->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer),
723 : "Children-only transforms only expected on SVG frames");
724 0 : return aFrame;
725 : }
726 :
727 : // Returns true if this function managed to successfully move a frame, and
728 : // false if it could not process the position change, and a reflow should
729 : // be performed instead.
730 : bool
731 0 : RecomputePosition(nsIFrame* aFrame)
732 : {
733 : // Don't process position changes on table frames, since we already handle
734 : // the dynamic position change on the table wrapper frame, and the
735 : // reflow-based fallback code path also ignores positions on inner table
736 : // frames.
737 0 : if (aFrame->IsTableFrame()) {
738 0 : return true;
739 : }
740 :
741 0 : const nsStyleDisplay* display = aFrame->StyleDisplay();
742 : // Changes to the offsets of a non-positioned element can safely be ignored.
743 0 : if (display->mPosition == NS_STYLE_POSITION_STATIC) {
744 0 : return true;
745 : }
746 :
747 : // Don't process position changes on frames which have views or the ones which
748 : // have a view somewhere in their descendants, because the corresponding view
749 : // needs to be repositioned properly as well.
750 0 : if (aFrame->HasView() ||
751 0 : (aFrame->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW)) {
752 0 : StyleChangeReflow(aFrame, nsChangeHint_NeedReflow);
753 0 : return false;
754 : }
755 :
756 0 : aFrame->SchedulePaint();
757 :
758 : // For relative positioning, we can simply update the frame rect
759 0 : if (display->IsRelativelyPositionedStyle()) {
760 : // Move the frame
761 0 : if (display->mPosition == NS_STYLE_POSITION_STICKY) {
762 0 : if (display->IsInnerTableStyle()) {
763 : // We don't currently support sticky positioning of inner table
764 : // elements (bug 975644). Bail.
765 : //
766 : // When this is fixed, remove the null-check for the computed
767 : // offsets in nsTableRowFrame::ReflowChildren.
768 0 : return true;
769 : }
770 :
771 : // Update sticky positioning for an entire element at once, starting with
772 : // the first continuation or ib-split sibling.
773 : // It's rare that the frame we already have isn't already the first
774 : // continuation or ib-split sibling, but it can happen when styles differ
775 : // across continuations such as ::first-line or ::first-letter, and in
776 : // those cases we will generally (but maybe not always) do the work twice.
777 : nsIFrame* firstContinuation =
778 0 : nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
779 :
780 0 : StickyScrollContainer::ComputeStickyOffsets(firstContinuation);
781 : StickyScrollContainer* ssc =
782 : StickyScrollContainer::GetStickyScrollContainerForFrame(
783 0 : firstContinuation);
784 0 : if (ssc) {
785 0 : ssc->PositionContinuations(firstContinuation);
786 : }
787 : } else {
788 0 : MOZ_ASSERT(NS_STYLE_POSITION_RELATIVE == display->mPosition,
789 : "Unexpected type of positioning");
790 0 : for (nsIFrame* cont = aFrame; cont;
791 : cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
792 0 : nsIFrame* cb = cont->GetContainingBlock();
793 0 : nsMargin newOffsets;
794 0 : WritingMode wm = cb->GetWritingMode();
795 0 : const LogicalSize size(wm, cb->GetContentRectRelativeToSelf().Size());
796 :
797 0 : ReflowInput::ComputeRelativeOffsets(wm, cont, size, newOffsets);
798 0 : NS_ASSERTION(newOffsets.left == -newOffsets.right &&
799 : newOffsets.top == -newOffsets.bottom,
800 : "ComputeRelativeOffsets should return valid results");
801 :
802 : // ReflowInput::ApplyRelativePositioning would work here, but
803 : // since we've already checked mPosition and aren't changing the frame's
804 : // normal position, go ahead and add the offsets directly.
805 : // First, we need to ensure that the normal position is stored though.
806 : bool hasProperty;
807 0 : nsPoint normalPosition = cont->GetNormalPosition(&hasProperty);
808 0 : if (!hasProperty) {
809 0 : cont->AddProperty(nsIFrame::NormalPositionProperty(),
810 0 : new nsPoint(normalPosition));
811 : }
812 0 : cont->SetPosition(normalPosition +
813 0 : nsPoint(newOffsets.left, newOffsets.top));
814 : }
815 : }
816 :
817 0 : return true;
818 : }
819 :
820 : // For the absolute positioning case, set up a fake HTML reflow state for
821 : // the frame, and then get the offsets and size from it. If the frame's size
822 : // doesn't need to change, we can simply update the frame position. Otherwise
823 : // we fall back to a reflow.
824 : RefPtr<gfxContext> rc =
825 0 : aFrame->PresContext()->PresShell()->CreateReferenceRenderingContext();
826 :
827 : // Construct a bogus parent reflow state so that there's a usable
828 : // containing block reflow state.
829 0 : nsIFrame* parentFrame = aFrame->GetParent();
830 0 : WritingMode parentWM = parentFrame->GetWritingMode();
831 0 : WritingMode frameWM = aFrame->GetWritingMode();
832 0 : LogicalSize parentSize = parentFrame->GetLogicalSize();
833 :
834 0 : nsFrameState savedState = parentFrame->GetStateBits();
835 : ReflowInput parentReflowInput(aFrame->PresContext(), parentFrame, rc,
836 0 : parentSize);
837 0 : parentFrame->RemoveStateBits(~nsFrameState(0));
838 0 : parentFrame->AddStateBits(savedState);
839 :
840 : // The bogus parent state here was created with no parent state of its own,
841 : // and therefore it won't have an mCBReflowInput set up.
842 : // But we may need one (for InitCBReflowInput in a child state), so let's
843 : // try to create one here for the cases where it will be needed.
844 0 : Maybe<ReflowInput> cbReflowInput;
845 0 : nsIFrame* cbFrame = parentFrame->GetContainingBlock();
846 0 : if (cbFrame && (aFrame->GetContainingBlock() != parentFrame ||
847 0 : parentFrame->IsTableFrame())) {
848 0 : LogicalSize cbSize = cbFrame->GetLogicalSize();
849 0 : cbReflowInput.emplace(cbFrame->PresContext(), cbFrame, rc, cbSize);
850 0 : cbReflowInput->ComputedPhysicalMargin() = cbFrame->GetUsedMargin();
851 0 : cbReflowInput->ComputedPhysicalPadding() = cbFrame->GetUsedPadding();
852 0 : cbReflowInput->ComputedPhysicalBorderPadding() =
853 0 : cbFrame->GetUsedBorderAndPadding();
854 0 : parentReflowInput.mCBReflowInput = cbReflowInput.ptr();
855 : }
856 :
857 0 : NS_WARNING_ASSERTION(parentSize.ISize(parentWM) != NS_INTRINSICSIZE &&
858 : parentSize.BSize(parentWM) != NS_INTRINSICSIZE,
859 : "parentSize should be valid");
860 0 : parentReflowInput.SetComputedISize(std::max(parentSize.ISize(parentWM), 0));
861 0 : parentReflowInput.SetComputedBSize(std::max(parentSize.BSize(parentWM), 0));
862 0 : parentReflowInput.ComputedPhysicalMargin().SizeTo(0, 0, 0, 0);
863 :
864 0 : parentReflowInput.ComputedPhysicalPadding() = parentFrame->GetUsedPadding();
865 0 : parentReflowInput.ComputedPhysicalBorderPadding() =
866 0 : parentFrame->GetUsedBorderAndPadding();
867 0 : LogicalSize availSize = parentSize.ConvertTo(frameWM, parentWM);
868 0 : availSize.BSize(frameWM) = NS_INTRINSICSIZE;
869 :
870 0 : ViewportFrame* viewport = do_QueryFrame(parentFrame);
871 : nsSize cbSize = viewport ?
872 0 : viewport->AdjustReflowInputAsContainingBlock(&parentReflowInput).Size()
873 0 : : aFrame->GetContainingBlock()->GetSize();
874 : const nsMargin& parentBorder =
875 0 : parentReflowInput.mStyleBorder->GetComputedBorder();
876 0 : cbSize -= nsSize(parentBorder.LeftRight(), parentBorder.TopBottom());
877 0 : LogicalSize lcbSize(frameWM, cbSize);
878 : ReflowInput reflowInput(aFrame->PresContext(), parentReflowInput, aFrame,
879 0 : availSize, &lcbSize);
880 0 : nsSize computedSize(reflowInput.ComputedWidth(),
881 0 : reflowInput.ComputedHeight());
882 0 : computedSize.width += reflowInput.ComputedPhysicalBorderPadding().LeftRight();
883 0 : if (computedSize.height != NS_INTRINSICSIZE) {
884 0 : computedSize.height +=
885 0 : reflowInput.ComputedPhysicalBorderPadding().TopBottom();
886 : }
887 0 : nsSize size = aFrame->GetSize();
888 : // The RecomputePosition hint is not used if any offset changed between auto
889 : // and non-auto. If computedSize.height == NS_INTRINSICSIZE then the new
890 : // element height will be its intrinsic height, and since 'top' and 'bottom''s
891 : // auto-ness hasn't changed, the old height must also be its intrinsic
892 : // height, which we can assume hasn't changed (or reflow would have
893 : // been triggered).
894 0 : if (computedSize.width == size.width &&
895 0 : (computedSize.height == NS_INTRINSICSIZE || computedSize.height == size.height)) {
896 : // If we're solving for 'left' or 'top', then compute it here, in order to
897 : // match the reflow code path.
898 0 : if (NS_AUTOOFFSET == reflowInput.ComputedPhysicalOffsets().left) {
899 0 : reflowInput.ComputedPhysicalOffsets().left = cbSize.width -
900 0 : reflowInput.ComputedPhysicalOffsets().right -
901 0 : reflowInput.ComputedPhysicalMargin().right -
902 0 : size.width -
903 0 : reflowInput.ComputedPhysicalMargin().left;
904 : }
905 :
906 0 : if (NS_AUTOOFFSET == reflowInput.ComputedPhysicalOffsets().top) {
907 0 : reflowInput.ComputedPhysicalOffsets().top = cbSize.height -
908 0 : reflowInput.ComputedPhysicalOffsets().bottom -
909 0 : reflowInput.ComputedPhysicalMargin().bottom -
910 0 : size.height -
911 0 : reflowInput.ComputedPhysicalMargin().top;
912 : }
913 :
914 : // Move the frame
915 0 : nsPoint pos(parentBorder.left + reflowInput.ComputedPhysicalOffsets().left +
916 0 : reflowInput.ComputedPhysicalMargin().left,
917 0 : parentBorder.top + reflowInput.ComputedPhysicalOffsets().top +
918 0 : reflowInput.ComputedPhysicalMargin().top);
919 0 : aFrame->SetPosition(pos);
920 :
921 0 : return true;
922 : }
923 :
924 : // Fall back to a reflow
925 0 : StyleChangeReflow(aFrame, nsChangeHint_NeedReflow);
926 0 : return false;
927 : }
928 :
929 : static bool
930 0 : HasBoxAncestor(nsIFrame* aFrame)
931 : {
932 0 : for (nsIFrame* f = aFrame; f; f = f->GetParent()) {
933 0 : if (f->IsXULBoxFrame()) {
934 0 : return true;
935 : }
936 : }
937 0 : return false;
938 : }
939 :
940 : /**
941 : * Return true if aFrame's subtree has placeholders for out-of-flow content
942 : * whose 'position' style's bit in aPositionMask is set.
943 : */
944 : static bool
945 2 : FrameHasPositionedPlaceholderDescendants(nsIFrame* aFrame,
946 : uint32_t aPositionMask)
947 : {
948 2 : MOZ_ASSERT(aPositionMask & (1 << NS_STYLE_POSITION_FIXED));
949 :
950 4 : for (nsIFrame::ChildListIterator lists(aFrame); !lists.IsDone(); lists.Next()) {
951 0 : for (nsIFrame* f : lists.CurrentList()) {
952 0 : if (f->IsPlaceholderFrame()) {
953 : nsIFrame* outOfFlow =
954 0 : nsPlaceholderFrame::GetRealFrameForPlaceholder(f);
955 : // If SVG text frames could appear here, they could confuse us since
956 : // they ignore their position style ... but they can't.
957 0 : NS_ASSERTION(!nsSVGUtils::IsInSVGTextSubtree(outOfFlow),
958 : "SVG text frames can't be out of flow");
959 0 : if (aPositionMask & (1 << outOfFlow->StyleDisplay()->mPosition)) {
960 0 : return true;
961 : }
962 : }
963 0 : uint32_t positionMask = aPositionMask;
964 : // NOTE: It's tempting to check f->IsAbsPosContainingBlock() or
965 : // f->IsFixedPosContainingBlock() here. However, that would only
966 : // be testing the *new* style of the frame, which might exclude
967 : // descendants that currently have this frame as an abs-pos
968 : // containing block. Taking the codepath where we don't reframe
969 : // could lead to an unsafe call to
970 : // cont->MarkAsNotAbsoluteContainingBlock() before we've reframed
971 : // the descendant and taken it off the absolute list.
972 0 : if (FrameHasPositionedPlaceholderDescendants(f, positionMask)) {
973 0 : return true;
974 : }
975 : }
976 : }
977 2 : return false;
978 : }
979 :
980 : static bool
981 2 : NeedToReframeForAddingOrRemovingTransform(nsIFrame* aFrame)
982 : {
983 : static_assert(0 <= NS_STYLE_POSITION_ABSOLUTE &&
984 : NS_STYLE_POSITION_ABSOLUTE < 32, "Style constant out of range");
985 : static_assert(0 <= NS_STYLE_POSITION_FIXED &&
986 : NS_STYLE_POSITION_FIXED < 32, "Style constant out of range");
987 :
988 : uint32_t positionMask;
989 : // Don't call aFrame->IsPositioned here, since that returns true if
990 : // the frame already has a transform, and we want to ignore that here
991 2 : if (aFrame->IsAbsolutelyPositioned() || aFrame->IsRelativelyPositioned()) {
992 : // This frame is a container for abs-pos descendants whether or not it
993 : // has a transform.
994 : // So abs-pos descendants are no problem; we only need to reframe if
995 : // we have fixed-pos descendants.
996 0 : positionMask = 1 << NS_STYLE_POSITION_FIXED;
997 : } else {
998 : // This frame may not be a container for abs-pos descendants already.
999 : // So reframe if we have abs-pos or fixed-pos descendants.
1000 2 : positionMask =
1001 : (1 << NS_STYLE_POSITION_FIXED) | (1 << NS_STYLE_POSITION_ABSOLUTE);
1002 : }
1003 4 : for (nsIFrame* f = aFrame; f;
1004 : f = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(f)) {
1005 2 : if (FrameHasPositionedPlaceholderDescendants(f, positionMask)) {
1006 0 : return true;
1007 : }
1008 : }
1009 2 : return false;
1010 : }
1011 :
1012 : /* static */ nsIFrame*
1013 0 : RestyleManager::GetNearestAncestorFrame(nsIContent* aContent)
1014 : {
1015 0 : nsIFrame* ancestorFrame = nullptr;
1016 0 : for (nsIContent* ancestor = aContent->GetParent();
1017 0 : ancestor && !ancestorFrame;
1018 0 : ancestor = ancestor->GetParent()) {
1019 0 : ancestorFrame = ancestor->GetPrimaryFrame();
1020 : }
1021 0 : return ancestorFrame;
1022 : }
1023 :
1024 : /* static */ nsIFrame*
1025 61 : RestyleManager::GetNextBlockInInlineSibling(nsIFrame* aFrame)
1026 : {
1027 61 : NS_ASSERTION(!aFrame->GetPrevContinuation(),
1028 : "must start with the first continuation");
1029 : // Might we have ib-split siblings?
1030 61 : if (!(aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
1031 : // nothing more to do here
1032 61 : return nullptr;
1033 : }
1034 :
1035 0 : return aFrame->GetProperty(nsIFrame::IBSplitSibling());
1036 : }
1037 :
1038 : static void
1039 60 : DoApplyRenderingChangeToTree(nsIFrame* aFrame,
1040 : nsChangeHint aChange)
1041 : {
1042 60 : NS_PRECONDITION(gInApplyRenderingChangeToTree,
1043 : "should only be called within ApplyRenderingChangeToTree");
1044 :
1045 180 : for ( ; aFrame; aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame)) {
1046 : // Invalidate and sync views on all descendant frames, following placeholders.
1047 : // We don't need to update transforms in SyncViewsAndInvalidateDescendants, because
1048 : // there can't be any out-of-flows or popups that need to be transformed;
1049 : // all out-of-flow descendants of the transformed element must also be
1050 : // descendants of the transformed frame.
1051 60 : SyncViewsAndInvalidateDescendants(aFrame,
1052 : nsChangeHint(aChange & (nsChangeHint_RepaintFrame |
1053 : nsChangeHint_SyncFrameView |
1054 : nsChangeHint_UpdateOpacityLayer |
1055 60 : nsChangeHint_SchedulePaint)));
1056 : // This must be set to true if the rendering change needs to
1057 : // invalidate content. If it's false, a composite-only paint
1058 : // (empty transaction) will be scheduled.
1059 60 : bool needInvalidatingPaint = false;
1060 :
1061 : // if frame has view, will already be invalidated
1062 60 : if (aChange & nsChangeHint_RepaintFrame) {
1063 : // Note that this whole block will be skipped when painting is suppressed
1064 : // (due to our caller ApplyRendingChangeToTree() discarding the
1065 : // nsChangeHint_RepaintFrame hint). If you add handling for any other
1066 : // hints within this block, be sure that they too should be ignored when
1067 : // painting is suppressed.
1068 27 : needInvalidatingPaint = true;
1069 27 : aFrame->InvalidateFrameSubtree();
1070 54 : if ((aChange & nsChangeHint_UpdateEffects) &&
1071 27 : aFrame->IsFrameOfType(nsIFrame::eSVG) &&
1072 0 : !(aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)) {
1073 : // Need to update our overflow rects:
1074 0 : nsSVGUtils::ScheduleReflowSVG(aFrame);
1075 : }
1076 : }
1077 60 : if (aChange & nsChangeHint_UpdateTextPath) {
1078 0 : if (nsSVGUtils::IsInSVGTextSubtree(aFrame)) {
1079 : // Invalidate and reflow the entire SVGTextFrame:
1080 0 : NS_ASSERTION(aFrame->GetContent()->IsSVGElement(nsGkAtoms::textPath),
1081 : "expected frame for a <textPath> element");
1082 : nsIFrame* text = nsLayoutUtils::GetClosestFrameOfType(
1083 0 : aFrame, LayoutFrameType::SVGText);
1084 0 : NS_ASSERTION(text, "expected to find an ancestor SVGTextFrame");
1085 0 : static_cast<SVGTextFrame*>(text)->NotifyGlyphMetricsChange();
1086 : } else {
1087 0 : MOZ_ASSERT(false, "unexpected frame got nsChangeHint_UpdateTextPath");
1088 : }
1089 : }
1090 60 : if (aChange & nsChangeHint_UpdateOpacityLayer) {
1091 : // FIXME/bug 796697: we can get away with empty transactions for
1092 : // opacity updates in many cases.
1093 21 : needInvalidatingPaint = true;
1094 :
1095 21 : ActiveLayerTracker::NotifyRestyle(aFrame, eCSSProperty_opacity);
1096 21 : if (nsSVGIntegrationUtils::UsingEffectsForFrame(aFrame)) {
1097 : // SVG effects paints the opacity without using
1098 : // nsDisplayOpacity. We need to invalidate manually.
1099 0 : aFrame->InvalidateFrameSubtree();
1100 : }
1101 : }
1102 60 : if ((aChange & nsChangeHint_UpdateTransformLayer) &&
1103 0 : aFrame->IsTransformed()) {
1104 0 : ActiveLayerTracker::NotifyRestyle(aFrame, eCSSProperty_transform);
1105 : // If we're not already going to do an invalidating paint, see
1106 : // if we can get away with only updating the transform on a
1107 : // layer for this frame, and not scheduling an invalidating
1108 : // paint.
1109 0 : if (!needInvalidatingPaint) {
1110 : Layer* layer;
1111 0 : needInvalidatingPaint |= !aFrame->TryUpdateTransformOnly(&layer);
1112 :
1113 0 : if (!needInvalidatingPaint) {
1114 : // Since we're not going to paint, we need to resend animation
1115 : // data to the layer.
1116 0 : MOZ_ASSERT(layer, "this can't happen if there's no layer");
1117 : nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(
1118 0 : layer, nullptr, nullptr, aFrame, eCSSProperty_transform);
1119 : }
1120 : }
1121 : }
1122 60 : if (aChange & nsChangeHint_ChildrenOnlyTransform) {
1123 0 : needInvalidatingPaint = true;
1124 : nsIFrame* childFrame =
1125 0 : GetFrameForChildrenOnlyTransformHint(aFrame)->PrincipalChildList().FirstChild();
1126 0 : for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
1127 0 : ActiveLayerTracker::NotifyRestyle(childFrame, eCSSProperty_transform);
1128 : }
1129 : }
1130 60 : if (aChange & nsChangeHint_SchedulePaint) {
1131 30 : needInvalidatingPaint = true;
1132 : }
1133 60 : aFrame->SchedulePaint(needInvalidatingPaint
1134 : ? nsIFrame::PAINT_DEFAULT
1135 60 : : nsIFrame::PAINT_COMPOSITE_ONLY);
1136 : }
1137 60 : }
1138 :
1139 : static void
1140 163 : SyncViewsAndInvalidateDescendants(nsIFrame* aFrame, nsChangeHint aChange)
1141 : {
1142 163 : NS_PRECONDITION(gInApplyRenderingChangeToTree,
1143 : "should only be called within ApplyRenderingChangeToTree");
1144 163 : NS_ASSERTION(nsChangeHint_size_t(aChange) ==
1145 : (aChange & (nsChangeHint_RepaintFrame |
1146 : nsChangeHint_SyncFrameView |
1147 : nsChangeHint_UpdateOpacityLayer |
1148 : nsChangeHint_SchedulePaint)),
1149 : "Invalid change flag");
1150 :
1151 163 : if (aChange & nsChangeHint_SyncFrameView) {
1152 108 : aFrame->SyncFrameViewProperties();
1153 : }
1154 :
1155 326 : nsIFrame::ChildListIterator lists(aFrame);
1156 343 : for (; !lists.IsDone(); lists.Next()) {
1157 211 : for (nsIFrame* child : lists.CurrentList()) {
1158 121 : if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
1159 : // only do frames that don't have placeholders
1160 121 : if (child->IsPlaceholderFrame()) {
1161 : // do the out-of-flow frame and its continuations
1162 : nsIFrame* outOfFlowFrame =
1163 5 : nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
1164 5 : DoApplyRenderingChangeToTree(outOfFlowFrame, aChange);
1165 116 : } else if (lists.CurrentID() == nsIFrame::kPopupList) {
1166 13 : DoApplyRenderingChangeToTree(child, aChange);
1167 : } else { // regular frame
1168 103 : SyncViewsAndInvalidateDescendants(child, aChange);
1169 : }
1170 : }
1171 : }
1172 : }
1173 163 : }
1174 :
1175 : static void
1176 51 : ApplyRenderingChangeToTree(nsIPresShell* aPresShell,
1177 : nsIFrame* aFrame,
1178 : nsChangeHint aChange)
1179 : {
1180 : // We check StyleDisplay()->HasTransformStyle() in addition to checking
1181 : // IsTransformed() since we can get here for some frames that don't support
1182 : // CSS transforms.
1183 51 : NS_ASSERTION(!(aChange & nsChangeHint_UpdateTransformLayer) ||
1184 : aFrame->IsTransformed() ||
1185 : aFrame->StyleDisplay()->HasTransformStyle(),
1186 : "Unexpected UpdateTransformLayer hint");
1187 :
1188 51 : if (aPresShell->IsPaintingSuppressed()) {
1189 : // Don't allow synchronous rendering changes when painting is turned off.
1190 17 : aChange &= ~nsChangeHint_RepaintFrame;
1191 17 : if (!aChange) {
1192 9 : return;
1193 : }
1194 : }
1195 :
1196 : // Trigger rendering updates by damaging this frame and any
1197 : // continuations of this frame.
1198 : #ifdef DEBUG
1199 42 : gInApplyRenderingChangeToTree = true;
1200 : #endif
1201 42 : if (aChange & nsChangeHint_RepaintFrame) {
1202 : // If the frame's background is propagated to an ancestor, walk up to
1203 : // that ancestor and apply the RepaintFrame change hint to it.
1204 : nsStyleContext* bgSC;
1205 16 : nsIFrame* propagatedFrame = aFrame;
1206 16 : while (!nsCSSRendering::FindBackground(propagatedFrame, &bgSC)) {
1207 0 : propagatedFrame = propagatedFrame->GetParent();
1208 0 : NS_ASSERTION(aFrame, "root frame must paint");
1209 : }
1210 :
1211 16 : if (propagatedFrame != aFrame) {
1212 0 : DoApplyRenderingChangeToTree(propagatedFrame, nsChangeHint_RepaintFrame);
1213 0 : aChange &= ~nsChangeHint_RepaintFrame;
1214 0 : if (!aChange) {
1215 0 : return;
1216 : }
1217 : }
1218 : }
1219 42 : DoApplyRenderingChangeToTree(aFrame, aChange);
1220 : #ifdef DEBUG
1221 42 : gInApplyRenderingChangeToTree = false;
1222 : #endif
1223 : }
1224 :
1225 : static void
1226 0 : AddSubtreeToOverflowTracker(nsIFrame* aFrame,
1227 : OverflowChangedTracker& aOverflowChangedTracker)
1228 : {
1229 0 : if (aFrame->FrameMaintainsOverflow()) {
1230 : aOverflowChangedTracker.AddFrame(aFrame,
1231 0 : OverflowChangedTracker::CHILDREN_CHANGED);
1232 : }
1233 0 : nsIFrame::ChildListIterator lists(aFrame);
1234 0 : for (; !lists.IsDone(); lists.Next()) {
1235 0 : for (nsIFrame* child : lists.CurrentList()) {
1236 0 : AddSubtreeToOverflowTracker(child, aOverflowChangedTracker);
1237 : }
1238 : }
1239 0 : }
1240 :
1241 : static void
1242 28 : StyleChangeReflow(nsIFrame* aFrame, nsChangeHint aHint)
1243 : {
1244 : nsIPresShell::IntrinsicDirty dirtyType;
1245 28 : if (aHint & nsChangeHint_ClearDescendantIntrinsics) {
1246 24 : NS_ASSERTION(aHint & nsChangeHint_ClearAncestorIntrinsics,
1247 : "Please read the comments in nsChangeHint.h");
1248 24 : NS_ASSERTION(aHint & nsChangeHint_NeedDirtyReflow,
1249 : "ClearDescendantIntrinsics requires NeedDirtyReflow");
1250 24 : dirtyType = nsIPresShell::eStyleChange;
1251 4 : } else if ((aHint & nsChangeHint_UpdateComputedBSize) &&
1252 0 : aFrame->HasAnyStateBits(
1253 : NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE)) {
1254 0 : dirtyType = nsIPresShell::eStyleChange;
1255 4 : } else if (aHint & nsChangeHint_ClearAncestorIntrinsics) {
1256 2 : dirtyType = nsIPresShell::eTreeChange;
1257 2 : } else if ((aHint & nsChangeHint_UpdateComputedBSize) &&
1258 0 : HasBoxAncestor(aFrame)) {
1259 : // The frame's computed BSize is changing, and we have a box ancestor
1260 : // whose cached intrinsic height may need to be updated.
1261 0 : dirtyType = nsIPresShell::eTreeChange;
1262 : } else {
1263 2 : dirtyType = nsIPresShell::eResize;
1264 : }
1265 :
1266 : nsFrameState dirtyBits;
1267 28 : if (aFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
1268 2 : dirtyBits = nsFrameState(0);
1269 26 : } else if ((aHint & nsChangeHint_NeedDirtyReflow) ||
1270 : dirtyType == nsIPresShell::eStyleChange) {
1271 24 : dirtyBits = NS_FRAME_IS_DIRTY;
1272 : } else {
1273 2 : dirtyBits = NS_FRAME_HAS_DIRTY_CHILDREN;
1274 : }
1275 :
1276 : // If we're not going to clear any intrinsic sizes on the frames, and
1277 : // there are no dirty bits to set, then there's nothing to do.
1278 28 : if (dirtyType == nsIPresShell::eResize && !dirtyBits)
1279 1 : return;
1280 :
1281 : nsIPresShell::ReflowRootHandling rootHandling;
1282 27 : if (aHint & nsChangeHint_ReflowChangesSizeOrPosition) {
1283 26 : rootHandling = nsIPresShell::ePositionOrSizeChange;
1284 : } else {
1285 1 : rootHandling = nsIPresShell::eNoPositionOrSizeChange;
1286 : }
1287 :
1288 0 : do {
1289 27 : aFrame->PresContext()->PresShell()->FrameNeedsReflow(
1290 27 : aFrame, dirtyType, dirtyBits, rootHandling);
1291 27 : aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame);
1292 27 : } while (aFrame);
1293 : }
1294 :
1295 : /* static */ nsIFrame*
1296 2550 : RestyleManager::GetNextContinuationWithSameStyle(
1297 : nsIFrame* aFrame, nsStyleContext* aOldStyleContext,
1298 : bool* aHaveMoreContinuations)
1299 : {
1300 : // See GetPrevContinuationWithSameStyle about {ib} splits.
1301 :
1302 2550 : nsIFrame* nextContinuation = aFrame->GetNextContinuation();
1303 5100 : if (!nextContinuation &&
1304 2550 : (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
1305 : // We're the last continuation, so we have to hop back to the first
1306 : // before getting the frame property
1307 : nextContinuation =
1308 0 : aFrame->FirstContinuation()->GetProperty(nsIFrame::IBSplitSibling());
1309 0 : if (nextContinuation) {
1310 : nextContinuation =
1311 0 : nextContinuation->GetProperty(nsIFrame::IBSplitSibling());
1312 : }
1313 : }
1314 :
1315 2550 : if (!nextContinuation) {
1316 2550 : return nullptr;
1317 : }
1318 :
1319 0 : NS_ASSERTION(nextContinuation->GetContent() == aFrame->GetContent(),
1320 : "unexpected content mismatch");
1321 :
1322 0 : nsStyleContext* nextStyle = nextContinuation->StyleContext();
1323 0 : if (nextStyle != aOldStyleContext) {
1324 0 : NS_ASSERTION(aOldStyleContext->GetPseudo() != nextStyle->GetPseudo() ||
1325 : aOldStyleContext->GetParentAllowServo() !=
1326 : nextStyle->GetParentAllowServo(),
1327 : "continuations should have the same style context");
1328 0 : nextContinuation = nullptr;
1329 0 : if (aHaveMoreContinuations) {
1330 0 : *aHaveMoreContinuations = true;
1331 : }
1332 : }
1333 0 : return nextContinuation;
1334 : }
1335 :
1336 : void
1337 72 : RestyleManager::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
1338 : {
1339 72 : NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
1340 : "Someone forgot a script blocker");
1341 72 : MOZ_ASSERT(!mDestroyedFrames);
1342 :
1343 72 : if (aChangeList.IsEmpty()) {
1344 19 : return;
1345 : }
1346 :
1347 53 : mDestroyedFrames = MakeUnique<nsTHashtable<nsPtrHashKey<const nsIFrame>>>();
1348 :
1349 106 : AUTO_PROFILER_LABEL("RestyleManager::ProcessRestyledFrames", CSS);
1350 :
1351 53 : nsPresContext* presContext = PresContext();
1352 53 : nsCSSFrameConstructor* frameConstructor = presContext->FrameConstructor();
1353 :
1354 : // Handle nsChangeHint_CSSOverflowChange, by either updating the
1355 : // scrollbars on the viewport, or upgrading the change hint to frame-reconstruct.
1356 149 : for (nsStyleChangeData& data : aChangeList) {
1357 96 : if (data.mHint & nsChangeHint_CSSOverflowChange) {
1358 0 : data.mHint &= ~nsChangeHint_CSSOverflowChange;
1359 0 : bool doReconstruct = true; // assume the worst
1360 :
1361 : // Only bother with this if we're html/body, since:
1362 : // (a) It'd be *expensive* to reframe these particular nodes. They're
1363 : // at the root, so reframing would mean rebuilding the world.
1364 : // (b) It's often *unnecessary* to reframe for "overflow" changes on
1365 : // these particular nodes. In general, the only reason we reframe
1366 : // for "overflow" changes is so we can construct (or destroy) a
1367 : // scrollframe & scrollbars -- and the html/body nodes often don't
1368 : // need their own scrollframe/scrollbars because they coopt the ones
1369 : // on the viewport (which always exist). So depending on whether
1370 : // that's happening, we can skip the reframe for these nodes.
1371 0 : if (data.mContent->IsAnyOfHTMLElements(nsGkAtoms::body,
1372 : nsGkAtoms::html)) {
1373 : // If the restyled element provided/provides the scrollbar styles for
1374 : // the viewport before and/or after this restyle, AND it's not coopting
1375 : // that responsibility from some other element (which would need
1376 : // reconstruction to make its own scrollframe now), THEN: we don't need
1377 : // to reconstruct - we can just reflow, because no scrollframe is being
1378 : // added/removed.
1379 : nsIContent* prevOverrideNode =
1380 0 : presContext->GetViewportScrollbarStylesOverrideNode();
1381 : nsIContent* newOverrideNode =
1382 0 : presContext->UpdateViewportScrollbarStylesOverride();
1383 :
1384 0 : if (data.mContent == prevOverrideNode ||
1385 0 : data.mContent == newOverrideNode) {
1386 : // If we get here, the restyled element provided the scrollbar styles
1387 : // for viewport before this restyle, OR it will provide them after.
1388 0 : if (!prevOverrideNode || !newOverrideNode ||
1389 : prevOverrideNode == newOverrideNode) {
1390 : // If we get here, the restyled element is NOT replacing (or being
1391 : // replaced by) some other element as the viewport's
1392 : // scrollbar-styles provider. (If it were, we'd potentially need to
1393 : // reframe to create a dedicated scrollframe for whichever element
1394 : // is being booted from providing viewport scrollbar styles.)
1395 : //
1396 : // Under these conditions, we're OK to assume that this "overflow"
1397 : // change only impacts the root viewport's scrollframe, which
1398 : // already exists, so we can simply reflow instead of reframing.
1399 : // When requesting this reflow, we send the exact same change hints
1400 : // that "width" and "height" would send (since conceptually,
1401 : // adding/removing scrollbars is like changing the available
1402 : // space).
1403 : data.mHint |= (nsChangeHint_ReflowHintsForISizeChange |
1404 0 : nsChangeHint_ReflowHintsForBSizeChange);
1405 0 : doReconstruct = false;
1406 : }
1407 : }
1408 : }
1409 0 : if (doReconstruct) {
1410 0 : data.mHint |= nsChangeHint_ReconstructFrame;
1411 : }
1412 : }
1413 : }
1414 :
1415 : // Make sure to not rebuild quote or counter lists while we're
1416 : // processing restyles
1417 53 : frameConstructor->BeginUpdate();
1418 :
1419 53 : bool didUpdateCursor = false;
1420 :
1421 149 : for (size_t i = 0; i < aChangeList.Length(); ++i) {
1422 :
1423 : // Collect and coalesce adjacent siblings for lazy frame construction.
1424 : // Eventually it would be even better to make RecreateFramesForContent
1425 : // accept a range and coalesce all adjacent reconstructs (bug 1344139).
1426 96 : size_t lazyRangeStart = i;
1427 288 : while (i < aChangeList.Length() &&
1428 191 : aChangeList[i].mContent &&
1429 191 : aChangeList[i].mContent->HasFlag(NODE_NEEDS_FRAME) &&
1430 0 : (i == lazyRangeStart ||
1431 0 : aChangeList[i - 1].mContent->GetNextSibling() == aChangeList[i].mContent))
1432 : {
1433 0 : MOZ_ASSERT(aChangeList[i].mHint & nsChangeHint_ReconstructFrame);
1434 0 : MOZ_ASSERT(!aChangeList[i].mFrame);
1435 0 : ++i;
1436 : }
1437 96 : if (i != lazyRangeStart) {
1438 0 : nsIContent* start = aChangeList[lazyRangeStart].mContent;
1439 0 : nsIContent* end = aChangeList[i-1].mContent->GetNextSibling();
1440 0 : nsIContent* container = start->GetParent();
1441 0 : MOZ_ASSERT(container);
1442 0 : if (!end) {
1443 0 : frameConstructor->ContentAppended(container, start, false);
1444 : } else {
1445 0 : frameConstructor->ContentRangeInserted(container, start, end, nullptr, false);
1446 : }
1447 : }
1448 96 : for (size_t j = lazyRangeStart; j < i; ++j) {
1449 0 : MOZ_ASSERT(!aChangeList[j].mContent->GetPrimaryFrame() ||
1450 : !aChangeList[j].mContent->HasFlag(NODE_NEEDS_FRAME));
1451 : }
1452 96 : if (i == aChangeList.Length()) {
1453 0 : break;
1454 : }
1455 :
1456 96 : nsStyleChangeData& mutable_data = aChangeList[i];
1457 96 : const nsStyleChangeData& data = mutable_data;
1458 96 : nsIFrame* frame = data.mFrame;
1459 96 : nsIContent* content = data.mContent;
1460 96 : nsChangeHint hint = data.mHint;
1461 96 : bool didReflowThisFrame = false;
1462 :
1463 96 : NS_ASSERTION(!(hint & nsChangeHint_AllReflowHints) ||
1464 : (hint & nsChangeHint_NeedReflow),
1465 : "Reflow hint bits set without actually asking for a reflow");
1466 :
1467 : // skip any frame that has been destroyed due to a ripple effect
1468 96 : if (frame && mDestroyedFrames->Contains(frame)) {
1469 0 : continue;
1470 : }
1471 :
1472 96 : if (frame && frame->GetContent() != content) {
1473 : // XXXbz this is due to image maps messing with the primary frame of
1474 : // <area>s. See bug 135040. Remove this block once that's fixed.
1475 0 : frame = nullptr;
1476 0 : if (!(hint & nsChangeHint_ReconstructFrame)) {
1477 0 : continue;
1478 : }
1479 : }
1480 :
1481 98 : if ((hint & nsChangeHint_UpdateContainingBlock) && frame &&
1482 2 : !(hint & nsChangeHint_ReconstructFrame)) {
1483 6 : if (NeedToReframeForAddingOrRemovingTransform(frame) ||
1484 4 : frame->IsFieldSetFrame() ||
1485 2 : frame->GetContentInsertionFrame() != frame) {
1486 : // The frame has positioned children that need to be reparented, or
1487 : // it can't easily be converted to/from being an abs-pos container correctly.
1488 2 : hint |= nsChangeHint_ReconstructFrame;
1489 : } else {
1490 0 : for (nsIFrame* cont = frame; cont;
1491 : cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
1492 : // Normally frame construction would set state bits as needed,
1493 : // but we're not going to reconstruct the frame so we need to set them.
1494 : // It's because we need to set this state on each affected frame
1495 : // that we can't coalesce nsChangeHint_UpdateContainingBlock hints up
1496 : // to ancestors (i.e. it can't be an change hint that is handled for
1497 : // descendants).
1498 0 : if (cont->IsAbsPosContainingBlock()) {
1499 0 : if (!cont->IsAbsoluteContainer() &&
1500 0 : (cont->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)) {
1501 0 : cont->MarkAsAbsoluteContainingBlock();
1502 : }
1503 : } else {
1504 0 : if (cont->IsAbsoluteContainer()) {
1505 0 : if (cont->HasAbsolutelyPositionedChildren()) {
1506 : // If |cont| still has absolutely positioned children,
1507 : // we can't call MarkAsNotAbsoluteContainingBlock. This
1508 : // will remove a frame list that still has children in
1509 : // it that we need to keep track of.
1510 : // The optimization of removing it isn't particularly
1511 : // important, although it does mean we skip some tests.
1512 0 : NS_WARNING("skipping removal of absolute containing block");
1513 : } else {
1514 0 : cont->MarkAsNotAbsoluteContainingBlock();
1515 : }
1516 : }
1517 : }
1518 : }
1519 : }
1520 : }
1521 :
1522 98 : if ((hint & nsChangeHint_AddOrRemoveTransform) && frame &&
1523 2 : !(hint & nsChangeHint_ReconstructFrame)) {
1524 0 : for (nsIFrame* cont = frame; cont;
1525 : cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
1526 0 : if (cont->StyleDisplay()->HasTransform(cont)) {
1527 0 : cont->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
1528 : }
1529 : // Don't remove NS_FRAME_MAY_BE_TRANSFORMED since it may still be
1530 : // transformed by other means. It's OK to have the bit even if it's
1531 : // not needed.
1532 : }
1533 : }
1534 :
1535 96 : if (hint & nsChangeHint_ReconstructFrame) {
1536 : // If we ever start passing true here, be careful of restyles
1537 : // that involve a reframe and animations. In particular, if the
1538 : // restyle we're processing here is an animation restyle, but
1539 : // the style resolution we will do for the frame construction
1540 : // happens async when we're not in an animation restyle already,
1541 : // problems could arise.
1542 : // We could also have problems with triggering of CSS transitions
1543 : // on elements whose frames are reconstructed, since we depend on
1544 : // the reconstruction happening synchronously.
1545 : frameConstructor->RecreateFramesForContent(content, false,
1546 15 : nsCSSFrameConstructor::REMOVE_FOR_RECONSTRUCTION, nullptr);
1547 : } else {
1548 81 : NS_ASSERTION(frame, "This shouldn't happen");
1549 :
1550 81 : if (!frame->FrameMaintainsOverflow()) {
1551 : // frame does not maintain overflow rects, so avoid calling
1552 : // FinishAndStoreOverflow on it:
1553 : hint &= ~(nsChangeHint_UpdateOverflow |
1554 : nsChangeHint_ChildrenOnlyTransform |
1555 : nsChangeHint_UpdatePostTransformOverflow |
1556 6 : nsChangeHint_UpdateParentOverflow);
1557 : }
1558 :
1559 81 : if (!(frame->GetStateBits() & NS_FRAME_MAY_BE_TRANSFORMED)) {
1560 : // Frame can not be transformed, and thus a change in transform will
1561 : // have no effect and we should not use the
1562 : // nsChangeHint_UpdatePostTransformOverflow hint.
1563 50 : hint &= ~nsChangeHint_UpdatePostTransformOverflow;
1564 : }
1565 :
1566 81 : if (hint & nsChangeHint_UpdateEffects) {
1567 0 : for (nsIFrame* cont = frame; cont;
1568 : cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
1569 0 : nsSVGEffects::UpdateEffects(cont);
1570 : }
1571 : }
1572 162 : if ((hint & nsChangeHint_InvalidateRenderingObservers) ||
1573 79 : ((hint & nsChangeHint_UpdateOpacityLayer) &&
1574 21 : frame->IsFrameOfType(nsIFrame::eSVG) &&
1575 0 : !(frame->GetStateBits() & NS_STATE_IS_OUTER_SVG))) {
1576 23 : nsSVGEffects::InvalidateRenderingObservers(frame);
1577 : }
1578 81 : if (hint & nsChangeHint_NeedReflow) {
1579 28 : StyleChangeReflow(frame, hint);
1580 28 : didReflowThisFrame = true;
1581 : }
1582 :
1583 98 : if ((hint & nsChangeHint_UpdateUsesOpacity) &&
1584 17 : frame->IsFrameOfType(nsIFrame::eTablePart)) {
1585 0 : NS_ASSERTION(hint & nsChangeHint_UpdateOpacityLayer,
1586 : "should only return UpdateUsesOpacity hint "
1587 : "when also returning UpdateOpacityLayer hint");
1588 : // When an internal table part (including cells) changes between
1589 : // having opacity 1 and non-1, it changes whether its
1590 : // backgrounds (and those of table parts inside of it) are
1591 : // painted as part of the table's nsDisplayTableBorderBackground
1592 : // display item, or part of its own display item. That requires
1593 : // invalidation, so change UpdateOpacityLayer to RepaintFrame.
1594 0 : hint &= ~nsChangeHint_UpdateOpacityLayer;
1595 0 : hint |= nsChangeHint_RepaintFrame;
1596 : }
1597 :
1598 : // Opacity disables preserve-3d, so if we toggle it, then we also need
1599 : // to update the overflow areas of all potentially affected frames.
1600 98 : if ((hint & nsChangeHint_UpdateUsesOpacity) &&
1601 17 : frame->StyleDisplay()->mTransformStyle == NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D) {
1602 0 : hint |= nsChangeHint_UpdateSubtreeOverflow;
1603 : }
1604 :
1605 81 : if (hint & nsChangeHint_UpdateBackgroundPosition) {
1606 : // For most frame types, DLBI can detect background position changes,
1607 : // so we only need to schedule a paint.
1608 0 : hint |= nsChangeHint_SchedulePaint;
1609 0 : if (frame->IsFrameOfType(nsIFrame::eTablePart) ||
1610 0 : frame->IsFrameOfType(nsIFrame::eMathML)) {
1611 : // Table parts and MathML frames don't build display items for their
1612 : // backgrounds, so DLBI can't detect background-position changes for
1613 : // these frames. Repaint the whole frame.
1614 0 : hint |= nsChangeHint_RepaintFrame;
1615 : }
1616 : }
1617 :
1618 81 : if (hint & (nsChangeHint_RepaintFrame | nsChangeHint_SyncFrameView |
1619 : nsChangeHint_UpdateOpacityLayer | nsChangeHint_UpdateTransformLayer |
1620 : nsChangeHint_ChildrenOnlyTransform | nsChangeHint_SchedulePaint)) {
1621 51 : ApplyRenderingChangeToTree(presContext->PresShell(), frame, hint);
1622 : }
1623 81 : if ((hint & nsChangeHint_RecomputePosition) && !didReflowThisFrame) {
1624 0 : ActiveLayerTracker::NotifyOffsetRestyle(frame);
1625 : // It is possible for this to fall back to a reflow
1626 0 : if (!RecomputePosition(frame)) {
1627 0 : didReflowThisFrame = true;
1628 : }
1629 : }
1630 81 : NS_ASSERTION(!(hint & nsChangeHint_ChildrenOnlyTransform) ||
1631 : (hint & nsChangeHint_UpdateOverflow),
1632 : "nsChangeHint_UpdateOverflow should be passed too");
1633 134 : if (!didReflowThisFrame &&
1634 53 : (hint & (nsChangeHint_UpdateOverflow |
1635 : nsChangeHint_UpdatePostTransformOverflow |
1636 : nsChangeHint_UpdateParentOverflow |
1637 : nsChangeHint_UpdateSubtreeOverflow))) {
1638 0 : if (hint & nsChangeHint_UpdateSubtreeOverflow) {
1639 0 : for (nsIFrame* cont = frame; cont; cont =
1640 : nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
1641 0 : AddSubtreeToOverflowTracker(cont, mOverflowChangedTracker);
1642 : }
1643 : // The work we just did in AddSubtreeToOverflowTracker
1644 : // subsumes some of the other hints:
1645 : hint &= ~(nsChangeHint_UpdateOverflow |
1646 0 : nsChangeHint_UpdatePostTransformOverflow);
1647 : }
1648 0 : if (hint & nsChangeHint_ChildrenOnlyTransform) {
1649 : // The overflow areas of the child frames need to be updated:
1650 0 : nsIFrame* hintFrame = GetFrameForChildrenOnlyTransformHint(frame);
1651 0 : nsIFrame* childFrame = hintFrame->PrincipalChildList().FirstChild();
1652 0 : NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(frame),
1653 : "SVG frames should not have continuations "
1654 : "or ib-split siblings");
1655 0 : NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(hintFrame),
1656 : "SVG frames should not have continuations "
1657 : "or ib-split siblings");
1658 0 : for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
1659 0 : MOZ_ASSERT(childFrame->IsFrameOfType(nsIFrame::eSVG),
1660 : "Not expecting non-SVG children");
1661 : // If |childFrame| is dirty or has dirty children, we don't bother
1662 : // updating overflows since that will happen when it's reflowed.
1663 0 : if (!(childFrame->GetStateBits() &
1664 : (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN))) {
1665 0 : mOverflowChangedTracker.AddFrame(childFrame,
1666 0 : OverflowChangedTracker::CHILDREN_CHANGED);
1667 : }
1668 0 : NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(childFrame),
1669 : "SVG frames should not have continuations "
1670 : "or ib-split siblings");
1671 0 : NS_ASSERTION(childFrame->GetParent() == hintFrame,
1672 : "SVG child frame not expected to have different parent");
1673 : }
1674 : }
1675 : // If |frame| is dirty or has dirty children, we don't bother updating
1676 : // overflows since that will happen when it's reflowed.
1677 0 : if (!(frame->GetStateBits() &
1678 : (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN))) {
1679 0 : if (hint & (nsChangeHint_UpdateOverflow |
1680 : nsChangeHint_UpdatePostTransformOverflow)) {
1681 : OverflowChangedTracker::ChangeKind changeKind;
1682 : // If we have both nsChangeHint_UpdateOverflow and
1683 : // nsChangeHint_UpdatePostTransformOverflow,
1684 : // CHILDREN_CHANGED is selected as it is
1685 : // strictly stronger.
1686 0 : if (hint & nsChangeHint_UpdateOverflow) {
1687 0 : changeKind = OverflowChangedTracker::CHILDREN_CHANGED;
1688 : } else {
1689 0 : changeKind = OverflowChangedTracker::TRANSFORM_CHANGED;
1690 : }
1691 0 : for (nsIFrame* cont = frame; cont; cont =
1692 : nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
1693 0 : mOverflowChangedTracker.AddFrame(cont, changeKind);
1694 : }
1695 : }
1696 : // UpdateParentOverflow hints need to be processed in addition
1697 : // to the above, since if the processing of the above hints
1698 : // yields no change, the update will not propagate to the
1699 : // parent.
1700 0 : if (hint & nsChangeHint_UpdateParentOverflow) {
1701 0 : MOZ_ASSERT(frame->GetParent(),
1702 : "shouldn't get style hints for the root frame");
1703 0 : for (nsIFrame* cont = frame; cont; cont =
1704 : nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
1705 0 : mOverflowChangedTracker.AddFrame(cont->GetParent(),
1706 0 : OverflowChangedTracker::CHILDREN_CHANGED);
1707 : }
1708 : }
1709 : }
1710 : }
1711 81 : if ((hint & nsChangeHint_UpdateCursor) && !didUpdateCursor) {
1712 0 : presContext->PresShell()->SynthesizeMouseMove(false);
1713 0 : didUpdateCursor = true;
1714 : }
1715 81 : if (hint & nsChangeHint_UpdateWidgetProperties) {
1716 0 : frame->UpdateWidgetProperties();
1717 : }
1718 : }
1719 : }
1720 :
1721 53 : frameConstructor->EndUpdate();
1722 53 : mDestroyedFrames.reset(nullptr);
1723 :
1724 : #ifdef DEBUG
1725 : // Verify the style tree. Note that this needs to happen once we've
1726 : // processed the whole list, since until then the tree is not in fact in a
1727 : // consistent state.
1728 149 : for (const nsStyleChangeData& data : aChangeList) {
1729 : // reget frame from content since it may have been regenerated...
1730 96 : if (data.mContent) {
1731 95 : nsIFrame* frame = data.mContent->GetPrimaryFrame();
1732 95 : if (frame) {
1733 92 : DebugVerifyStyleTree(frame);
1734 : }
1735 1 : } else if (!data.mFrame || !data.mFrame->IsViewportFrame()) {
1736 : NS_WARNING("Unable to test style tree integrity -- no content node "
1737 0 : "(and not a viewport frame)");
1738 : }
1739 : }
1740 : #endif
1741 :
1742 53 : aChangeList.Clear();
1743 : }
1744 :
1745 : /* static */ uint64_t
1746 1200 : RestyleManager::GetAnimationGenerationForFrame(nsIFrame* aFrame)
1747 : {
1748 1200 : EffectSet* effectSet = EffectSet::GetEffectSet(aFrame);
1749 1200 : return effectSet ? effectSet->GetAnimationGeneration() : 0;
1750 : }
1751 :
1752 : void
1753 2 : RestyleManager::IncrementAnimationGeneration()
1754 : {
1755 : // We update the animation generation at start of each call to
1756 : // ProcessPendingRestyles so we should ignore any subsequent (redundant)
1757 : // calls that occur while we are still processing restyles.
1758 4 : if ((IsGecko() && !AsGecko()->IsProcessingRestyles()) ||
1759 0 : (IsServo() && !mInStyleRefresh)) {
1760 2 : ++mAnimationGeneration;
1761 : }
1762 2 : }
1763 :
1764 : /* static */ void
1765 1202 : RestyleManager::AddLayerChangesForAnimation(nsIFrame* aFrame,
1766 : nsIContent* aContent,
1767 : nsStyleChangeList&
1768 : aChangeListToProcess)
1769 : {
1770 1202 : if (!aFrame || !aContent) {
1771 2 : return;
1772 : }
1773 :
1774 : uint64_t frameGeneration =
1775 1200 : RestyleManager::GetAnimationGenerationForFrame(aFrame);
1776 :
1777 1200 : nsChangeHint hint = nsChangeHint(0);
1778 2400 : for (const LayerAnimationInfo::Record& layerInfo :
1779 1200 : LayerAnimationInfo::sRecords) {
1780 : layers::Layer* layer =
1781 2400 : FrameLayerBuilder::GetDedicatedLayer(aFrame, layerInfo.mLayerType);
1782 2400 : if (layer && frameGeneration != layer->GetAnimationGeneration()) {
1783 : // If we have a transform layer but don't have any transform style, we
1784 : // probably just removed the transform but haven't destroyed the layer
1785 : // yet. In this case we will add the appropriate change hint
1786 : // (nsChangeHint_UpdateContainingBlock) when we compare style contexts
1787 : // so we can skip adding any change hint here. (If we *were* to add
1788 : // nsChangeHint_UpdateTransformLayer, ApplyRenderingChangeToTree would
1789 : // complain that we're updating a transform layer without a transform).
1790 0 : if (layerInfo.mLayerType == nsDisplayItem::TYPE_TRANSFORM &&
1791 0 : !aFrame->StyleDisplay()->HasTransformStyle()) {
1792 0 : continue;
1793 : }
1794 0 : hint |= layerInfo.mChangeHint;
1795 : }
1796 :
1797 : // We consider it's the first paint for the frame if we have an animation
1798 : // for the property but have no layer.
1799 : // Note that in case of animations which has properties preventing running
1800 : // on the compositor, e.g., width or height, corresponding layer is not
1801 : // created at all, but even in such cases, we normally set valid change
1802 : // hint for such animations in each tick, i.e. restyles in each tick. As
1803 : // a result, we usually do restyles for such animations in every tick on
1804 : // the main-thread. The only animations which will be affected by this
1805 : // explicit change hint are animations that have opacity/transform but did
1806 : // not have those properies just before. e.g, setting transform by
1807 : // setKeyframes or changing target element from other target which prevents
1808 : // running on the compositor, etc.
1809 4800 : if (!layer &&
1810 2400 : nsLayoutUtils::HasEffectiveAnimation(aFrame, layerInfo.mProperty)) {
1811 14 : hint |= layerInfo.mChangeHint;
1812 : }
1813 : }
1814 :
1815 1200 : if (hint) {
1816 14 : aChangeListToProcess.AppendChange(aFrame, aContent, hint);
1817 : }
1818 : }
1819 :
1820 25 : RestyleManager::AnimationsWithDestroyedFrame::AnimationsWithDestroyedFrame(
1821 25 : RestyleManager* aRestyleManager)
1822 : : mRestyleManager(aRestyleManager)
1823 25 : , mRestorePointer(mRestyleManager->mAnimationsWithDestroyedFrame)
1824 : {
1825 25 : MOZ_ASSERT(!mRestyleManager->mAnimationsWithDestroyedFrame,
1826 : "shouldn't construct recursively");
1827 25 : mRestyleManager->mAnimationsWithDestroyedFrame = this;
1828 25 : }
1829 :
1830 : void
1831 25 : RestyleManager::AnimationsWithDestroyedFrame
1832 : ::StopAnimationsForElementsWithoutFrames()
1833 : {
1834 25 : StopAnimationsWithoutFrame(mContents, CSSPseudoElementType::NotPseudo);
1835 25 : StopAnimationsWithoutFrame(mBeforeContents, CSSPseudoElementType::before);
1836 25 : StopAnimationsWithoutFrame(mAfterContents, CSSPseudoElementType::after);
1837 25 : }
1838 :
1839 : void
1840 75 : RestyleManager::AnimationsWithDestroyedFrame
1841 : ::StopAnimationsWithoutFrame(
1842 : nsTArray<RefPtr<nsIContent>>& aArray,
1843 : CSSPseudoElementType aPseudoType)
1844 : {
1845 : nsAnimationManager* animationManager =
1846 75 : mRestyleManager->PresContext()->AnimationManager();
1847 : nsTransitionManager* transitionManager =
1848 75 : mRestyleManager->PresContext()->TransitionManager();
1849 77 : for (nsIContent* content : aArray) {
1850 2 : if (content->GetPrimaryFrame()) {
1851 2 : continue;
1852 : }
1853 0 : dom::Element* element = content->AsElement();
1854 :
1855 0 : animationManager->StopAnimationsForElement(element, aPseudoType);
1856 0 : transitionManager->StopAnimationsForElement(element, aPseudoType);
1857 :
1858 : // All other animations should keep running but not running on the
1859 : // *compositor* at this point.
1860 0 : EffectSet* effectSet = EffectSet::GetEffectSet(element, aPseudoType);
1861 0 : if (effectSet) {
1862 0 : for (KeyframeEffectReadOnly* effect : *effectSet) {
1863 0 : effect->ResetIsRunningOnCompositor();
1864 : }
1865 : }
1866 : }
1867 75 : }
1868 :
1869 : } // namespace mozilla
|