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/RestyleManagerBase.h"
8 #include "mozilla/StyleSetHandle.h"
9 #include "nsIFrame.h"
10 
11 namespace mozilla {
12 
RestyleManagerBase(nsPresContext * aPresContext)13 RestyleManagerBase::RestyleManagerBase(nsPresContext* aPresContext)
14   : mPresContext(aPresContext)
15   , mRestyleGeneration(1)
16   , mHoverGeneration(0)
17   , mObservingRefreshDriver(false)
18   , mInStyleRefresh(false)
19 {
20   MOZ_ASSERT(mPresContext);
21 }
22 
23 /**
24  * Calculates the change hint and the restyle hint for a given content state
25  * change.
26  *
27  * This is called from both Restyle managers.
28  */
29 void
ContentStateChangedInternal(Element * aElement,EventStates aStateMask,nsChangeHint * aOutChangeHint,nsRestyleHint * aOutRestyleHint)30 RestyleManagerBase::ContentStateChangedInternal(Element* aElement,
31                                                 EventStates aStateMask,
32                                                 nsChangeHint* aOutChangeHint,
33                                                 nsRestyleHint* aOutRestyleHint)
34 {
35   MOZ_ASSERT(aOutChangeHint);
36   MOZ_ASSERT(aOutRestyleHint);
37 
38   StyleSetHandle styleSet = PresContext()->StyleSet();
39   NS_ASSERTION(styleSet, "couldn't get style set");
40 
41   *aOutChangeHint = nsChangeHint(0);
42   // Any change to a content state that affects which frames we construct
43   // must lead to a frame reconstruct here if we already have a frame.
44   // Note that we never decide through non-CSS means to not create frames
45   // based on content states, so if we already don't have a frame we don't
46   // need to force a reframe -- if it's needed, the HasStateDependentStyle
47   // call will handle things.
48   nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
49   CSSPseudoElementType pseudoType = CSSPseudoElementType::NotPseudo;
50   if (primaryFrame) {
51     // If it's generated content, ignore LOADING/etc state changes on it.
52     if (!primaryFrame->IsGeneratedContentFrame() &&
53         aStateMask.HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN |
54                                          NS_EVENT_STATE_USERDISABLED |
55                                          NS_EVENT_STATE_SUPPRESSED |
56                                          NS_EVENT_STATE_LOADING)) {
57       *aOutChangeHint = nsChangeHint_ReconstructFrame;
58     } else {
59       uint8_t app = primaryFrame->StyleDisplay()->mAppearance;
60       if (app) {
61         nsITheme* theme = PresContext()->GetTheme();
62         if (theme &&
63             theme->ThemeSupportsWidget(PresContext(), primaryFrame, app)) {
64           bool repaint = false;
65           theme->WidgetStateChanged(primaryFrame, app, nullptr, &repaint,
66                                     nullptr);
67           if (repaint) {
68             *aOutChangeHint |= nsChangeHint_RepaintFrame;
69           }
70         }
71       }
72     }
73 
74     pseudoType = primaryFrame->StyleContext()->GetPseudoType();
75 
76     primaryFrame->ContentStatesChanged(aStateMask);
77   }
78 
79   if (pseudoType >= CSSPseudoElementType::Count) {
80     *aOutRestyleHint = styleSet->HasStateDependentStyle(aElement, aStateMask);
81   } else if (nsCSSPseudoElements::PseudoElementSupportsUserActionState(
82                pseudoType)) {
83     // If aElement is a pseudo-element, we want to check to see whether there
84     // are any state-dependent rules applying to that pseudo.
85     Element* ancestor =
86       ElementForStyleContext(nullptr, primaryFrame, pseudoType);
87     *aOutRestyleHint = styleSet->HasStateDependentStyle(ancestor, pseudoType,
88                                                         aElement, aStateMask);
89   } else {
90     *aOutRestyleHint = nsRestyleHint(0);
91   }
92 
93   if (aStateMask.HasState(NS_EVENT_STATE_HOVER) && *aOutRestyleHint != 0) {
94     IncrementHoverGeneration();
95   }
96 
97   if (aStateMask.HasState(NS_EVENT_STATE_VISITED)) {
98     // Exposing information to the page about whether the link is
99     // visited or not isn't really something we can worry about here.
100     // FIXME: We could probably do this a bit better.
101     *aOutChangeHint |= nsChangeHint_RepaintFrame;
102   }
103 }
104 
105 /* static */ nsCString
RestyleHintToString(nsRestyleHint aHint)106 RestyleManagerBase::RestyleHintToString(nsRestyleHint aHint)
107 {
108   nsCString result;
109   bool any = false;
110   const char* names[] = {
111     "Self", "SomeDescendants", "Subtree", "LaterSiblings", "CSSTransitions",
112     "CSSAnimations", "SVGAttrAnimations", "StyleAttribute",
113     "StyleAttribute_Animations", "Force", "ForceDescendants"
114   };
115   uint32_t hint = aHint & ((1 << ArrayLength(names)) - 1);
116   uint32_t rest = aHint & ~((1 << ArrayLength(names)) - 1);
117   for (uint32_t i = 0; i < ArrayLength(names); i++) {
118     if (hint & (1 << i)) {
119       if (any) {
120         result.AppendLiteral(" | ");
121       }
122       result.AppendPrintf("eRestyle_%s", names[i]);
123       any = true;
124     }
125   }
126   if (rest) {
127     if (any) {
128       result.AppendLiteral(" | ");
129     }
130     result.AppendPrintf("0x%0x", rest);
131   } else {
132     if (!any) {
133       result.AppendLiteral("0");
134     }
135   }
136   return result;
137 }
138 
139 #ifdef DEBUG
140 /* static */ nsCString
ChangeHintToString(nsChangeHint aHint)141 RestyleManagerBase::ChangeHintToString(nsChangeHint aHint)
142 {
143   nsCString result;
144   bool any = false;
145   const char* names[] = {
146     "RepaintFrame", "NeedReflow", "ClearAncestorIntrinsics",
147     "ClearDescendantIntrinsics", "NeedDirtyReflow", "SyncFrameView",
148     "UpdateCursor", "UpdateEffects", "UpdateOpacityLayer",
149     "UpdateTransformLayer", "ReconstructFrame", "UpdateOverflow",
150     "UpdateSubtreeOverflow", "UpdatePostTransformOverflow",
151     "UpdateParentOverflow",
152     "ChildrenOnlyTransform", "RecomputePosition", "AddOrRemoveTransform",
153     "BorderStyleNoneChange", "UpdateTextPath", "SchedulePaint",
154     "NeutralChange", "InvalidateRenderingObservers",
155     "ReflowChangesSizeOrPosition", "UpdateComputedBSize",
156     "UpdateUsesOpacity", "UpdateBackgroundPosition",
157     "AddOrRemoveTransform"
158   };
159   static_assert(nsChangeHint_AllHints == (1 << ArrayLength(names)) - 1,
160                 "Name list doesn't match change hints.");
161   uint32_t hint = aHint & ((1 << ArrayLength(names)) - 1);
162   uint32_t rest = aHint & ~((1 << ArrayLength(names)) - 1);
163   if (hint == nsChangeHint_Hints_NotHandledForDescendants) {
164     result.AppendLiteral("nsChangeHint_Hints_NotHandledForDescendants");
165     hint = 0;
166     any = true;
167   } else {
168     if ((hint & NS_STYLE_HINT_REFLOW) == NS_STYLE_HINT_REFLOW) {
169       result.AppendLiteral("NS_STYLE_HINT_REFLOW");
170       hint = hint & ~NS_STYLE_HINT_REFLOW;
171       any = true;
172     } else if ((hint & nsChangeHint_AllReflowHints) == nsChangeHint_AllReflowHints) {
173       result.AppendLiteral("nsChangeHint_AllReflowHints");
174       hint = hint & ~nsChangeHint_AllReflowHints;
175       any = true;
176     } else if ((hint & NS_STYLE_HINT_VISUAL) == NS_STYLE_HINT_VISUAL) {
177       result.AppendLiteral("NS_STYLE_HINT_VISUAL");
178       hint = hint & ~NS_STYLE_HINT_VISUAL;
179       any = true;
180     }
181   }
182   for (uint32_t i = 0; i < ArrayLength(names); i++) {
183     if (hint & (1 << i)) {
184       if (any) {
185         result.AppendLiteral(" | ");
186       }
187       result.AppendPrintf("nsChangeHint_%s", names[i]);
188       any = true;
189     }
190   }
191   if (rest) {
192     if (any) {
193       result.AppendLiteral(" | ");
194     }
195     result.AppendPrintf("0x%0x", rest);
196   } else {
197     if (!any) {
198       result.AppendLiteral("nsChangeHint(0)");
199     }
200   }
201   return result;
202 }
203 #endif
204 
205 void
PostRestyleEventInternal(bool aForLazyConstruction)206 RestyleManagerBase::PostRestyleEventInternal(bool aForLazyConstruction)
207 {
208   // Make sure we're not in a style refresh; if we are, we still have
209   // a call to ProcessPendingRestyles coming and there's no need to
210   // add ourselves as a refresh observer until then.
211   bool inRefresh = !aForLazyConstruction && mInStyleRefresh;
212   nsIPresShell* presShell = PresContext()->PresShell();
213   if (!ObservingRefreshDriver() && !inRefresh) {
214     SetObservingRefreshDriver(PresContext()->RefreshDriver()->
215         AddStyleFlushObserver(presShell));
216   }
217 
218   // Unconditionally flag our document as needing a flush.  The other
219   // option here would be a dedicated boolean to track whether we need
220   // to do so (set here and unset in ProcessPendingRestyles).
221   presShell->GetDocument()->SetNeedStyleFlush();
222 }
223 
224 /**
225  * Frame construction helpers follow.
226  */
227 #ifdef DEBUG
228 static bool gInApplyRenderingChangeToTree = false;
229 #endif
230 
231 #ifdef DEBUG
232 static void
DumpContext(nsIFrame * aFrame,nsStyleContext * aContext)233 DumpContext(nsIFrame* aFrame, nsStyleContext* aContext)
234 {
235   if (aFrame) {
236     fputs("frame: ", stdout);
237     nsAutoString name;
238     aFrame->GetFrameName(name);
239     fputs(NS_LossyConvertUTF16toASCII(name).get(), stdout);
240     fprintf(stdout, " (%p)", static_cast<void*>(aFrame));
241   }
242   if (aContext) {
243     fprintf(stdout, " style: %p ", static_cast<void*>(aContext));
244 
245     nsIAtom* pseudoTag = aContext->GetPseudo();
246     if (pseudoTag) {
247       nsAutoString buffer;
248       pseudoTag->ToString(buffer);
249       fputs(NS_LossyConvertUTF16toASCII(buffer).get(), stdout);
250       fputs(" ", stdout);
251     }
252     fputs("{}\n", stdout);
253   }
254 }
255 
256 static void
VerifySameTree(nsStyleContext * aContext1,nsStyleContext * aContext2)257 VerifySameTree(nsStyleContext* aContext1, nsStyleContext* aContext2)
258 {
259   nsStyleContext* top1 = aContext1;
260   nsStyleContext* top2 = aContext2;
261   nsStyleContext* parent;
262   for (;;) {
263     parent = top1->GetParent();
264     if (!parent)
265       break;
266     top1 = parent;
267   }
268   for (;;) {
269     parent = top2->GetParent();
270     if (!parent)
271       break;
272     top2 = parent;
273   }
274   NS_ASSERTION(top1 == top2,
275                "Style contexts are not in the same style context tree");
276 }
277 
278 static void
VerifyContextParent(nsIFrame * aFrame,nsStyleContext * aContext,nsStyleContext * aParentContext)279 VerifyContextParent(nsIFrame* aFrame, nsStyleContext* aContext,
280                     nsStyleContext* aParentContext)
281 {
282   // get the contexts not provided
283   if (!aContext) {
284     aContext = aFrame->StyleContext();
285   }
286 
287   if (!aParentContext) {
288     nsIFrame* providerFrame;
289     aParentContext = aFrame->GetParentStyleContext(&providerFrame);
290     // aParentContext could still be null
291   }
292 
293   NS_ASSERTION(aContext, "Failure to get required contexts");
294   nsStyleContext* actualParentContext = aContext->GetParent();
295 
296   if (aParentContext) {
297     if (aParentContext != actualParentContext) {
298       DumpContext(aFrame, aContext);
299       if (aContext == aParentContext) {
300         NS_ERROR("Using parent's style context");
301       } else {
302         NS_ERROR("Wrong parent style context");
303         fputs("Wrong parent style context: ", stdout);
304         DumpContext(nullptr, actualParentContext);
305         fputs("should be using: ", stdout);
306         DumpContext(nullptr, aParentContext);
307         VerifySameTree(actualParentContext, aParentContext);
308         fputs("\n", stdout);
309       }
310     }
311 
312   } else {
313     if (actualParentContext) {
314       NS_ERROR("Have parent context and shouldn't");
315       DumpContext(aFrame, aContext);
316       fputs("Has parent context: ", stdout);
317       DumpContext(nullptr, actualParentContext);
318       fputs("Should be null\n\n", stdout);
319     }
320   }
321 
322   nsStyleContext* childStyleIfVisited = aContext->GetStyleIfVisited();
323   // Either childStyleIfVisited has aContext->GetParent()->GetStyleIfVisited()
324   // as the parent or it has a different rulenode from aContext _and_ has
325   // aContext->GetParent() as the parent.
326   if (childStyleIfVisited &&
327       !((childStyleIfVisited->RuleNode() != aContext->RuleNode() &&
328          childStyleIfVisited->GetParent() == aContext->GetParent()) ||
329         childStyleIfVisited->GetParent() ==
330           aContext->GetParent()->GetStyleIfVisited())) {
331     NS_ERROR("Visited style has wrong parent");
332     DumpContext(aFrame, aContext);
333     fputs("\n", stdout);
334   }
335 }
336 
337 static void
VerifyStyleTree(nsIFrame * aFrame)338 VerifyStyleTree(nsIFrame* aFrame)
339 {
340   nsStyleContext* context = aFrame->StyleContext();
341   VerifyContextParent(aFrame, context, nullptr);
342 
343   nsIFrame::ChildListIterator lists(aFrame);
344   for (; !lists.IsDone(); lists.Next()) {
345     for (nsIFrame* child : lists.CurrentList()) {
346       if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
347         // only do frames that are in flow
348         if (nsGkAtoms::placeholderFrame == child->GetType()) {
349           // placeholder: first recurse and verify the out of flow frame,
350           // then verify the placeholder's context
351           nsIFrame* outOfFlowFrame =
352             nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
353 
354           // recurse to out of flow frame, letting the parent context get resolved
355           do {
356             VerifyStyleTree(outOfFlowFrame);
357           } while ((outOfFlowFrame = outOfFlowFrame->GetNextContinuation()));
358 
359           // verify placeholder using the parent frame's context as
360           // parent context
361           VerifyContextParent(child, nullptr, nullptr);
362         } else { // regular frame
363           VerifyStyleTree(child);
364         }
365       }
366     }
367   }
368 
369   // do additional contexts
370   int32_t contextIndex = 0;
371   for (nsStyleContext* extraContext;
372        (extraContext = aFrame->GetAdditionalStyleContext(contextIndex));
373        ++contextIndex) {
374     VerifyContextParent(aFrame, extraContext, context);
375   }
376 }
377 
378 void
DebugVerifyStyleTree(nsIFrame * aFrame)379 RestyleManagerBase::DebugVerifyStyleTree(nsIFrame* aFrame)
380 {
381   if (aFrame) {
382     VerifyStyleTree(aFrame);
383   }
384 }
385 
386 #endif // DEBUG
387 
388 NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(ChangeListProperty, bool)
389 
390 /**
391  * Sync views on aFrame and all of aFrame's descendants (following placeholders),
392  * if aChange has nsChangeHint_SyncFrameView.
393  * Calls DoApplyRenderingChangeToTree on all aFrame's out-of-flow descendants
394  * (following placeholders), if aChange has nsChangeHint_RepaintFrame.
395  * aFrame should be some combination of nsChangeHint_SyncFrameView,
396  * nsChangeHint_RepaintFrame, nsChangeHint_UpdateOpacityLayer and
397  * nsChangeHint_SchedulePaint, nothing else.
398 */
399 static void SyncViewsAndInvalidateDescendants(nsIFrame* aFrame,
400                                               nsChangeHint aChange);
401 
402 static void StyleChangeReflow(nsIFrame* aFrame, nsChangeHint aHint);
403 
404 /**
405  * To handle nsChangeHint_ChildrenOnlyTransform we must iterate over the child
406  * frames of the SVG frame concerned. This helper function is used to find that
407  * SVG frame when we encounter nsChangeHint_ChildrenOnlyTransform to ensure
408  * that we iterate over the intended children, since sometimes we end up
409  * handling that hint while processing hints for one of the SVG frame's
410  * ancestor frames.
411  *
412  * The reason that we sometimes end up trying to process the hint for an
413  * ancestor of the SVG frame that the hint is intended for is due to the way we
414  * process restyle events. ApplyRenderingChangeToTree adjusts the frame from
415  * the restyled element's principle frame to one of its ancestor frames based
416  * on what nsCSSRendering::FindBackground returns, since the background style
417  * may have been propagated up to an ancestor frame. Processing hints using an
418  * ancestor frame is fine in general, but nsChangeHint_ChildrenOnlyTransform is
419  * a special case since it is intended to update the children of a specific
420  * frame.
421  */
422 static nsIFrame*
GetFrameForChildrenOnlyTransformHint(nsIFrame * aFrame)423 GetFrameForChildrenOnlyTransformHint(nsIFrame* aFrame)
424 {
425   if (aFrame->GetType() == nsGkAtoms::viewportFrame) {
426     // This happens if the root-<svg> is fixed positioned, in which case we
427     // can't use aFrame->GetContent() to find the primary frame, since
428     // GetContent() returns nullptr for ViewportFrame.
429     aFrame = aFrame->PrincipalChildList().FirstChild();
430   }
431   // For an nsHTMLScrollFrame, this will get the SVG frame that has the
432   // children-only transforms:
433   aFrame = aFrame->GetContent()->GetPrimaryFrame();
434   if (aFrame->GetType() == nsGkAtoms::svgOuterSVGFrame) {
435     aFrame = aFrame->PrincipalChildList().FirstChild();
436     MOZ_ASSERT(aFrame->GetType() == nsGkAtoms::svgOuterSVGAnonChildFrame,
437                "Where is the nsSVGOuterSVGFrame's anon child??");
438   }
439   MOZ_ASSERT(aFrame->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer),
440              "Children-only transforms only expected on SVG frames");
441   return aFrame;
442 }
443 
444 // Returns true if this function managed to successfully move a frame, and
445 // false if it could not process the position change, and a reflow should
446 // be performed instead.
447 bool
RecomputePosition(nsIFrame * aFrame)448 RecomputePosition(nsIFrame* aFrame)
449 {
450   // Don't process position changes on table frames, since we already handle
451   // the dynamic position change on the table wrapper frame, and the
452   // reflow-based fallback code path also ignores positions on inner table
453   // frames.
454   if (aFrame->GetType() == nsGkAtoms::tableFrame) {
455     return true;
456   }
457 
458   const nsStyleDisplay* display = aFrame->StyleDisplay();
459   // Changes to the offsets of a non-positioned element can safely be ignored.
460   if (display->mPosition == NS_STYLE_POSITION_STATIC) {
461     return true;
462   }
463 
464   // Don't process position changes on frames which have views or the ones which
465   // have a view somewhere in their descendants, because the corresponding view
466   // needs to be repositioned properly as well.
467   if (aFrame->HasView() ||
468       (aFrame->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW)) {
469     StyleChangeReflow(aFrame, nsChangeHint_NeedReflow);
470     return false;
471   }
472 
473   aFrame->SchedulePaint();
474 
475   // For relative positioning, we can simply update the frame rect
476   if (display->IsRelativelyPositionedStyle()) {
477     // Move the frame
478     if (display->mPosition == NS_STYLE_POSITION_STICKY) {
479       if (display->IsInnerTableStyle()) {
480         // We don't currently support sticky positioning of inner table
481         // elements (bug 975644). Bail.
482         //
483         // When this is fixed, remove the null-check for the computed
484         // offsets in nsTableRowFrame::ReflowChildren.
485         return true;
486       }
487 
488       // Update sticky positioning for an entire element at once, starting with
489       // the first continuation or ib-split sibling.
490       // It's rare that the frame we already have isn't already the first
491       // continuation or ib-split sibling, but it can happen when styles differ
492       // across continuations such as ::first-line or ::first-letter, and in
493       // those cases we will generally (but maybe not always) do the work twice.
494       nsIFrame* firstContinuation =
495         nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
496 
497       StickyScrollContainer::ComputeStickyOffsets(firstContinuation);
498       StickyScrollContainer* ssc =
499         StickyScrollContainer::GetStickyScrollContainerForFrame(
500           firstContinuation);
501       if (ssc) {
502         ssc->PositionContinuations(firstContinuation);
503       }
504     } else {
505       MOZ_ASSERT(NS_STYLE_POSITION_RELATIVE == display->mPosition,
506                  "Unexpected type of positioning");
507       for (nsIFrame* cont = aFrame; cont;
508            cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
509         nsIFrame* cb = cont->GetContainingBlock();
510         nsMargin newOffsets;
511         WritingMode wm = cb->GetWritingMode();
512         const LogicalSize size(wm, cb->GetContentRectRelativeToSelf().Size());
513 
514         ReflowInput::ComputeRelativeOffsets(wm, cont, size, newOffsets);
515         NS_ASSERTION(newOffsets.left == -newOffsets.right &&
516                      newOffsets.top == -newOffsets.bottom,
517                      "ComputeRelativeOffsets should return valid results");
518 
519         // ReflowInput::ApplyRelativePositioning would work here, but
520         // since we've already checked mPosition and aren't changing the frame's
521         // normal position, go ahead and add the offsets directly.
522         // First, we need to ensure that the normal position is stored though.
523         nsPoint normalPosition = cont->GetNormalPosition();
524         auto props = cont->Properties();
525         const auto& prop = nsIFrame::NormalPositionProperty();
526         if (!props.Get(prop)) {
527           props.Set(prop, new nsPoint(normalPosition));
528         }
529         cont->SetPosition(normalPosition +
530                           nsPoint(newOffsets.left, newOffsets.top));
531       }
532     }
533 
534     return true;
535   }
536 
537   // For the absolute positioning case, set up a fake HTML reflow state for
538   // the frame, and then get the offsets and size from it. If the frame's size
539   // doesn't need to change, we can simply update the frame position. Otherwise
540   // we fall back to a reflow.
541   nsRenderingContext rc(
542     aFrame->PresContext()->PresShell()->CreateReferenceRenderingContext());
543 
544   // Construct a bogus parent reflow state so that there's a usable
545   // containing block reflow state.
546   nsIFrame* parentFrame = aFrame->GetParent();
547   WritingMode parentWM = parentFrame->GetWritingMode();
548   WritingMode frameWM = aFrame->GetWritingMode();
549   LogicalSize parentSize = parentFrame->GetLogicalSize();
550 
551   nsFrameState savedState = parentFrame->GetStateBits();
552   ReflowInput parentReflowInput(aFrame->PresContext(), parentFrame, &rc,
553                                 parentSize);
554   parentFrame->RemoveStateBits(~nsFrameState(0));
555   parentFrame->AddStateBits(savedState);
556 
557   // The bogus parent state here was created with no parent state of its own,
558   // and therefore it won't have an mCBReflowInput set up.
559   // But we may need one (for InitCBReflowInput in a child state), so let's
560   // try to create one here for the cases where it will be needed.
561   Maybe<ReflowInput> cbReflowInput;
562   nsIFrame* cbFrame = parentFrame->GetContainingBlock();
563   if (cbFrame && (aFrame->GetContainingBlock() != parentFrame ||
564                   parentFrame->GetType() == nsGkAtoms::tableFrame)) {
565     LogicalSize cbSize = cbFrame->GetLogicalSize();
566     cbReflowInput.emplace(cbFrame->PresContext(), cbFrame, &rc, cbSize);
567     cbReflowInput->ComputedPhysicalMargin() = cbFrame->GetUsedMargin();
568     cbReflowInput->ComputedPhysicalPadding() = cbFrame->GetUsedPadding();
569     cbReflowInput->ComputedPhysicalBorderPadding() =
570       cbFrame->GetUsedBorderAndPadding();
571     parentReflowInput.mCBReflowInput = cbReflowInput.ptr();
572   }
573 
574   NS_WARNING_ASSERTION(parentSize.ISize(parentWM) != NS_INTRINSICSIZE &&
575                        parentSize.BSize(parentWM) != NS_INTRINSICSIZE,
576                        "parentSize should be valid");
577   parentReflowInput.SetComputedISize(std::max(parentSize.ISize(parentWM), 0));
578   parentReflowInput.SetComputedBSize(std::max(parentSize.BSize(parentWM), 0));
579   parentReflowInput.ComputedPhysicalMargin().SizeTo(0, 0, 0, 0);
580 
581   parentReflowInput.ComputedPhysicalPadding() = parentFrame->GetUsedPadding();
582   parentReflowInput.ComputedPhysicalBorderPadding() =
583     parentFrame->GetUsedBorderAndPadding();
584   LogicalSize availSize = parentSize.ConvertTo(frameWM, parentWM);
585   availSize.BSize(frameWM) = NS_INTRINSICSIZE;
586 
587   ViewportFrame* viewport = do_QueryFrame(parentFrame);
588   nsSize cbSize = viewport ?
589     viewport->AdjustReflowInputAsContainingBlock(&parentReflowInput).Size()
590     : aFrame->GetContainingBlock()->GetSize();
591   const nsMargin& parentBorder =
592     parentReflowInput.mStyleBorder->GetComputedBorder();
593   cbSize -= nsSize(parentBorder.LeftRight(), parentBorder.TopBottom());
594   LogicalSize lcbSize(frameWM, cbSize);
595   ReflowInput reflowInput(aFrame->PresContext(), parentReflowInput, aFrame,
596                           availSize, &lcbSize);
597   nsSize computedSize(reflowInput.ComputedWidth(),
598                       reflowInput.ComputedHeight());
599   computedSize.width += reflowInput.ComputedPhysicalBorderPadding().LeftRight();
600   if (computedSize.height != NS_INTRINSICSIZE) {
601     computedSize.height +=
602       reflowInput.ComputedPhysicalBorderPadding().TopBottom();
603   }
604   nsSize size = aFrame->GetSize();
605   // The RecomputePosition hint is not used if any offset changed between auto
606   // and non-auto. If computedSize.height == NS_INTRINSICSIZE then the new
607   // element height will be its intrinsic height, and since 'top' and 'bottom''s
608   // auto-ness hasn't changed, the old height must also be its intrinsic
609   // height, which we can assume hasn't changed (or reflow would have
610   // been triggered).
611   if (computedSize.width == size.width &&
612       (computedSize.height == NS_INTRINSICSIZE || computedSize.height == size.height)) {
613     // If we're solving for 'left' or 'top', then compute it here, in order to
614     // match the reflow code path.
615     if (NS_AUTOOFFSET == reflowInput.ComputedPhysicalOffsets().left) {
616       reflowInput.ComputedPhysicalOffsets().left = cbSize.width -
617                                           reflowInput.ComputedPhysicalOffsets().right -
618                                           reflowInput.ComputedPhysicalMargin().right -
619                                           size.width -
620                                           reflowInput.ComputedPhysicalMargin().left;
621     }
622 
623     if (NS_AUTOOFFSET == reflowInput.ComputedPhysicalOffsets().top) {
624       reflowInput.ComputedPhysicalOffsets().top = cbSize.height -
625                                          reflowInput.ComputedPhysicalOffsets().bottom -
626                                          reflowInput.ComputedPhysicalMargin().bottom -
627                                          size.height -
628                                          reflowInput.ComputedPhysicalMargin().top;
629     }
630 
631     // Move the frame
632     nsPoint pos(parentBorder.left + reflowInput.ComputedPhysicalOffsets().left +
633                 reflowInput.ComputedPhysicalMargin().left,
634                 parentBorder.top + reflowInput.ComputedPhysicalOffsets().top +
635                 reflowInput.ComputedPhysicalMargin().top);
636     aFrame->SetPosition(pos);
637 
638     return true;
639   }
640 
641   // Fall back to a reflow
642   StyleChangeReflow(aFrame, nsChangeHint_NeedReflow);
643   return false;
644 }
645 
646 static bool
HasBoxAncestor(nsIFrame * aFrame)647 HasBoxAncestor(nsIFrame* aFrame)
648 {
649   for (nsIFrame* f = aFrame; f; f = f->GetParent()) {
650     if (f->IsXULBoxFrame()) {
651       return true;
652     }
653   }
654   return false;
655 }
656 
657 /**
658  * Return true if aFrame's subtree has placeholders for out-of-flow content
659  * whose 'position' style's bit in aPositionMask is set.
660  */
661 static bool
FrameHasPositionedPlaceholderDescendants(nsIFrame * aFrame,uint32_t aPositionMask)662 FrameHasPositionedPlaceholderDescendants(nsIFrame* aFrame,
663                                          uint32_t aPositionMask)
664 {
665   MOZ_ASSERT(aPositionMask & (1 << NS_STYLE_POSITION_FIXED));
666 
667   for (nsIFrame::ChildListIterator lists(aFrame); !lists.IsDone(); lists.Next()) {
668     for (nsIFrame* f : lists.CurrentList()) {
669       if (f->GetType() == nsGkAtoms::placeholderFrame) {
670         nsIFrame* outOfFlow =
671           nsPlaceholderFrame::GetRealFrameForPlaceholder(f);
672         // If SVG text frames could appear here, they could confuse us since
673         // they ignore their position style ... but they can't.
674         NS_ASSERTION(!outOfFlow->IsSVGText(),
675                      "SVG text frames can't be out of flow");
676         if (aPositionMask & (1 << outOfFlow->StyleDisplay()->mPosition)) {
677           return true;
678         }
679       }
680       uint32_t positionMask = aPositionMask;
681       // NOTE:  It's tempting to check f->IsAbsPosContainingBlock() or
682       // f->IsFixedPosContainingBlock() here.  However, that would only
683       // be testing the *new* style of the frame, which might exclude
684       // descendants that currently have this frame as an abs-pos
685       // containing block.  Taking the codepath where we don't reframe
686       // could lead to an unsafe call to
687       // cont->MarkAsNotAbsoluteContainingBlock() before we've reframed
688       // the descendant and taken it off the absolute list.
689       if (FrameHasPositionedPlaceholderDescendants(f, positionMask)) {
690         return true;
691       }
692     }
693   }
694   return false;
695 }
696 
697 static bool
NeedToReframeForAddingOrRemovingTransform(nsIFrame * aFrame)698 NeedToReframeForAddingOrRemovingTransform(nsIFrame* aFrame)
699 {
700   static_assert(0 <= NS_STYLE_POSITION_ABSOLUTE &&
701                 NS_STYLE_POSITION_ABSOLUTE < 32, "Style constant out of range");
702   static_assert(0 <= NS_STYLE_POSITION_FIXED &&
703                 NS_STYLE_POSITION_FIXED < 32, "Style constant out of range");
704 
705   uint32_t positionMask;
706   // Don't call aFrame->IsPositioned here, since that returns true if
707   // the frame already has a transform, and we want to ignore that here
708   if (aFrame->IsAbsolutelyPositioned() || aFrame->IsRelativelyPositioned()) {
709     // This frame is a container for abs-pos descendants whether or not it
710     // has a transform.
711     // So abs-pos descendants are no problem; we only need to reframe if
712     // we have fixed-pos descendants.
713     positionMask = 1 << NS_STYLE_POSITION_FIXED;
714   } else {
715     // This frame may not be a container for abs-pos descendants already.
716     // So reframe if we have abs-pos or fixed-pos descendants.
717     positionMask =
718       (1 << NS_STYLE_POSITION_FIXED) | (1 << NS_STYLE_POSITION_ABSOLUTE);
719   }
720   for (nsIFrame* f = aFrame; f;
721        f = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(f)) {
722     if (FrameHasPositionedPlaceholderDescendants(f, positionMask)) {
723       return true;
724     }
725   }
726   return false;
727 }
728 
729 /* static */ nsIFrame*
GetNearestAncestorFrame(nsIContent * aContent)730 RestyleManagerBase::GetNearestAncestorFrame(nsIContent* aContent)
731 {
732   nsIFrame* ancestorFrame = nullptr;
733   for (nsIContent* ancestor = aContent->GetParent();
734        ancestor && !ancestorFrame;
735        ancestor = ancestor->GetParent()) {
736     ancestorFrame = ancestor->GetPrimaryFrame();
737   }
738   return ancestorFrame;
739 }
740 
741 /* static */ nsIFrame*
GetNextBlockInInlineSibling(FramePropertyTable * aPropTable,nsIFrame * aFrame)742 RestyleManagerBase::GetNextBlockInInlineSibling(FramePropertyTable* aPropTable,
743                                                 nsIFrame* aFrame)
744 {
745   NS_ASSERTION(!aFrame->GetPrevContinuation(),
746                "must start with the first continuation");
747   // Might we have ib-split siblings?
748   if (!(aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
749     // nothing more to do here
750     return nullptr;
751   }
752 
753   return static_cast<nsIFrame*>
754     (aPropTable->Get(aFrame, nsIFrame::IBSplitSibling()));
755 }
756 
757 static void
DoApplyRenderingChangeToTree(nsIFrame * aFrame,nsChangeHint aChange)758 DoApplyRenderingChangeToTree(nsIFrame* aFrame,
759                              nsChangeHint aChange)
760 {
761   NS_PRECONDITION(gInApplyRenderingChangeToTree,
762                   "should only be called within ApplyRenderingChangeToTree");
763 
764   for ( ; aFrame; aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame)) {
765     // Invalidate and sync views on all descendant frames, following placeholders.
766     // We don't need to update transforms in SyncViewsAndInvalidateDescendants, because
767     // there can't be any out-of-flows or popups that need to be transformed;
768     // all out-of-flow descendants of the transformed element must also be
769     // descendants of the transformed frame.
770     SyncViewsAndInvalidateDescendants(aFrame,
771       nsChangeHint(aChange & (nsChangeHint_RepaintFrame |
772                               nsChangeHint_SyncFrameView |
773                               nsChangeHint_UpdateOpacityLayer |
774                               nsChangeHint_SchedulePaint)));
775     // This must be set to true if the rendering change needs to
776     // invalidate content.  If it's false, a composite-only paint
777     // (empty transaction) will be scheduled.
778     bool needInvalidatingPaint = false;
779 
780     // if frame has view, will already be invalidated
781     if (aChange & nsChangeHint_RepaintFrame) {
782       // Note that this whole block will be skipped when painting is suppressed
783       // (due to our caller ApplyRendingChangeToTree() discarding the
784       // nsChangeHint_RepaintFrame hint).  If you add handling for any other
785       // hints within this block, be sure that they too should be ignored when
786       // painting is suppressed.
787       needInvalidatingPaint = true;
788       aFrame->InvalidateFrameSubtree();
789       if ((aChange & nsChangeHint_UpdateEffects) &&
790           aFrame->IsFrameOfType(nsIFrame::eSVG) &&
791           !(aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)) {
792         // Need to update our overflow rects:
793         nsSVGUtils::ScheduleReflowSVG(aFrame);
794       }
795     }
796     if (aChange & nsChangeHint_UpdateTextPath) {
797       if (aFrame->IsSVGText()) {
798         // Invalidate and reflow the entire SVGTextFrame:
799         NS_ASSERTION(aFrame->GetContent()->IsSVGElement(nsGkAtoms::textPath),
800                      "expected frame for a <textPath> element");
801         nsIFrame* text =
802           nsLayoutUtils::GetClosestFrameOfType(aFrame, nsGkAtoms::svgTextFrame);
803         NS_ASSERTION(text, "expected to find an ancestor SVGTextFrame");
804         static_cast<SVGTextFrame*>(text)->NotifyGlyphMetricsChange();
805       } else {
806         MOZ_ASSERT(false, "unexpected frame got nsChangeHint_UpdateTextPath");
807       }
808     }
809     if (aChange & nsChangeHint_UpdateOpacityLayer) {
810       // FIXME/bug 796697: we can get away with empty transactions for
811       // opacity updates in many cases.
812       needInvalidatingPaint = true;
813 
814       ActiveLayerTracker::NotifyRestyle(aFrame, eCSSProperty_opacity);
815       if (nsSVGIntegrationUtils::UsingEffectsForFrame(aFrame)) {
816         // SVG effects paints the opacity without using
817         // nsDisplayOpacity. We need to invalidate manually.
818         aFrame->InvalidateFrameSubtree();
819       }
820     }
821     if ((aChange & nsChangeHint_UpdateTransformLayer) &&
822         aFrame->IsTransformed()) {
823       ActiveLayerTracker::NotifyRestyle(aFrame, eCSSProperty_transform);
824       // If we're not already going to do an invalidating paint, see
825       // if we can get away with only updating the transform on a
826       // layer for this frame, and not scheduling an invalidating
827       // paint.
828       if (!needInvalidatingPaint) {
829         Layer* layer;
830         needInvalidatingPaint |= !aFrame->TryUpdateTransformOnly(&layer);
831 
832         if (!needInvalidatingPaint) {
833           // Since we're not going to paint, we need to resend animation
834           // data to the layer.
835           MOZ_ASSERT(layer, "this can't happen if there's no layer");
836           nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(
837             layer, nullptr, nullptr, aFrame, eCSSProperty_transform);
838         }
839       }
840     }
841     if (aChange & nsChangeHint_ChildrenOnlyTransform) {
842       needInvalidatingPaint = true;
843       nsIFrame* childFrame =
844         GetFrameForChildrenOnlyTransformHint(aFrame)->PrincipalChildList().FirstChild();
845       for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
846         ActiveLayerTracker::NotifyRestyle(childFrame, eCSSProperty_transform);
847       }
848     }
849     if (aChange & nsChangeHint_SchedulePaint) {
850       needInvalidatingPaint = true;
851     }
852     aFrame->SchedulePaint(needInvalidatingPaint
853                             ? nsIFrame::PAINT_DEFAULT
854                             : nsIFrame::PAINT_COMPOSITE_ONLY);
855   }
856 }
857 
858 static void
SyncViewsAndInvalidateDescendants(nsIFrame * aFrame,nsChangeHint aChange)859 SyncViewsAndInvalidateDescendants(nsIFrame* aFrame, nsChangeHint aChange)
860 {
861   NS_PRECONDITION(gInApplyRenderingChangeToTree,
862                   "should only be called within ApplyRenderingChangeToTree");
863   NS_ASSERTION(nsChangeHint_size_t(aChange) ==
864                           (aChange & (nsChangeHint_RepaintFrame |
865                                       nsChangeHint_SyncFrameView |
866                                       nsChangeHint_UpdateOpacityLayer |
867                                       nsChangeHint_SchedulePaint)),
868                "Invalid change flag");
869 
870   nsView* view = aFrame->GetView();
871   if (view) {
872     if (aChange & nsChangeHint_SyncFrameView) {
873       nsContainerFrame::SyncFrameViewProperties(aFrame->PresContext(), aFrame,
874                                                 nullptr, view);
875     }
876   }
877 
878   nsIFrame::ChildListIterator lists(aFrame);
879   for (; !lists.IsDone(); lists.Next()) {
880     for (nsIFrame* child : lists.CurrentList()) {
881       if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
882         // only do frames that don't have placeholders
883         if (nsGkAtoms::placeholderFrame == child->GetType()) {
884           // do the out-of-flow frame and its continuations
885           nsIFrame* outOfFlowFrame =
886             nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
887           DoApplyRenderingChangeToTree(outOfFlowFrame, aChange);
888         } else if (lists.CurrentID() == nsIFrame::kPopupList) {
889           DoApplyRenderingChangeToTree(child, aChange);
890         } else { // regular frame
891           SyncViewsAndInvalidateDescendants(child, aChange);
892         }
893       }
894     }
895   }
896 }
897 
898 static void
ApplyRenderingChangeToTree(nsIPresShell * aPresShell,nsIFrame * aFrame,nsChangeHint aChange)899 ApplyRenderingChangeToTree(nsIPresShell* aPresShell,
900                            nsIFrame* aFrame,
901                            nsChangeHint aChange)
902 {
903   // We check StyleDisplay()->HasTransformStyle() in addition to checking
904   // IsTransformed() since we can get here for some frames that don't support
905   // CSS transforms.
906   NS_ASSERTION(!(aChange & nsChangeHint_UpdateTransformLayer) ||
907                aFrame->IsTransformed() ||
908                aFrame->StyleDisplay()->HasTransformStyle(),
909                "Unexpected UpdateTransformLayer hint");
910 
911   if (aPresShell->IsPaintingSuppressed()) {
912     // Don't allow synchronous rendering changes when painting is turned off.
913     aChange &= ~nsChangeHint_RepaintFrame;
914     if (!aChange) {
915       return;
916     }
917   }
918 
919 // Trigger rendering updates by damaging this frame and any
920 // continuations of this frame.
921 #ifdef DEBUG
922   gInApplyRenderingChangeToTree = true;
923 #endif
924   if (aChange & nsChangeHint_RepaintFrame) {
925     // If the frame's background is propagated to an ancestor, walk up to
926     // that ancestor and apply the RepaintFrame change hint to it.
927     nsStyleContext* bgSC;
928     nsIFrame* propagatedFrame = aFrame;
929     while (!nsCSSRendering::FindBackground(propagatedFrame, &bgSC)) {
930       propagatedFrame = propagatedFrame->GetParent();
931       NS_ASSERTION(aFrame, "root frame must paint");
932     }
933 
934     if (propagatedFrame != aFrame) {
935       DoApplyRenderingChangeToTree(propagatedFrame, nsChangeHint_RepaintFrame);
936       aChange &= ~nsChangeHint_RepaintFrame;
937       if (!aChange) {
938         return;
939       }
940     }
941   }
942   DoApplyRenderingChangeToTree(aFrame, aChange);
943 #ifdef DEBUG
944   gInApplyRenderingChangeToTree = false;
945 #endif
946 }
947 
948 static void
AddSubtreeToOverflowTracker(nsIFrame * aFrame,OverflowChangedTracker & aOverflowChangedTracker)949 AddSubtreeToOverflowTracker(nsIFrame* aFrame,
950                             OverflowChangedTracker& aOverflowChangedTracker)
951 {
952   if (aFrame->FrameMaintainsOverflow()) {
953     aOverflowChangedTracker.AddFrame(aFrame,
954                                      OverflowChangedTracker::CHILDREN_CHANGED);
955   }
956   nsIFrame::ChildListIterator lists(aFrame);
957   for (; !lists.IsDone(); lists.Next()) {
958     for (nsIFrame* child : lists.CurrentList()) {
959       AddSubtreeToOverflowTracker(child, aOverflowChangedTracker);
960     }
961   }
962 }
963 
964 static void
StyleChangeReflow(nsIFrame * aFrame,nsChangeHint aHint)965 StyleChangeReflow(nsIFrame* aFrame, nsChangeHint aHint)
966 {
967   nsIPresShell::IntrinsicDirty dirtyType;
968   if (aHint & nsChangeHint_ClearDescendantIntrinsics) {
969     NS_ASSERTION(aHint & nsChangeHint_ClearAncestorIntrinsics,
970                  "Please read the comments in nsChangeHint.h");
971     NS_ASSERTION(aHint & nsChangeHint_NeedDirtyReflow,
972                  "ClearDescendantIntrinsics requires NeedDirtyReflow");
973     dirtyType = nsIPresShell::eStyleChange;
974   } else if ((aHint & nsChangeHint_UpdateComputedBSize) &&
975              aFrame->HasAnyStateBits(
976                NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE)) {
977     dirtyType = nsIPresShell::eStyleChange;
978   } else if (aHint & nsChangeHint_ClearAncestorIntrinsics) {
979     dirtyType = nsIPresShell::eTreeChange;
980   } else if ((aHint & nsChangeHint_UpdateComputedBSize) &&
981              HasBoxAncestor(aFrame)) {
982     // The frame's computed BSize is changing, and we have a box ancestor
983     // whose cached intrinsic height may need to be updated.
984     dirtyType = nsIPresShell::eTreeChange;
985   } else {
986     dirtyType = nsIPresShell::eResize;
987   }
988 
989   nsFrameState dirtyBits;
990   if (aFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
991     dirtyBits = nsFrameState(0);
992   } else if ((aHint & nsChangeHint_NeedDirtyReflow) ||
993              dirtyType == nsIPresShell::eStyleChange) {
994     dirtyBits = NS_FRAME_IS_DIRTY;
995   } else {
996     dirtyBits = NS_FRAME_HAS_DIRTY_CHILDREN;
997   }
998 
999   // If we're not going to clear any intrinsic sizes on the frames, and
1000   // there are no dirty bits to set, then there's nothing to do.
1001   if (dirtyType == nsIPresShell::eResize && !dirtyBits)
1002     return;
1003 
1004   nsIPresShell::ReflowRootHandling rootHandling;
1005   if (aHint & nsChangeHint_ReflowChangesSizeOrPosition) {
1006     rootHandling = nsIPresShell::ePositionOrSizeChange;
1007   } else {
1008     rootHandling = nsIPresShell::eNoPositionOrSizeChange;
1009   }
1010 
1011   do {
1012     aFrame->PresContext()->PresShell()->FrameNeedsReflow(
1013       aFrame, dirtyType, dirtyBits, rootHandling);
1014     aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame);
1015   } while (aFrame);
1016 }
1017 
1018 /* static */ nsIFrame*
GetNextContinuationWithSameStyle(nsIFrame * aFrame,nsStyleContext * aOldStyleContext,bool * aHaveMoreContinuations)1019 RestyleManagerBase::GetNextContinuationWithSameStyle(
1020   nsIFrame* aFrame, nsStyleContext* aOldStyleContext,
1021   bool* aHaveMoreContinuations)
1022 {
1023   // See GetPrevContinuationWithSameStyle about {ib} splits.
1024 
1025   nsIFrame* nextContinuation = aFrame->GetNextContinuation();
1026   if (!nextContinuation &&
1027       (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
1028     // We're the last continuation, so we have to hop back to the first
1029     // before getting the frame property
1030     nextContinuation =
1031       aFrame->FirstContinuation()->Properties().Get(nsIFrame::IBSplitSibling());
1032     if (nextContinuation) {
1033       nextContinuation =
1034         nextContinuation->Properties().Get(nsIFrame::IBSplitSibling());
1035     }
1036   }
1037 
1038   if (!nextContinuation) {
1039     return nullptr;
1040   }
1041 
1042   NS_ASSERTION(nextContinuation->GetContent() == aFrame->GetContent(),
1043                "unexpected content mismatch");
1044 
1045   nsStyleContext* nextStyle = nextContinuation->StyleContext();
1046   if (nextStyle != aOldStyleContext) {
1047     NS_ASSERTION(aOldStyleContext->GetPseudo() != nextStyle->GetPseudo() ||
1048                  aOldStyleContext->GetParent() != nextStyle->GetParent(),
1049                  "continuations should have the same style context");
1050     nextContinuation = nullptr;
1051     if (aHaveMoreContinuations) {
1052       *aHaveMoreContinuations = true;
1053     }
1054   }
1055   return nextContinuation;
1056 }
1057 
1058 nsresult
ProcessRestyledFrames(nsStyleChangeList & aChangeList)1059 RestyleManagerBase::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
1060 {
1061   NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
1062                "Someone forgot a script blocker");
1063   if (aChangeList.IsEmpty())
1064     return NS_OK;
1065 
1066   PROFILER_LABEL("RestyleManager", "ProcessRestyledFrames",
1067                  js::ProfileEntry::Category::CSS);
1068 
1069   nsPresContext* presContext = PresContext();
1070   FramePropertyTable* propTable = presContext->PropertyTable();
1071   nsCSSFrameConstructor* frameConstructor = presContext->FrameConstructor();
1072 
1073   // Make sure to not rebuild quote or counter lists while we're
1074   // processing restyles
1075   frameConstructor->BeginUpdate();
1076 
1077   // Mark frames so that we skip frames that die along the way, bug 123049.
1078   // A frame can be in the list multiple times with different hints. Further
1079   // optmization is possible if nsStyleChangeList::AppendChange could coalesce
1080   for (const nsStyleChangeData& data : aChangeList) {
1081     if (data.mFrame) {
1082       propTable->Set(data.mFrame, ChangeListProperty(), true);
1083     }
1084   }
1085 
1086   bool didUpdateCursor = false;
1087 
1088   for (const nsStyleChangeData& data : aChangeList) {
1089     nsIFrame* frame = data.mFrame;
1090     nsIContent* content = data.mContent;
1091     nsChangeHint hint = data.mHint;
1092     bool didReflowThisFrame = false;
1093 
1094     NS_ASSERTION(!(hint & nsChangeHint_AllReflowHints) ||
1095                  (hint & nsChangeHint_NeedReflow),
1096                  "Reflow hint bits set without actually asking for a reflow");
1097 
1098     // skip any frame that has been destroyed due to a ripple effect
1099     if (frame && !propTable->Get(frame, ChangeListProperty())) {
1100       continue;
1101     }
1102 
1103     if (frame && frame->GetContent() != content) {
1104       // XXXbz this is due to image maps messing with the primary frame of
1105       // <area>s.  See bug 135040.  Remove this block once that's fixed.
1106       frame = nullptr;
1107       if (!(hint & nsChangeHint_ReconstructFrame)) {
1108         continue;
1109       }
1110     }
1111 
1112     if ((hint & nsChangeHint_UpdateContainingBlock) && frame &&
1113         !(hint & nsChangeHint_ReconstructFrame)) {
1114       if (NeedToReframeForAddingOrRemovingTransform(frame) ||
1115           frame->GetType() == nsGkAtoms::fieldSetFrame ||
1116           frame->GetContentInsertionFrame() != frame) {
1117         // The frame has positioned children that need to be reparented, or
1118         // it can't easily be converted to/from being an abs-pos container correctly.
1119         hint |= nsChangeHint_ReconstructFrame;
1120       } else {
1121         for (nsIFrame* cont = frame; cont;
1122              cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
1123           // Normally frame construction would set state bits as needed,
1124           // but we're not going to reconstruct the frame so we need to set them.
1125           // It's because we need to set this state on each affected frame
1126           // that we can't coalesce nsChangeHint_UpdateContainingBlock hints up
1127           // to ancestors (i.e. it can't be an change hint that is handled for
1128           // descendants).
1129           if (cont->IsAbsPosContainingBlock()) {
1130             if (!cont->IsAbsoluteContainer() &&
1131                 (cont->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)) {
1132               cont->MarkAsAbsoluteContainingBlock();
1133             }
1134           } else {
1135             if (cont->IsAbsoluteContainer()) {
1136               if (cont->HasAbsolutelyPositionedChildren()) {
1137                 // If |cont| still has absolutely positioned children,
1138                 // we can't call MarkAsNotAbsoluteContainingBlock.  This
1139                 // will remove a frame list that still has children in
1140                 // it that we need to keep track of.
1141                 // The optimization of removing it isn't particularly
1142                 // important, although it does mean we skip some tests.
1143                 NS_WARNING("skipping removal of absolute containing block");
1144               } else {
1145                 cont->MarkAsNotAbsoluteContainingBlock();
1146               }
1147             }
1148           }
1149         }
1150       }
1151     }
1152 
1153     if ((hint & nsChangeHint_AddOrRemoveTransform) && frame &&
1154         !(hint & nsChangeHint_ReconstructFrame)) {
1155       for (nsIFrame* cont = frame; cont;
1156            cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
1157         if (cont->StyleDisplay()->HasTransform(cont)) {
1158           cont->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
1159         }
1160         // Don't remove NS_FRAME_MAY_BE_TRANSFORMED since it may still be
1161         // transformed by other means. It's OK to have the bit even if it's
1162         // not needed.
1163       }
1164     }
1165 
1166     if (hint & nsChangeHint_ReconstructFrame) {
1167       // If we ever start passing true here, be careful of restyles
1168       // that involve a reframe and animations.  In particular, if the
1169       // restyle we're processing here is an animation restyle, but
1170       // the style resolution we will do for the frame construction
1171       // happens async when we're not in an animation restyle already,
1172       // problems could arise.
1173       // We could also have problems with triggering of CSS transitions
1174       // on elements whose frames are reconstructed, since we depend on
1175       // the reconstruction happening synchronously.
1176       frameConstructor->RecreateFramesForContent(content, false,
1177         nsCSSFrameConstructor::REMOVE_FOR_RECONSTRUCTION, nullptr);
1178     } else {
1179       NS_ASSERTION(frame, "This shouldn't happen");
1180 
1181       if (!frame->FrameMaintainsOverflow()) {
1182         // frame does not maintain overflow rects, so avoid calling
1183         // FinishAndStoreOverflow on it:
1184         hint &= ~(nsChangeHint_UpdateOverflow |
1185                   nsChangeHint_ChildrenOnlyTransform |
1186                   nsChangeHint_UpdatePostTransformOverflow |
1187                   nsChangeHint_UpdateParentOverflow);
1188       }
1189 
1190       if (!(frame->GetStateBits() & NS_FRAME_MAY_BE_TRANSFORMED)) {
1191         // Frame can not be transformed, and thus a change in transform will
1192         // have no effect and we should not use the
1193         // nsChangeHint_UpdatePostTransformOverflow hint.
1194         hint &= ~nsChangeHint_UpdatePostTransformOverflow;
1195       }
1196 
1197       if (hint & nsChangeHint_UpdateEffects) {
1198         for (nsIFrame* cont = frame; cont;
1199              cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
1200           nsSVGEffects::UpdateEffects(cont);
1201         }
1202       }
1203       if ((hint & nsChangeHint_InvalidateRenderingObservers) ||
1204           ((hint & nsChangeHint_UpdateOpacityLayer) &&
1205            frame->IsFrameOfType(nsIFrame::eSVG) &&
1206            !(frame->GetStateBits() & NS_STATE_IS_OUTER_SVG))) {
1207         nsSVGEffects::InvalidateRenderingObservers(frame);
1208       }
1209       if (hint & nsChangeHint_NeedReflow) {
1210         StyleChangeReflow(frame, hint);
1211         didReflowThisFrame = true;
1212       }
1213 
1214       if ((hint & nsChangeHint_UpdateUsesOpacity) &&
1215           frame->IsFrameOfType(nsIFrame::eTablePart)) {
1216         NS_ASSERTION(hint & nsChangeHint_UpdateOpacityLayer,
1217                      "should only return UpdateUsesOpacity hint "
1218                      "when also returning UpdateOpacityLayer hint");
1219         // When an internal table part (including cells) changes between
1220         // having opacity 1 and non-1, it changes whether its
1221         // backgrounds (and those of table parts inside of it) are
1222         // painted as part of the table's nsDisplayTableBorderBackground
1223         // display item, or part of its own display item.  That requires
1224         // invalidation, so change UpdateOpacityLayer to RepaintFrame.
1225         hint &= ~nsChangeHint_UpdateOpacityLayer;
1226         hint |= nsChangeHint_RepaintFrame;
1227       }
1228 
1229       // Opacity disables preserve-3d, so if we toggle it, then we also need
1230       // to update the overflow areas of all potentially affected frames.
1231       if ((hint & nsChangeHint_UpdateUsesOpacity) &&
1232           frame->StyleDisplay()->mTransformStyle == NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D) {
1233         hint |= nsChangeHint_UpdateSubtreeOverflow;
1234       }
1235 
1236       if (hint & nsChangeHint_UpdateBackgroundPosition) {
1237         // For most frame types, DLBI can detect background position changes,
1238         // so we only need to schedule a paint.
1239         hint |= nsChangeHint_SchedulePaint;
1240         if (frame->IsFrameOfType(nsIFrame::eTablePart) ||
1241             frame->IsFrameOfType(nsIFrame::eMathML)) {
1242           // Table parts and MathML frames don't build display items for their
1243           // backgrounds, so DLBI can't detect background-position changes for
1244           // these frames. Repaint the whole frame.
1245           hint |= nsChangeHint_RepaintFrame;
1246         }
1247       }
1248 
1249       if (hint & (nsChangeHint_RepaintFrame | nsChangeHint_SyncFrameView |
1250                   nsChangeHint_UpdateOpacityLayer | nsChangeHint_UpdateTransformLayer |
1251                   nsChangeHint_ChildrenOnlyTransform | nsChangeHint_SchedulePaint)) {
1252         ApplyRenderingChangeToTree(presContext->PresShell(), frame, hint);
1253       }
1254       if ((hint & nsChangeHint_RecomputePosition) && !didReflowThisFrame) {
1255         ActiveLayerTracker::NotifyOffsetRestyle(frame);
1256         // It is possible for this to fall back to a reflow
1257         if (!RecomputePosition(frame)) {
1258           didReflowThisFrame = true;
1259         }
1260       }
1261       NS_ASSERTION(!(hint & nsChangeHint_ChildrenOnlyTransform) ||
1262                    (hint & nsChangeHint_UpdateOverflow),
1263                    "nsChangeHint_UpdateOverflow should be passed too");
1264       if (!didReflowThisFrame &&
1265           (hint & (nsChangeHint_UpdateOverflow |
1266                    nsChangeHint_UpdatePostTransformOverflow |
1267                    nsChangeHint_UpdateParentOverflow |
1268                    nsChangeHint_UpdateSubtreeOverflow))) {
1269         if (hint & nsChangeHint_UpdateSubtreeOverflow) {
1270           for (nsIFrame* cont = frame; cont; cont =
1271                  nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
1272             AddSubtreeToOverflowTracker(cont, mOverflowChangedTracker);
1273           }
1274           // The work we just did in AddSubtreeToOverflowTracker
1275           // subsumes some of the other hints:
1276           hint &= ~(nsChangeHint_UpdateOverflow |
1277                     nsChangeHint_UpdatePostTransformOverflow);
1278         }
1279         if (hint & nsChangeHint_ChildrenOnlyTransform) {
1280           // The overflow areas of the child frames need to be updated:
1281           nsIFrame* hintFrame = GetFrameForChildrenOnlyTransformHint(frame);
1282           nsIFrame* childFrame = hintFrame->PrincipalChildList().FirstChild();
1283           NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(frame),
1284                        "SVG frames should not have continuations "
1285                        "or ib-split siblings");
1286           NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(hintFrame),
1287                        "SVG frames should not have continuations "
1288                        "or ib-split siblings");
1289           for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
1290             MOZ_ASSERT(childFrame->IsFrameOfType(nsIFrame::eSVG),
1291                        "Not expecting non-SVG children");
1292             // If |childFrame| is dirty or has dirty children, we don't bother
1293             // updating overflows since that will happen when it's reflowed.
1294             if (!(childFrame->GetStateBits() &
1295                   (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN))) {
1296               mOverflowChangedTracker.AddFrame(childFrame,
1297                                         OverflowChangedTracker::CHILDREN_CHANGED);
1298             }
1299             NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(childFrame),
1300                          "SVG frames should not have continuations "
1301                          "or ib-split siblings");
1302             NS_ASSERTION(childFrame->GetParent() == hintFrame,
1303                          "SVG child frame not expected to have different parent");
1304           }
1305         }
1306         // If |frame| is dirty or has dirty children, we don't bother updating
1307         // overflows since that will happen when it's reflowed.
1308         if (!(frame->GetStateBits() &
1309               (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN))) {
1310           if (hint & (nsChangeHint_UpdateOverflow |
1311                       nsChangeHint_UpdatePostTransformOverflow)) {
1312             OverflowChangedTracker::ChangeKind changeKind;
1313             // If we have both nsChangeHint_UpdateOverflow and
1314             // nsChangeHint_UpdatePostTransformOverflow,
1315             // CHILDREN_CHANGED is selected as it is
1316             // strictly stronger.
1317             if (hint & nsChangeHint_UpdateOverflow) {
1318               changeKind = OverflowChangedTracker::CHILDREN_CHANGED;
1319             } else {
1320               changeKind = OverflowChangedTracker::TRANSFORM_CHANGED;
1321             }
1322             for (nsIFrame* cont = frame; cont; cont =
1323                    nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
1324               mOverflowChangedTracker.AddFrame(cont, changeKind);
1325             }
1326           }
1327           // UpdateParentOverflow hints need to be processed in addition
1328           // to the above, since if the processing of the above hints
1329           // yields no change, the update will not propagate to the
1330           // parent.
1331           if (hint & nsChangeHint_UpdateParentOverflow) {
1332             MOZ_ASSERT(frame->GetParent(),
1333                        "shouldn't get style hints for the root frame");
1334             for (nsIFrame* cont = frame; cont; cont =
1335                    nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
1336               mOverflowChangedTracker.AddFrame(cont->GetParent(),
1337                                    OverflowChangedTracker::CHILDREN_CHANGED);
1338             }
1339           }
1340         }
1341       }
1342       if ((hint & nsChangeHint_UpdateCursor) && !didUpdateCursor) {
1343         presContext->PresShell()->SynthesizeMouseMove(false);
1344         didUpdateCursor = true;
1345       }
1346     }
1347   }
1348 
1349   frameConstructor->EndUpdate();
1350 
1351   // cleanup references and verify the style tree.  Note that the latter needs
1352   // to happen once we've processed the whole list, since until then the tree
1353   // is not in fact in a consistent state.
1354   for (const nsStyleChangeData& data : aChangeList) {
1355     if (data.mFrame) {
1356       propTable->Delete(data.mFrame, ChangeListProperty());
1357     }
1358 
1359 #ifdef DEBUG
1360     // reget frame from content since it may have been regenerated...
1361     if (data.mContent) {
1362       nsIFrame* frame = data.mContent->GetPrimaryFrame();
1363       if (frame) {
1364         DebugVerifyStyleTree(frame);
1365       }
1366     } else if (!data.mFrame ||
1367                data.mFrame->GetType() != nsGkAtoms::viewportFrame) {
1368       NS_WARNING("Unable to test style tree integrity -- no content node "
1369                  "(and not a viewport frame)");
1370     }
1371 #endif
1372   }
1373 
1374   aChangeList.Clear();
1375   return NS_OK;
1376 }
1377 
1378 } // namespace mozilla
1379