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 /**
8  * Code responsible for managing style changes: tracking what style
9  * changes need to happen, scheduling them, and doing them.
10  */
11 
12 #include "mozilla/GeckoRestyleManager.h"
13 
14 #include <algorithm>  // For std::max
15 #include "gfxContext.h"
16 #include "mozilla/EffectSet.h"
17 #include "mozilla/GeckoStyleContext.h"
18 #include "mozilla/EventStates.h"
19 #include "mozilla/UndisplayedNode.h"
20 #include "mozilla/ViewportFrame.h"
21 #include "mozilla/css/StyleRule.h"  // For nsCSSSelector
22 #include "mozilla/dom/MutationEventBinding.h"
23 #include "nsLayoutUtils.h"
24 #include "AnimationCommon.h"  // For GetLayerAnimationInfo
25 #include "FrameLayerBuilder.h"
26 #include "GeckoProfiler.h"
27 #include "nsAutoPtr.h"
28 #include "nsStyleChangeList.h"
29 #include "nsRuleProcessorData.h"
30 #include "nsStyleContextInlines.h"
31 #include "nsStyleSet.h"
32 #include "nsStyleUtil.h"
33 #include "nsCSSFrameConstructor.h"
34 #include "SVGObserverUtils.h"
35 #include "nsCSSPseudoElements.h"
36 #include "nsCSSRendering.h"
37 #include "nsAnimationManager.h"
38 #include "nsTransitionManager.h"
39 #include "nsViewManager.h"
40 #include "nsSVGIntegrationUtils.h"
41 #include "nsCSSAnonBoxes.h"
42 #include "nsContainerFrame.h"
43 #include "nsPlaceholderFrame.h"
44 #include "nsBlockFrame.h"
45 #include "SVGTextFrame.h"
46 #include "StickyScrollContainer.h"
47 #include "nsIRootBox.h"
48 #include "nsContentUtils.h"
49 #include "nsIFrameInlines.h"
50 #include "ActiveLayerTracker.h"
51 #include "nsDisplayList.h"
52 #include "RestyleTrackerInlines.h"
53 #include "nsSMILAnimationController.h"
54 #include "nsCSSRuleProcessor.h"
55 #include "ChildIterator.h"
56 
57 #ifdef ACCESSIBILITY
58 #include "nsAccessibilityService.h"
59 #endif
60 
61 namespace mozilla {
62 
63 using namespace layers;
64 using namespace dom;
65 
66 #define LOG_RESTYLE_CONTINUE(reason_, ...) \
67   LOG_RESTYLE("continuing restyle since " reason_, ##__VA_ARGS__)
68 
69 #ifdef RESTYLE_LOGGING
FrameTagToString(const nsIFrame * aFrame)70 static nsCString FrameTagToString(const nsIFrame* aFrame) {
71   nsCString result;
72   aFrame->ListTag(result);
73   return result;
74 }
75 
ElementTagToString(dom::Element * aElement)76 static nsCString ElementTagToString(dom::Element* aElement) {
77   nsCString result;
78   nsDependentAtomString buf(aElement->NodeInfo()->NameAtom());
79   result.AppendPrintf("(%s@%p)", NS_ConvertUTF16toUTF8(buf).get(), aElement);
80   return result;
81 }
82 #endif
83 
GeckoRestyleManager(nsPresContext * aPresContext)84 GeckoRestyleManager::GeckoRestyleManager(nsPresContext* aPresContext)
85     : RestyleManager(StyleBackendType::Gecko, aPresContext),
86       mDoRebuildAllStyleData(false),
87       mInRebuildAllStyleData(false),
88       mSkipAnimationRules(false),
89       mHavePendingNonAnimationRestyles(false),
90       mRebuildAllExtraHint(nsChangeHint(0)),
91       mRebuildAllRestyleHint(nsRestyleHint(0)),
92       mReframingStyleContexts(nullptr),
93       mPendingRestyles(ELEMENT_HAS_PENDING_RESTYLE |
94                        ELEMENT_IS_POTENTIAL_RESTYLE_ROOT |
95                        ELEMENT_IS_CONDITIONAL_RESTYLE_ANCESTOR),
96       mIsProcessingRestyles(false)
97 #ifdef RESTYLE_LOGGING
98       ,
99       mLoggingDepth(0)
100 #endif
101 {
102   mPendingRestyles.Init(this);
103 }
104 
GetNearestAncestorFrame(nsIContent * aContent)105 static nsIFrame* GetNearestAncestorFrame(nsIContent* aContent) {
106   nsIFrame* ancestorFrame = nullptr;
107   for (nsIContent* ancestor = aContent->GetParent(); ancestor && !ancestorFrame;
108        ancestor = ancestor->GetParent()) {
109     ancestorFrame = ancestor->GetPrimaryFrame();
110   }
111   return ancestorFrame;
112 }
113 
GetNextBlockInInlineSibling(nsIFrame * aFrame)114 static nsIFrame* GetNextBlockInInlineSibling(nsIFrame* aFrame) {
115   NS_ASSERTION(!aFrame->GetPrevContinuation(),
116                "must start with the first continuation");
117   // Might we have ib-split siblings?
118   if (!(aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
119     // nothing more to do here
120     return nullptr;
121   }
122 
123   return aFrame->GetProperty(nsIFrame::IBSplitSibling());
124 }
125 
126 /**
127  * Get the next continuation or similar ib-split sibling (assuming
128  * block/inline alternation), conditionally on it having the same style.
129  *
130  * Since this is used when deciding to copy the new style context, it
131  * takes as an argument the old style context to check if the style is
132  * the same.  When it is used in other contexts (i.e., where the next
133  * continuation would already have the new style context), the current
134  * style context should be passed.
135  */
GetNextContinuationWithSameStyle(nsIFrame * aFrame,GeckoStyleContext * aOldStyleContext,bool * aHaveMoreContinuations=nullptr)136 static nsIFrame* GetNextContinuationWithSameStyle(
137     nsIFrame* aFrame, GeckoStyleContext* aOldStyleContext,
138     bool* aHaveMoreContinuations = nullptr) {
139   // See GetPrevContinuationWithSameStyle about {ib} splits.
140 
141   nsIFrame* nextContinuation = aFrame->GetNextContinuation();
142   if (!nextContinuation &&
143       (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
144     // We're the last continuation, so we have to hop back to the first
145     // before getting the frame property
146     nextContinuation =
147         aFrame->FirstContinuation()->GetProperty(nsIFrame::IBSplitSibling());
148     if (nextContinuation) {
149       nextContinuation =
150           nextContinuation->GetProperty(nsIFrame::IBSplitSibling());
151     }
152   }
153 
154   if (!nextContinuation) {
155     return nullptr;
156   }
157 
158   NS_ASSERTION(nextContinuation->GetContent() == aFrame->GetContent(),
159                "unexpected content mismatch");
160 
161   GeckoStyleContext* nextStyle = nextContinuation->StyleContext()->AsGecko();
162   if (nextStyle != aOldStyleContext) {
163     NS_ASSERTION(aOldStyleContext->GetPseudo() != nextStyle->GetPseudo() ||
164                      aOldStyleContext->GetParent() != nextStyle->GetParent(),
165                  "continuations should have the same style context");
166     nextContinuation = nullptr;
167     if (aHaveMoreContinuations) {
168       *aHaveMoreContinuations = true;
169     }
170   }
171   return nextContinuation;
172 }
173 
RestyleElement(Element * aElement,nsIFrame * aPrimaryFrame,nsChangeHint aMinHint,RestyleTracker & aRestyleTracker,nsRestyleHint aRestyleHint,const RestyleHintData & aRestyleHintData)174 void GeckoRestyleManager::RestyleElement(
175     Element* aElement, nsIFrame* aPrimaryFrame, nsChangeHint aMinHint,
176     RestyleTracker& aRestyleTracker, nsRestyleHint aRestyleHint,
177     const RestyleHintData& aRestyleHintData) {
178   MOZ_ASSERT(mReframingStyleContexts, "should have rsc");
179   NS_ASSERTION(aPrimaryFrame == aElement->GetPrimaryFrame(),
180                "frame/content mismatch");
181   if (aPrimaryFrame && aPrimaryFrame->GetContent() != aElement) {
182     // XXXbz this is due to image maps messing with the primary frame pointer
183     // of <area>s.  See bug 135040.  We can remove this block once that's fixed.
184     aPrimaryFrame = nullptr;
185   }
186   NS_ASSERTION(!aPrimaryFrame || aPrimaryFrame->GetContent() == aElement,
187                "frame/content mismatch");
188 
189   // If we're restyling the root element and there are 'rem' units in
190   // use, handle dynamic changes to the definition of a 'rem' here.
191   if (PresContext()->UsesRootEMUnits() && aPrimaryFrame &&
192       !mInRebuildAllStyleData) {
193     GeckoStyleContext* oldContext = aPrimaryFrame->StyleContext()->AsGecko();
194     if (!oldContext->GetParent()) {  // check that we're the root element
195       RefPtr<GeckoStyleContext> newContext = StyleSet()->ResolveStyleFor(
196           aElement, nullptr /* == oldContext->GetParent() */);
197       if (oldContext->StyleFont()->mFont.size !=
198           newContext->StyleFont()->mFont.size) {
199         // The basis for 'rem' units has changed.
200         mRebuildAllRestyleHint |= aRestyleHint;
201         if (aRestyleHint & eRestyle_SomeDescendants) {
202           mRebuildAllRestyleHint |= eRestyle_Subtree;
203         }
204         mRebuildAllExtraHint |= aMinHint;
205         StartRebuildAllStyleData(aRestyleTracker);
206         return;
207       }
208     }
209   }
210 
211   if (aMinHint & nsChangeHint_ReconstructFrame) {
212     FrameConstructor()->RecreateFramesForContent(
213         aElement, nsCSSFrameConstructor::InsertionKind::Sync);
214   } else if (aPrimaryFrame) {
215     ComputeAndProcessStyleChange(aPrimaryFrame, aMinHint, aRestyleTracker,
216                                  aRestyleHint, aRestyleHintData);
217   } else if (aRestyleHint & ~eRestyle_LaterSiblings) {
218     // We're restyling an element with no frame, so we should try to
219     // make one if its new style says it should have one.  But in order
220     // to try to honor the restyle hint (which we'd like to do so that,
221     // for example, an animation-only style flush doesn't flush other
222     // buffered style changes), we only do this if the restyle hint says
223     // we have *some* restyling for this frame.  This means we'll
224     // potentially get ahead of ourselves in that case, but not as much
225     // as we would if we didn't check the restyle hint.
226     nsStyleContext* newContext =
227         FrameConstructor()->MaybeRecreateFramesForElement(aElement);
228     if (newContext &&
229         newContext->StyleDisplay()->mDisplay == StyleDisplay::Contents) {
230       // Style change for a display:contents node that did not recreate frames.
231       ComputeAndProcessStyleChange(newContext->AsGecko(), aElement, aMinHint,
232                                    aRestyleTracker, aRestyleHint,
233                                    aRestyleHintData);
234     }
235   }
236 }
237 
ReframingStyleContexts(GeckoRestyleManager * aRestyleManager)238 GeckoRestyleManager::ReframingStyleContexts ::ReframingStyleContexts(
239     GeckoRestyleManager* aRestyleManager)
240     : mRestyleManager(aRestyleManager),
241       mRestorePointer(mRestyleManager->mReframingStyleContexts) {
242   MOZ_ASSERT(!mRestyleManager->mReframingStyleContexts,
243              "shouldn't construct recursively");
244   mRestyleManager->mReframingStyleContexts = this;
245 }
246 
~ReframingStyleContexts()247 GeckoRestyleManager::ReframingStyleContexts::~ReframingStyleContexts() {
248   // Before we go away, we need to flush out any frame construction that
249   // was enqueued, so that we initiate transitions.
250   // Note that this is a little bit evil in that we're calling into code
251   // that calls our member functions from our destructor, but it's at
252   // the beginning of our destructor, so it shouldn't be too bad.
253   mRestyleManager->PresContext()->FrameConstructor()->CreateNeededFrames();
254 }
255 
256 static inline dom::Element* ElementForStyleContext(
257     nsIContent* aParentContent, nsIFrame* aFrame,
258     CSSPseudoElementType aPseudoType);
259 
260 // Forwarded nsIDocumentObserver method, to handle restyling (and
261 // passing the notification to the frame).
ContentStateChanged(nsIContent * aContent,EventStates aStateMask)262 void GeckoRestyleManager::ContentStateChanged(nsIContent* aContent,
263                                               EventStates aStateMask) {
264   MOZ_ASSERT(!mInStyleRefresh);
265 
266   // XXXbz it would be good if this function only took Elements, but
267   // we'd have to make ESM guarantee that usefully.
268   if (!aContent->IsElement()) {
269     return;
270   }
271 
272   Element* aElement = aContent->AsElement();
273 
274   nsChangeHint changeHint;
275   ContentStateChangedInternal(aElement, aStateMask, &changeHint);
276 
277   // Assemble what we'll need to calculate the nsRestyleHint.
278   nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
279   CSSPseudoElementType pseudoType = CSSPseudoElementType::NotPseudo;
280   if (primaryFrame) {
281     pseudoType = primaryFrame->StyleContext()->GetPseudoType();
282   }
283 
284   nsStyleSet* styleSet = PresContext()->StyleSet()->AsGecko();
285   MOZ_ASSERT(styleSet);
286 
287   nsRestyleHint restyleHint;
288   if (pseudoType >= CSSPseudoElementType::Count) {
289     restyleHint = styleSet->HasStateDependentStyle(aElement, aStateMask);
290   } else if (nsCSSPseudoElements::PseudoElementSupportsUserActionState(
291                  pseudoType)) {
292     // If aElement is a pseudo-element, we want to check to see whether there
293     // are any state-dependent rules applying to that pseudo.
294     Element* ancestor =
295         ElementForStyleContext(nullptr, primaryFrame, pseudoType);
296     restyleHint = styleSet->HasStateDependentStyle(ancestor, pseudoType,
297                                                    aElement, aStateMask);
298   } else {
299     restyleHint = nsRestyleHint(0);
300   }
301 
302   if (aStateMask.HasState(NS_EVENT_STATE_HOVER) && restyleHint != 0) {
303     IncrementHoverGeneration();
304   }
305 
306   PostRestyleEvent(aElement, restyleHint, changeHint);
307 }
308 
309 // Forwarded nsIMutationObserver method, to handle restyling.
AttributeWillChange(Element * aElement,int32_t aNameSpaceID,nsAtom * aAttribute,int32_t aModType,const nsAttrValue * aNewValue)310 void GeckoRestyleManager::AttributeWillChange(Element* aElement,
311                                               int32_t aNameSpaceID,
312                                               nsAtom* aAttribute,
313                                               int32_t aModType,
314                                               const nsAttrValue* aNewValue) {
315   MOZ_ASSERT(!mInStyleRefresh);
316 
317   RestyleHintData rsdata;
318   nsRestyleHint rshint = StyleSet()->HasAttributeDependentStyle(
319       aElement, aNameSpaceID, aAttribute, aModType, false, aNewValue, rsdata);
320   PostRestyleEvent(aElement, rshint, nsChangeHint(0), &rsdata);
321 }
322 
323 // Forwarded nsIMutationObserver method, to handle restyling (and
324 // passing the notification to the frame).
AttributeChanged(Element * aElement,int32_t aNameSpaceID,nsAtom * aAttribute,int32_t aModType,const nsAttrValue * aOldValue)325 void GeckoRestyleManager::AttributeChanged(Element* aElement,
326                                            int32_t aNameSpaceID,
327                                            nsAtom* aAttribute, int32_t aModType,
328                                            const nsAttrValue* aOldValue) {
329   MOZ_ASSERT(!mInStyleRefresh);
330 
331   // Hold onto the PresShell to prevent ourselves from being destroyed.
332   // XXXbz how, exactly, would this attribute change cause us to be
333   // destroyed from inside this function?
334   nsCOMPtr<nsIPresShell> shell = PresContext()->GetPresShell();
335   mozilla::Unused << shell;  // Unused within this function
336 
337   // Get the frame associated with the content which is the highest in the frame
338   // tree
339   nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
340 
341 #if 0
342   NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
343      ("RestyleManager::AttributeChanged: content=%p[%s] frame=%p",
344       aContent, ContentTag(aElement, 0), frame));
345 #endif
346 
347   // the style tag has its own interpretation based on aHint
348   nsChangeHint hint = aElement->GetAttributeChangeHint(aAttribute, aModType);
349 
350   bool reframe = (hint & nsChangeHint_ReconstructFrame) != 0;
351 
352 #ifdef MOZ_XUL
353   // The following listbox widget trap prevents offscreen listbox widget
354   // content from being removed and re-inserted (which is what would
355   // happen otherwise).
356   if (!primaryFrame && !reframe) {
357     int32_t namespaceID;
358     nsAtom* tag = PresContext()->Document()->BindingManager()->ResolveTag(
359         aElement, &namespaceID);
360 
361     if (namespaceID == kNameSpaceID_XUL &&
362         (tag == nsGkAtoms::listitem || tag == nsGkAtoms::listcell))
363       return;
364   }
365 #endif  // MOZ_XUL
366 
367   if (primaryFrame) {
368     // See if we have appearance information for a theme.
369     const nsStyleDisplay* disp = primaryFrame->StyleDisplay();
370     if (disp->mAppearance) {
371       nsITheme* theme = PresContext()->GetTheme();
372       if (theme && theme->ThemeSupportsWidget(PresContext(), primaryFrame,
373                                               disp->mAppearance)) {
374         bool repaint = false;
375         theme->WidgetStateChanged(primaryFrame, disp->mAppearance, aAttribute,
376                                   &repaint, aOldValue);
377         if (repaint) hint |= nsChangeHint_RepaintFrame;
378       }
379     }
380 
381     // let the frame deal with it now, so we don't have to deal later
382     primaryFrame->AttributeChanged(aNameSpaceID, aAttribute, aModType);
383     // XXXwaterson should probably check for IB split siblings
384     // here, and propagate the AttributeChanged notification to
385     // them, as well. Currently, inline frames don't do anything on
386     // this notification, so it's not that big a deal.
387   }
388 
389   // See if we can optimize away the style re-resolution -- must be called after
390   // the frame's AttributeChanged() in case it does something that affects the
391   // style
392   RestyleHintData rsdata;
393   nsRestyleHint rshint = StyleSet()->HasAttributeDependentStyle(
394       aElement, aNameSpaceID, aAttribute, aModType, true, aOldValue, rsdata);
395   PostRestyleEvent(aElement, rshint, hint, &rsdata);
396 }
397 
RebuildAllStyleData(nsChangeHint aExtraHint,nsRestyleHint aRestyleHint)398 void GeckoRestyleManager::RebuildAllStyleData(nsChangeHint aExtraHint,
399                                               nsRestyleHint aRestyleHint) {
400   NS_ASSERTION(!(aExtraHint & nsChangeHint_ReconstructFrame),
401                "Should not reconstruct the root of the frame tree.  "
402                "Use ReconstructDocElementHierarchy instead.");
403   MOZ_ASSERT(!(aRestyleHint & ~(eRestyle_Subtree | eRestyle_ForceDescendants)),
404              "the only bits allowed in aRestyleHint are eRestyle_Subtree and "
405              "eRestyle_ForceDescendants");
406 
407   mRebuildAllExtraHint |= aExtraHint;
408   mRebuildAllRestyleHint |= aRestyleHint;
409 
410   // Processing the style changes could cause a flush that propagates to
411   // the parent frame and thus destroys the pres shell, so we must hold
412   // a reference.
413   nsCOMPtr<nsIPresShell> presShell = PresContext()->GetPresShell();
414   if (!presShell || !presShell->GetRootFrame()) {
415     mDoRebuildAllStyleData = false;
416     return;
417   }
418 
419   // Make sure that the viewmanager will outlive the presshell
420   RefPtr<nsViewManager> vm = presShell->GetViewManager();
421   mozilla::Unused << vm;  // Not used within this function
422 
423   // We may reconstruct frames below and hence process anything that is in the
424   // tree. We don't want to get notified to process those items again after.
425   presShell->GetDocument()->FlushPendingNotifications(
426       FlushType::ContentAndNotify);
427 
428   nsAutoScriptBlocker scriptBlocker;
429 
430   mDoRebuildAllStyleData = true;
431 
432   ProcessPendingRestyles();
433 }
434 
StartRebuildAllStyleData(RestyleTracker & aRestyleTracker)435 void GeckoRestyleManager::StartRebuildAllStyleData(
436     RestyleTracker& aRestyleTracker) {
437   MOZ_ASSERT(mIsProcessingRestyles);
438 
439   nsIFrame* rootFrame = PresContext()->PresShell()->GetRootFrame();
440   if (!rootFrame) {
441     // No need to do anything.
442     return;
443   }
444 
445   mInRebuildAllStyleData = true;
446 
447   // Tell the style set to get the old rule tree out of the way
448   // so we can recalculate while maintaining rule tree immutability
449   nsresult rv = StyleSet()->BeginReconstruct();
450   if (NS_FAILED(rv)) {
451     MOZ_CRASH("unable to rebuild style data");
452   }
453 
454   nsRestyleHint restyleHint = mRebuildAllRestyleHint;
455   nsChangeHint changeHint = mRebuildAllExtraHint;
456   mRebuildAllExtraHint = nsChangeHint(0);
457   mRebuildAllRestyleHint = nsRestyleHint(0);
458 
459   restyleHint |= eRestyle_ForceDescendants;
460 
461   if (!(restyleHint & eRestyle_Subtree) &&
462       (restyleHint & ~(eRestyle_Force | eRestyle_ForceDescendants))) {
463     // We want this hint to apply to the root node's primary frame
464     // rather than the root frame, since it's the primary frame that has
465     // the styles for the root element (rather than the ancestors of the
466     // primary frame whose mContent is the root node but which have
467     // different styles).  If we use up the hint for one of the
468     // ancestors that we hit first, then we'll fail to do the restyling
469     // we need to do.
470     Element* root = PresContext()->Document()->GetRootElement();
471     if (root) {
472       // If the root element is gone, dropping the hint on the floor
473       // should be fine.
474       aRestyleTracker.AddPendingRestyle(root, restyleHint, nsChangeHint(0));
475     }
476     restyleHint = nsRestyleHint(0);
477   }
478 
479   // Recalculate all of the style contexts for the document, from the
480   // root frame.  We can't do this with a change hint, since we can't
481   // post a change hint for the root frame.
482   // Note that we can ignore the return value of ComputeStyleChangeFor
483   // because we never need to reframe the root frame.
484   // XXX Does it matter that we're passing aExtraHint to the real root
485   // frame and not the root node's primary frame?  (We could do
486   // roughly what we do for aRestyleHint above.)
487   ComputeAndProcessStyleChange(rootFrame, changeHint, aRestyleTracker,
488                                restyleHint, RestyleHintData());
489 }
490 
FinishRebuildAllStyleData()491 void GeckoRestyleManager::FinishRebuildAllStyleData() {
492   MOZ_ASSERT(mInRebuildAllStyleData, "bad caller");
493 
494   // Tell the style set it's safe to destroy the old rule tree.  We
495   // must do this after the ProcessRestyledFrames call in case the
496   // change list has frame reconstructs in it (since frames to be
497   // reconstructed will still have their old style context pointers
498   // until they are destroyed).
499   StyleSet()->EndReconstruct();
500 
501   mInRebuildAllStyleData = false;
502 }
503 
ProcessPendingRestyles()504 void GeckoRestyleManager::ProcessPendingRestyles() {
505   NS_PRECONDITION(PresContext()->Document(), "No document?  Pshaw!");
506   NS_PRECONDITION(!nsContentUtils::IsSafeToRunScript(),
507                   "Missing a script blocker!");
508   // NOTE(emilio): Gecko calls into here from RebuildAllStyleData synchronously,
509   // without ensuring that all the media-query state is up-to-date.
510   //
511   // That is slightly bogus on its own, but it's long-standing and working on
512   // not rebuilding the rule tree synchronously on the old style system at this
513   // point is probably not worth the effort. Note bug 1089417 comment 21, which
514   // may or may not be an issue here.
515   MOZ_ASSERT(
516       !PresContext()->HasPendingMediaQueryUpdates() || mDoRebuildAllStyleData,
517       "Someone forgot to update media queries?");
518 
519   // First do any queued-up frame creation.  (We should really
520   // merge this into the rest of the process, though; see bug 827239.)
521   PresContext()->FrameConstructor()->CreateNeededFrames();
522 
523   // Process non-animation restyles...
524   MOZ_ASSERT(!mIsProcessingRestyles,
525              "Nesting calls to ProcessPendingRestyles?");
526   mIsProcessingRestyles = true;
527 
528   // Before we process any restyles, we need to ensure that style
529   // resulting from any animations is up-to-date, so that if any style
530   // changes we cause trigger transitions, we have the correct old style
531   // for starting the transition.
532   bool haveNonAnimation =
533       mHavePendingNonAnimationRestyles || mDoRebuildAllStyleData;
534   if (haveNonAnimation) {
535     ++mAnimationGeneration;
536     UpdateOnlyAnimationStyles();
537   } else {
538     // If we don't have non-animation style updates, then we have queued
539     // up animation style updates from the refresh driver tick.  This
540     // doesn't necessarily include *all* animation style updates, since
541     // we might be suppressing main-thread updates for some animations,
542     // so we don't want to call UpdateOnlyAnimationStyles, which updates
543     // all animations.  In other words, the work that we're about to do
544     // to process the pending restyles queue is a *subset* of the work
545     // that UpdateOnlyAnimationStyles would do, since we're *not*
546     // updating transitions that are running on the compositor thread
547     // and suppressed on the main thread.
548     //
549     // But when we update those styles, we want to suppress updates to
550     // transitions just like we do in UpdateOnlyAnimationStyles.  So we
551     // want to tell the transition manager to act as though we're in
552     // UpdateOnlyAnimationStyles.
553     //
554     // FIXME: In the future, we might want to refactor the way the
555     // animation and transition manager do their refresh driver ticks so
556     // that we can use UpdateOnlyAnimationStyles, with a different
557     // boolean argument, for this update as well, instead of having them
558     // post style updates in their WillRefresh methods.
559     PresContext()->TransitionManager()->SetInAnimationOnlyStyleUpdate(true);
560   }
561 
562   ProcessRestyles(mPendingRestyles);
563 
564   if (!haveNonAnimation) {
565     PresContext()->TransitionManager()->SetInAnimationOnlyStyleUpdate(false);
566   }
567 
568   mIsProcessingRestyles = false;
569 
570   NS_ASSERTION(haveNonAnimation || !mHavePendingNonAnimationRestyles,
571                "should not have added restyles");
572   mHavePendingNonAnimationRestyles = false;
573 
574   if (mDoRebuildAllStyleData) {
575     // We probably wasted a lot of work up above, but this seems safest
576     // and it should be rarely used.
577     // This might add us as a refresh observer again; that's ok.
578     ProcessPendingRestyles();
579 
580     NS_ASSERTION(!mDoRebuildAllStyleData,
581                  "repeatedly setting mDoRebuildAllStyleData?");
582   }
583 
584   MOZ_ASSERT(!mInRebuildAllStyleData,
585              "should have called FinishRebuildAllStyleData");
586 }
587 
BeginProcessingRestyles(RestyleTracker & aRestyleTracker)588 void GeckoRestyleManager::BeginProcessingRestyles(
589     RestyleTracker& aRestyleTracker) {
590   mInStyleRefresh = true;
591 
592   if (ShouldStartRebuildAllFor(aRestyleTracker)) {
593     mDoRebuildAllStyleData = false;
594     StartRebuildAllStyleData(aRestyleTracker);
595   }
596 }
597 
EndProcessingRestyles()598 void GeckoRestyleManager::EndProcessingRestyles() {
599   FlushOverflowChangedTracker();
600 
601   MOZ_ASSERT(mAnimationsWithDestroyedFrame);
602   mAnimationsWithDestroyedFrame->StopAnimationsForElementsWithoutFrames();
603 
604   // Set mInStyleRefresh to false now, since the EndUpdate call might
605   // add more restyles.
606   mInStyleRefresh = false;
607 
608   if (mInRebuildAllStyleData) {
609     FinishRebuildAllStyleData();
610   }
611 
612 #ifdef DEBUG
613   PresContext()->PresShell()->VerifyStyleTree();
614 #endif
615 }
616 
UpdateOnlyAnimationStyles()617 void GeckoRestyleManager::UpdateOnlyAnimationStyles() {
618   bool doCSS = PresContext()->EffectCompositor()->HasPendingStyleUpdates();
619 
620   nsIDocument* document = PresContext()->Document();
621   nsSMILAnimationController* animationController =
622       document->HasAnimationController() ? document->GetAnimationController()
623                                          : nullptr;
624   bool doSMIL = animationController &&
625                 animationController->MightHavePendingStyleUpdates();
626 
627   if (!doCSS && !doSMIL) {
628     return;
629   }
630 
631   nsTransitionManager* transitionManager = PresContext()->TransitionManager();
632 
633   transitionManager->SetInAnimationOnlyStyleUpdate(true);
634 
635   RestyleTracker tracker(ELEMENT_HAS_PENDING_ANIMATION_ONLY_RESTYLE |
636                          ELEMENT_IS_POTENTIAL_ANIMATION_ONLY_RESTYLE_ROOT);
637   tracker.Init(this);
638 
639   if (doCSS) {
640     PresContext()->EffectCompositor()->AddStyleUpdatesTo(tracker);
641   }
642 
643   if (doSMIL) {
644     animationController->AddStyleUpdatesTo(tracker);
645   }
646 
647   ProcessRestyles(tracker);
648 
649   transitionManager->SetInAnimationOnlyStyleUpdate(false);
650 }
651 
PostRestyleEventInternal()652 void GeckoRestyleManager::PostRestyleEventInternal() {
653   // Make sure we're not in a style refresh; if we are, we still have
654   // a call to ProcessPendingRestyles coming and there's no need to
655   // add ourselves as a refresh observer until then.
656   nsIPresShell* presShell = PresContext()->PresShell();
657   if (!mInStyleRefresh) {
658     presShell->ObserveStyleFlushes();
659   }
660 
661   // Unconditionally flag our document as needing a flush.  The other
662   // option here would be a dedicated boolean to track whether we need
663   // to do so (set here and unset in ProcessPendingRestyles).
664   presShell->SetNeedStyleFlush();
665 }
666 
PostRestyleEvent(Element * aElement,nsRestyleHint aRestyleHint,nsChangeHint aMinChangeHint,const RestyleHintData * aRestyleHintData)667 void GeckoRestyleManager::PostRestyleEvent(
668     Element* aElement, nsRestyleHint aRestyleHint, nsChangeHint aMinChangeHint,
669     const RestyleHintData* aRestyleHintData) {
670   if (MOZ_UNLIKELY(IsDisconnected()) ||
671       MOZ_UNLIKELY(PresContext()->PresShell()->IsDestroying())) {
672     return;
673   }
674 
675   if (aRestyleHint == 0 && !aMinChangeHint) {
676     // Nothing to do here
677     return;
678   }
679 
680   mPendingRestyles.AddPendingRestyle(aElement, aRestyleHint, aMinChangeHint,
681                                      aRestyleHintData);
682 
683   // Set mHavePendingNonAnimationRestyles for any restyle that could
684   // possibly contain non-animation styles (i.e., those that require us
685   // to do an animation-only style flush before processing style changes
686   // to ensure correct initialization of CSS transitions).
687   if (aRestyleHint & ~eRestyle_AllHintsWithAnimations) {
688     mHavePendingNonAnimationRestyles = true;
689   }
690 
691   PostRestyleEventInternal();
692 }
693 
PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint,nsRestyleHint aRestyleHint)694 void GeckoRestyleManager::PostRebuildAllStyleDataEvent(
695     nsChangeHint aExtraHint, nsRestyleHint aRestyleHint) {
696   NS_ASSERTION(!(aExtraHint & nsChangeHint_ReconstructFrame),
697                "Should not reconstruct the root of the frame tree.  "
698                "Use ReconstructDocElementHierarchy instead.");
699   MOZ_ASSERT(!(aRestyleHint & eRestyle_SomeDescendants),
700              "PostRebuildAllStyleDataEvent does not handle "
701              "eRestyle_SomeDescendants");
702 
703   mDoRebuildAllStyleData = true;
704   mRebuildAllExtraHint |= aExtraHint;
705   mRebuildAllRestyleHint |= aRestyleHint;
706 
707   // Get a restyle event posted if necessary
708   PostRestyleEventInternal();
709 }
710 
711 // aContent must be the content for the frame in question, which may be
712 // :before/:after content
TryInitiatingTransition(nsPresContext * aPresContext,nsIContent * aContent,GeckoStyleContext * aOldStyleContext,RefPtr<GeckoStyleContext> * aNewStyleContext)713 /* static */ bool GeckoRestyleManager::TryInitiatingTransition(
714     nsPresContext* aPresContext, nsIContent* aContent,
715     GeckoStyleContext* aOldStyleContext,
716     RefPtr<GeckoStyleContext>* aNewStyleContext /* inout */) {
717   if (!aContent || !aContent->IsElement()) {
718     return false;
719   }
720 
721   // Notify the transition manager.  If it starts a transition,
722   // it might modify the new style context.
723   RefPtr<GeckoStyleContext> sc = *aNewStyleContext;
724   aPresContext->TransitionManager()->StyleContextChanged(
725       aContent->AsElement(), aOldStyleContext, aNewStyleContext);
726   return *aNewStyleContext != sc;
727 }
728 
ElementForStyleContext(nsIContent * aParentContent,nsIFrame * aFrame,CSSPseudoElementType aPseudoType)729 static dom::Element* ElementForStyleContext(nsIContent* aParentContent,
730                                             nsIFrame* aFrame,
731                                             CSSPseudoElementType aPseudoType) {
732   // We don't expect XUL tree stuff here.
733   NS_PRECONDITION(
734       aPseudoType == CSSPseudoElementType::NotPseudo ||
735           aPseudoType == CSSPseudoElementType::InheritingAnonBox ||
736           aPseudoType == CSSPseudoElementType::NonInheritingAnonBox ||
737           aPseudoType < CSSPseudoElementType::Count,
738       "Unexpected pseudo");
739   // XXX see the comments about the various element confusion in
740   // ElementRestyler::Restyle.
741   if (aPseudoType == CSSPseudoElementType::NotPseudo) {
742     return aFrame->GetContent()->AsElement();
743   }
744 
745   if (aPseudoType == CSSPseudoElementType::InheritingAnonBox ||
746       aPseudoType == CSSPseudoElementType::NonInheritingAnonBox) {
747     return nullptr;
748   }
749 
750   if (aPseudoType == CSSPseudoElementType::firstLetter) {
751     NS_ASSERTION(aFrame->IsLetterFrame(),
752                  "firstLetter pseudoTag without a nsFirstLetterFrame");
753     nsBlockFrame* block = nsBlockFrame::GetNearestAncestorBlock(aFrame);
754     return block->GetContent()->AsElement();
755   }
756 
757   if (aPseudoType == CSSPseudoElementType::mozColorSwatch) {
758     MOZ_ASSERT(aFrame->GetParent() && aFrame->GetParent()->GetParent(),
759                "Color swatch frame should have a parent & grandparent");
760 
761     nsIFrame* grandparentFrame = aFrame->GetParent()->GetParent();
762     MOZ_ASSERT(grandparentFrame->IsColorControlFrame(),
763                "Color swatch's grandparent should be nsColorControlFrame");
764 
765     return grandparentFrame->GetContent()->AsElement();
766   }
767 
768   if (aPseudoType == CSSPseudoElementType::mozNumberText ||
769       aPseudoType == CSSPseudoElementType::mozNumberWrapper ||
770       aPseudoType == CSSPseudoElementType::mozNumberSpinBox ||
771       aPseudoType == CSSPseudoElementType::mozNumberSpinUp ||
772       aPseudoType == CSSPseudoElementType::mozNumberSpinDown) {
773     // Get content for nearest nsNumberControlFrame:
774     nsIFrame* f = aFrame->GetParent();
775     MOZ_ASSERT(f);
776     while (!f->IsNumberControlFrame()) {
777       f = f->GetParent();
778       MOZ_ASSERT(f);
779     }
780     return f->GetContent()->AsElement();
781   }
782 
783   Element* frameElement = aFrame->GetContent()->AsElement();
784   if (frameElement->IsNativeAnonymous()) {
785     // NAC-implemented pseudos use the closest non-NAC element as their
786     // element to inherit from.
787     Element* originatingElement =
788         nsContentUtils::GetClosestNonNativeAnonymousAncestor(frameElement);
789     if (originatingElement) {
790       return originatingElement;
791     }
792   }
793 
794   if (aParentContent) {
795     return aParentContent->AsElement();
796   }
797 
798   MOZ_ASSERT(aFrame->GetContent()->GetParent(),
799              "should not have got here for the root element");
800   return aFrame->GetContent()->GetParent()->AsElement();
801 }
802 
803 /**
804  * Some pseudo-elements actually have a content node created for them,
805  * whereas others have only a frame but not a content node.  In some
806  * cases, we want to support style attributes or states on those
807  * elements.  For those pseudo-elements, we need to pass the
808  * anonymous pseudo-element content to selector matching processes in
809  * addition to the element that the pseudo-element is for; in other
810  * cases we should pass null instead.  This function returns the
811  * pseudo-element content that we should pass.
812  */
PseudoElementForStyleContext(nsIFrame * aFrame,CSSPseudoElementType aPseudoType)813 static dom::Element* PseudoElementForStyleContext(
814     nsIFrame* aFrame, CSSPseudoElementType aPseudoType) {
815   if (aPseudoType >= CSSPseudoElementType::Count) {
816     return nullptr;
817   }
818 
819   if (nsCSSPseudoElements::PseudoElementSupportsStyleAttribute(aPseudoType) ||
820       nsCSSPseudoElements::PseudoElementSupportsUserActionState(aPseudoType)) {
821     return aFrame->GetContent()->AsElement();
822   }
823 
824   return nullptr;
825 }
826 
827 /**
828  * FIXME: Temporary.  Should merge with following function.
829  */
GetPrevContinuationWithPossiblySameStyle(nsIFrame * aFrame)830 static nsIFrame* GetPrevContinuationWithPossiblySameStyle(nsIFrame* aFrame) {
831   // Account for {ib} splits when looking for "prevContinuation".  In
832   // particular, for the first-continuation of a part of an {ib} split
833   // we want to use the previous ib-split sibling of the previous
834   // ib-split sibling of aFrame, which should have the same style
835   // context as aFrame itself.  In particular, if aFrame is the first
836   // continuation of an inline part of a block-in-inline split then its
837   // previous ib-split sibling is a block, and the previous ib-split
838   // sibling of _that_ is an inline, just like aFrame.  Similarly, if
839   // aFrame is the first continuation of a block part of an
840   // block-in-inline split (a block-in-inline wrapper block), then its
841   // previous ib-split sibling is an inline and the previous ib-split
842   // sibling of that is either another block-in-inline wrapper block box
843   // or null.
844   nsIFrame* prevContinuation = aFrame->GetPrevContinuation();
845   if (!prevContinuation &&
846       (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
847     // We're the first continuation, so we can just get the frame
848     // property directly
849     prevContinuation = aFrame->GetProperty(nsIFrame::IBSplitPrevSibling());
850     if (prevContinuation) {
851       prevContinuation =
852           prevContinuation->GetProperty(nsIFrame::IBSplitPrevSibling());
853     }
854   }
855 
856   NS_ASSERTION(!prevContinuation ||
857                    prevContinuation->GetContent() == aFrame->GetContent(),
858                "unexpected content mismatch");
859 
860   return prevContinuation;
861 }
862 
863 /**
864  * Get the previous continuation or similar ib-split sibling (assuming
865  * block/inline alternation), conditionally on it having the same style.
866  * This assumes that we're not between resolving the two (i.e., that
867  * they're both already resolved.
868  */
GetPrevContinuationWithSameStyle(nsIFrame * aFrame)869 static nsIFrame* GetPrevContinuationWithSameStyle(nsIFrame* aFrame) {
870   nsIFrame* prevContinuation = GetPrevContinuationWithPossiblySameStyle(aFrame);
871   if (!prevContinuation) {
872     return nullptr;
873   }
874 
875   GeckoStyleContext* prevStyle = prevContinuation->StyleContext()->AsGecko();
876   GeckoStyleContext* selfStyle = aFrame->StyleContext()->AsGecko();
877   if (prevStyle != selfStyle) {
878     NS_ASSERTION(prevStyle->GetPseudo() != selfStyle->GetPseudo() ||
879                      prevStyle->GetParent() != selfStyle->GetParent(),
880                  "continuations should have the same style context");
881     prevContinuation = nullptr;
882   }
883   return prevContinuation;
884 }
885 
ReparentStyleContext(nsIFrame * aFrame)886 nsresult GeckoRestyleManager::ReparentStyleContext(nsIFrame* aFrame) {
887   LayoutFrameType frameType = aFrame->Type();
888   if (frameType == LayoutFrameType::Placeholder) {
889     // Also reparent the out-of-flow and all its continuations.
890     nsIFrame* outOfFlow =
891         nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame);
892     NS_ASSERTION(outOfFlow, "no out-of-flow frame");
893     do {
894       ReparentStyleContext(outOfFlow);
895     } while ((outOfFlow = outOfFlow->GetNextContinuation()));
896   } else if (frameType == LayoutFrameType::Backdrop) {
897     // Style context of backdrop frame has no parent style context, and
898     // thus we do not need to reparent it.
899     return NS_OK;
900   }
901 
902   // DO NOT verify the style tree before reparenting.  The frame
903   // tree has already been changed, so this check would just fail.
904   GeckoStyleContext* oldContext = aFrame->StyleContext()->AsGecko();
905 
906   RefPtr<GeckoStyleContext> newContext;
907   nsIFrame* providerFrame;
908   nsStyleContext* newParentContext =
909       aFrame->GetParentStyleContext(&providerFrame);
910   bool isChild = providerFrame && providerFrame->GetParent() == aFrame;
911   nsIFrame* providerChild = nullptr;
912   if (isChild) {
913     ReparentStyleContext(providerFrame);
914     // Get the style context again after ReparentStyleContext() which might have
915     // changed it.
916     newParentContext = providerFrame->StyleContext();
917     providerChild = providerFrame;
918   }
919 
920 #ifdef DEBUG
921   {
922     // Check that our assumption that continuations of the same
923     // pseudo-type and with the same style context parent have the
924     // same style context is valid before the reresolution.  (We need
925     // to check the pseudo-type and style context parent because of
926     // :first-letter and :first-line, where we create styled and
927     // unstyled letter/line frames distinguished by pseudo-type, and
928     // then need to distinguish their descendants based on having
929     // different parents.)
930     nsIFrame* nextContinuation = aFrame->GetNextContinuation();
931     if (nextContinuation) {
932       GeckoStyleContext* nextContinuationContext =
933           nextContinuation->StyleContext()->AsGecko();
934       NS_ASSERTION(
935           oldContext == nextContinuationContext ||
936               oldContext->GetPseudo() != nextContinuationContext->GetPseudo() ||
937               oldContext->GetParent() != nextContinuationContext->GetParent(),
938           "continuations should have the same style context");
939     }
940   }
941 #endif
942 
943   if (!newParentContext && !oldContext->GetParent()) {
944     // No need to do anything here for this frame, but we should still reparent
945     // its descendants, because those may have styles that inherit from the
946     // parent of this frame (e.g. non-anonymous columns in an anonymous
947     // colgroup).
948     MOZ_ASSERT(aFrame->StyleContext()->IsNonInheritingAnonBox(),
949                "Why did this frame not end up with a parent context?");
950     ReparentFrameDescendants(aFrame, providerChild);
951     return NS_OK;
952   }
953 
954   NS_ASSERTION(newParentContext,
955                "Reparenting something that has no usable"
956                " parent? Shouldn't happen!");
957   // XXX need to do something here to produce the correct style context for
958   // an IB split whose first inline part is inside a first-line frame.
959   // Currently the first IB anonymous block's style context takes the first
960   // part's style context as parent, which is wrong since first-line style
961   // should not apply to the anonymous block.
962 
963   nsIFrame* prevContinuation = GetPrevContinuationWithPossiblySameStyle(aFrame);
964   GeckoStyleContext* prevContinuationContext;
965   bool copyFromContinuation =
966       prevContinuation &&
967       (prevContinuationContext = prevContinuation->StyleContext()->AsGecko())
968               ->GetPseudo() == oldContext->GetPseudo() &&
969       prevContinuationContext->GetParent() == newParentContext;
970   if (copyFromContinuation) {
971     // Just use the style context from the frame's previous
972     // continuation (see assertion about aFrame->GetNextContinuation()
973     // above, which we would have previously hit for aFrame's previous
974     // continuation).
975     newContext = prevContinuationContext;
976   } else {
977     nsIFrame* parentFrame = aFrame->GetParent();
978     Element* element = ElementForStyleContext(
979         parentFrame ? parentFrame->GetContent() : nullptr, aFrame,
980         oldContext->GetPseudoType());
981     newContext = StyleSet()->ReparentStyleContext(
982         oldContext, newParentContext->AsGecko(), element);
983   }
984 
985   if (newContext) {
986     if (newContext != oldContext) {
987     // We probably don't want to initiate transitions from
988     // ReparentStyleContext, since we call it during frame
989     // construction rather than in response to dynamic changes.
990     // Also see the comment at the start of
991     // nsTransitionManager::ConsiderInitiatingTransition.
992 #if 0
993       if (!copyFromContinuation) {
994         TryInitiatingTransition(mPresContext, aFrame->GetContent(),
995                                 oldContext, &newContext);
996       }
997 #endif
998 
999       // Ensure the new context ends up resolving all the structs the old
1000       // context resolved.
1001       if (!copyFromContinuation) {
1002         newContext->AsGecko()->EnsureSameStructsCached(oldContext);
1003       }
1004 
1005       aFrame->SetStyleContext(newContext);
1006 
1007       ReparentFrameDescendants(aFrame, providerChild);
1008 
1009       // If this frame is part of an IB split, then the style context of
1010       // the next part of the split might be a child of our style context.
1011       // Reparent its style context just in case one of our ancestors
1012       // (split or not) hasn't done so already). It's not a problem to
1013       // reparent the same frame twice because the "if (newContext !=
1014       // oldContext)" check will prevent us from redoing work.
1015       if ((aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) &&
1016           !aFrame->GetPrevContinuation()) {
1017         nsIFrame* sib = aFrame->GetProperty(nsIFrame::IBSplitSibling());
1018         if (sib) {
1019           ReparentStyleContext(sib);
1020         }
1021       }
1022 
1023       // do additional contexts
1024       int32_t contextIndex = 0;
1025       for (nsStyleContext* oldExtraContext;
1026            (oldExtraContext = aFrame->GetAdditionalStyleContext(contextIndex));
1027            ++contextIndex) {
1028         RefPtr<GeckoStyleContext> newExtraContext;
1029         newExtraContext = StyleSet()->ReparentStyleContext(
1030             oldExtraContext->AsGecko(), newContext, nullptr);
1031         if (newExtraContext) {
1032           if (newExtraContext != oldExtraContext) {
1033             // Ensure the new context ends up resolving all the structs the old
1034             // context resolved.
1035             newContext->AsGecko()->EnsureSameStructsCached(oldContext);
1036           }
1037 
1038           aFrame->SetAdditionalStyleContext(contextIndex, newExtraContext);
1039         }
1040       }
1041 #ifdef DEBUG
1042       DebugVerifyStyleTree(aFrame);
1043 #endif
1044     }
1045   }
1046 
1047   return NS_OK;
1048 }
1049 
ReparentFrameDescendants(nsIFrame * aFrame,nsIFrame * aProviderChild)1050 void GeckoRestyleManager::ReparentFrameDescendants(nsIFrame* aFrame,
1051                                                    nsIFrame* aProviderChild) {
1052   nsIFrame::ChildListIterator lists(aFrame);
1053   for (; !lists.IsDone(); lists.Next()) {
1054     for (nsIFrame* child : lists.CurrentList()) {
1055       // only do frames that are in flow
1056       if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
1057           child != aProviderChild) {
1058 #ifdef DEBUG
1059         if (child->IsPlaceholderFrame()) {
1060           nsIFrame* outOfFlowFrame =
1061               nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
1062           NS_ASSERTION(outOfFlowFrame, "no out-of-flow frame");
1063 
1064           NS_ASSERTION(outOfFlowFrame != aProviderChild,
1065                        "Out of flow provider?");
1066         }
1067 #endif
1068         ReparentStyleContext(child);
1069       }
1070     }
1071   }
1072 }
1073 
ElementRestyler(nsPresContext * aPresContext,nsIFrame * aFrame,nsStyleChangeList * aChangeList,nsChangeHint aHintsHandledByAncestors,RestyleTracker & aRestyleTracker,nsTArray<nsCSSSelector * > & aSelectorsForDescendants,TreeMatchContext & aTreeMatchContext,nsTArray<nsIContent * > & aVisibleKidsOfHiddenElement,nsTArray<ContextToClear> & aContextsToClear,nsTArray<RefPtr<GeckoStyleContext>> & aSwappedStructOwners)1074 ElementRestyler::ElementRestyler(
1075     nsPresContext* aPresContext, nsIFrame* aFrame,
1076     nsStyleChangeList* aChangeList, nsChangeHint aHintsHandledByAncestors,
1077     RestyleTracker& aRestyleTracker,
1078     nsTArray<nsCSSSelector*>& aSelectorsForDescendants,
1079     TreeMatchContext& aTreeMatchContext,
1080     nsTArray<nsIContent*>& aVisibleKidsOfHiddenElement,
1081     nsTArray<ContextToClear>& aContextsToClear,
1082     nsTArray<RefPtr<GeckoStyleContext>>& aSwappedStructOwners)
1083     : mPresContext(aPresContext),
1084       mFrame(aFrame),
1085       mParentContent(nullptr)
1086       // XXXldb Why does it make sense to use aParentContent?  (See
1087       // comment above assertion at start of ElementRestyler::Restyle.)
1088       ,
1089       mContent(mFrame->GetContent() ? mFrame->GetContent() : mParentContent),
1090       mChangeList(aChangeList),
1091       mHintsHandledByAncestors(aHintsHandledByAncestors),
1092       mHintsHandledBySelf(nsChangeHint(0)),
1093       mRestyleTracker(aRestyleTracker),
1094       mSelectorsForDescendants(aSelectorsForDescendants),
1095       mTreeMatchContext(aTreeMatchContext),
1096       mResolvedChild(nullptr),
1097       mContextsToClear(aContextsToClear),
1098       mSwappedStructOwners(aSwappedStructOwners),
1099       mIsRootOfRestyle(true)
1100 #ifdef ACCESSIBILITY
1101       ,
1102       mDesiredA11yNotifications(eSendAllNotifications),
1103       mKidsDesiredA11yNotifications(mDesiredA11yNotifications),
1104       mOurA11yNotification(eDontNotify),
1105       mVisibleKidsOfHiddenElement(aVisibleKidsOfHiddenElement)
1106 #endif
1107 #ifdef RESTYLE_LOGGING
1108       ,
1109       mLoggingDepth(aRestyleTracker.LoggingDepth() + 1)
1110 #endif
1111 {
1112   MOZ_ASSERT(!mContent || !mContent->IsStyledByServo());
1113   MOZ_ASSERT(!(mHintsHandledByAncestors & nsChangeHint_ReconstructFrame),
1114              "why restyle descendants if we are reconstructing the frame for "
1115              "an ancestor?");
1116 }
1117 
ElementRestyler(const ElementRestyler & aParentRestyler,nsIFrame * aFrame,uint32_t aConstructorFlags)1118 ElementRestyler::ElementRestyler(const ElementRestyler& aParentRestyler,
1119                                  nsIFrame* aFrame, uint32_t aConstructorFlags)
1120     : mPresContext(aParentRestyler.mPresContext),
1121       mFrame(aFrame),
1122       mParentContent(aParentRestyler.mContent)
1123       // XXXldb Why does it make sense to use aParentContent?  (See
1124       // comment above assertion at start of ElementRestyler::Restyle.)
1125       ,
1126       mContent(mFrame->GetContent() ? mFrame->GetContent() : mParentContent),
1127       mChangeList(aParentRestyler.mChangeList),
1128       mHintsHandledByAncestors(
1129           // Note that when FOR_OUT_OF_FLOW_CHILD, the out-of-flow may not be a
1130           // geometric descendant of the frame where we started the reresolve.
1131           // Therefore, even if mHintsHandledByAncestors already includes
1132           // nsChangeHint_AllReflowHints/ we don't want to pass that on to the
1133           // out-of-flow reresolve, since that can lead to the out-of-flow not
1134           // getting reflowed when it should be (eg a reresolve starting at
1135           // <body> that involves reflowing the <body> would miss reflowing
1136           // fixed-pos nodes that also need reflow).  In the cases when the
1137           // out-of-flow _is_ a geometric descendant of a frame we already have
1138           // a reflow hint for, reflow coalescing should keep us from doing the
1139           // work twice.
1140           (aParentRestyler.mHintsHandledByAncestors |
1141            aParentRestyler.mHintsHandledBySelf) &
1142           ((aConstructorFlags & FOR_OUT_OF_FLOW_CHILD)
1143                ? ~nsChangeHint_AllReflowHints
1144                : ~nsChangeHint(0))),
1145       mHintsHandledBySelf(nsChangeHint(0)),
1146       mRestyleTracker(aParentRestyler.mRestyleTracker),
1147       mSelectorsForDescendants(aParentRestyler.mSelectorsForDescendants),
1148       mTreeMatchContext(aParentRestyler.mTreeMatchContext),
1149       mResolvedChild(nullptr),
1150       mContextsToClear(aParentRestyler.mContextsToClear),
1151       mSwappedStructOwners(aParentRestyler.mSwappedStructOwners),
1152       mIsRootOfRestyle(false)
1153 #ifdef ACCESSIBILITY
1154       ,
1155       mDesiredA11yNotifications(aParentRestyler.mKidsDesiredA11yNotifications),
1156       mKidsDesiredA11yNotifications(mDesiredA11yNotifications),
1157       mOurA11yNotification(eDontNotify),
1158       mVisibleKidsOfHiddenElement(aParentRestyler.mVisibleKidsOfHiddenElement)
1159 #endif
1160 #ifdef RESTYLE_LOGGING
1161       ,
1162       mLoggingDepth(aParentRestyler.mLoggingDepth + 1)
1163 #endif
1164 {
1165   MOZ_ASSERT(!mContent || !mContent->IsStyledByServo());
1166   MOZ_ASSERT(!(mHintsHandledByAncestors & nsChangeHint_ReconstructFrame),
1167              "why restyle descendants if we are reconstructing the frame for "
1168              "an ancestor?");
1169 }
1170 
ElementRestyler(ParentContextFromChildFrame,const ElementRestyler & aParentRestyler,nsIFrame * aFrame)1171 ElementRestyler::ElementRestyler(ParentContextFromChildFrame,
1172                                  const ElementRestyler& aParentRestyler,
1173                                  nsIFrame* aFrame)
1174     : mPresContext(aParentRestyler.mPresContext),
1175       mFrame(aFrame),
1176       mParentContent(aParentRestyler.mParentContent)
1177       // XXXldb Why does it make sense to use aParentContent?  (See
1178       // comment above assertion at start of ElementRestyler::Restyle.)
1179       ,
1180       mContent(mFrame->GetContent() ? mFrame->GetContent() : mParentContent),
1181       mChangeList(aParentRestyler.mChangeList),
1182       mHintsHandledByAncestors(aParentRestyler.mHintsHandledByAncestors |
1183                                aParentRestyler.mHintsHandledBySelf),
1184       mHintsHandledBySelf(nsChangeHint(0)),
1185       mRestyleTracker(aParentRestyler.mRestyleTracker),
1186       mSelectorsForDescendants(aParentRestyler.mSelectorsForDescendants),
1187       mTreeMatchContext(aParentRestyler.mTreeMatchContext),
1188       mResolvedChild(nullptr),
1189       mContextsToClear(aParentRestyler.mContextsToClear),
1190       mSwappedStructOwners(aParentRestyler.mSwappedStructOwners),
1191       mIsRootOfRestyle(false)
1192 #ifdef ACCESSIBILITY
1193       ,
1194       mDesiredA11yNotifications(aParentRestyler.mDesiredA11yNotifications),
1195       mKidsDesiredA11yNotifications(mDesiredA11yNotifications),
1196       mOurA11yNotification(eDontNotify),
1197       mVisibleKidsOfHiddenElement(aParentRestyler.mVisibleKidsOfHiddenElement)
1198 #endif
1199 #ifdef RESTYLE_LOGGING
1200       ,
1201       mLoggingDepth(aParentRestyler.mLoggingDepth + 1)
1202 #endif
1203 {
1204   MOZ_ASSERT(!mContent || !mContent->IsStyledByServo());
1205 
1206   // We would assert here that we're not restyling a child provider frame if
1207   // mHintsHandledByAncestors includes nsChangeHint_ReconstructFrame, but
1208   // we do actually do this if the ReconstructFrame hint came from the
1209   // RestyleTracker, rather than generated from CalcDifference.  (We could
1210   // even try to avoid restyling the child provider frame, by returning
1211   // early in ElementRestyler::Restyle if we grab out a ReconstructFrame
1212   // hint from the RestyleTracker, but it's trickier to verify its correctness
1213   // with all of the tree patching that happens currently, so for now we just
1214   // skip the assertion.)
1215 }
1216 
ElementRestyler(nsPresContext * aPresContext,nsIContent * aContent,nsStyleChangeList * aChangeList,nsChangeHint aHintsHandledByAncestors,RestyleTracker & aRestyleTracker,nsTArray<nsCSSSelector * > & aSelectorsForDescendants,TreeMatchContext & aTreeMatchContext,nsTArray<nsIContent * > & aVisibleKidsOfHiddenElement,nsTArray<ContextToClear> & aContextsToClear,nsTArray<RefPtr<GeckoStyleContext>> & aSwappedStructOwners)1217 ElementRestyler::ElementRestyler(
1218     nsPresContext* aPresContext, nsIContent* aContent,
1219     nsStyleChangeList* aChangeList, nsChangeHint aHintsHandledByAncestors,
1220     RestyleTracker& aRestyleTracker,
1221     nsTArray<nsCSSSelector*>& aSelectorsForDescendants,
1222     TreeMatchContext& aTreeMatchContext,
1223     nsTArray<nsIContent*>& aVisibleKidsOfHiddenElement,
1224     nsTArray<ContextToClear>& aContextsToClear,
1225     nsTArray<RefPtr<GeckoStyleContext>>& aSwappedStructOwners)
1226     : mPresContext(aPresContext),
1227       mFrame(nullptr),
1228       mParentContent(nullptr),
1229       mContent(aContent),
1230       mChangeList(aChangeList),
1231       mHintsHandledByAncestors(aHintsHandledByAncestors),
1232       mHintsHandledBySelf(nsChangeHint(0)),
1233       mRestyleTracker(aRestyleTracker),
1234       mSelectorsForDescendants(aSelectorsForDescendants),
1235       mTreeMatchContext(aTreeMatchContext),
1236       mResolvedChild(nullptr),
1237       mContextsToClear(aContextsToClear),
1238       mSwappedStructOwners(aSwappedStructOwners),
1239       mIsRootOfRestyle(true)
1240 #ifdef ACCESSIBILITY
1241       ,
1242       mDesiredA11yNotifications(eSendAllNotifications),
1243       mKidsDesiredA11yNotifications(mDesiredA11yNotifications),
1244       mOurA11yNotification(eDontNotify),
1245       mVisibleKidsOfHiddenElement(aVisibleKidsOfHiddenElement)
1246 #endif
1247 {
1248   MOZ_ASSERT(!(mHintsHandledByAncestors & nsChangeHint_ReconstructFrame),
1249              "why restyle descendants if we are reconstructing the frame for "
1250              "an ancestor?");
1251 }
1252 
CaptureChange(GeckoStyleContext * aOldContext,GeckoStyleContext * aNewContext,nsChangeHint aChangeToAssume,uint32_t * aEqualStructs,uint32_t * aSamePointerStructs)1253 void ElementRestyler::CaptureChange(GeckoStyleContext* aOldContext,
1254                                     GeckoStyleContext* aNewContext,
1255                                     nsChangeHint aChangeToAssume,
1256                                     uint32_t* aEqualStructs,
1257                                     uint32_t* aSamePointerStructs) {
1258   static_assert(nsStyleStructID_Length <= 32,
1259                 "aEqualStructs is not big enough");
1260 
1261   // Check some invariants about replacing one style context with another.
1262   NS_ASSERTION(aOldContext->GetPseudo() == aNewContext->GetPseudo(),
1263                "old and new style contexts should have the same pseudo");
1264   NS_ASSERTION(aOldContext->GetPseudoType() == aNewContext->GetPseudoType(),
1265                "old and new style contexts should have the same pseudo");
1266 
1267   nsChangeHint ourChange = aOldContext->CalcStyleDifference(
1268       aNewContext, aEqualStructs, aSamePointerStructs);
1269   NS_ASSERTION(!(ourChange & nsChangeHint_AllReflowHints) ||
1270                    (ourChange & nsChangeHint_NeedReflow),
1271                "Reflow hint bits set without actually asking for a reflow");
1272 
1273   LOG_RESTYLE("CaptureChange, ourChange = %s, aChangeToAssume = %s",
1274               GeckoRestyleManager::ChangeHintToString(ourChange).get(),
1275               GeckoRestyleManager::ChangeHintToString(aChangeToAssume).get());
1276   LOG_RESTYLE_INDENT();
1277 
1278   // nsChangeHint_UpdateEffects is not handled for descendants, but it can be
1279   // set due to changes in inherited properties (fill and stroke).  Avoid
1280   // propagating it into text nodes.
1281   if ((ourChange & nsChangeHint_UpdateEffects) && mContent &&
1282       !mContent->IsElement()) {
1283     ourChange &= ~nsChangeHint_UpdateEffects;
1284   }
1285 
1286   ourChange |= aChangeToAssume;
1287 
1288   nsChangeHint changeToAppend =
1289       NS_RemoveSubsumedHints(ourChange, mHintsHandledByAncestors);
1290 
1291   // mHintsHandledBySelf starts off as nsChangeHint(0), when restyling a given
1292   // frame, and accumulates change hints for each same-style-continuation and
1293   // {ib}-split sibling following it.  Most of the time, any subsequent frames
1294   // we restyle with this ElementRestyler will generate exactly the same
1295   // |changeToAppend| that we have already stored in mHintsHandledBySelf.  If
1296   // we generate some hints that weren't handled by an earler same-style-
1297   // continuation or {ib}-split sibling, then we record the entire
1298   // |changeToAppend| value.  (We could use something like
1299   // NS_RemoveSubsumedHints, but aimed at removing hints handled only for the
1300   // current element instead.  However, we should probably just fix these rare
1301   // cases as part of bug 918064.)
1302   if (!NS_IsHintSubset(changeToAppend, mHintsHandledBySelf)) {
1303     mHintsHandledBySelf |= changeToAppend;
1304     if (!(ourChange & nsChangeHint_ReconstructFrame) || mContent) {
1305       LOG_RESTYLE("appending change %s",
1306                   RestyleManager::ChangeHintToString(changeToAppend).get());
1307       mChangeList->AppendChange(mFrame, mContent, changeToAppend);
1308     } else {
1309       LOG_RESTYLE("ignoring ReconstructFrame change with no content");
1310     }
1311   } else {
1312     LOG_RESTYLE("change has already been handled");
1313   }
1314 }
1315 
1316 class MOZ_RAII AutoSelectorArrayTruncater final {
1317  public:
AutoSelectorArrayTruncater(nsTArray<nsCSSSelector * > & aSelectorsForDescendants)1318   explicit AutoSelectorArrayTruncater(
1319       nsTArray<nsCSSSelector*>& aSelectorsForDescendants)
1320       : mSelectorsForDescendants(aSelectorsForDescendants),
1321         mOriginalLength(aSelectorsForDescendants.Length()) {}
1322 
~AutoSelectorArrayTruncater()1323   ~AutoSelectorArrayTruncater() {
1324     mSelectorsForDescendants.TruncateLength(mOriginalLength);
1325   }
1326 
1327  private:
1328   nsTArray<nsCSSSelector*>& mSelectorsForDescendants;
1329   size_t mOriginalLength;
1330 };
1331 
1332 /**
1333  * Called when we are stopping a restyle with eRestyle_SomeDescendants, to
1334  * search for descendants that match any of the selectors in
1335  * mSelectorsForDescendants.  If the element does match one of the selectors,
1336  * we cause it to be restyled with eRestyle_Self.
1337  *
1338  * We traverse down the frame tree (and through the flattened content tree
1339  * when we find undisplayed content) unless we find an element that (a) already
1340  * has a pending restyle, or (b) does not have a pending restyle but does match
1341  * one of the selectors in mSelectorsForDescendants.  For (a), we add the
1342  * current mSelectorsForDescendants into the existing restyle data, and for (b)
1343  * we add a new pending restyle with that array.  So in both cases, when we
1344  * come to restyling this element back up in ProcessPendingRestyles, we will
1345  * again find the eRestyle_SomeDescendants hint and its selectors array.
1346  *
1347  * This ensures that we don't visit descendant elements and check them
1348  * against mSelectorsForDescendants more than once.
1349  */
ConditionallyRestyleChildren()1350 void ElementRestyler::ConditionallyRestyleChildren() {
1351   MOZ_ASSERT(mContent == mFrame->GetContent());
1352 
1353   if (!mContent->IsElement() || mSelectorsForDescendants.IsEmpty()) {
1354     return;
1355   }
1356 
1357   Element* element = mContent->AsElement();
1358 
1359   LOG_RESTYLE(
1360       "traversing descendants of frame %s (with element %s) to "
1361       "propagate eRestyle_SomeDescendants for these %d selectors:",
1362       FrameTagToString(mFrame).get(), ElementTagToString(element).get(),
1363       int(mSelectorsForDescendants.Length()));
1364   LOG_RESTYLE_INDENT();
1365 #ifdef RESTYLE_LOGGING
1366   for (nsCSSSelector* sel : mSelectorsForDescendants) {
1367     LOG_RESTYLE("%s", sel->RestrictedSelectorToString().get());
1368   }
1369 #endif
1370 
1371   Element* restyleRoot = mRestyleTracker.FindClosestRestyleRoot(element);
1372   ConditionallyRestyleChildren(mFrame, restyleRoot);
1373 }
1374 
ConditionallyRestyleChildren(nsIFrame * aFrame,Element * aRestyleRoot)1375 void ElementRestyler::ConditionallyRestyleChildren(nsIFrame* aFrame,
1376                                                    Element* aRestyleRoot) {
1377   MOZ_ASSERT(aFrame->GetContent());
1378   MOZ_ASSERT(aFrame->GetContent()->IsElement());
1379   MOZ_ASSERT(!aFrame->GetContent()->IsStyledByServo());
1380 
1381   ConditionallyRestyleUndisplayedDescendants(aFrame, aRestyleRoot);
1382   ConditionallyRestyleContentChildren(aFrame, aRestyleRoot);
1383 }
1384 
1385 // The structure of this method parallels RestyleContentChildren.
1386 // If you update this method, you probably want to update that one too.
ConditionallyRestyleContentChildren(nsIFrame * aFrame,Element * aRestyleRoot)1387 void ElementRestyler::ConditionallyRestyleContentChildren(
1388     nsIFrame* aFrame, Element* aRestyleRoot) {
1389   MOZ_ASSERT(aFrame->GetContent());
1390   MOZ_ASSERT(aFrame->GetContent()->IsElement());
1391   MOZ_ASSERT(!aFrame->GetContent()->IsStyledByServo());
1392 
1393   if (aFrame->GetContent()->HasFlag(mRestyleTracker.RootBit())) {
1394     aRestyleRoot = aFrame->GetContent()->AsElement();
1395   }
1396 
1397   for (nsIFrame* f = aFrame; f;
1398        f = GetNextContinuationWithSameStyle(f, f->StyleContext()->AsGecko())) {
1399     nsIFrame::ChildListIterator lists(f);
1400     for (; !lists.IsDone(); lists.Next()) {
1401       for (nsIFrame* child : lists.CurrentList()) {
1402         // Out-of-flows are reached through their placeholders.  Continuations
1403         // and block-in-inline splits are reached through those chains.
1404         if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
1405             !GetPrevContinuationWithSameStyle(child)) {
1406           // only do frames that are in flow
1407           if (child->IsPlaceholderFrame()) {  // placeholder
1408             // get out of flow frame and recur there
1409             nsIFrame* outOfFlowFrame =
1410                 nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
1411 
1412             // |nsFrame::GetParentStyleContext| checks being out
1413             // of flow so that this works correctly.
1414             do {
1415               if (GetPrevContinuationWithSameStyle(outOfFlowFrame)) {
1416                 continue;
1417               }
1418               if (!ConditionallyRestyle(outOfFlowFrame, aRestyleRoot)) {
1419                 ConditionallyRestyleChildren(outOfFlowFrame, aRestyleRoot);
1420               }
1421             } while ((outOfFlowFrame = outOfFlowFrame->GetNextContinuation()));
1422           } else {  // regular child frame
1423             if (child != mResolvedChild) {
1424               if (!ConditionallyRestyle(child, aRestyleRoot)) {
1425                 ConditionallyRestyleChildren(child, aRestyleRoot);
1426               }
1427             }
1428           }
1429         }
1430       }
1431     }
1432   }
1433 }
1434 
1435 // The structure of this method parallels RestyleUndisplayedDescendants.
1436 // If you update this method, you probably want to update that one too.
ConditionallyRestyleUndisplayedDescendants(nsIFrame * aFrame,Element * aRestyleRoot)1437 void ElementRestyler::ConditionallyRestyleUndisplayedDescendants(
1438     nsIFrame* aFrame, Element* aRestyleRoot) {
1439   nsIContent* undisplayedParent;
1440   if (MustCheckUndisplayedContent(aFrame, undisplayedParent)) {
1441     DoConditionallyRestyleUndisplayedDescendants(undisplayedParent,
1442                                                  aRestyleRoot);
1443   }
1444 }
1445 
1446 // The structure of this method parallels DoRestyleUndisplayedDescendants.
1447 // If you update this method, you probably want to update that one too.
DoConditionallyRestyleUndisplayedDescendants(nsIContent * aParent,Element * aRestyleRoot)1448 void ElementRestyler::DoConditionallyRestyleUndisplayedDescendants(
1449     nsIContent* aParent, Element* aRestyleRoot) {
1450   nsCSSFrameConstructor* fc = mPresContext->FrameConstructor();
1451   UndisplayedNode* nodes = fc->GetAllRegisteredDisplayNoneStylesIn(aParent);
1452   ConditionallyRestyleUndisplayedNodes(nodes, aParent, StyleDisplay::None,
1453                                        aRestyleRoot);
1454   nodes = fc->GetAllRegisteredDisplayContentsStylesIn(aParent);
1455   ConditionallyRestyleUndisplayedNodes(nodes, aParent, StyleDisplay::Contents,
1456                                        aRestyleRoot);
1457 }
1458 
1459 // The structure of this method parallels RestyleUndisplayedNodes.
1460 // If you update this method, you probably want to update that one too.
ConditionallyRestyleUndisplayedNodes(UndisplayedNode * aUndisplayed,nsIContent * aUndisplayedParent,const StyleDisplay aDisplay,Element * aRestyleRoot)1461 void ElementRestyler::ConditionallyRestyleUndisplayedNodes(
1462     UndisplayedNode* aUndisplayed, nsIContent* aUndisplayedParent,
1463     const StyleDisplay aDisplay, Element* aRestyleRoot) {
1464   MOZ_ASSERT(aDisplay == StyleDisplay::None ||
1465              aDisplay == StyleDisplay::Contents);
1466   if (!aUndisplayed) {
1467     return;
1468   }
1469 
1470   if (aUndisplayedParent && aUndisplayedParent->IsElement() &&
1471       aUndisplayedParent->HasFlag(mRestyleTracker.RootBit())) {
1472     MOZ_ASSERT(!aUndisplayedParent->IsStyledByServo());
1473     aRestyleRoot = aUndisplayedParent->AsElement();
1474   }
1475 
1476   for (UndisplayedNode* undisplayed = aUndisplayed; undisplayed;
1477        undisplayed = undisplayed->getNext()) {
1478     if (!undisplayed->mContent->IsElement()) {
1479       continue;
1480     }
1481 
1482     Element* element = undisplayed->mContent->AsElement();
1483 
1484     if (!ConditionallyRestyle(element, aRestyleRoot)) {
1485       if (aDisplay == StyleDisplay::None) {
1486         ConditionallyRestyleContentDescendants(element, aRestyleRoot);
1487       } else {  // StyleDisplay::Contents
1488         DoConditionallyRestyleUndisplayedDescendants(element, aRestyleRoot);
1489       }
1490     }
1491   }
1492 }
1493 
ConditionallyRestyleContentDescendants(Element * aElement,Element * aRestyleRoot)1494 void ElementRestyler::ConditionallyRestyleContentDescendants(
1495     Element* aElement, Element* aRestyleRoot) {
1496   MOZ_ASSERT(!aElement->IsStyledByServo());
1497   if (aElement->HasFlag(mRestyleTracker.RootBit())) {
1498     aRestyleRoot = aElement;
1499   }
1500 
1501   FlattenedChildIterator it(aElement);
1502   for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
1503     if (n->IsElement()) {
1504       Element* e = n->AsElement();
1505       if (!ConditionallyRestyle(e, aRestyleRoot)) {
1506         ConditionallyRestyleContentDescendants(e, aRestyleRoot);
1507       }
1508     }
1509   }
1510 }
1511 
ConditionallyRestyle(nsIFrame * aFrame,Element * aRestyleRoot)1512 bool ElementRestyler::ConditionallyRestyle(nsIFrame* aFrame,
1513                                            Element* aRestyleRoot) {
1514   MOZ_ASSERT(aFrame->GetContent());
1515 
1516   if (!aFrame->GetContent()->IsElement()) {
1517     return true;
1518   }
1519 
1520   return ConditionallyRestyle(aFrame->GetContent()->AsElement(), aRestyleRoot);
1521 }
1522 
ConditionallyRestyle(Element * aElement,Element * aRestyleRoot)1523 bool ElementRestyler::ConditionallyRestyle(Element* aElement,
1524                                            Element* aRestyleRoot) {
1525   MOZ_ASSERT(!aElement->IsStyledByServo());
1526   LOG_RESTYLE("considering element %s for eRestyle_SomeDescendants",
1527               ElementTagToString(aElement).get());
1528   LOG_RESTYLE_INDENT();
1529 
1530   if (aElement->HasFlag(mRestyleTracker.RootBit())) {
1531     aRestyleRoot = aElement;
1532   }
1533 
1534   if (mRestyleTracker.HasRestyleData(aElement)) {
1535     nsRestyleHint rshint = eRestyle_SomeDescendants;
1536     if (SelectorMatchesForRestyle(aElement)) {
1537       LOG_RESTYLE("element has existing restyle data and matches a selector");
1538       rshint |= eRestyle_Self;
1539     } else {
1540       LOG_RESTYLE(
1541           "element has existing restyle data but doesn't match selectors");
1542     }
1543     RestyleHintData data;
1544     data.mSelectorsForDescendants = mSelectorsForDescendants;
1545     mRestyleTracker.AddPendingRestyle(aElement, rshint, nsChangeHint(0), &data,
1546                                       Some(aRestyleRoot));
1547     return true;
1548   }
1549 
1550   if (SelectorMatchesForRestyle(aElement)) {
1551     LOG_RESTYLE("element has no restyle data but matches a selector");
1552     RestyleHintData data;
1553     data.mSelectorsForDescendants = mSelectorsForDescendants;
1554     mRestyleTracker.AddPendingRestyle(
1555         aElement, eRestyle_Self | eRestyle_SomeDescendants, nsChangeHint(0),
1556         &data, Some(aRestyleRoot));
1557     return true;
1558   }
1559 
1560   return false;
1561 }
1562 
MustCheckUndisplayedContent(nsIFrame * aFrame,nsIContent * & aUndisplayedParent)1563 bool ElementRestyler::MustCheckUndisplayedContent(
1564     nsIFrame* aFrame, nsIContent*& aUndisplayedParent) {
1565   // When the root element is display:none, we still construct *some*
1566   // frames that have the root element as their mContent, down to the
1567   // DocElementContainingBlock.
1568   if (aFrame->StyleContext()->GetPseudo()) {
1569     aUndisplayedParent = nullptr;
1570     return aFrame ==
1571            mPresContext->FrameConstructor()->GetDocElementContainingBlock();
1572   }
1573 
1574   aUndisplayedParent = aFrame->GetContent();
1575   return !!aUndisplayedParent;
1576 }
1577 
1578 /**
1579  * Helper for MoveStyleContextsForChildren, below.  Appends the style
1580  * contexts to be moved to mFrame's current (new) style context to
1581  * aContextsToMove.
1582  */
MoveStyleContextsForContentChildren(nsIFrame * aParent,GeckoStyleContext * aOldContext,nsTArray<GeckoStyleContext * > & aContextsToMove)1583 bool ElementRestyler::MoveStyleContextsForContentChildren(
1584     nsIFrame* aParent, GeckoStyleContext* aOldContext,
1585     nsTArray<GeckoStyleContext*>& aContextsToMove) {
1586   nsIFrame::ChildListIterator lists(aParent);
1587   for (; !lists.IsDone(); lists.Next()) {
1588     for (nsIFrame* child : lists.CurrentList()) {
1589       // Bail out if we have out-of-flow frames.
1590       // FIXME: It might be safe to just continue here instead of bailing out.
1591       if (child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
1592         return false;
1593       }
1594       if (GetPrevContinuationWithSameStyle(child)) {
1595         continue;
1596       }
1597       // Bail out if we have placeholder frames.
1598       // FIXME: It is probably safe to just continue here instead of bailing
1599       // out.
1600       if (child->IsPlaceholderFrame()) {
1601         return false;
1602       }
1603       GeckoStyleContext* sc = child->StyleContext()->AsGecko();
1604       if (sc->GetParent() != aOldContext) {
1605         return false;
1606       }
1607       LayoutFrameType type = child->Type();
1608       if (type == LayoutFrameType::Letter || type == LayoutFrameType::Line) {
1609         return false;
1610       }
1611       if (sc->HasChildThatUsesGrandancestorStyle()) {
1612         // XXX Not sure if we need this?
1613         return false;
1614       }
1615       nsAtom* pseudoTag = sc->GetPseudo();
1616       if (pseudoTag && !nsCSSAnonBoxes::IsNonElement(pseudoTag)) {
1617         return false;
1618       }
1619       aContextsToMove.AppendElement(sc);
1620     }
1621   }
1622   return true;
1623 }
1624 
1625 /**
1626  * Traverses to child elements (through the current frame's same style
1627  * continuations, just like RestyleChildren does) and moves any style context
1628  * for those children to be parented under mFrame's current (new) style
1629  * context.
1630  *
1631  * False is returned if it encounters any conditions on the child elements'
1632  * frames and style contexts that means it is impossible to move a
1633  * style context.  If false is returned, no style contexts will have been
1634  * moved.
1635  */
MoveStyleContextsForChildren(GeckoStyleContext * aOldContext)1636 bool ElementRestyler::MoveStyleContextsForChildren(
1637     GeckoStyleContext* aOldContext) {
1638   // Bail out if there are undisplayed or display:contents children.
1639   // FIXME: We could get this to work if we need to.
1640   nsIContent* undisplayedParent;
1641   if (MustCheckUndisplayedContent(mFrame, undisplayedParent)) {
1642     nsCSSFrameConstructor* fc = mPresContext->FrameConstructor();
1643     if (fc->GetAllRegisteredDisplayNoneStylesIn(undisplayedParent) ||
1644         fc->GetAllRegisteredDisplayContentsStylesIn(undisplayedParent)) {
1645       return false;
1646     }
1647   }
1648 
1649   nsTArray<GeckoStyleContext*> contextsToMove;
1650 
1651   MOZ_ASSERT(!MustReframeForBeforePseudo(),
1652              "shouldn't need to reframe ::before as we would have had "
1653              "eRestyle_Subtree and wouldn't get in here");
1654 
1655   DebugOnly<nsIFrame*> lastContinuation;
1656   for (nsIFrame* f = mFrame; f;
1657        f = GetNextContinuationWithSameStyle(f, f->StyleContext()->AsGecko())) {
1658     lastContinuation = f;
1659     if (!MoveStyleContextsForContentChildren(f, aOldContext, contextsToMove)) {
1660       return false;
1661     }
1662   }
1663 
1664   MOZ_ASSERT(!MustReframeForAfterPseudo(lastContinuation),
1665              "shouldn't need to reframe ::after as we would have had "
1666              "eRestyle_Subtree and wouldn't get in here");
1667 
1668   GeckoStyleContext* newParent = mFrame->StyleContext()->AsGecko();
1669   for (GeckoStyleContext* child : contextsToMove) {
1670     // We can have duplicate entries in contextsToMove, so only move
1671     // each style context once.
1672     if (child->GetParent() != newParent) {
1673       child->MoveTo(newParent);
1674     }
1675   }
1676 
1677   return true;
1678 }
1679 
1680 /**
1681  * Recompute style for mFrame (which should not have a prev continuation
1682  * with the same style), all of its next continuations with the same
1683  * style, and all ib-split siblings of the same type (either block or
1684  * inline, skipping the intermediates of the other type) and accumulate
1685  * changes into mChangeList given that mHintsHandledByAncestors is already
1686  * accumulated for an ancestor.
1687  * mParentContent is the content node used to resolve the parent style
1688  * context.  This means that, for pseudo-elements, it is the content
1689  * that should be used for selector matching (rather than the fake
1690  * content node attached to the frame).
1691  */
Restyle(nsRestyleHint aRestyleHint)1692 void ElementRestyler::Restyle(nsRestyleHint aRestyleHint) {
1693   // It would be nice if we could make stronger assertions here; they
1694   // would let us simplify the ?: expressions below setting |content|
1695   // and |pseudoContent| in sensible ways as well as making what
1696   // |content| and |pseudoContent| mean, and their relationship to
1697   // |mFrame->GetContent()|, make more sense.  However, we can't,
1698   // because of frame trees like the one in
1699   // https://bugzilla.mozilla.org/show_bug.cgi?id=472353#c14 .  Once we
1700   // fix bug 242277 we should be able to make this make more sense.
1701   NS_ASSERTION(
1702       mFrame->GetContent() || !mParentContent || !mParentContent->GetParent(),
1703       "frame must have content (unless at the top of the tree)");
1704   MOZ_ASSERT(mPresContext == mFrame->PresContext(), "pres contexts match");
1705 
1706   NS_ASSERTION(!GetPrevContinuationWithSameStyle(mFrame),
1707                "should not be trying to restyle this frame separately");
1708 
1709   MOZ_ASSERT(!(aRestyleHint & eRestyle_LaterSiblings),
1710              "eRestyle_LaterSiblings must not be part of aRestyleHint");
1711 
1712   AutoDisplayContentsAncestorPusher adcp(
1713       mTreeMatchContext, mPresContext,
1714       mFrame->GetContent() ? mFrame->GetContent()->GetParent() : nullptr);
1715 
1716   AutoSelectorArrayTruncater asat(mSelectorsForDescendants);
1717 
1718   // List of descendant elements of mContent we know we will eventually need to
1719   // restyle.  Before we return from this function, we call
1720   // RestyleTracker::AddRestyleRootsIfAwaitingRestyle to ensure they get
1721   // restyled in RestyleTracker::DoProcessRestyles.
1722   nsTArray<RefPtr<Element>> descendants;
1723 
1724   nsRestyleHint hintToRestore = nsRestyleHint(0);
1725   RestyleHintData hintDataToRestore;
1726   if (mContent && mContent->IsElement() &&
1727       // If we're resolving from the root of the frame tree (which
1728       // we do when mDoRebuildAllStyleData), we need to avoid getting the
1729       // root's restyle data until we get to its primary frame, since
1730       // it's the primary frame that has the styles for the root element
1731       // (rather than the ancestors of the primary frame whose mContent
1732       // is the root node but which have different styles).  If we use
1733       // up the hint for one of the ancestors that we hit first, then
1734       // we'll fail to do the restyling we need to do.
1735       // Likewise, if we're restyling something with two nested frames,
1736       // and we post a restyle from the transition manager while
1737       // computing style for the outer frame (to be computed after the
1738       // descendants have been resolved), we don't want to consume it
1739       // for the inner frame.
1740       mContent->GetPrimaryFrame() == mFrame) {
1741     nsAutoPtr<RestyleTracker::RestyleData> restyleData;
1742     if (mRestyleTracker.GetRestyleData(mContent->AsElement(), restyleData)) {
1743       nsChangeHint changeToAppend = NS_RemoveSubsumedHints(
1744           restyleData->mChangeHint, mHintsHandledByAncestors);
1745       // See the comment in CaptureChange about why we use NS_IsHintSubset here.
1746       if (!NS_IsHintSubset(changeToAppend, mHintsHandledBySelf)) {
1747         mHintsHandledBySelf |= changeToAppend;
1748         mChangeList->AppendChange(mFrame, mContent, changeToAppend);
1749       }
1750       mSelectorsForDescendants.AppendElements(
1751           restyleData->mRestyleHintData.mSelectorsForDescendants);
1752       hintToRestore = restyleData->mRestyleHint;
1753       hintDataToRestore = Move(restyleData->mRestyleHintData);
1754       aRestyleHint = nsRestyleHint(aRestyleHint | restyleData->mRestyleHint);
1755       descendants.SwapElements(restyleData->mDescendants);
1756     }
1757   }
1758 
1759   // If we are restyling this frame with eRestyle_Self or weaker hints,
1760   // we restyle children with nsRestyleHint(0).  But we pass the
1761   // eRestyle_ForceDescendants flag down too.
1762   nsRestyleHint childRestyleHint = nsRestyleHint(
1763       aRestyleHint & (eRestyle_SomeDescendants | eRestyle_Subtree |
1764                       eRestyle_ForceDescendants));
1765 
1766   RefPtr<GeckoStyleContext> oldContext = mFrame->StyleContext()->AsGecko();
1767 
1768   nsTArray<SwapInstruction> swaps;
1769 
1770   // TEMPORARY (until bug 918064):  Call RestyleSelf for each
1771   // continuation or block-in-inline sibling.
1772 
1773   // We must make a single decision on how to process this frame and
1774   // its descendants, yet RestyleSelf might return different RestyleResult
1775   // values for the different same-style continuations.  |result| is our
1776   // overall decision.
1777   RestyleResult result = RestyleResult::eNone;
1778   uint32_t swappedStructs = 0;
1779 
1780   nsRestyleHint thisRestyleHint = aRestyleHint;
1781 
1782   bool haveMoreContinuations = false;
1783   for (nsIFrame* f = mFrame; f;) {
1784     RestyleResult thisResult =
1785         RestyleSelf(f, thisRestyleHint, &swappedStructs, swaps);
1786 
1787     if (thisResult != RestyleResult::eStop) {
1788       // Calls to RestyleSelf for later same-style continuations must not
1789       // return RestyleResult::eStop, so pass eRestyle_Force in to them.
1790       thisRestyleHint = nsRestyleHint(thisRestyleHint | eRestyle_Force);
1791 
1792       if (result == RestyleResult::eStop) {
1793         // We received RestyleResult::eStop for earlier same-style
1794         // continuations, and RestyleResult::eStopWithStyleChange or
1795         // RestyleResult::eContinue(AndForceDescendants) for this one; go
1796         // back and force-restyle the earlier continuations.
1797         result = thisResult;
1798         f = mFrame;
1799         continue;
1800       }
1801     }
1802 
1803     if (thisResult > result) {
1804       // We take the highest RestyleResult value when working out what to do
1805       // with this frame and its descendants.  Higher RestyleResult values
1806       // represent a superset of the work done by lower values.
1807       result = thisResult;
1808     }
1809 
1810     f = GetNextContinuationWithSameStyle(f, oldContext, &haveMoreContinuations);
1811   }
1812 
1813   // Some changes to animations don't affect the computed style and yet still
1814   // require the layer to be updated. For example, pausing an animation via
1815   // the Web Animations API won't affect an element's style but still
1816   // requires to update the animation on the layer.
1817   //
1818   // Although we only expect this code path to be called when computed style
1819   // is not changing, we can sometimes reach this at the end of a transition
1820   // when the animated style is being removed. Since
1821   // AddLayerChangesForAnimation checks if mFrame has a transform style or not,
1822   // we need to call it *after* calling RestyleSelf to ensure the animated
1823   // transform has been removed first.
1824   RestyleManager::AddLayerChangesForAnimation(mFrame, mContent, *mChangeList);
1825 
1826   if (haveMoreContinuations && hintToRestore) {
1827     // If we have more continuations with different style (e.g., because
1828     // we're inside a ::first-letter or ::first-line), put the restyle
1829     // hint back.
1830     mRestyleTracker.AddPendingRestyleToTable(mContent->AsElement(),
1831                                              hintToRestore, nsChangeHint(0));
1832   }
1833 
1834   if (result == RestyleResult::eStop) {
1835     MOZ_ASSERT(mFrame->StyleContext() == oldContext,
1836                "frame should have been left with its old style context");
1837 
1838     nsIFrame* unused;
1839     GeckoStyleContext* newParent =
1840         mFrame->GetParentStyleContext(&unused)->AsGecko();
1841     if (oldContext->GetParent() != newParent) {
1842       // If we received RestyleResult::eStop, then the old style context was
1843       // left on mFrame.  Since we ended up restyling our parent, change
1844       // this old style context to point to its new parent.
1845       LOG_RESTYLE("moving style context %p from old parent %p to new parent %p",
1846                   oldContext.get(), oldContext->GetParent(), newParent);
1847       // We keep strong references to the new parent around until the end
1848       // of the restyle, in case:
1849       //   (a) we swapped structs between the old and new parent,
1850       //   (b) some descendants of the old parent are not getting restyled
1851       //       (which is the reason for the existence of
1852       //       ClearCachedInheritedStyleDataOnDescendants),
1853       //   (c) something under ProcessPendingRestyles (which notably is called
1854       //       *before* ClearCachedInheritedStyleDataOnDescendants is called
1855       //       on the old context) causes the new parent to be destroyed, thus
1856       //       destroying its owned structs, and
1857       //   (d) something under ProcessPendingRestyles then wants to use of those
1858       //       now destroyed structs (through the old parent's descendants).
1859       mSwappedStructOwners.AppendElement(newParent);
1860       oldContext->MoveTo(newParent);
1861     }
1862 
1863     // Send the accessibility notifications that RestyleChildren otherwise
1864     // would have sent.
1865     if (!(mHintsHandledBySelf & nsChangeHint_ReconstructFrame)) {
1866       InitializeAccessibilityNotifications(mFrame->StyleContext());
1867       SendAccessibilityNotifications();
1868     }
1869 
1870     mRestyleTracker.AddRestyleRootsIfAwaitingRestyle(descendants);
1871     if (aRestyleHint & eRestyle_SomeDescendants) {
1872       ConditionallyRestyleChildren();
1873     }
1874     return;
1875   }
1876 
1877   if (result == RestyleResult::eStopWithStyleChange &&
1878       !(mHintsHandledBySelf & nsChangeHint_ReconstructFrame)) {
1879     MOZ_ASSERT(mFrame->StyleContext() != oldContext,
1880                "RestyleResult::eStopWithStyleChange should only be returned "
1881                "if we got a new style context or we will reconstruct");
1882     MOZ_ASSERT(swappedStructs == 0,
1883                "should have ensured we didn't swap structs when "
1884                "returning RestyleResult::eStopWithStyleChange");
1885 
1886     // We need to ensure that all of the frames that inherit their style
1887     // from oldContext are able to be moved across to newContext.
1888     // MoveStyleContextsForChildren will check for certain conditions
1889     // to ensure it is safe to move all of the relevant child style
1890     // contexts to newContext.  If these conditions fail, it will
1891     // return false, and we'll have to continue restyling.
1892     const bool canStop = MoveStyleContextsForChildren(oldContext);
1893 
1894     if (canStop) {
1895       // Send the accessibility notifications that RestyleChildren otherwise
1896       // would have sent.
1897       if (!(mHintsHandledBySelf & nsChangeHint_ReconstructFrame)) {
1898         InitializeAccessibilityNotifications(mFrame->StyleContext());
1899         SendAccessibilityNotifications();
1900       }
1901 
1902       mRestyleTracker.AddRestyleRootsIfAwaitingRestyle(descendants);
1903       if (aRestyleHint & eRestyle_SomeDescendants) {
1904         ConditionallyRestyleChildren();
1905       }
1906       return;
1907     }
1908 
1909     // Turns out we couldn't stop restyling here.  Process the struct
1910     // swaps that RestyleSelf would've done had we not returned
1911     // RestyleResult::eStopWithStyleChange.
1912     for (SwapInstruction& swap : swaps) {
1913       LOG_RESTYLE("swapping style structs between %p and %p",
1914                   swap.mOldContext.get(), swap.mNewContext.get());
1915       swap.mOldContext->AsGecko()->SwapStyleData(swap.mNewContext->AsGecko(),
1916                                                  swap.mStructsToSwap);
1917       swappedStructs |= swap.mStructsToSwap;
1918     }
1919     swaps.Clear();
1920   }
1921 
1922   if (!swappedStructs) {
1923     // If we swapped any structs from the old context, then we need to keep
1924     // it alive until after the RestyleChildren call so that we can fix up
1925     // its descendants' cached structs.
1926     oldContext = nullptr;
1927   }
1928 
1929   if (result == RestyleResult::eContinueAndForceDescendants) {
1930     childRestyleHint =
1931         nsRestyleHint(childRestyleHint | eRestyle_ForceDescendants);
1932   }
1933 
1934   // No need to do this if we're planning to reframe already.
1935   // It's also important to check mHintsHandledBySelf since we use
1936   // mFrame->StyleContext(), which is out of date if mHintsHandledBySelf
1937   // has a ReconstructFrame hint.  Using an out of date style
1938   // context could trigger assertions about mismatched rule trees.
1939   if (!(mHintsHandledBySelf & nsChangeHint_ReconstructFrame)) {
1940     RestyleChildren(childRestyleHint);
1941   }
1942 
1943   if (oldContext && !oldContext->HasSingleReference()) {
1944     // If we swapped some structs out of oldContext in the RestyleSelf call
1945     // and after the RestyleChildren call we still have other strong references
1946     // to it, we need to make ensure its descendants don't cache any of the
1947     // structs that were swapped out.
1948     //
1949     // Much of the time we will not get in here; we do for example when the
1950     // style context is shared with a later IB split sibling (which we won't
1951     // restyle until a bit later) or if other code is holding a strong reference
1952     // to the style context (as is done by nsTransformedTextRun objects, which
1953     // can be referenced by a text frame's mTextRun longer than the frame's
1954     // mStyleContext).
1955     //
1956     // Also, we don't want this style context to get any more uses by being
1957     // returned from GeckoStyleContext::FindChildWithRules, so we add the
1958     // NS_STYLE_INELIGIBLE_FOR_SHARING bit to it.
1959     oldContext->SetIneligibleForSharing();
1960 
1961     ContextToClear* toClear = mContextsToClear.AppendElement();
1962     toClear->mStyleContext = Move(oldContext);
1963     toClear->mStructs = swappedStructs;
1964   }
1965 
1966   mRestyleTracker.AddRestyleRootsIfAwaitingRestyle(descendants);
1967 }
1968 
1969 /**
1970  * Depending on the details of the frame we are restyling or its old style
1971  * context, we may or may not be able to stop restyling after this frame if
1972  * we find we had no style changes.
1973  *
1974  * This function returns RestyleResult::eStop if it does not find any
1975  * conditions that would preclude stopping restyling, and
1976  * RestyleResult::eContinue if it does.
1977  */
ComputeRestyleResultFromFrame(nsIFrame * aSelf,RestyleResult & aRestyleResult,bool & aCanStopWithStyleChange)1978 void ElementRestyler::ComputeRestyleResultFromFrame(
1979     nsIFrame* aSelf, RestyleResult& aRestyleResult,
1980     bool& aCanStopWithStyleChange) {
1981   // We can't handle situations where the primary style context of a frame
1982   // has not had any style data changes, but its additional style contexts
1983   // have, so we don't considering stopping if this frame has any additional
1984   // style contexts.
1985   if (aSelf->GetAdditionalStyleContext(0)) {
1986     LOG_RESTYLE_CONTINUE("there are additional style contexts");
1987     aRestyleResult = RestyleResult::eContinue;
1988     aCanStopWithStyleChange = false;
1989     return;
1990   }
1991 
1992   // Each NAC element inherits from the first non-NAC ancestor, so child
1993   // NAC may inherit from our parent instead of us. That means we can't
1994   // cull traversal if our style context didn't change.
1995   if (aSelf->GetContent() && aSelf->GetContent()->IsNativeAnonymous()) {
1996     LOG_RESTYLE_CONTINUE("native anonymous content");
1997     aRestyleResult = RestyleResult::eContinue;
1998     aCanStopWithStyleChange = false;
1999     return;
2000   }
2001 
2002   // Style changes might have moved children between the two nsLetterFrames
2003   // (the one matching ::first-letter and the one containing the rest of the
2004   // content).  Continue restyling to the children of the nsLetterFrame so
2005   // that they get the correct style context parent.  Similarly for
2006   // nsLineFrames.
2007   LayoutFrameType type = aSelf->Type();
2008 
2009   if (type == LayoutFrameType::Letter) {
2010     LOG_RESTYLE_CONTINUE("frame is a letter frame");
2011     aRestyleResult = RestyleResult::eContinue;
2012     aCanStopWithStyleChange = false;
2013     return;
2014   }
2015 
2016   if (type == LayoutFrameType::Line) {
2017     LOG_RESTYLE_CONTINUE("frame is a line frame");
2018     aRestyleResult = RestyleResult::eContinue;
2019     aCanStopWithStyleChange = false;
2020     return;
2021   }
2022 
2023   // Some style computations depend not on the parent's style, but a grandparent
2024   // or one the grandparent's ancestors.  An example is an explicit 'inherit'
2025   // value for align-self, where if the parent frame's value for the property is
2026   // 'auto' we end up inheriting the computed value from the grandparent.  We
2027   // can't stop the restyling process on this frame (the one with 'auto', in
2028   // this example), as the grandparent's computed value might have changed
2029   // and we need to recompute the child's 'inherit' to that new value.
2030   GeckoStyleContext* oldContext = aSelf->StyleContext()->AsGecko();
2031   if (oldContext->HasChildThatUsesGrandancestorStyle()) {
2032     LOG_RESTYLE_CONTINUE("the old context uses grandancestor style");
2033     aRestyleResult = RestyleResult::eContinue;
2034     aCanStopWithStyleChange = false;
2035     return;
2036   }
2037 
2038   // We ignore all situations that involve :visited style.
2039   if (oldContext->GetStyleIfVisited()) {
2040     LOG_RESTYLE_CONTINUE("the old style context has StyleIfVisited");
2041     aRestyleResult = RestyleResult::eContinue;
2042     aCanStopWithStyleChange = false;
2043     return;
2044   }
2045 
2046   GeckoStyleContext* parentContext = oldContext->GetParent();
2047   if (parentContext && parentContext->GetStyleIfVisited()) {
2048     LOG_RESTYLE_CONTINUE("the old style context's parent has StyleIfVisited");
2049     aRestyleResult = RestyleResult::eContinue;
2050     aCanStopWithStyleChange = false;
2051     return;
2052   }
2053 
2054   // We also ignore frames for pseudos, as their style contexts have
2055   // inheritance structures that do not match the frame inheritance
2056   // structure.  To avoid enumerating and checking all of the cases
2057   // where we have this kind of inheritance, we keep restyling past
2058   // pseudos.
2059   nsAtom* pseudoTag = oldContext->GetPseudo();
2060   if (pseudoTag && !nsCSSAnonBoxes::IsNonElement(pseudoTag)) {
2061     LOG_RESTYLE_CONTINUE("the old style context is for a pseudo");
2062     aRestyleResult = RestyleResult::eContinue;
2063     aCanStopWithStyleChange = false;
2064     return;
2065   }
2066 
2067   nsIFrame* parent = mFrame->GetParent();
2068 
2069   if (parent) {
2070     // Also if the parent has a pseudo, as this frame's style context will
2071     // be inheriting from a grandparent frame's style context (or a further
2072     // ancestor).
2073     nsAtom* parentPseudoTag = parent->StyleContext()->GetPseudo();
2074     if (parentPseudoTag &&
2075         parentPseudoTag != nsCSSAnonBoxes::firstLetterContinuation) {
2076       MOZ_ASSERT(parentPseudoTag != nsCSSAnonBoxes::mozText,
2077                  "Style of text node should not be parent of anything");
2078       MOZ_ASSERT(parentPseudoTag != nsCSSAnonBoxes::oofPlaceholder,
2079                  "Style of placeholder should not be parent of anything");
2080       LOG_RESTYLE_CONTINUE("the old style context's parent is for a pseudo");
2081       aRestyleResult = RestyleResult::eContinue;
2082       // Parent style context pseudo-ness doesn't affect whether we can
2083       // return RestyleResult::eStopWithStyleChange.
2084       //
2085       // If we had later conditions to check in this function, we would
2086       // continue to check them, in case we set aCanStopWithStyleChange to
2087       // false.
2088     }
2089   }
2090 }
2091 
ComputeRestyleResultFromNewContext(nsIFrame * aSelf,GeckoStyleContext * aNewContext,RestyleResult & aRestyleResult,bool & aCanStopWithStyleChange)2092 void ElementRestyler::ComputeRestyleResultFromNewContext(
2093     nsIFrame* aSelf, GeckoStyleContext* aNewContext,
2094     RestyleResult& aRestyleResult, bool& aCanStopWithStyleChange) {
2095   // If we've already determined that we must continue styling, we don't
2096   // need to check anything.
2097   if (aRestyleResult == RestyleResult::eContinue && !aCanStopWithStyleChange) {
2098     return;
2099   }
2100 
2101   // Keep restyling if the new style context has any style-if-visted style, so
2102   // that we can avoid the style context tree surgery having to deal to deal
2103   // with visited styles.
2104   if (aNewContext->GetStyleIfVisited()) {
2105     LOG_RESTYLE_CONTINUE("the new style context has StyleIfVisited");
2106     aRestyleResult = RestyleResult::eContinue;
2107     aCanStopWithStyleChange = false;
2108     return;
2109   }
2110 
2111   // If link-related information has changed, or the pseudo for the frame has
2112   // changed, or the new style context points to a different rule node, we can't
2113   // leave the old style context on the frame.
2114   GeckoStyleContext* oldContext = aSelf->StyleContext()->AsGecko();
2115   if (oldContext->IsLinkContext() != aNewContext->IsLinkContext() ||
2116       oldContext->RelevantLinkVisited() != aNewContext->RelevantLinkVisited() ||
2117       oldContext->GetPseudo() != aNewContext->GetPseudo() ||
2118       oldContext->GetPseudoType() != aNewContext->GetPseudoType()) {
2119     LOG_RESTYLE_CONTINUE(
2120         "the old and new style contexts have different link/"
2121         "visited/pseudo");
2122     aRestyleResult = RestyleResult::eContinue;
2123     aCanStopWithStyleChange = false;
2124     return;
2125   }
2126 
2127   if (oldContext->RuleNode() != aNewContext->RuleNode()) {
2128     LOG_RESTYLE_CONTINUE(
2129         "the old and new style contexts have different "
2130         "rulenodes");
2131     aRestyleResult = RestyleResult::eContinue;
2132     // Continue to check other conditions if aCanStopWithStyleChange might
2133     // still need to be set to false.
2134     if (!aCanStopWithStyleChange) {
2135       return;
2136     }
2137   }
2138 
2139   if (auto* position = oldContext->PeekStylePosition()) {
2140     const bool wasLegacyJustifyItems =
2141         position->mJustifyItems & NS_STYLE_JUSTIFY_LEGACY;
2142     const auto newJustifyItems = aNewContext->StylePosition()->mJustifyItems;
2143     const bool isLegacyJustifyItems = newJustifyItems & NS_STYLE_JUSTIFY_LEGACY;
2144 
2145     // Children with justify-items: legacy may depend on our value.
2146     if (wasLegacyJustifyItems != isLegacyJustifyItems ||
2147         (wasLegacyJustifyItems && position->mJustifyItems != newJustifyItems)) {
2148       LOG_RESTYLE_CONTINUE(
2149           "legacy justify-items changed between old and new"
2150           " style contexts");
2151       aRestyleResult = RestyleResult::eContinue;
2152       aCanStopWithStyleChange = false;
2153       return;
2154     }
2155   }
2156 
2157   // If the old and new style contexts differ in their
2158   // NS_STYLE_HAS_TEXT_DECORATION_LINES or NS_STYLE_HAS_PSEUDO_ELEMENT_DATA
2159   // bits, then we must keep restyling so that those new bit values are
2160   // propagated.
2161   if (oldContext->HasTextDecorationLines() !=
2162       aNewContext->HasTextDecorationLines()) {
2163     LOG_RESTYLE_CONTINUE(
2164         "NS_STYLE_HAS_TEXT_DECORATION_LINES differs between old"
2165         " and new style contexts");
2166     aRestyleResult = RestyleResult::eContinue;
2167     aCanStopWithStyleChange = false;
2168     return;
2169   }
2170 
2171   if (oldContext->HasPseudoElementData() !=
2172       aNewContext->HasPseudoElementData()) {
2173     LOG_RESTYLE_CONTINUE(
2174         "NS_STYLE_HAS_PSEUDO_ELEMENT_DATA differs between old"
2175         " and new style contexts");
2176     aRestyleResult = RestyleResult::eContinue;
2177     aCanStopWithStyleChange = false;
2178     return;
2179   }
2180 
2181   if (oldContext->ShouldSuppressLineBreak() !=
2182       aNewContext->ShouldSuppressLineBreak()) {
2183     LOG_RESTYLE_CONTINUE(
2184         "NS_STYLE_SUPPRESS_LINEBREAK differs"
2185         "between old and new style contexts");
2186     aRestyleResult = RestyleResult::eContinue;
2187     aCanStopWithStyleChange = false;
2188     return;
2189   }
2190 
2191   if (oldContext->IsInDisplayNoneSubtree() !=
2192       aNewContext->IsInDisplayNoneSubtree()) {
2193     LOG_RESTYLE_CONTINUE(
2194         "NS_STYLE_IN_DISPLAY_NONE_SUBTREE differs between old"
2195         " and new style contexts");
2196     aRestyleResult = RestyleResult::eContinue;
2197     aCanStopWithStyleChange = false;
2198     return;
2199   }
2200 
2201   if (oldContext->IsTextCombined() != aNewContext->IsTextCombined()) {
2202     LOG_RESTYLE_CONTINUE(
2203         "NS_STYLE_IS_TEXT_COMBINED differs between "
2204         "old and new style contexts");
2205     aRestyleResult = RestyleResult::eContinue;
2206     aCanStopWithStyleChange = false;
2207     return;
2208   }
2209 }
2210 
SelectorMatchesForRestyle(Element * aElement)2211 bool ElementRestyler::SelectorMatchesForRestyle(Element* aElement) {
2212   if (!aElement) {
2213     return false;
2214   }
2215   for (nsCSSSelector* selector : mSelectorsForDescendants) {
2216     if (nsCSSRuleProcessor::RestrictedSelectorMatches(aElement, selector,
2217                                                       mTreeMatchContext)) {
2218       return true;
2219     }
2220   }
2221   return false;
2222 }
2223 
MustRestyleSelf(nsRestyleHint aRestyleHint,Element * aElement)2224 bool ElementRestyler::MustRestyleSelf(nsRestyleHint aRestyleHint,
2225                                       Element* aElement) {
2226   return (aRestyleHint & (eRestyle_Self | eRestyle_Subtree)) ||
2227          ((aRestyleHint & eRestyle_SomeDescendants) &&
2228           SelectorMatchesForRestyle(aElement));
2229 }
2230 
CanReparentStyleContext(nsRestyleHint aRestyleHint)2231 bool ElementRestyler::CanReparentStyleContext(nsRestyleHint aRestyleHint) {
2232   // If we had any restyle hints other than the ones listed below,
2233   // which don't control whether the current frame/element needs
2234   // a new style context by looking up a new rule node, or if
2235   // we are reconstructing the entire rule tree, then we can't
2236   // use ReparentStyleContext.
2237   return !(aRestyleHint & ~(eRestyle_Force | eRestyle_ForceDescendants |
2238                             eRestyle_SomeDescendants)) &&
2239          !StyleSet()->IsInRuleTreeReconstruct();
2240 }
2241 
2242 // Returns true iff any rule node that is an ancestor-or-self of the
2243 // two specified rule nodes, but which is not an ancestor of both,
2244 // has any inherited style data.  If false is returned, then we know
2245 // that a change from one rule node to the other must not result in
2246 // any change in inherited style data.
CommonInheritedStyleData(nsRuleNode * aRuleNode1,nsRuleNode * aRuleNode2)2247 static bool CommonInheritedStyleData(nsRuleNode* aRuleNode1,
2248                                      nsRuleNode* aRuleNode2) {
2249   if (aRuleNode1 == aRuleNode2) {
2250     return true;
2251   }
2252 
2253   nsRuleNode* n1 = aRuleNode1->GetParent();
2254   nsRuleNode* n2 = aRuleNode2->GetParent();
2255 
2256   if (n1 == n2) {
2257     // aRuleNode1 and aRuleNode2 sharing a parent is a common case, e.g.
2258     // when modifying a style="" attribute.  (We must null check GetRule()'s
2259     // result since although we know the two parents are the same, it might
2260     // be null, as in the case of the two rule nodes being roots of two
2261     // different rule trees.)
2262     if (aRuleNode1->GetRule() &&
2263         aRuleNode1->GetRule()->MightMapInheritedStyleData()) {
2264       return false;
2265     }
2266     if (aRuleNode2->GetRule() &&
2267         aRuleNode2->GetRule()->MightMapInheritedStyleData()) {
2268       return false;
2269     }
2270     return true;
2271   }
2272 
2273   // Compute the depths of aRuleNode1 and aRuleNode2.
2274   int d1 = 0, d2 = 0;
2275   while (n1) {
2276     ++d1;
2277     n1 = n1->GetParent();
2278   }
2279   while (n2) {
2280     ++d2;
2281     n2 = n2->GetParent();
2282   }
2283 
2284   // Make aRuleNode1 be the deeper node.
2285   if (d2 > d1) {
2286     std::swap(d1, d2);
2287     std::swap(aRuleNode1, aRuleNode2);
2288   }
2289 
2290   // Check all of the rule nodes in the deeper branch until we reach
2291   // the same depth as the shallower branch.
2292   n1 = aRuleNode1;
2293   n2 = aRuleNode2;
2294   while (d1 > d2) {
2295     nsIStyleRule* rule = n1->GetRule();
2296     MOZ_ASSERT(rule, "non-root rule node should have a rule");
2297     if (rule->MightMapInheritedStyleData()) {
2298       return false;
2299     }
2300     n1 = n1->GetParent();
2301     --d1;
2302   }
2303 
2304   // Check both branches simultaneously until we reach a common ancestor.
2305   while (n1 != n2) {
2306     MOZ_ASSERT(n1);
2307     MOZ_ASSERT(n2);
2308     // As above, we must null check GetRule()'s result since we won't find
2309     // a common ancestor if the two rule nodes come from different rule trees,
2310     // and thus we might reach the root (which has a null rule).
2311     if (n1->GetRule() && n1->GetRule()->MightMapInheritedStyleData()) {
2312       return false;
2313     }
2314     if (n2->GetRule() && n2->GetRule()->MightMapInheritedStyleData()) {
2315       return false;
2316     }
2317     n1 = n1->GetParent();
2318     n2 = n2->GetParent();
2319   }
2320 
2321   return true;
2322 }
2323 
RestyleSelf(nsIFrame * aSelf,nsRestyleHint aRestyleHint,uint32_t * aSwappedStructs,nsTArray<SwapInstruction> & aSwaps)2324 ElementRestyler::RestyleResult ElementRestyler::RestyleSelf(
2325     nsIFrame* aSelf, nsRestyleHint aRestyleHint, uint32_t* aSwappedStructs,
2326     nsTArray<SwapInstruction>& aSwaps) {
2327   MOZ_ASSERT(!(aRestyleHint & eRestyle_LaterSiblings),
2328              "eRestyle_LaterSiblings must not be part of aRestyleHint");
2329 
2330   // XXXldb get new context from prev-in-flow if possible, to avoid
2331   // duplication.  (Or should we just let |GetContext| handle that?)
2332   // Getting the hint would be nice too, but that's harder.
2333 
2334   // XXXbryner we may be able to avoid some of the refcounting goop here.
2335   // We do need a reference to oldContext for the lifetime of this function, and
2336   // it's possible that the frame has the last reference to it, so AddRef it
2337   // here.
2338 
2339   LOG_RESTYLE("RestyleSelf %s, aRestyleHint = %s",
2340               FrameTagToString(aSelf).get(),
2341               RestyleManager::RestyleHintToString(aRestyleHint).get());
2342   LOG_RESTYLE_INDENT();
2343 
2344   // Initially assume that it is safe to stop restyling.
2345   //
2346   // Throughout most of this function, we update the following two variables
2347   // independently.  |result| is set to RestyleResult::eContinue when we
2348   // detect a condition that would not allow us to return RestyleResult::eStop.
2349   // |canStopWithStyleChange| is set to false when we detect a condition
2350   // that would not allow us to return RestyleResult::eStopWithStyleChange.
2351   //
2352   // Towards the end of this function, we reconcile these two variables --
2353   // if |canStopWithStyleChange| is true, we convert |result| into
2354   // RestyleResult::eStopWithStyleChange.
2355   RestyleResult result = RestyleResult::eStop;
2356   bool canStopWithStyleChange = true;
2357 
2358   if (aRestyleHint & ~eRestyle_SomeDescendants) {
2359     // If we are doing any restyling of the current element, or if we're
2360     // forced to continue, we must.
2361     result = RestyleResult::eContinue;
2362 
2363     // If we have to restyle children, we can't return
2364     // RestyleResult::eStopWithStyleChange.
2365     if (aRestyleHint &
2366         (eRestyle_Subtree | eRestyle_Force | eRestyle_ForceDescendants)) {
2367       canStopWithStyleChange = false;
2368     }
2369   }
2370 
2371   // We only consider returning RestyleResult::eStopWithStyleChange if this
2372   // is the root of the restyle.  (Otherwise, we would need to track the
2373   // style changes of the ancestors we just restyled.)
2374   if (!mIsRootOfRestyle) {
2375     canStopWithStyleChange = false;
2376   }
2377 
2378   // Look at the frame and its current style context for conditions
2379   // that would change our RestyleResult.
2380   ComputeRestyleResultFromFrame(aSelf, result, canStopWithStyleChange);
2381 
2382   nsChangeHint assumeDifferenceHint = nsChangeHint(0);
2383   RefPtr<GeckoStyleContext> oldContext = aSelf->StyleContext()->AsGecko();
2384   nsStyleSet* styleSet = StyleSet();
2385 
2386 #ifdef ACCESSIBILITY
2387   mWasFrameVisible = nsIPresShell::IsAccessibilityActive()
2388                          ? oldContext->StyleVisibility()->IsVisible()
2389                          : false;
2390 #endif
2391 
2392   nsAtom* const pseudoTag = oldContext->GetPseudo();
2393   const CSSPseudoElementType pseudoType = oldContext->GetPseudoType();
2394 
2395   // Get the frame providing the parent style context.  If it is a
2396   // child, then resolve the provider first.
2397   nsIFrame* providerFrame;
2398   nsStyleContext* parentContext_ = aSelf->GetParentStyleContext(&providerFrame);
2399   bool isChild = providerFrame && providerFrame->GetParent() == aSelf;
2400   if (isChild) {
2401     MOZ_ASSERT(providerFrame->GetContent() == aSelf->GetContent(),
2402                "Postcondition for GetParentStyleContext() violated. "
2403                "That means we need to add the current element to the "
2404                "ancestor filter.");
2405 
2406     // resolve the provider here (before aSelf below).
2407     LOG_RESTYLE("resolving child provider frame");
2408 
2409     // assumeDifferenceHint forces the parent's change to be also
2410     // applied to this frame, no matter what
2411     // GeckoStyleContext::CalcStyleDifference says. CalcStyleDifference
2412     // can't be trusted because it assumes any changes to the parent
2413     // style context provider will be automatically propagated to
2414     // the frame(s) with child style contexts.
2415 
2416     ElementRestyler providerRestyler(PARENT_CONTEXT_FROM_CHILD_FRAME, *this,
2417                                      providerFrame);
2418     providerRestyler.Restyle(aRestyleHint);
2419     assumeDifferenceHint = providerRestyler.HintsHandledForFrame();
2420 
2421     // The provider's new context becomes the parent context of
2422     // aSelf's context.
2423     parentContext_ = providerFrame->StyleContext();
2424     // Set |mResolvedChild| so we don't bother resolving the
2425     // provider again.
2426     mResolvedChild = providerFrame;
2427     LOG_RESTYLE_CONTINUE("we had a provider frame");
2428     // Continue restyling past the odd style context inheritance.
2429     result = RestyleResult::eContinue;
2430     canStopWithStyleChange = false;
2431   }
2432 
2433   auto* parentContext = parentContext_ ? parentContext_->AsGecko() : nullptr;
2434   LOG_RESTYLE("parentContext = %p", parentContext);
2435 
2436   // do primary context
2437   RefPtr<GeckoStyleContext> newContext;
2438   nsIFrame* prevContinuation = GetPrevContinuationWithPossiblySameStyle(aSelf);
2439   GeckoStyleContext* prevContinuationContext;
2440   bool copyFromContinuation =
2441       prevContinuation &&
2442       (prevContinuationContext = prevContinuation->StyleContext()->AsGecko())
2443               ->GetPseudo() == oldContext->GetPseudo() &&
2444       prevContinuationContext->GetParent() == parentContext;
2445   if (copyFromContinuation) {
2446     // Just use the style context from the frame's previous
2447     // continuation.
2448     LOG_RESTYLE("using previous continuation's context");
2449     newContext = prevContinuationContext;
2450   } else if (pseudoTag == nsCSSAnonBoxes::mozText) {
2451     MOZ_ASSERT(aSelf->IsTextFrame());
2452     newContext =
2453         styleSet->ResolveStyleForText(aSelf->GetContent(), parentContext);
2454   } else if (pseudoTag == nsCSSAnonBoxes::firstLetterContinuation) {
2455     newContext =
2456         styleSet->ResolveStyleForFirstLetterContinuation(parentContext);
2457   } else if (pseudoTag == nsCSSAnonBoxes::oofPlaceholder) {
2458     // We still need to ResolveStyleForPlaceholder() here, because we may be
2459     // doing a ruletree reconstruct and hence actually changing our style
2460     // context.
2461     newContext = styleSet->ResolveStyleForPlaceholder();
2462   } else if (pseudoType == CSSPseudoElementType::NonInheritingAnonBox) {
2463     // We still need to ResolveNonInheritingAnonymousBoxStyle() here, because we
2464     // may be doing a ruletree reconstruct and hence actually changing our style
2465     // context.
2466     newContext = styleSet->ResolveNonInheritingAnonymousBoxStyle(pseudoTag);
2467   } else {
2468     Element* element =
2469         ElementForStyleContext(mParentContent, aSelf, pseudoType);
2470     if (!MustRestyleSelf(aRestyleHint, element)) {
2471       if (CanReparentStyleContext(aRestyleHint)) {
2472         LOG_RESTYLE("reparenting style context");
2473         newContext =
2474             styleSet->ReparentStyleContext(oldContext, parentContext, element);
2475       } else {
2476         // Use ResolveStyleWithReplacement either for actual replacements
2477         // or, with no replacements, as a substitute for
2478         // ReparentStyleContext that rebuilds the path in the rule tree
2479         // rather than reusing the rule node, as we need to do during a
2480         // rule tree reconstruct.
2481         Element* pseudoElement =
2482             PseudoElementForStyleContext(aSelf, pseudoType);
2483         MOZ_ASSERT(!element || element != pseudoElement,
2484                    "pseudo-element for selector matching should be "
2485                    "the anonymous content node that we create, "
2486                    "not the real element");
2487         LOG_RESTYLE("resolving style with replacement");
2488         nsRestyleHint rshint = aRestyleHint & ~eRestyle_SomeDescendants;
2489         newContext = styleSet->ResolveStyleWithReplacement(
2490             element, pseudoElement, parentContext, oldContext, rshint);
2491       }
2492     } else if (pseudoType == CSSPseudoElementType::InheritingAnonBox) {
2493       newContext = styleSet->ResolveInheritingAnonymousBoxStyle(pseudoTag,
2494                                                                 parentContext);
2495     } else {
2496       if (pseudoTag) {
2497         if (pseudoTag == nsCSSPseudoElements::before ||
2498             pseudoTag == nsCSSPseudoElements::after) {
2499           // XXX what other pseudos do we need to treat like this?
2500           newContext = styleSet->ProbePseudoElementStyle(
2501               element, pseudoType, parentContext, mTreeMatchContext);
2502           if (!newContext) {
2503             // This pseudo should no longer exist; gotta reframe
2504             mHintsHandledBySelf |= nsChangeHint_ReconstructFrame;
2505             mChangeList->AppendChange(aSelf, element,
2506                                       nsChangeHint_ReconstructFrame);
2507             // We're reframing anyway; just keep the same context
2508             newContext = oldContext;
2509 #ifdef DEBUG
2510             // oldContext's parent might have had its style structs swapped out
2511             // with parentContext, so to avoid any assertions that might
2512             // otherwise trigger in oldContext's parent's destructor, we set a
2513             // flag on oldContext to skip it and its descendants in
2514             // GeckoStyleContext::AssertStructsNotUsedElsewhere.
2515             if (oldContext->GetParent() != parentContext) {
2516               oldContext->AddStyleBit(NS_STYLE_IS_GOING_AWAY);
2517             }
2518 #endif
2519           }
2520         } else {
2521           // Don't expect XUL tree stuff here, since it needs a comparator and
2522           // all.
2523           NS_ASSERTION(pseudoType < CSSPseudoElementType::Count,
2524                        "Unexpected pseudo type");
2525           Element* pseudoElement =
2526               PseudoElementForStyleContext(aSelf, pseudoType);
2527           MOZ_ASSERT(element != pseudoElement,
2528                      "pseudo-element for selector matching should be "
2529                      "the anonymous content node that we create, "
2530                      "not the real element");
2531           newContext = styleSet->ResolvePseudoElementStyle(
2532               element, pseudoType, parentContext, pseudoElement);
2533         }
2534       } else {
2535         NS_ASSERTION(aSelf->GetContent(),
2536                      "non pseudo-element frame without content node");
2537         // Skip parent display based style fixup for anonymous subtrees:
2538         TreeMatchContext::AutoParentDisplayBasedStyleFixupSkipper
2539             parentDisplayBasedFixupSkipper(
2540                 mTreeMatchContext, element->IsRootOfNativeAnonymousSubtree());
2541         newContext = styleSet->ResolveStyleFor(element, parentContext,
2542                                                mTreeMatchContext);
2543       }
2544     }
2545   }
2546 
2547   MOZ_ASSERT(newContext);
2548 
2549   if (!parentContext) {
2550     if (oldContext->RuleNode() == newContext->RuleNode() &&
2551         oldContext->IsLinkContext() == newContext->IsLinkContext() &&
2552         oldContext->RelevantLinkVisited() ==
2553             newContext->RelevantLinkVisited()) {
2554       // We're the root of the style context tree and the new style
2555       // context returned has the same rule node.  This means that
2556       // we can use FindChildWithRules to keep a lot of the old
2557       // style contexts around.  However, we need to start from the
2558       // same root.
2559       LOG_RESTYLE("restyling root and keeping old context");
2560       LOG_RESTYLE_IF(this, result != RestyleResult::eContinue,
2561                      "continuing restyle since this is the root");
2562       newContext = oldContext;
2563       // Never consider stopping restyling at the root.
2564       result = RestyleResult::eContinue;
2565       canStopWithStyleChange = false;
2566     }
2567   }
2568 
2569   LOG_RESTYLE(
2570       "oldContext = %p, newContext = %p%s", oldContext.get(), newContext.get(),
2571       oldContext == newContext ? (const char*)" (same)" : (const char*)"");
2572 
2573   if (newContext != oldContext) {
2574     if (oldContext->IsShared()) {
2575       // If the old style context was shared, then we can't return
2576       // RestyleResult::eStop and patch its parent to point to the
2577       // new parent style context, as that change might not be valid
2578       // for the other frames sharing the style context.
2579       LOG_RESTYLE_CONTINUE("the old style context is shared");
2580       result = RestyleResult::eContinue;
2581 
2582       // It is not safe to return RestyleResult::eStopWithStyleChange
2583       // when oldContext is shared and newContext has different
2584       // inherited style data, regardless of whether the oldContext has
2585       // that inherited style data cached.  We can't simply rely on the
2586       // samePointerStructs check later on, as the descendent style
2587       // contexts just might not have had their inherited style data
2588       // requested yet (which is possible for example if we flush style
2589       // between resolving an initial style context for a frame and
2590       // building its display list items).  Therefore we must compare
2591       // the rule nodes of oldContext and newContext to see if the
2592       // restyle results in new inherited style data.  If not, then
2593       // we can continue assuming that RestyleResult::eStopWithStyleChange
2594       // is safe.  Without this check, we could end up with style contexts
2595       // shared between elements which should have different styles.
2596       if (!CommonInheritedStyleData(oldContext->RuleNode(),
2597                                     newContext->RuleNode())) {
2598         canStopWithStyleChange = false;
2599       }
2600     }
2601 
2602     // Look at some details of the new style context to see if it would
2603     // be safe to stop restyling, if we discover it has the same style
2604     // data as the old style context.
2605     ComputeRestyleResultFromNewContext(aSelf, newContext, result,
2606                                        canStopWithStyleChange);
2607 
2608     uint32_t equalStructs = 0;
2609     uint32_t samePointerStructs = 0;
2610 
2611     if (copyFromContinuation) {
2612       // In theory we should know whether there was any style data difference,
2613       // since we would have calculated that in the previous call to
2614       // RestyleSelf, so until we perform only one restyling per chain-of-
2615       // same-style continuations (bug 918064), we need to check again here to
2616       // determine whether it is safe to stop restyling.
2617       if (result == RestyleResult::eStop) {
2618         oldContext->CalcStyleDifference(newContext, &equalStructs,
2619                                         &samePointerStructs);
2620         if (equalStructs != NS_STYLE_INHERIT_MASK) {
2621           // At least one struct had different data in it, so we must
2622           // continue restyling children.
2623           LOG_RESTYLE_CONTINUE("there is different style data: %s",
2624                                GeckoRestyleManager::StructNamesToString(
2625                                    ~equalStructs & NS_STYLE_INHERIT_MASK)
2626                                    .get());
2627           result = RestyleResult::eContinue;
2628         }
2629       }
2630     } else {
2631       bool changedStyle = GeckoRestyleManager::TryInitiatingTransition(
2632           mPresContext, aSelf->GetContent(), oldContext, &newContext);
2633       if (changedStyle) {
2634         LOG_RESTYLE_CONTINUE(
2635             "TryInitiatingTransition changed the new style "
2636             "context");
2637         result = RestyleResult::eContinue;
2638         canStopWithStyleChange = false;
2639       }
2640       CaptureChange(oldContext, newContext, assumeDifferenceHint, &equalStructs,
2641                     &samePointerStructs);
2642       if (equalStructs != NS_STYLE_INHERIT_MASK) {
2643         // At least one struct had different data in it, so we must
2644         // continue restyling children.
2645         LOG_RESTYLE_CONTINUE("there is different style data: %s",
2646                              GeckoRestyleManager::StructNamesToString(
2647                                  ~equalStructs & NS_STYLE_INHERIT_MASK)
2648                                  .get());
2649         result = RestyleResult::eContinue;
2650       }
2651     }
2652 
2653     if (canStopWithStyleChange) {
2654       // If any inherited struct pointers are different, or if any
2655       // reset struct pointers are different and we have descendants
2656       // that rely on those reset struct pointers, we can't return
2657       // RestyleResult::eStopWithStyleChange.
2658       if ((samePointerStructs & NS_STYLE_INHERITED_STRUCT_MASK) !=
2659           NS_STYLE_INHERITED_STRUCT_MASK) {
2660         LOG_RESTYLE(
2661             "can't return RestyleResult::eStopWithStyleChange since "
2662             "there is different inherited data");
2663         canStopWithStyleChange = false;
2664       } else if ((samePointerStructs & NS_STYLE_RESET_STRUCT_MASK) !=
2665                      NS_STYLE_RESET_STRUCT_MASK &&
2666                  oldContext->HasChildThatUsesResetStyle()) {
2667         LOG_RESTYLE(
2668             "can't return RestyleResult::eStopWithStyleChange since "
2669             "there is different reset data and descendants use it");
2670         canStopWithStyleChange = false;
2671       }
2672     }
2673 
2674     if (result == RestyleResult::eStop) {
2675       // Since we currently have RestyleResult::eStop, we know at this
2676       // point that all of our style structs are equal in terms of styles.
2677       // However, some of them might be different pointers.  Since our
2678       // descendants might share those pointers, we have to continue to
2679       // restyling our descendants.
2680       //
2681       // However, because of the swapping of equal structs we've done on
2682       // ancestors (later in this function), we've ensured that for structs
2683       // that cannot be stored in the rule tree, we keep the old equal structs
2684       // around rather than replacing them with new ones.  This means that we
2685       // only time we hit this deoptimization is either
2686       //
2687       // (a) when at least one of the (old or new) equal structs could be stored
2688       //     in the rule tree, and those structs are then inherited (by pointer
2689       //     sharing) to descendant style contexts; or
2690       //
2691       // (b) when we were unable to swap the structs on the parent because
2692       //     either or both of the old parent and new parent are shared.
2693       //
2694       // FIXME This loop could be rewritten as bit operations on
2695       //       oldContext->mBits and samePointerStructs.
2696       for (nsStyleStructID sid = nsStyleStructID(0);
2697            sid < nsStyleStructID_Length; sid = nsStyleStructID(sid + 1)) {
2698         if (oldContext->HasCachedDependentStyleData(sid) &&
2699             !(samePointerStructs & nsCachedStyleData::GetBitForSID(sid))) {
2700           LOG_RESTYLE_CONTINUE("there are different struct pointers");
2701           result = RestyleResult::eContinue;
2702           break;
2703         }
2704       }
2705     }
2706 
2707     // From this point we no longer do any assignments of
2708     // RestyleResult::eContinue to |result|.  If canStopWithStyleChange is true,
2709     // it means that we can convert |result| (whether it is
2710     // RestyleResult::eContinue or RestyleResult::eStop) into
2711     // RestyleResult::eStopWithStyleChange.
2712     if (canStopWithStyleChange) {
2713       LOG_RESTYLE("converting %s into RestyleResult::eStopWithStyleChange",
2714                   RestyleResultToString(result).get());
2715       result = RestyleResult::eStopWithStyleChange;
2716     }
2717 
2718     if (aRestyleHint & eRestyle_ForceDescendants) {
2719       result = RestyleResult::eContinueAndForceDescendants;
2720     }
2721 
2722     if (!(mHintsHandledBySelf & nsChangeHint_ReconstructFrame)) {
2723       // If the frame gets regenerated, let it keep its old context,
2724       // which is important to maintain various invariants about
2725       // frame types matching their style contexts.
2726       // Note that this check even makes sense if we didn't call
2727       // CaptureChange because of copyFromContinuation being true,
2728       // since we'll have copied the existing context from the
2729       // previous continuation, so newContext == oldContext.
2730 
2731       if (result != RestyleResult::eStop) {
2732         if (copyFromContinuation) {
2733           LOG_RESTYLE(
2734               "not swapping style structs, since we copied from a "
2735               "continuation");
2736         } else if (oldContext->IsShared() && newContext->IsShared()) {
2737           LOG_RESTYLE(
2738               "not swapping style structs, since both old and contexts "
2739               "are shared");
2740         } else if (oldContext->IsShared()) {
2741           LOG_RESTYLE(
2742               "not swapping style structs, since the old context is "
2743               "shared");
2744         } else if (newContext->IsShared()) {
2745           LOG_RESTYLE(
2746               "not swapping style structs, since the new context is "
2747               "shared");
2748         } else {
2749           if (result == RestyleResult::eStopWithStyleChange) {
2750             LOG_RESTYLE(
2751                 "recording a style struct swap between %p and %p to "
2752                 "do if RestyleResult::eStopWithStyleChange fails",
2753                 oldContext.get(), newContext.get());
2754             SwapInstruction* swap = aSwaps.AppendElement();
2755             swap->mOldContext = oldContext;
2756             swap->mNewContext = newContext;
2757             swap->mStructsToSwap = equalStructs;
2758           } else {
2759             LOG_RESTYLE("swapping style structs between %p and %p",
2760                         oldContext.get(), newContext.get());
2761             oldContext->AsGecko()->SwapStyleData(newContext->AsGecko(),
2762                                                  equalStructs);
2763             *aSwappedStructs |= equalStructs;
2764           }
2765 #ifdef RESTYLE_LOGGING
2766           uint32_t structs = GeckoRestyleManager::StructsToLog() & equalStructs;
2767           if (structs) {
2768             LOG_RESTYLE_INDENT();
2769             LOG_RESTYLE("old style context now has: %s",
2770                         oldContext->AsGecko()
2771                             ->GetCachedStyleDataAsString(structs)
2772                             .get());
2773             LOG_RESTYLE("new style context now has: %s",
2774                         newContext->AsGecko()
2775                             ->GetCachedStyleDataAsString(structs)
2776                             .get());
2777           }
2778 #endif
2779         }
2780         LOG_RESTYLE("setting new style context");
2781         aSelf->SetStyleContext(newContext);
2782       }
2783     } else {
2784       LOG_RESTYLE("not setting new style context, since we'll reframe");
2785       // We need to keep the new parent alive, in case it had structs
2786       // swapped into it that our frame's style context still has cached.
2787       // This is a similar scenario to the one described in the
2788       // ElementRestyler::Restyle comment where we append to
2789       // mSwappedStructOwners.
2790       //
2791       // We really only need to do this if we did swap structs on the
2792       // parent, but we don't have that information here.
2793       mSwappedStructOwners.AppendElement(newContext->GetParent());
2794     }
2795   } else {
2796     if (aRestyleHint & eRestyle_ForceDescendants) {
2797       result = RestyleResult::eContinueAndForceDescendants;
2798     }
2799   }
2800   oldContext = nullptr;
2801 
2802   // do additional contexts
2803   // XXXbz might be able to avoid selector matching here in some
2804   // cases; won't worry about it for now.
2805   int32_t contextIndex = 0;
2806   for (nsStyleContext* oldExtraContext;
2807        (oldExtraContext = aSelf->GetAdditionalStyleContext(contextIndex));
2808        ++contextIndex) {
2809     LOG_RESTYLE("extra context %d", contextIndex);
2810     LOG_RESTYLE_INDENT();
2811     RefPtr<GeckoStyleContext> newExtraContext;
2812     nsAtom* const extraPseudoTag = oldExtraContext->GetPseudo();
2813     const CSSPseudoElementType extraPseudoType =
2814         oldExtraContext->GetPseudoType();
2815     NS_ASSERTION(
2816         extraPseudoTag && !nsCSSAnonBoxes::IsNonElement(extraPseudoTag),
2817         "extra style context is not pseudo element");
2818     Element* element =
2819         (extraPseudoType != CSSPseudoElementType::InheritingAnonBox &&
2820          extraPseudoType != CSSPseudoElementType::NonInheritingAnonBox)
2821             ? mContent->AsElement()
2822             : nullptr;
2823     if (extraPseudoType == CSSPseudoElementType::NonInheritingAnonBox) {
2824       newExtraContext =
2825           styleSet->ResolveNonInheritingAnonymousBoxStyle(extraPseudoTag);
2826     } else if (!MustRestyleSelf(aRestyleHint, element)) {
2827       if (CanReparentStyleContext(aRestyleHint)) {
2828         newExtraContext = styleSet->ReparentStyleContext(
2829             oldExtraContext->AsGecko(), newContext, element);
2830       } else {
2831         // Use ResolveStyleWithReplacement as a substitute for
2832         // ReparentStyleContext that rebuilds the path in the rule tree
2833         // rather than reusing the rule node, as we need to do during a
2834         // rule tree reconstruct.
2835         Element* pseudoElement =
2836             PseudoElementForStyleContext(aSelf, extraPseudoType);
2837         MOZ_ASSERT(!element || element != pseudoElement,
2838                    "pseudo-element for selector matching should be "
2839                    "the anonymous content node that we create, "
2840                    "not the real element");
2841         newExtraContext = styleSet->ResolveStyleWithReplacement(
2842             element, pseudoElement, newContext, oldExtraContext->AsGecko(),
2843             nsRestyleHint(0));
2844       }
2845     } else if (extraPseudoType == CSSPseudoElementType::InheritingAnonBox) {
2846       newExtraContext = styleSet->ResolveInheritingAnonymousBoxStyle(
2847           extraPseudoTag, newContext);
2848     } else {
2849       // Don't expect XUL tree stuff here, since it needs a comparator and
2850       // all.
2851       NS_ASSERTION(extraPseudoType < CSSPseudoElementType::Count,
2852                    "Unexpected type");
2853       newExtraContext = styleSet->ResolvePseudoElementStyle(
2854           mContent->AsElement(), extraPseudoType, newContext, nullptr);
2855     }
2856 
2857     MOZ_ASSERT(newExtraContext);
2858 
2859     LOG_RESTYLE("newExtraContext = %p", newExtraContext.get());
2860 
2861     if (oldExtraContext != newExtraContext) {
2862       uint32_t equalStructs;
2863       uint32_t samePointerStructs;
2864       CaptureChange(oldExtraContext->AsGecko(), newExtraContext,
2865                     assumeDifferenceHint, &equalStructs, &samePointerStructs);
2866       if (!(mHintsHandledBySelf & nsChangeHint_ReconstructFrame)) {
2867         LOG_RESTYLE("setting new extra style context");
2868         aSelf->SetAdditionalStyleContext(contextIndex, newExtraContext);
2869       } else {
2870         LOG_RESTYLE("not setting new extra style context, since we'll reframe");
2871       }
2872     }
2873   }
2874 
2875   LOG_RESTYLE("returning %s", RestyleResultToString(result).get());
2876 
2877   return result;
2878 }
2879 
RestyleChildren(nsRestyleHint aChildRestyleHint)2880 void ElementRestyler::RestyleChildren(nsRestyleHint aChildRestyleHint) {
2881   MOZ_ASSERT(!(mHintsHandledBySelf & nsChangeHint_ReconstructFrame),
2882              "No need to do this if we're planning to reframe already.");
2883 
2884   // We'd like style resolution to be exact in the sense that an
2885   // animation-only style flush flushes only the styles it requests
2886   // flushing and doesn't update any other styles.  This means avoiding
2887   // constructing new frames during such a flush.
2888   //
2889   // For a ::before or ::after, we'll do an eRestyle_Subtree due to
2890   // RestyleHintForOp in nsCSSRuleProcessor.cpp (via its
2891   // HasAttributeDependentStyle or HasStateDependentStyle), given that
2892   // we store pseudo-elements in selectors like they were children.
2893   //
2894   // Also, it's faster to skip the work we do on undisplayed children
2895   // and pseudo-elements when we can skip it.
2896   bool mightReframePseudos = aChildRestyleHint & eRestyle_Subtree;
2897 
2898   RestyleUndisplayedDescendants(aChildRestyleHint);
2899 
2900   // Check whether we might need to create a new ::before frame.
2901   // There's no need to do this if we're planning to reframe already
2902   // or if we're not forcing restyles on kids.
2903   // It's also important to check mHintsHandledBySelf since we use
2904   // mFrame->StyleContext(), which is out of date if mHintsHandledBySelf
2905   // has a ReconstructFrame hint.  Using an out of date style context could
2906   // trigger assertions about mismatched rule trees.
2907   if (!(mHintsHandledBySelf & nsChangeHint_ReconstructFrame) &&
2908       mightReframePseudos) {
2909     MaybeReframeForBeforePseudo();
2910   }
2911 
2912   // There is no need to waste time crawling into a frame's children
2913   // on a frame change.  The act of reconstructing frames will force
2914   // new style contexts to be resolved on all of this frame's
2915   // descendants anyway, so we want to avoid wasting time processing
2916   // style contexts that we're just going to throw away anyway. - dwh
2917   // It's also important to check mHintsHandledBySelf since reresolving the
2918   // kids would use mFrame->StyleContext(), which is out of date if
2919   // mHintsHandledBySelf has a ReconstructFrame hint; doing this could
2920   // trigger assertions about mismatched rule trees.
2921   nsIFrame* lastContinuation;
2922   if (!(mHintsHandledBySelf & nsChangeHint_ReconstructFrame)) {
2923     InitializeAccessibilityNotifications(mFrame->StyleContext());
2924 
2925     for (nsIFrame* f = mFrame; f; f = GetNextContinuationWithSameStyle(
2926                                       f, f->StyleContext()->AsGecko())) {
2927       lastContinuation = f;
2928       RestyleContentChildren(f, aChildRestyleHint);
2929     }
2930 
2931     SendAccessibilityNotifications();
2932   }
2933 
2934   // Check whether we might need to create a new ::after frame.
2935   // See comments above regarding :before.
2936   if (!(mHintsHandledBySelf & nsChangeHint_ReconstructFrame) &&
2937       mightReframePseudos) {
2938     MaybeReframeForAfterPseudo(lastContinuation);
2939   }
2940 }
2941 
RestyleChildrenOfDisplayContentsElement(nsIFrame * aParentFrame,GeckoStyleContext * aNewContext,nsChangeHint aMinHint,RestyleTracker & aRestyleTracker,nsRestyleHint aRestyleHint,const RestyleHintData & aRestyleHintData)2942 void ElementRestyler::RestyleChildrenOfDisplayContentsElement(
2943     nsIFrame* aParentFrame, GeckoStyleContext* aNewContext,
2944     nsChangeHint aMinHint, RestyleTracker& aRestyleTracker,
2945     nsRestyleHint aRestyleHint, const RestyleHintData& aRestyleHintData) {
2946   MOZ_ASSERT(!(mHintsHandledBySelf & nsChangeHint_ReconstructFrame),
2947              "why call me?");
2948 
2949   const bool mightReframePseudos = aRestyleHint & eRestyle_Subtree;
2950   DoRestyleUndisplayedDescendants(nsRestyleHint(0), mContent, aNewContext);
2951   if (!(mHintsHandledBySelf & nsChangeHint_ReconstructFrame) &&
2952       mightReframePseudos) {
2953     MaybeReframeForPseudo(CSSPseudoElementType::before, aParentFrame, nullptr,
2954                           mContent, aNewContext);
2955   }
2956   if (!(mHintsHandledBySelf & nsChangeHint_ReconstructFrame) &&
2957       mightReframePseudos) {
2958     MaybeReframeForPseudo(CSSPseudoElementType::after, aParentFrame, nullptr,
2959                           mContent, aNewContext);
2960   }
2961   if (!(mHintsHandledBySelf & nsChangeHint_ReconstructFrame)) {
2962     InitializeAccessibilityNotifications(aNewContext);
2963 
2964     // Then process child frames for content that is a descendant of mContent.
2965     // XXX perhaps it's better to walk child frames (before reresolving
2966     // XXX undisplayed contexts above) and mark those that has a stylecontext
2967     // XXX leading up to mContent's old context? (instead of the
2968     // XXX ContentIsDescendantOf check below)
2969     nsIFrame::ChildListIterator lists(aParentFrame);
2970     for (; !lists.IsDone(); lists.Next()) {
2971       for (nsIFrame* f : lists.CurrentList()) {
2972         if (nsContentUtils::ContentIsDescendantOf(f->GetContent(), mContent) &&
2973             !f->GetPrevContinuation()) {
2974           if (!(f->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
2975             ComputeStyleChangeFor(f, mChangeList, aMinHint, aRestyleTracker,
2976                                   aRestyleHint, aRestyleHintData,
2977                                   mContextsToClear, mSwappedStructOwners);
2978           }
2979         }
2980       }
2981     }
2982   }
2983   if (!(mHintsHandledBySelf & nsChangeHint_ReconstructFrame)) {
2984     SendAccessibilityNotifications();
2985   }
2986 }
2987 
ComputeStyleChangeFor(nsIFrame * aFrame,nsStyleChangeList * aChangeList,nsChangeHint aMinChange,RestyleTracker & aRestyleTracker,nsRestyleHint aRestyleHint,const RestyleHintData & aRestyleHintData,nsTArray<ContextToClear> & aContextsToClear,nsTArray<RefPtr<GeckoStyleContext>> & aSwappedStructOwners)2988 void ElementRestyler::ComputeStyleChangeFor(
2989     nsIFrame* aFrame, nsStyleChangeList* aChangeList, nsChangeHint aMinChange,
2990     RestyleTracker& aRestyleTracker, nsRestyleHint aRestyleHint,
2991     const RestyleHintData& aRestyleHintData,
2992     nsTArray<ContextToClear>& aContextsToClear,
2993     nsTArray<RefPtr<GeckoStyleContext>>& aSwappedStructOwners) {
2994   AUTO_PROFILER_LABEL("ElementRestyler::ComputeStyleChangeFor", CSS);
2995 
2996   nsIContent* content = aFrame->GetContent();
2997   if (aMinChange) {
2998     aChangeList->AppendChange(aFrame, content, aMinChange);
2999   }
3000 
3001   NS_ASSERTION(!aFrame->GetPrevContinuation(),
3002                "must start with the first continuation");
3003 
3004   // We want to start with this frame and walk all its next-in-flows,
3005   // as well as all its ib-split siblings and their next-in-flows,
3006   // reresolving style on all the frames we encounter in this walk that
3007   // we didn't reach already.  In the normal case, this will mean only
3008   // restyling the first two block-in-inline splits and no
3009   // continuations, and skipping everything else.  However, when we have
3010   // a style change targeted at an element inside a context where styles
3011   // vary between continuations (e.g., a style change on an element that
3012   // extends from inside a styled ::first-line to outside of that first
3013   // line), we might restyle more than that.
3014 
3015   nsPresContext* presContext = aFrame->PresContext();
3016 
3017   TreeMatchContext treeMatchContext(true, nsRuleWalker::eRelevantLinkUnvisited,
3018                                     presContext->Document());
3019   Element* parent =
3020       content ? content->GetParentElementCrossingShadowRoot() : nullptr;
3021   treeMatchContext.InitAncestors(parent);
3022   nsTArray<nsCSSSelector*> selectorsForDescendants;
3023   selectorsForDescendants.AppendElements(
3024       aRestyleHintData.mSelectorsForDescendants);
3025   nsTArray<nsIContent*> visibleKidsOfHiddenElement;
3026   nsIFrame* nextIBSibling;
3027   for (nsIFrame* ibSibling = aFrame; ibSibling; ibSibling = nextIBSibling) {
3028     nextIBSibling = GetNextBlockInInlineSibling(ibSibling);
3029 
3030     if (nextIBSibling) {
3031       // Don't allow some ib-split siblings to be processed with
3032       // RestyleResult::eStopWithStyleChange and others not.
3033       aRestyleHint |= eRestyle_Force;
3034     }
3035 
3036     // Outer loop over ib-split siblings
3037     for (nsIFrame* cont = ibSibling; cont; cont = cont->GetNextContinuation()) {
3038       if (GetPrevContinuationWithSameStyle(cont)) {
3039         // We already handled this element when dealing with its earlier
3040         // continuation.
3041         continue;
3042       }
3043 
3044       // Inner loop over next-in-flows of the current frame
3045       ElementRestyler restyler(presContext, cont, aChangeList, aMinChange,
3046                                aRestyleTracker, selectorsForDescendants,
3047                                treeMatchContext, visibleKidsOfHiddenElement,
3048                                aContextsToClear, aSwappedStructOwners);
3049 
3050       restyler.Restyle(aRestyleHint);
3051 
3052       if (restyler.HintsHandledForFrame() & nsChangeHint_ReconstructFrame) {
3053         // If it's going to cause a framechange, then don't bother
3054         // with the continuations or ib-split siblings since they'll be
3055         // clobbered by the frame reconstruct anyway.
3056         NS_ASSERTION(
3057             !cont->GetPrevContinuation(),
3058             "continuing frame had more severe impact than first-in-flow");
3059         return;
3060       }
3061     }
3062   }
3063 }
3064 
3065 // The structure of this method parallels
3066 // ConditionallyRestyleUndisplayedDescendants. If you update this method, you
3067 // probably want to update that one too.
RestyleUndisplayedDescendants(nsRestyleHint aChildRestyleHint)3068 void ElementRestyler::RestyleUndisplayedDescendants(
3069     nsRestyleHint aChildRestyleHint) {
3070   nsIContent* undisplayedParent;
3071   if (MustCheckUndisplayedContent(mFrame, undisplayedParent)) {
3072     DoRestyleUndisplayedDescendants(aChildRestyleHint, undisplayedParent,
3073                                     mFrame->StyleContext()->AsGecko());
3074   }
3075 }
3076 
3077 // The structure of this method parallels
3078 // DoConditionallyRestyleUndisplayedDescendants. If you update this method, you
3079 // probably want to update that one too.
DoRestyleUndisplayedDescendants(nsRestyleHint aChildRestyleHint,nsIContent * aParent,GeckoStyleContext * aParentContext)3080 void ElementRestyler::DoRestyleUndisplayedDescendants(
3081     nsRestyleHint aChildRestyleHint, nsIContent* aParent,
3082     GeckoStyleContext* aParentContext) {
3083   nsCSSFrameConstructor* fc = mPresContext->FrameConstructor();
3084   UndisplayedNode* nodes = fc->GetAllRegisteredDisplayNoneStylesIn(aParent);
3085   RestyleUndisplayedNodes(aChildRestyleHint, nodes, aParent, aParentContext,
3086                           StyleDisplay::None);
3087   nodes = fc->GetAllRegisteredDisplayContentsStylesIn(aParent);
3088   RestyleUndisplayedNodes(aChildRestyleHint, nodes, aParent, aParentContext,
3089                           StyleDisplay::Contents);
3090 }
3091 
3092 // The structure of this method parallels ConditionallyRestyleUndisplayedNodes.
3093 // If you update this method, you probably want to update that one too.
RestyleUndisplayedNodes(nsRestyleHint aChildRestyleHint,UndisplayedNode * aUndisplayed,nsIContent * aUndisplayedParent,GeckoStyleContext * aParentContext,const StyleDisplay aDisplay)3094 void ElementRestyler::RestyleUndisplayedNodes(nsRestyleHint aChildRestyleHint,
3095                                               UndisplayedNode* aUndisplayed,
3096                                               nsIContent* aUndisplayedParent,
3097                                               GeckoStyleContext* aParentContext,
3098                                               const StyleDisplay aDisplay) {
3099   nsIContent* undisplayedParent = aUndisplayedParent;
3100   UndisplayedNode* undisplayed = aUndisplayed;
3101   TreeMatchContext::AutoAncestorPusher pusher(&mTreeMatchContext);
3102   if (undisplayed) {
3103     pusher.PushAncestorAndStyleScope(undisplayedParent);
3104   }
3105   for (; undisplayed; undisplayed = undisplayed->getNext()) {
3106     NS_ASSERTION(
3107         undisplayedParent ||
3108             undisplayed->mContent == mPresContext->Document()->GetRootElement(),
3109         "undisplayed node child of null must be root");
3110     NS_ASSERTION(!undisplayed->mStyle->GetPseudo(),
3111                  "Shouldn't have random pseudo style contexts in the "
3112                  "undisplayed map");
3113 
3114     LOG_RESTYLE("RestyleUndisplayedChildren: undisplayed->mContent = %p",
3115                 undisplayed->mContent.get());
3116 
3117     // Get the parent of the undisplayed content and check if it is a XBL
3118     // children element. Push the children element as an ancestor here because
3119     // it does not have a frame and would not otherwise be pushed as an
3120     // ancestor.
3121     nsIContent* parent = undisplayed->mContent->GetParent();
3122     TreeMatchContext::AutoAncestorPusher insertionPointPusher(
3123         &mTreeMatchContext);
3124     if (parent && parent->IsActiveChildrenElement()) {
3125       insertionPointPusher.PushAncestorAndStyleScope(parent);
3126     }
3127 
3128     nsRestyleHint thisChildHint = aChildRestyleHint;
3129     nsAutoPtr<RestyleTracker::RestyleData> undisplayedRestyleData;
3130     Element* element = undisplayed->mContent->AsElement();
3131     if (mRestyleTracker.GetRestyleData(element, undisplayedRestyleData)) {
3132       thisChildHint =
3133           nsRestyleHint(thisChildHint | undisplayedRestyleData->mRestyleHint);
3134     }
3135     RefPtr<GeckoStyleContext> undisplayedContext;
3136     nsStyleSet* styleSet = StyleSet();
3137     if (MustRestyleSelf(thisChildHint, element)) {
3138       undisplayedContext =
3139           styleSet->ResolveStyleFor(element, aParentContext, mTreeMatchContext);
3140     } else if (CanReparentStyleContext(thisChildHint)) {
3141       undisplayedContext = styleSet->ReparentStyleContext(
3142           undisplayed->mStyle->AsGecko(), aParentContext, element);
3143     } else {
3144       // Use ResolveStyleWithReplacement either for actual
3145       // replacements, or as a substitute for ReparentStyleContext
3146       // that rebuilds the path in the rule tree rather than reusing
3147       // the rule node, as we need to do during a rule tree
3148       // reconstruct.
3149       nsRestyleHint rshint = thisChildHint & ~eRestyle_SomeDescendants;
3150       undisplayedContext = styleSet->ResolveStyleWithReplacement(
3151           element, nullptr, aParentContext, undisplayed->mStyle->AsGecko(),
3152           rshint);
3153     }
3154     const nsStyleDisplay* display = undisplayedContext->StyleDisplay();
3155     if (display->mDisplay != aDisplay) {
3156       NS_ASSERTION(element, "Must have undisplayed content");
3157       mChangeList->AppendChange(nullptr, element,
3158                                 nsChangeHint_ReconstructFrame);
3159       // The node should be removed from the undisplayed map when
3160       // we reframe it.
3161     } else {
3162       // update the undisplayed node with the new context
3163       undisplayed->mStyle = undisplayedContext;
3164 
3165       if (aDisplay == StyleDisplay::Contents) {
3166         DoRestyleUndisplayedDescendants(aChildRestyleHint, element,
3167                                         undisplayed->mStyle->AsGecko());
3168       }
3169     }
3170   }
3171 }
3172 
MaybeReframeForBeforePseudo()3173 void ElementRestyler::MaybeReframeForBeforePseudo() {
3174   MaybeReframeForPseudo(CSSPseudoElementType::before, mFrame, mFrame,
3175                         mFrame->GetContent(),
3176                         mFrame->StyleContext()->AsGecko());
3177 }
3178 
3179 /**
3180  * aFrame is the last continuation or block-in-inline sibling that this
3181  * ElementRestyler is restyling.
3182  */
MaybeReframeForAfterPseudo(nsIFrame * aFrame)3183 void ElementRestyler::MaybeReframeForAfterPseudo(nsIFrame* aFrame) {
3184   MOZ_ASSERT(aFrame);
3185   MaybeReframeForPseudo(CSSPseudoElementType::after, aFrame, aFrame,
3186                         aFrame->GetContent(),
3187                         aFrame->StyleContext()->AsGecko());
3188 }
3189 
3190 #ifdef DEBUG
MustReframeForBeforePseudo()3191 bool ElementRestyler::MustReframeForBeforePseudo() {
3192   return MustReframeForPseudo(CSSPseudoElementType::before, mFrame, mFrame,
3193                               mFrame->GetContent(),
3194                               mFrame->StyleContext()->AsGecko());
3195 }
3196 
MustReframeForAfterPseudo(nsIFrame * aFrame)3197 bool ElementRestyler::MustReframeForAfterPseudo(nsIFrame* aFrame) {
3198   MOZ_ASSERT(aFrame);
3199   return MustReframeForPseudo(CSSPseudoElementType::after, aFrame, aFrame,
3200                               aFrame->GetContent(),
3201                               aFrame->StyleContext()->AsGecko());
3202 }
3203 #endif
3204 
MaybeReframeForPseudo(CSSPseudoElementType aPseudoType,nsIFrame * aGenConParentFrame,nsIFrame * aFrame,nsIContent * aContent,GeckoStyleContext * aStyleContext)3205 void ElementRestyler::MaybeReframeForPseudo(CSSPseudoElementType aPseudoType,
3206                                             nsIFrame* aGenConParentFrame,
3207                                             nsIFrame* aFrame,
3208                                             nsIContent* aContent,
3209                                             GeckoStyleContext* aStyleContext) {
3210   if (MustReframeForPseudo(aPseudoType, aGenConParentFrame, aFrame, aContent,
3211                            aStyleContext)) {
3212     // Have to create the new ::before/::after frame.
3213     LOG_RESTYLE(
3214         "MaybeReframeForPseudo, appending "
3215         "nsChangeHint_ReconstructFrame");
3216     mHintsHandledBySelf |= nsChangeHint_ReconstructFrame;
3217     mChangeList->AppendChange(aFrame, aContent, nsChangeHint_ReconstructFrame);
3218   }
3219 }
3220 
MustReframeForPseudo(CSSPseudoElementType aPseudoType,nsIFrame * aGenConParentFrame,nsIFrame * aFrame,nsIContent * aContent,GeckoStyleContext * aStyleContext)3221 bool ElementRestyler::MustReframeForPseudo(CSSPseudoElementType aPseudoType,
3222                                            nsIFrame* aGenConParentFrame,
3223                                            nsIFrame* aFrame,
3224                                            nsIContent* aContent,
3225                                            GeckoStyleContext* aStyleContext) {
3226   MOZ_ASSERT(aPseudoType == CSSPseudoElementType::before ||
3227              aPseudoType == CSSPseudoElementType::after);
3228 
3229   // Make sure not to do this for pseudo-frames...
3230   if (aStyleContext->GetPseudo()) {
3231     return false;
3232   }
3233 
3234   // ... or frames that can't have generated content.
3235   if (!(aGenConParentFrame->GetStateBits() &
3236         NS_FRAME_MAY_HAVE_GENERATED_CONTENT)) {
3237     // Our content insertion frame might have gotten flagged.
3238     nsContainerFrame* cif = aGenConParentFrame->GetContentInsertionFrame();
3239     if (!cif || !(cif->GetStateBits() & NS_FRAME_MAY_HAVE_GENERATED_CONTENT)) {
3240       return false;
3241     }
3242   }
3243 
3244   if (aPseudoType == CSSPseudoElementType::before) {
3245     // Check for a ::before pseudo style and the absence of a ::before content,
3246     // but only if aFrame is null or is the first continuation/ib-split.
3247     if ((aFrame &&
3248          !nsLayoutUtils::IsFirstContinuationOrIBSplitSibling(aFrame)) ||
3249         nsLayoutUtils::GetBeforeFrame(aContent)) {
3250       return false;
3251     }
3252   } else {
3253     // Similarly for ::after, but check for being the last continuation/
3254     // ib-split.
3255     if ((aFrame &&
3256          nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame)) ||
3257         nsLayoutUtils::GetAfterFrame(aContent)) {
3258       return false;
3259     }
3260   }
3261 
3262   // Checking for a ::before frame (which we do above) is cheaper than getting
3263   // the ::before style context here.
3264   return nsLayoutUtils::HasPseudoStyle(aContent, aStyleContext, aPseudoType,
3265                                        mPresContext);
3266 }
3267 
InitializeAccessibilityNotifications(nsStyleContext * aNewContext)3268 void ElementRestyler::InitializeAccessibilityNotifications(
3269     nsStyleContext* aNewContext) {
3270 #ifdef ACCESSIBILITY
3271   // Notify a11y for primary frame only if it's a root frame of visibility
3272   // changes or its parent frame was hidden while it stays visible and
3273   // it is not inside a {ib} split or is the first frame of {ib} split.
3274   if (nsIPresShell::IsAccessibilityActive() &&
3275       (!mFrame || (!mFrame->GetPrevContinuation() &&
3276                    !mFrame->FrameIsNonFirstInIBSplit()))) {
3277     if (mDesiredA11yNotifications == eSendAllNotifications) {
3278       bool isFrameVisible = aNewContext->StyleVisibility()->IsVisible();
3279       if (isFrameVisible != mWasFrameVisible) {
3280         if (isFrameVisible) {
3281           // Notify a11y the element (perhaps with its children) was shown.
3282           // We don't fall into this case if this element gets or stays shown
3283           // while its parent becomes hidden.
3284           mKidsDesiredA11yNotifications = eSkipNotifications;
3285           mOurA11yNotification = eNotifyShown;
3286         } else {
3287           // The element is being hidden; its children may stay visible, or
3288           // become visible after being hidden previously. If we'll find
3289           // visible children then we should notify a11y about that as if
3290           // they were inserted into tree. Notify a11y this element was
3291           // hidden.
3292           mKidsDesiredA11yNotifications = eNotifyIfShown;
3293           mOurA11yNotification = eNotifyHidden;
3294         }
3295       }
3296     } else if (mDesiredA11yNotifications == eNotifyIfShown &&
3297                aNewContext->StyleVisibility()->IsVisible()) {
3298       // Notify a11y that element stayed visible while its parent was hidden.
3299       nsIContent* c = mFrame ? mFrame->GetContent() : mContent;
3300       mVisibleKidsOfHiddenElement.AppendElement(c);
3301       mKidsDesiredA11yNotifications = eSkipNotifications;
3302     }
3303   }
3304 #endif
3305 }
3306 
3307 // The structure of this method parallels ConditionallyRestyleContentChildren.
3308 // If you update this method, you probably want to update that one too.
RestyleContentChildren(nsIFrame * aParent,nsRestyleHint aChildRestyleHint)3309 void ElementRestyler::RestyleContentChildren(nsIFrame* aParent,
3310                                              nsRestyleHint aChildRestyleHint) {
3311   LOG_RESTYLE("RestyleContentChildren");
3312 
3313   nsIFrame::ChildListIterator lists(aParent);
3314   TreeMatchContext::AutoAncestorPusher ancestorPusher(&mTreeMatchContext);
3315   if (!lists.IsDone()) {
3316     ancestorPusher.PushAncestorAndStyleScope(mContent);
3317   }
3318   for (; !lists.IsDone(); lists.Next()) {
3319     for (nsIFrame* child : lists.CurrentList()) {
3320       // Out-of-flows are reached through their placeholders.  Continuations
3321       // and block-in-inline splits are reached through those chains.
3322       if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
3323           !GetPrevContinuationWithSameStyle(child)) {
3324         // Get the parent of the child frame's content and check if it
3325         // is a XBL children element. Push the children element as an
3326         // ancestor here because it does not have a frame and would not
3327         // otherwise be pushed as an ancestor.
3328 
3329         // Check if the frame has a content because |child| may be a
3330         // nsPageFrame that does not have a content.
3331         nsIContent* parent =
3332             child->GetContent() ? child->GetContent()->GetParent() : nullptr;
3333         TreeMatchContext::AutoAncestorPusher insertionPointPusher(
3334             &mTreeMatchContext);
3335         if (parent && parent->IsActiveChildrenElement()) {
3336           insertionPointPusher.PushAncestorAndStyleScope(parent);
3337         }
3338 
3339         // only do frames that are in flow
3340         if (child->IsPlaceholderFrame()) {  // placeholder
3341           // get out of flow frame and recur there
3342           nsIFrame* outOfFlowFrame =
3343               nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
3344           NS_ASSERTION(outOfFlowFrame, "no out-of-flow frame");
3345           NS_ASSERTION(outOfFlowFrame != mResolvedChild,
3346                        "out-of-flow frame not a true descendant");
3347 
3348           // |nsFrame::GetParentStyleContext| checks being out
3349           // of flow so that this works correctly.
3350           do {
3351             if (GetPrevContinuationWithSameStyle(outOfFlowFrame)) {
3352               // Later continuations are likely restyled as a result of
3353               // the restyling of the previous continuation.
3354               // (Currently that's always true, but it's likely to
3355               // change if we implement overflow:fragments or similar.)
3356               continue;
3357             }
3358             ElementRestyler oofRestyler(*this, outOfFlowFrame,
3359                                         FOR_OUT_OF_FLOW_CHILD);
3360             oofRestyler.Restyle(aChildRestyleHint);
3361           } while ((outOfFlowFrame = outOfFlowFrame->GetNextContinuation()));
3362 
3363           // reresolve placeholder's context under the same parent
3364           // as the out-of-flow frame
3365           ElementRestyler phRestyler(*this, child, 0);
3366           phRestyler.Restyle(aChildRestyleHint);
3367         } else {  // regular child frame
3368           if (child != mResolvedChild) {
3369             ElementRestyler childRestyler(*this, child, 0);
3370             childRestyler.Restyle(aChildRestyleHint);
3371           }
3372         }
3373       }
3374     }
3375   }
3376   // XXX need to do overflow frames???
3377 }
3378 
SendAccessibilityNotifications()3379 void ElementRestyler::SendAccessibilityNotifications() {
3380 #ifdef ACCESSIBILITY
3381   // Send notifications about visibility changes.
3382   if (mOurA11yNotification == eNotifyShown) {
3383     nsAccessibilityService* accService = nsIPresShell::AccService();
3384     if (accService) {
3385       nsIPresShell* presShell = mPresContext->GetPresShell();
3386       nsIContent* content = mFrame ? mFrame->GetContent() : mContent;
3387 
3388       accService->ContentRangeInserted(presShell, content->GetParent(), content,
3389                                        content->GetNextSibling());
3390     }
3391   } else if (mOurA11yNotification == eNotifyHidden) {
3392     nsAccessibilityService* accService = nsIPresShell::AccService();
3393     if (accService) {
3394       nsIPresShell* presShell = mPresContext->GetPresShell();
3395       nsIContent* content = mFrame ? mFrame->GetContent() : mContent;
3396       accService->ContentRemoved(presShell, content);
3397 
3398       // Process children staying shown.
3399       uint32_t visibleContentCount = mVisibleKidsOfHiddenElement.Length();
3400       for (uint32_t idx = 0; idx < visibleContentCount; idx++) {
3401         nsIContent* childContent = mVisibleKidsOfHiddenElement[idx];
3402         accService->ContentRangeInserted(presShell, childContent->GetParent(),
3403                                          childContent,
3404                                          childContent->GetNextSibling());
3405       }
3406       mVisibleKidsOfHiddenElement.Clear();
3407     }
3408   }
3409 #endif
3410 }
3411 
ClearCachedInheritedStyleDataOnDescendants(nsTArray<ElementRestyler::ContextToClear> & aContextsToClear)3412 static void ClearCachedInheritedStyleDataOnDescendants(
3413     nsTArray<ElementRestyler::ContextToClear>& aContextsToClear) {
3414   for (size_t i = 0; i < aContextsToClear.Length(); i++) {
3415     auto& entry = aContextsToClear[i];
3416     if (!entry.mStyleContext->HasSingleReference()) {
3417       entry.mStyleContext->AsGecko()
3418           ->ClearCachedInheritedStyleDataOnDescendants(entry.mStructs);
3419     }
3420     entry.mStyleContext = nullptr;
3421   }
3422 }
3423 
ComputeAndProcessStyleChange(nsIFrame * aFrame,nsChangeHint aMinChange,RestyleTracker & aRestyleTracker,nsRestyleHint aRestyleHint,const RestyleHintData & aRestyleHintData)3424 void GeckoRestyleManager::ComputeAndProcessStyleChange(
3425     nsIFrame* aFrame, nsChangeHint aMinChange, RestyleTracker& aRestyleTracker,
3426     nsRestyleHint aRestyleHint, const RestyleHintData& aRestyleHintData) {
3427   MOZ_ASSERT(mReframingStyleContexts, "should have rsc");
3428   nsStyleChangeList changeList(StyleBackendType::Gecko);
3429   nsTArray<ElementRestyler::ContextToClear> contextsToClear;
3430 
3431   // swappedStructOwners needs to be kept alive until after
3432   // ProcessRestyledFrames and ClearCachedInheritedStyleDataOnDescendants
3433   // calls; see comment in ElementRestyler::Restyle.
3434   nsTArray<RefPtr<GeckoStyleContext>> swappedStructOwners;
3435   ElementRestyler::ComputeStyleChangeFor(
3436       aFrame, &changeList, aMinChange, aRestyleTracker, aRestyleHint,
3437       aRestyleHintData, contextsToClear, swappedStructOwners);
3438   ProcessRestyledFrames(changeList);
3439   ClearCachedInheritedStyleDataOnDescendants(contextsToClear);
3440 }
3441 
ComputeAndProcessStyleChange(GeckoStyleContext * aNewContext,Element * aElement,nsChangeHint aMinChange,RestyleTracker & aRestyleTracker,nsRestyleHint aRestyleHint,const RestyleHintData & aRestyleHintData)3442 void GeckoRestyleManager::ComputeAndProcessStyleChange(
3443     GeckoStyleContext* aNewContext, Element* aElement, nsChangeHint aMinChange,
3444     RestyleTracker& aRestyleTracker, nsRestyleHint aRestyleHint,
3445     const RestyleHintData& aRestyleHintData) {
3446   MOZ_ASSERT(mReframingStyleContexts, "should have rsc");
3447   MOZ_ASSERT(aNewContext->StyleDisplay()->mDisplay == StyleDisplay::Contents);
3448   nsIFrame* frame = GetNearestAncestorFrame(aElement);
3449   MOZ_ASSERT(frame,
3450              "display:contents node in map although it's a "
3451              "display:none descendant?");
3452   TreeMatchContext treeMatchContext(true, nsRuleWalker::eRelevantLinkUnvisited,
3453                                     frame->PresContext()->Document());
3454   nsIContent* parent = aElement->GetParent();
3455   Element* parentElement =
3456       parent && parent->IsElement() ? parent->AsElement() : nullptr;
3457   treeMatchContext.InitAncestors(parentElement);
3458 
3459   nsTArray<nsCSSSelector*> selectorsForDescendants;
3460   nsTArray<nsIContent*> visibleKidsOfHiddenElement;
3461   nsTArray<ElementRestyler::ContextToClear> contextsToClear;
3462 
3463   // swappedStructOwners needs to be kept alive until after
3464   // ProcessRestyledFrames and ClearCachedInheritedStyleDataOnDescendants
3465   // calls; see comment in ElementRestyler::Restyle.
3466   nsTArray<RefPtr<GeckoStyleContext>> swappedStructOwners;
3467   nsStyleChangeList changeList(StyleBackendType::Gecko);
3468   ElementRestyler r(frame->PresContext(), aElement, &changeList, aMinChange,
3469                     aRestyleTracker, selectorsForDescendants, treeMatchContext,
3470                     visibleKidsOfHiddenElement, contextsToClear,
3471                     swappedStructOwners);
3472   r.RestyleChildrenOfDisplayContentsElement(frame, aNewContext, aMinChange,
3473                                             aRestyleTracker, aRestyleHint,
3474                                             aRestyleHintData);
3475   ProcessRestyledFrames(changeList);
3476   ClearCachedInheritedStyleDataOnDescendants(contextsToClear);
3477 }
3478 
HasPendingRestyles() const3479 bool GeckoRestyleManager::HasPendingRestyles() const {
3480   return mPendingRestyles.Count() != 0;
3481 }
3482 
StyleSet() const3483 nsStyleSet* ElementRestyler::StyleSet() const {
3484   MOZ_ASSERT(mPresContext->StyleSet()->IsGecko(),
3485              "ElementRestyler should only be used with a Gecko-flavored "
3486              "style backend");
3487   return mPresContext->StyleSet()->AsGecko();
3488 }
3489 
AutoDisplayContentsAncestorPusher(TreeMatchContext & aTreeMatchContext,nsPresContext * aPresContext,nsIContent * aParent)3490 AutoDisplayContentsAncestorPusher::AutoDisplayContentsAncestorPusher(
3491     TreeMatchContext& aTreeMatchContext, nsPresContext* aPresContext,
3492     nsIContent* aParent)
3493     : mTreeMatchContext(aTreeMatchContext), mPresContext(aPresContext) {
3494   if (aParent) {
3495     nsFrameManager* fm = mPresContext->FrameConstructor();
3496     // Push display:contents mAncestors onto mTreeMatchContext.
3497     for (nsIContent* p = aParent; p && fm->GetDisplayContentsStyleFor(p);
3498          p = p->GetParent()) {
3499       mAncestors.AppendElement(p->AsElement());
3500     }
3501     bool hasFilter = mTreeMatchContext.mAncestorFilter.HasFilter();
3502     nsTArray<mozilla::dom::Element*>::size_type i = mAncestors.Length();
3503     while (i--) {
3504       if (hasFilter) {
3505         mTreeMatchContext.mAncestorFilter.PushAncestor(mAncestors[i]);
3506       }
3507       mTreeMatchContext.PushStyleScope(mAncestors[i]);
3508     }
3509   }
3510 }
3511 
~AutoDisplayContentsAncestorPusher()3512 AutoDisplayContentsAncestorPusher::~AutoDisplayContentsAncestorPusher() {
3513   // Pop the ancestors we pushed in the CTOR, if any.
3514   typedef nsTArray<mozilla::dom::Element*>::size_type sz;
3515   sz len = mAncestors.Length();
3516   bool hasFilter = mTreeMatchContext.mAncestorFilter.HasFilter();
3517   for (sz i = 0; i < len; ++i) {
3518     if (hasFilter) {
3519       mTreeMatchContext.mAncestorFilter.PopAncestor();
3520     }
3521     mTreeMatchContext.PopStyleScope(mAncestors[i]);
3522   }
3523 }
3524 
3525 #ifdef RESTYLE_LOGGING
StructsToLog()3526 uint32_t GeckoRestyleManager::StructsToLog() {
3527   static bool initialized = false;
3528   static uint32_t structs;
3529   if (!initialized) {
3530     structs = 0;
3531     const char* value = getenv("MOZ_DEBUG_RESTYLE_STRUCTS");
3532     if (value) {
3533       nsCString s(value);
3534       while (!s.IsEmpty()) {
3535         int32_t index = s.FindChar(',');
3536         nsStyleStructID sid;
3537         bool found;
3538         if (index == -1) {
3539           found = GeckoStyleContext::LookupStruct(s, sid);
3540           s.Truncate();
3541         } else {
3542           found = nsStyleContext::LookupStruct(Substring(s, 0, index), sid);
3543           s = Substring(s, index + 1);
3544         }
3545         if (found) {
3546           structs |= nsCachedStyleData::GetBitForSID(sid);
3547         }
3548       }
3549     }
3550     initialized = true;
3551   }
3552   return structs;
3553 }
3554 #endif
3555 
3556 #ifdef DEBUG
StructNamesToString(uint32_t aSIDs)3557 /* static */ nsCString GeckoRestyleManager::StructNamesToString(
3558     uint32_t aSIDs) {
3559   nsCString result;
3560   bool any = false;
3561   for (nsStyleStructID sid = nsStyleStructID(0); sid < nsStyleStructID_Length;
3562        sid = nsStyleStructID(sid + 1)) {
3563     if (aSIDs & nsCachedStyleData::GetBitForSID(sid)) {
3564       if (any) {
3565         result.AppendLiteral(",");
3566       }
3567       result.AppendPrintf("%s", nsStyleContext::StructName(sid));
3568       any = true;
3569     }
3570   }
3571   return result;
3572 }
3573 
RestyleResultToString(RestyleResult aRestyleResult)3574 /* static */ nsCString ElementRestyler::RestyleResultToString(
3575     RestyleResult aRestyleResult) {
3576   nsCString result;
3577   switch (aRestyleResult) {
3578     case RestyleResult::eStop:
3579       result.AssignLiteral("RestyleResult::eStop");
3580       break;
3581     case RestyleResult::eStopWithStyleChange:
3582       result.AssignLiteral("RestyleResult::eStopWithStyleChange");
3583       break;
3584     case RestyleResult::eContinue:
3585       result.AssignLiteral("RestyleResult::eContinue");
3586       break;
3587     case RestyleResult::eContinueAndForceDescendants:
3588       result.AssignLiteral("RestyleResult::eContinueAndForceDescendants");
3589       break;
3590     default:
3591       MOZ_ASSERT(aRestyleResult == RestyleResult::eNone,
3592                  "Unexpected RestyleResult");
3593   }
3594   return result;
3595 }
3596 #endif
3597 
3598 }  // namespace mozilla
3599