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