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