Bumping manifests a=b2g-bump
[gecko.git] / layout / base / RestyleManager.cpp
blob4791ba0a1ce69825b812a93d8eeefe406eee4110
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/. */
6 /**
7 * Code responsible for managing style changes: tracking what style
8 * changes need to happen, scheduling them, and doing them.
9 */
11 #include "RestyleManager.h"
12 #include "mozilla/EventStates.h"
13 #include "nsLayoutUtils.h"
14 #include "GeckoProfiler.h"
15 #include "nsStyleChangeList.h"
16 #include "nsRuleProcessorData.h"
17 #include "nsStyleUtil.h"
18 #include "nsCSSFrameConstructor.h"
19 #include "nsSVGEffects.h"
20 #include "nsCSSRendering.h"
21 #include "nsAnimationManager.h"
22 #include "nsTransitionManager.h"
23 #include "nsViewManager.h"
24 #include "nsRenderingContext.h"
25 #include "nsSVGIntegrationUtils.h"
26 #include "nsCSSAnonBoxes.h"
27 #include "nsContainerFrame.h"
28 #include "nsPlaceholderFrame.h"
29 #include "nsBlockFrame.h"
30 #include "nsViewportFrame.h"
31 #include "SVGTextFrame.h"
32 #include "StickyScrollContainer.h"
33 #include "nsIRootBox.h"
34 #include "nsIDOMMutationEvent.h"
35 #include "nsContentUtils.h"
36 #include "nsIFrameInlines.h"
37 #include "ActiveLayerTracker.h"
38 #include "nsDisplayList.h"
40 #ifdef ACCESSIBILITY
41 #include "nsAccessibilityService.h"
42 #endif
44 namespace mozilla {
46 using namespace layers;
48 RestyleManager::RestyleManager(nsPresContext* aPresContext)
49 : mPresContext(aPresContext)
50 , mRebuildAllStyleData(false)
51 , mObservingRefreshDriver(false)
52 , mInStyleRefresh(false)
53 , mHoverGeneration(0)
54 , mRebuildAllExtraHint(nsChangeHint(0))
55 , mLastUpdateForThrottledAnimations(aPresContext->RefreshDriver()->
56 MostRecentRefresh())
57 , mAnimationGeneration(0)
58 , mReframingStyleContexts(nullptr)
59 , mPendingRestyles(ELEMENT_HAS_PENDING_RESTYLE |
60 ELEMENT_IS_POTENTIAL_RESTYLE_ROOT)
61 , mPendingAnimationRestyles(ELEMENT_HAS_PENDING_ANIMATION_RESTYLE |
62 ELEMENT_IS_POTENTIAL_ANIMATION_RESTYLE_ROOT)
64 mPendingRestyles.Init(this);
65 mPendingAnimationRestyles.Init(this);
68 void
69 RestyleManager::NotifyDestroyingFrame(nsIFrame* aFrame)
71 mOverflowChangedTracker.RemoveFrame(aFrame);
74 #ifdef DEBUG
75 // To ensure that the functions below are only called within
76 // |ApplyRenderingChangeToTree|.
77 static bool gInApplyRenderingChangeToTree = false;
78 #endif
80 static void
81 DoApplyRenderingChangeToTree(nsIFrame* aFrame,
82 nsChangeHint aChange);
84 /**
85 * Sync views on aFrame and all of aFrame's descendants (following placeholders),
86 * if aChange has nsChangeHint_SyncFrameView.
87 * Calls DoApplyRenderingChangeToTree on all aFrame's out-of-flow descendants
88 * (following placeholders), if aChange has nsChangeHint_RepaintFrame.
89 * aFrame should be some combination of nsChangeHint_SyncFrameView,
90 * nsChangeHint_RepaintFrame, nsChangeHint_UpdateOpacityLayer and
91 * nsChangeHint_SchedulePaint, nothing else.
93 static void
94 SyncViewsAndInvalidateDescendants(nsIFrame* aFrame,
95 nsChangeHint aChange)
97 NS_PRECONDITION(gInApplyRenderingChangeToTree,
98 "should only be called within ApplyRenderingChangeToTree");
99 NS_ASSERTION(aChange == (aChange & (nsChangeHint_RepaintFrame |
100 nsChangeHint_SyncFrameView |
101 nsChangeHint_UpdateOpacityLayer |
102 nsChangeHint_SchedulePaint)),
103 "Invalid change flag");
105 nsView* view = aFrame->GetView();
106 if (view) {
107 if (aChange & nsChangeHint_SyncFrameView) {
108 nsContainerFrame::SyncFrameViewProperties(aFrame->PresContext(),
109 aFrame, nullptr, view);
113 nsIFrame::ChildListIterator lists(aFrame);
114 for (; !lists.IsDone(); lists.Next()) {
115 nsFrameList::Enumerator childFrames(lists.CurrentList());
116 for (; !childFrames.AtEnd(); childFrames.Next()) {
117 nsIFrame* child = childFrames.get();
118 if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
119 // only do frames that don't have placeholders
120 if (nsGkAtoms::placeholderFrame == child->GetType()) {
121 // do the out-of-flow frame and its continuations
122 nsIFrame* outOfFlowFrame =
123 nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
124 DoApplyRenderingChangeToTree(outOfFlowFrame, aChange);
125 } else if (lists.CurrentID() == nsIFrame::kPopupList) {
126 DoApplyRenderingChangeToTree(child, aChange);
127 } else { // regular frame
128 SyncViewsAndInvalidateDescendants(child, aChange);
136 * To handle nsChangeHint_ChildrenOnlyTransform we must iterate over the child
137 * frames of the SVG frame concerned. This helper function is used to find that
138 * SVG frame when we encounter nsChangeHint_ChildrenOnlyTransform to ensure
139 * that we iterate over the intended children, since sometimes we end up
140 * handling that hint while processing hints for one of the SVG frame's
141 * ancestor frames.
143 * The reason that we sometimes end up trying to process the hint for an
144 * ancestor of the SVG frame that the hint is intended for is due to the way we
145 * process restyle events. ApplyRenderingChangeToTree adjusts the frame from
146 * the restyled element's principle frame to one of its ancestor frames based
147 * on what nsCSSRendering::FindBackground returns, since the background style
148 * may have been propagated up to an ancestor frame. Processing hints using an
149 * ancestor frame is fine in general, but nsChangeHint_ChildrenOnlyTransform is
150 * a special case since it is intended to update the children of a specific
151 * frame.
153 static nsIFrame*
154 GetFrameForChildrenOnlyTransformHint(nsIFrame *aFrame)
156 if (aFrame->GetType() == nsGkAtoms::viewportFrame) {
157 // This happens if the root-<svg> is fixed positioned, in which case we
158 // can't use aFrame->GetContent() to find the primary frame, since
159 // GetContent() returns nullptr for ViewportFrame.
160 aFrame = aFrame->GetFirstPrincipalChild();
162 // For an nsHTMLScrollFrame, this will get the SVG frame that has the
163 // children-only transforms:
164 aFrame = aFrame->GetContent()->GetPrimaryFrame();
165 if (aFrame->GetType() == nsGkAtoms::svgOuterSVGFrame) {
166 aFrame = aFrame->GetFirstPrincipalChild();
167 NS_ABORT_IF_FALSE(aFrame->GetType() == nsGkAtoms::svgOuterSVGAnonChildFrame,
168 "Where is the nsSVGOuterSVGFrame's anon child??");
170 NS_ABORT_IF_FALSE(aFrame->IsFrameOfType(nsIFrame::eSVG |
171 nsIFrame::eSVGContainer),
172 "Children-only transforms only expected on SVG frames");
173 return aFrame;
176 static void
177 DoApplyRenderingChangeToTree(nsIFrame* aFrame,
178 nsChangeHint aChange)
180 NS_PRECONDITION(gInApplyRenderingChangeToTree,
181 "should only be called within ApplyRenderingChangeToTree");
183 for ( ; aFrame; aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame)) {
184 // Invalidate and sync views on all descendant frames, following placeholders.
185 // We don't need to update transforms in SyncViewsAndInvalidateDescendants, because
186 // there can't be any out-of-flows or popups that need to be transformed;
187 // all out-of-flow descendants of the transformed element must also be
188 // descendants of the transformed frame.
189 SyncViewsAndInvalidateDescendants(aFrame,
190 nsChangeHint(aChange & (nsChangeHint_RepaintFrame |
191 nsChangeHint_SyncFrameView |
192 nsChangeHint_UpdateOpacityLayer |
193 nsChangeHint_SchedulePaint)));
194 // This must be set to true if the rendering change needs to
195 // invalidate content. If it's false, a composite-only paint
196 // (empty transaction) will be scheduled.
197 bool needInvalidatingPaint = false;
199 // if frame has view, will already be invalidated
200 if (aChange & nsChangeHint_RepaintFrame) {
201 // Note that this whole block will be skipped when painting is suppressed
202 // (due to our caller ApplyRendingChangeToTree() discarding the
203 // nsChangeHint_RepaintFrame hint). If you add handling for any other
204 // hints within this block, be sure that they too should be ignored when
205 // painting is suppressed.
206 needInvalidatingPaint = true;
207 aFrame->InvalidateFrameSubtree();
208 if (aChange & nsChangeHint_UpdateEffects &&
209 aFrame->IsFrameOfType(nsIFrame::eSVG) &&
210 !(aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)) {
211 // Need to update our overflow rects:
212 nsSVGUtils::ScheduleReflowSVG(aFrame);
215 if (aChange & nsChangeHint_UpdateTextPath) {
216 if (aFrame->IsSVGText()) {
217 // Invalidate and reflow the entire SVGTextFrame:
218 NS_ASSERTION(aFrame->GetContent()->IsSVG(nsGkAtoms::textPath),
219 "expected frame for a <textPath> element");
220 nsIFrame* text = nsLayoutUtils::GetClosestFrameOfType(
221 aFrame,
222 nsGkAtoms::svgTextFrame);
223 NS_ASSERTION(text, "expected to find an ancestor SVGTextFrame");
224 static_cast<SVGTextFrame*>(text)->NotifyGlyphMetricsChange();
225 } else {
226 NS_ABORT_IF_FALSE(false, "unexpected frame got "
227 "nsChangeHint_UpdateTextPath");
230 if (aChange & nsChangeHint_UpdateOpacityLayer) {
231 // FIXME/bug 796697: we can get away with empty transactions for
232 // opacity updates in many cases.
233 needInvalidatingPaint = true;
235 ActiveLayerTracker::NotifyRestyle(aFrame, eCSSProperty_opacity);
236 if (nsSVGIntegrationUtils::UsingEffectsForFrame(aFrame)) {
237 // SVG effects paints the opacity without using
238 // nsDisplayOpacity. We need to invalidate manually.
239 aFrame->InvalidateFrameSubtree();
242 if ((aChange & nsChangeHint_UpdateTransformLayer) &&
243 aFrame->IsTransformed()) {
244 ActiveLayerTracker::NotifyRestyle(aFrame, eCSSProperty_transform);
245 // If we're not already going to do an invalidating paint, see
246 // if we can get away with only updating the transform on a
247 // layer for this frame, and not scheduling an invalidating
248 // paint.
249 if (!needInvalidatingPaint) {
250 Layer* layer;
251 needInvalidatingPaint |= !aFrame->TryUpdateTransformOnly(&layer);
253 if (!needInvalidatingPaint) {
254 // Since we're not going to paint, we need to resend animation
255 // data to the layer.
256 MOZ_ASSERT(layer, "this can't happen if there's no layer");
257 nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(layer,
258 nullptr, nullptr, aFrame, eCSSProperty_transform);
262 if (aChange & nsChangeHint_ChildrenOnlyTransform) {
263 needInvalidatingPaint = true;
264 nsIFrame* childFrame =
265 GetFrameForChildrenOnlyTransformHint(aFrame)->GetFirstPrincipalChild();
266 for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
267 ActiveLayerTracker::NotifyRestyle(childFrame, eCSSProperty_transform);
270 if (aChange & nsChangeHint_SchedulePaint) {
271 needInvalidatingPaint = true;
273 aFrame->SchedulePaint(needInvalidatingPaint ?
274 nsIFrame::PAINT_DEFAULT :
275 nsIFrame::PAINT_COMPOSITE_ONLY);
279 static void
280 ApplyRenderingChangeToTree(nsPresContext* aPresContext,
281 nsIFrame* aFrame,
282 nsChangeHint aChange)
284 // We check StyleDisplay()->HasTransformStyle() in addition to checking
285 // IsTransformed() since we can get here for some frames that don't support
286 // CSS transforms.
287 NS_ASSERTION(!(aChange & nsChangeHint_UpdateTransformLayer) ||
288 aFrame->IsTransformed() ||
289 aFrame->StyleDisplay()->HasTransformStyle(),
290 "Unexpected UpdateTransformLayer hint");
292 nsIPresShell *shell = aPresContext->PresShell();
293 if (shell->IsPaintingSuppressed()) {
294 // Don't allow synchronous rendering changes when painting is turned off.
295 aChange = NS_SubtractHint(aChange, nsChangeHint_RepaintFrame);
296 if (!aChange) {
297 return;
301 // Trigger rendering updates by damaging this frame and any
302 // continuations of this frame.
303 #ifdef DEBUG
304 gInApplyRenderingChangeToTree = true;
305 #endif
306 if (aChange & nsChangeHint_RepaintFrame) {
307 // If the frame's background is propagated to an ancestor, walk up to
308 // that ancestor and apply the RepaintFrame change hint to it.
309 nsStyleContext *bgSC;
310 nsIFrame* propagatedFrame = aFrame;
311 while (!nsCSSRendering::FindBackground(propagatedFrame, &bgSC)) {
312 propagatedFrame = propagatedFrame->GetParent();
313 NS_ASSERTION(aFrame, "root frame must paint");
316 if (propagatedFrame != aFrame) {
317 DoApplyRenderingChangeToTree(propagatedFrame, nsChangeHint_RepaintFrame);
318 aChange = NS_SubtractHint(aChange, nsChangeHint_RepaintFrame);
319 if (!aChange) {
320 return;
324 DoApplyRenderingChangeToTree(aFrame, aChange);
325 #ifdef DEBUG
326 gInApplyRenderingChangeToTree = false;
327 #endif
330 bool
331 RestyleManager::RecomputePosition(nsIFrame* aFrame)
333 // Don't process position changes on table frames, since we already handle
334 // the dynamic position change on the outer table frame, and the reflow-based
335 // fallback code path also ignores positions on inner table frames.
336 if (aFrame->GetType() == nsGkAtoms::tableFrame) {
337 return true;
340 const nsStyleDisplay* display = aFrame->StyleDisplay();
341 // Changes to the offsets of a non-positioned element can safely be ignored.
342 if (display->mPosition == NS_STYLE_POSITION_STATIC) {
343 return true;
346 // Don't process position changes on frames which have views or the ones which
347 // have a view somewhere in their descendants, because the corresponding view
348 // needs to be repositioned properly as well.
349 if (aFrame->HasView() ||
350 (aFrame->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW)) {
351 StyleChangeReflow(aFrame, nsChangeHint_NeedReflow);
352 return false;
355 aFrame->SchedulePaint();
357 // For relative positioning, we can simply update the frame rect
358 if (display->IsRelativelyPositionedStyle()) {
359 if (display->IsInnerTableStyle()) {
360 // We don't currently support relative positioning of inner table
361 // elements (bug 35168). If we apply offsets to things we haven't
362 // previously offset, we'll get confused. So bail.
363 return true;
367 // Move the frame
368 if (display->mPosition == NS_STYLE_POSITION_STICKY) {
369 // Update sticky positioning for an entire element at once, starting with
370 // the first continuation or ib-split sibling.
371 // It's rare that the frame we already have isn't already the first
372 // continuation or ib-split sibling, but it can happen when styles differ
373 // across continuations such as ::first-line or ::first-letter, and in
374 // those cases we will generally (but maybe not always) do the work twice.
375 nsIFrame *firstContinuation =
376 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
378 StickyScrollContainer::ComputeStickyOffsets(firstContinuation);
379 StickyScrollContainer* ssc =
380 StickyScrollContainer::GetStickyScrollContainerForFrame(firstContinuation);
381 if (ssc) {
382 ssc->PositionContinuations(firstContinuation);
384 } else {
385 MOZ_ASSERT(NS_STYLE_POSITION_RELATIVE == display->mPosition,
386 "Unexpected type of positioning");
387 for (nsIFrame *cont = aFrame; cont;
388 cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
389 nsIFrame* cb = cont->GetContainingBlock();
390 nsMargin newOffsets;
391 const nsSize size = cb->GetContentRectRelativeToSelf().Size();
393 nsHTMLReflowState::ComputeRelativeOffsets(
394 cb->StyleVisibility()->mDirection,
395 cont, size.width, size.height, newOffsets);
396 NS_ASSERTION(newOffsets.left == -newOffsets.right &&
397 newOffsets.top == -newOffsets.bottom,
398 "ComputeRelativeOffsets should return valid results");
400 // nsHTMLReflowState::ApplyRelativePositioning would work here, but
401 // since we've already checked mPosition and aren't changing the frame's
402 // normal position, go ahead and add the offsets directly.
403 cont->SetPosition(cont->GetNormalPosition() +
404 nsPoint(newOffsets.left, newOffsets.top));
408 return true;
411 // For the absolute positioning case, set up a fake HTML reflow state for
412 // the frame, and then get the offsets and size from it. If the frame's size
413 // doesn't need to change, we can simply update the frame position. Otherwise
414 // we fall back to a reflow.
415 nsRefPtr<nsRenderingContext> rc =
416 aFrame->PresContext()->PresShell()->CreateReferenceRenderingContext();
418 // Construct a bogus parent reflow state so that there's a usable
419 // containing block reflow state.
420 nsIFrame* parentFrame = aFrame->GetParent();
421 WritingMode parentWM = parentFrame->GetWritingMode();
422 WritingMode frameWM = aFrame->GetWritingMode();
423 LogicalSize parentSize = parentFrame->GetLogicalSize();
425 nsFrameState savedState = parentFrame->GetStateBits();
426 nsHTMLReflowState parentReflowState(aFrame->PresContext(), parentFrame,
427 rc, parentSize);
428 parentFrame->RemoveStateBits(~nsFrameState(0));
429 parentFrame->AddStateBits(savedState);
431 NS_WARN_IF_FALSE(parentSize.ISize(parentWM) != NS_INTRINSICSIZE &&
432 parentSize.BSize(parentWM) != NS_INTRINSICSIZE,
433 "parentSize should be valid");
434 parentReflowState.SetComputedISize(std::max(parentSize.ISize(parentWM), 0));
435 parentReflowState.SetComputedBSize(std::max(parentSize.BSize(parentWM), 0));
436 parentReflowState.ComputedPhysicalMargin().SizeTo(0, 0, 0, 0);
438 parentReflowState.ComputedPhysicalPadding() = parentFrame->GetUsedPadding();
439 parentReflowState.ComputedPhysicalBorderPadding() =
440 parentFrame->GetUsedBorderAndPadding();
441 LogicalSize availSize = parentSize.ConvertTo(frameWM, parentWM);
442 availSize.BSize(frameWM) = NS_INTRINSICSIZE;
444 ViewportFrame* viewport = do_QueryFrame(parentFrame);
445 nsSize cbSize = viewport ?
446 viewport->AdjustReflowStateAsContainingBlock(&parentReflowState).Size()
447 : aFrame->GetContainingBlock()->GetSize();
448 const nsMargin& parentBorder =
449 parentReflowState.mStyleBorder->GetComputedBorder();
450 cbSize -= nsSize(parentBorder.LeftRight(), parentBorder.TopBottom());
451 nsHTMLReflowState reflowState(aFrame->PresContext(), parentReflowState,
452 aFrame, availSize,
453 cbSize.width, cbSize.height);
454 nsSize computedSize(reflowState.ComputedWidth(), reflowState.ComputedHeight());
455 computedSize.width += reflowState.ComputedPhysicalBorderPadding().LeftRight();
456 if (computedSize.height != NS_INTRINSICSIZE) {
457 computedSize.height += reflowState.ComputedPhysicalBorderPadding().TopBottom();
459 nsSize size = aFrame->GetSize();
460 // The RecomputePosition hint is not used if any offset changed between auto
461 // and non-auto. If computedSize.height == NS_INTRINSICSIZE then the new
462 // element height will be its intrinsic height, and since 'top' and 'bottom''s
463 // auto-ness hasn't changed, the old height must also be its intrinsic
464 // height, which we can assume hasn't changed (or reflow would have
465 // been triggered).
466 if (computedSize.width == size.width &&
467 (computedSize.height == NS_INTRINSICSIZE || computedSize.height == size.height)) {
468 // If we're solving for 'left' or 'top', then compute it here, in order to
469 // match the reflow code path.
470 if (NS_AUTOOFFSET == reflowState.ComputedPhysicalOffsets().left) {
471 reflowState.ComputedPhysicalOffsets().left = cbSize.width -
472 reflowState.ComputedPhysicalOffsets().right -
473 reflowState.ComputedPhysicalMargin().right -
474 size.width -
475 reflowState.ComputedPhysicalMargin().left;
478 if (NS_AUTOOFFSET == reflowState.ComputedPhysicalOffsets().top) {
479 reflowState.ComputedPhysicalOffsets().top = cbSize.height -
480 reflowState.ComputedPhysicalOffsets().bottom -
481 reflowState.ComputedPhysicalMargin().bottom -
482 size.height -
483 reflowState.ComputedPhysicalMargin().top;
486 // Move the frame
487 nsPoint pos(parentBorder.left + reflowState.ComputedPhysicalOffsets().left +
488 reflowState.ComputedPhysicalMargin().left,
489 parentBorder.top + reflowState.ComputedPhysicalOffsets().top +
490 reflowState.ComputedPhysicalMargin().top);
491 aFrame->SetPosition(pos);
493 return true;
496 // Fall back to a reflow
497 StyleChangeReflow(aFrame, nsChangeHint_NeedReflow);
498 return false;
501 void
502 RestyleManager::StyleChangeReflow(nsIFrame* aFrame, nsChangeHint aHint)
504 nsIPresShell::IntrinsicDirty dirtyType;
505 if (aHint & nsChangeHint_ClearDescendantIntrinsics) {
506 NS_ASSERTION(aHint & nsChangeHint_ClearAncestorIntrinsics,
507 "Please read the comments in nsChangeHint.h");
508 dirtyType = nsIPresShell::eStyleChange;
509 } else if (aHint & nsChangeHint_ClearAncestorIntrinsics) {
510 dirtyType = nsIPresShell::eTreeChange;
511 } else {
512 dirtyType = nsIPresShell::eResize;
515 nsFrameState dirtyBits;
516 if (aFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
517 dirtyBits = nsFrameState(0);
518 } else if (aHint & nsChangeHint_NeedDirtyReflow) {
519 dirtyBits = NS_FRAME_IS_DIRTY;
520 } else {
521 dirtyBits = NS_FRAME_HAS_DIRTY_CHILDREN;
524 // If we're not going to clear any intrinsic sizes on the frames, and
525 // there are no dirty bits to set, then there's nothing to do.
526 if (dirtyType == nsIPresShell::eResize && !dirtyBits)
527 return;
529 do {
530 mPresContext->PresShell()->FrameNeedsReflow(aFrame, dirtyType, dirtyBits);
531 aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame);
532 } while (aFrame);
535 void
536 RestyleManager::AddSubtreeToOverflowTracker(nsIFrame* aFrame)
538 mOverflowChangedTracker.AddFrame(
539 aFrame,
540 OverflowChangedTracker::CHILDREN_AND_PARENT_CHANGED);
541 nsIFrame::ChildListIterator lists(aFrame);
542 for (; !lists.IsDone(); lists.Next()) {
543 nsFrameList::Enumerator childFrames(lists.CurrentList());
544 for (; !childFrames.AtEnd(); childFrames.Next()) {
545 nsIFrame* child = childFrames.get();
546 AddSubtreeToOverflowTracker(child);
551 NS_DECLARE_FRAME_PROPERTY(ChangeListProperty, nullptr)
554 * Return true if aFrame's subtree has placeholders for out-of-flow content
555 * whose 'position' style's bit in aPositionMask is set.
557 static bool
558 FrameHasPositionedPlaceholderDescendants(nsIFrame* aFrame, uint32_t aPositionMask)
560 const nsIFrame::ChildListIDs skip(nsIFrame::kAbsoluteList |
561 nsIFrame::kFixedList);
562 for (nsIFrame::ChildListIterator lists(aFrame); !lists.IsDone(); lists.Next()) {
563 if (!skip.Contains(lists.CurrentID())) {
564 for (nsFrameList::Enumerator childFrames(lists.CurrentList());
565 !childFrames.AtEnd(); childFrames.Next()) {
566 nsIFrame* f = childFrames.get();
567 if (f->GetType() == nsGkAtoms::placeholderFrame) {
568 nsIFrame* outOfFlow = nsPlaceholderFrame::GetRealFrameForPlaceholder(f);
569 // If SVG text frames could appear here, they could confuse us since
570 // they ignore their position style ... but they can't.
571 NS_ASSERTION(!outOfFlow->IsSVGText(),
572 "SVG text frames can't be out of flow");
573 if (aPositionMask & (1 << outOfFlow->StyleDisplay()->mPosition)) {
574 return true;
577 if (FrameHasPositionedPlaceholderDescendants(f, aPositionMask)) {
578 return true;
583 return false;
586 static bool
587 NeedToReframeForAddingOrRemovingTransform(nsIFrame* aFrame)
589 static_assert(0 <= NS_STYLE_POSITION_ABSOLUTE &&
590 NS_STYLE_POSITION_ABSOLUTE < 32, "Style constant out of range");
591 static_assert(0 <= NS_STYLE_POSITION_FIXED &&
592 NS_STYLE_POSITION_FIXED < 32, "Style constant out of range");
594 uint32_t positionMask;
595 // Don't call aFrame->IsPositioned here, since that returns true if
596 // the frame already has a transform, and we want to ignore that here
597 if (aFrame->IsAbsolutelyPositioned() ||
598 aFrame->IsRelativelyPositioned()) {
599 // This frame is a container for abs-pos descendants whether or not it
600 // has a transform.
601 // So abs-pos descendants are no problem; we only need to reframe if
602 // we have fixed-pos descendants.
603 positionMask = 1 << NS_STYLE_POSITION_FIXED;
604 } else {
605 // This frame may not be a container for abs-pos descendants already.
606 // So reframe if we have abs-pos or fixed-pos descendants.
607 positionMask = (1 << NS_STYLE_POSITION_FIXED) |
608 (1 << NS_STYLE_POSITION_ABSOLUTE);
610 for (nsIFrame* f = aFrame; f;
611 f = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(f)) {
612 if (FrameHasPositionedPlaceholderDescendants(f, positionMask)) {
613 return true;
616 return false;
619 nsresult
620 RestyleManager::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
622 NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
623 "Someone forgot a script blocker");
624 int32_t count = aChangeList.Count();
625 if (!count)
626 return NS_OK;
628 PROFILER_LABEL("RestyleManager", "ProcessRestyledFrames",
629 js::ProfileEntry::Category::CSS);
631 // Make sure to not rebuild quote or counter lists while we're
632 // processing restyles
633 FrameConstructor()->BeginUpdate();
635 FramePropertyTable* propTable = mPresContext->PropertyTable();
637 // Mark frames so that we skip frames that die along the way, bug 123049.
638 // A frame can be in the list multiple times with different hints. Further
639 // optmization is possible if nsStyleChangeList::AppendChange could coalesce
640 int32_t index = count;
642 while (0 <= --index) {
643 const nsStyleChangeData* changeData;
644 aChangeList.ChangeAt(index, &changeData);
645 if (changeData->mFrame) {
646 propTable->Set(changeData->mFrame, ChangeListProperty(),
647 NS_INT32_TO_PTR(1));
651 index = count;
653 bool didUpdateCursor = false;
655 while (0 <= --index) {
656 nsIFrame* frame;
657 nsIContent* content;
658 bool didReflowThisFrame = false;
659 nsChangeHint hint;
660 aChangeList.ChangeAt(index, frame, content, hint);
662 NS_ASSERTION(!(hint & nsChangeHint_AllReflowHints) ||
663 (hint & nsChangeHint_NeedReflow),
664 "Reflow hint bits set without actually asking for a reflow");
666 // skip any frame that has been destroyed due to a ripple effect
667 if (frame && !propTable->Get(frame, ChangeListProperty())) {
668 continue;
671 if (frame && frame->GetContent() != content) {
672 // XXXbz this is due to image maps messing with the primary frame of
673 // <area>s. See bug 135040. Remove this block once that's fixed.
674 frame = nullptr;
675 if (!(hint & nsChangeHint_ReconstructFrame)) {
676 continue;
680 if ((hint & nsChangeHint_AddOrRemoveTransform) && frame &&
681 !(hint & nsChangeHint_ReconstructFrame)) {
682 if (NeedToReframeForAddingOrRemovingTransform(frame) ||
683 frame->GetType() == nsGkAtoms::fieldSetFrame ||
684 frame->GetContentInsertionFrame() != frame) {
685 // The frame has positioned children that need to be reparented, or
686 // it can't easily be converted to/from being an abs-pos container correctly.
687 NS_UpdateHint(hint, nsChangeHint_ReconstructFrame);
688 } else {
689 for (nsIFrame *cont = frame; cont;
690 cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
691 // Normally frame construction would set state bits as needed,
692 // but we're not going to reconstruct the frame so we need to set them.
693 // It's because we need to set this state on each affected frame
694 // that we can't coalesce nsChangeHint_AddOrRemoveTransform hints up
695 // to ancestors (i.e. it can't be an inherited change hint).
696 if (cont->IsPositioned()) {
697 // If a transform has been added, we'll be taking this path,
698 // but we may be taking this path even if a transform has been
699 // removed. It's OK to add the bit even if it's not needed.
700 cont->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
701 if (!cont->IsAbsoluteContainer() &&
702 (cont->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)) {
703 cont->MarkAsAbsoluteContainingBlock();
705 } else {
706 // Don't remove NS_FRAME_MAY_BE_TRANSFORMED since it may still by
707 // transformed by other means. It's OK to have the bit even if it's
708 // not needed.
709 if (cont->IsAbsoluteContainer()) {
710 cont->MarkAsNotAbsoluteContainingBlock();
716 if (hint & nsChangeHint_ReconstructFrame) {
717 // If we ever start passing true here, be careful of restyles
718 // that involve a reframe and animations. In particular, if the
719 // restyle we're processing here is an animation restyle, but
720 // the style resolution we will do for the frame construction
721 // happens async when we're not in an animation restyle already,
722 // problems could arise.
723 // We could also have problems with triggering of CSS transitions
724 // on elements whose frames are reconstructed, since we depend on
725 // the reconstruction happening synchronously.
726 FrameConstructor()->RecreateFramesForContent(content, false);
727 } else {
728 NS_ASSERTION(frame, "This shouldn't happen");
730 if ((frame->GetStateBits() & NS_FRAME_SVG_LAYOUT) &&
731 (frame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
732 // frame does not maintain overflow rects, so avoid calling
733 // FinishAndStoreOverflow on it:
734 hint = NS_SubtractHint(hint,
735 NS_CombineHint(nsChangeHint_UpdateOverflow,
736 NS_CombineHint(nsChangeHint_ChildrenOnlyTransform,
737 nsChangeHint_UpdatePostTransformOverflow)));
740 if (!(frame->GetStateBits() & NS_FRAME_MAY_BE_TRANSFORMED)) {
741 // Frame can not be transformed, and thus a change in transform will
742 // have no effect and we should not use the
743 // nsChangeHint_UpdatePostTransformOverflow hint.
744 hint = NS_SubtractHint(hint, nsChangeHint_UpdatePostTransformOverflow);
747 if (hint & nsChangeHint_UpdateEffects) {
748 for (nsIFrame *cont = frame; cont;
749 cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
750 nsSVGEffects::UpdateEffects(cont);
753 if (hint & nsChangeHint_NeedReflow) {
754 StyleChangeReflow(frame, hint);
755 didReflowThisFrame = true;
758 if (hint & (nsChangeHint_RepaintFrame | nsChangeHint_SyncFrameView |
759 nsChangeHint_UpdateOpacityLayer | nsChangeHint_UpdateTransformLayer |
760 nsChangeHint_ChildrenOnlyTransform | nsChangeHint_SchedulePaint)) {
761 ApplyRenderingChangeToTree(mPresContext, frame, hint);
763 if ((hint & nsChangeHint_RecomputePosition) && !didReflowThisFrame) {
764 ActiveLayerTracker::NotifyOffsetRestyle(frame);
765 // It is possible for this to fall back to a reflow
766 if (!RecomputePosition(frame)) {
767 didReflowThisFrame = true;
770 NS_ASSERTION(!(hint & nsChangeHint_ChildrenOnlyTransform) ||
771 (hint & nsChangeHint_UpdateOverflow),
772 "nsChangeHint_UpdateOverflow should be passed too");
773 if (!didReflowThisFrame &&
774 (hint & (nsChangeHint_UpdateOverflow |
775 nsChangeHint_UpdatePostTransformOverflow |
776 nsChangeHint_UpdateSubtreeOverflow))) {
777 if (hint & nsChangeHint_UpdateSubtreeOverflow) {
778 AddSubtreeToOverflowTracker(frame);
780 OverflowChangedTracker::ChangeKind changeKind;
781 if (hint & nsChangeHint_ChildrenOnlyTransform) {
782 // The overflow areas of the child frames need to be updated:
783 nsIFrame* hintFrame = GetFrameForChildrenOnlyTransformHint(frame);
784 nsIFrame* childFrame = hintFrame->GetFirstPrincipalChild();
785 NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(frame),
786 "SVG frames should not have continuations "
787 "or ib-split siblings");
788 NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(hintFrame),
789 "SVG frames should not have continuations "
790 "or ib-split siblings");
791 for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
792 NS_ABORT_IF_FALSE(childFrame->IsFrameOfType(nsIFrame::eSVG),
793 "Not expecting non-SVG children");
794 // If |childFrame| is dirty or has dirty children, we don't bother
795 // updating overflows since that will happen when it's reflowed.
796 if (!(childFrame->GetStateBits() &
797 (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN))) {
798 mOverflowChangedTracker.AddFrame(childFrame,
799 OverflowChangedTracker::CHILDREN_AND_PARENT_CHANGED);
801 NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(childFrame),
802 "SVG frames should not have continuations "
803 "or ib-split siblings");
804 NS_ASSERTION(childFrame->GetParent() == hintFrame,
805 "SVG child frame not expected to have different parent");
808 // If |frame| is dirty or has dirty children, we don't bother updating
809 // overflows since that will happen when it's reflowed.
810 if (!(frame->GetStateBits() &
811 (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN))) {
812 // If we have both nsChangeHint_UpdateOverflow and
813 // nsChangeHint_UpdatePostTransformOverflow, CHILDREN_AND_PARENT_CHANGED
814 // is selected as it is stronger.
815 if (hint & (nsChangeHint_UpdateOverflow |
816 nsChangeHint_UpdateSubtreeOverflow)) {
817 changeKind = OverflowChangedTracker::CHILDREN_AND_PARENT_CHANGED;
818 } else {
819 changeKind = OverflowChangedTracker::TRANSFORM_CHANGED;
821 for (nsIFrame *cont = frame; cont; cont =
822 nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
823 mOverflowChangedTracker.AddFrame(cont, changeKind);
827 if ((hint & nsChangeHint_UpdateCursor) && !didUpdateCursor) {
828 mPresContext->PresShell()->SynthesizeMouseMove(false);
829 didUpdateCursor = true;
834 FrameConstructor()->EndUpdate();
836 // cleanup references and verify the style tree. Note that the latter needs
837 // to happen once we've processed the whole list, since until then the tree
838 // is not in fact in a consistent state.
839 index = count;
840 while (0 <= --index) {
841 const nsStyleChangeData* changeData;
842 aChangeList.ChangeAt(index, &changeData);
843 if (changeData->mFrame) {
844 propTable->Delete(changeData->mFrame, ChangeListProperty());
847 #ifdef DEBUG
848 // reget frame from content since it may have been regenerated...
849 if (changeData->mContent) {
850 if (!nsAnimationManager::ContentOrAncestorHasAnimation(changeData->mContent) &&
851 !nsTransitionManager::ContentOrAncestorHasTransition(changeData->mContent)) {
852 nsIFrame* frame = changeData->mContent->GetPrimaryFrame();
853 if (frame) {
854 DebugVerifyStyleTree(frame);
857 } else if (!changeData->mFrame ||
858 changeData->mFrame->GetType() != nsGkAtoms::viewportFrame) {
859 NS_WARNING("Unable to test style tree integrity -- no content node "
860 "(and not a viewport frame)");
862 #endif
865 aChangeList.Clear();
866 return NS_OK;
869 void
870 RestyleManager::RestyleElement(Element* aElement,
871 nsIFrame* aPrimaryFrame,
872 nsChangeHint aMinHint,
873 RestyleTracker& aRestyleTracker,
874 nsRestyleHint aRestyleHint)
876 NS_ASSERTION(aPrimaryFrame == aElement->GetPrimaryFrame(),
877 "frame/content mismatch");
878 if (aPrimaryFrame && aPrimaryFrame->GetContent() != aElement) {
879 // XXXbz this is due to image maps messing with the primary frame pointer
880 // of <area>s. See bug 135040. We can remove this block once that's fixed.
881 aPrimaryFrame = nullptr;
883 NS_ASSERTION(!aPrimaryFrame || aPrimaryFrame->GetContent() == aElement,
884 "frame/content mismatch");
886 // If we're restyling the root element and there are 'rem' units in
887 // use, handle dynamic changes to the definition of a 'rem' here.
888 if (mPresContext->UsesRootEMUnits() && aPrimaryFrame) {
889 nsStyleContext *oldContext = aPrimaryFrame->StyleContext();
890 if (!oldContext->GetParent()) { // check that we're the root element
891 nsRefPtr<nsStyleContext> newContext = mPresContext->StyleSet()->
892 ResolveStyleFor(aElement, nullptr /* == oldContext->GetParent() */);
893 if (oldContext->StyleFont()->mFont.size !=
894 newContext->StyleFont()->mFont.size) {
895 // The basis for 'rem' units has changed.
896 newContext = nullptr;
897 DoRebuildAllStyleData(aRestyleTracker, nsChangeHint(0), aRestyleHint);
898 if (aMinHint == 0) {
899 return;
901 aPrimaryFrame = aElement->GetPrimaryFrame();
906 if (aMinHint & nsChangeHint_ReconstructFrame) {
907 FrameConstructor()->RecreateFramesForContent(aElement, false);
908 } else if (aPrimaryFrame) {
909 ComputeAndProcessStyleChange(aPrimaryFrame, aMinHint, aRestyleTracker,
910 aRestyleHint);
911 } else if (aRestyleHint & ~eRestyle_LaterSiblings) {
912 // We're restyling an element with no frame, so we should try to
913 // make one if its new style says it should have one. But in order
914 // to try to honor the restyle hint (which we'd like to do so that,
915 // for example, an animation-only style flush doesn't flush other
916 // buffered style changes), we only do this if the restyle hint says
917 // we have *some* restyling for this frame. This means we'll
918 // potentially get ahead of ourselves in that case, but not as much
919 // as we would if we didn't check the restyle hint.
920 FrameConstructor()->MaybeRecreateFramesForElement(aElement);
924 static inline dom::Element*
925 ElementForStyleContext(nsIContent* aParentContent,
926 nsIFrame* aFrame,
927 nsCSSPseudoElements::Type aPseudoType);
929 // Forwarded nsIDocumentObserver method, to handle restyling (and
930 // passing the notification to the frame).
931 nsresult
932 RestyleManager::ContentStateChanged(nsIContent* aContent,
933 EventStates aStateMask)
935 // XXXbz it would be good if this function only took Elements, but
936 // we'd have to make ESM guarantee that usefully.
937 if (!aContent->IsElement()) {
938 return NS_OK;
941 Element* aElement = aContent->AsElement();
943 nsStyleSet* styleSet = mPresContext->StyleSet();
944 NS_ASSERTION(styleSet, "couldn't get style set");
946 nsChangeHint hint = NS_STYLE_HINT_NONE;
947 // Any change to a content state that affects which frames we construct
948 // must lead to a frame reconstruct here if we already have a frame.
949 // Note that we never decide through non-CSS means to not create frames
950 // based on content states, so if we already don't have a frame we don't
951 // need to force a reframe -- if it's needed, the HasStateDependentStyle
952 // call will handle things.
953 nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
954 nsCSSPseudoElements::Type pseudoType =
955 nsCSSPseudoElements::ePseudo_NotPseudoElement;
956 if (primaryFrame) {
957 // If it's generated content, ignore LOADING/etc state changes on it.
958 if (!primaryFrame->IsGeneratedContentFrame() &&
959 aStateMask.HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN |
960 NS_EVENT_STATE_USERDISABLED |
961 NS_EVENT_STATE_SUPPRESSED |
962 NS_EVENT_STATE_LOADING)) {
963 hint = nsChangeHint_ReconstructFrame;
964 } else {
965 uint8_t app = primaryFrame->StyleDisplay()->mAppearance;
966 if (app) {
967 nsITheme *theme = mPresContext->GetTheme();
968 if (theme && theme->ThemeSupportsWidget(mPresContext,
969 primaryFrame, app)) {
970 bool repaint = false;
971 theme->WidgetStateChanged(primaryFrame, app, nullptr, &repaint);
972 if (repaint) {
973 NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
979 pseudoType = primaryFrame->StyleContext()->GetPseudoType();
981 primaryFrame->ContentStatesChanged(aStateMask);
985 nsRestyleHint rshint;
987 if (pseudoType >= nsCSSPseudoElements::ePseudo_PseudoElementCount) {
988 rshint = styleSet->HasStateDependentStyle(mPresContext, aElement,
989 aStateMask);
990 } else if (nsCSSPseudoElements::PseudoElementSupportsUserActionState(
991 pseudoType)) {
992 // If aElement is a pseudo-element, we want to check to see whether there
993 // are any state-dependent rules applying to that pseudo.
994 Element* ancestor = ElementForStyleContext(nullptr, primaryFrame,
995 pseudoType);
996 rshint = styleSet->HasStateDependentStyle(mPresContext, ancestor,
997 pseudoType, aElement,
998 aStateMask);
999 } else {
1000 rshint = nsRestyleHint(0);
1003 if (aStateMask.HasState(NS_EVENT_STATE_HOVER) && rshint != 0) {
1004 ++mHoverGeneration;
1007 if (aStateMask.HasState(NS_EVENT_STATE_VISITED)) {
1008 // Exposing information to the page about whether the link is
1009 // visited or not isn't really something we can worry about here.
1010 // FIXME: We could probably do this a bit better.
1011 NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
1014 PostRestyleEvent(aElement, rshint, hint);
1015 return NS_OK;
1018 // Forwarded nsIMutationObserver method, to handle restyling.
1019 void
1020 RestyleManager::AttributeWillChange(Element* aElement,
1021 int32_t aNameSpaceID,
1022 nsIAtom* aAttribute,
1023 int32_t aModType)
1025 nsRestyleHint rshint =
1026 mPresContext->StyleSet()->HasAttributeDependentStyle(mPresContext,
1027 aElement,
1028 aAttribute,
1029 aModType,
1030 false);
1031 PostRestyleEvent(aElement, rshint, NS_STYLE_HINT_NONE);
1034 // Forwarded nsIMutationObserver method, to handle restyling (and
1035 // passing the notification to the frame).
1036 void
1037 RestyleManager::AttributeChanged(Element* aElement,
1038 int32_t aNameSpaceID,
1039 nsIAtom* aAttribute,
1040 int32_t aModType)
1042 // Hold onto the PresShell to prevent ourselves from being destroyed.
1043 // XXXbz how, exactly, would this attribute change cause us to be
1044 // destroyed from inside this function?
1045 nsCOMPtr<nsIPresShell> shell = mPresContext->GetPresShell();
1047 // Get the frame associated with the content which is the highest in the frame tree
1048 nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
1050 #if 0
1051 NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
1052 ("RestyleManager::AttributeChanged: content=%p[%s] frame=%p",
1053 aContent, ContentTag(aElement, 0), frame));
1054 #endif
1056 // the style tag has its own interpretation based on aHint
1057 nsChangeHint hint = aElement->GetAttributeChangeHint(aAttribute, aModType);
1059 bool reframe = (hint & nsChangeHint_ReconstructFrame) != 0;
1061 #ifdef MOZ_XUL
1062 // The following listbox widget trap prevents offscreen listbox widget
1063 // content from being removed and re-inserted (which is what would
1064 // happen otherwise).
1065 if (!primaryFrame && !reframe) {
1066 int32_t namespaceID;
1067 nsIAtom* tag = mPresContext->Document()->BindingManager()->
1068 ResolveTag(aElement, &namespaceID);
1070 if (namespaceID == kNameSpaceID_XUL &&
1071 (tag == nsGkAtoms::listitem ||
1072 tag == nsGkAtoms::listcell))
1073 return;
1076 if (aAttribute == nsGkAtoms::tooltiptext ||
1077 aAttribute == nsGkAtoms::tooltip)
1079 nsIRootBox* rootBox = nsIRootBox::GetRootBox(mPresContext->GetPresShell());
1080 if (rootBox) {
1081 if (aModType == nsIDOMMutationEvent::REMOVAL)
1082 rootBox->RemoveTooltipSupport(aElement);
1083 if (aModType == nsIDOMMutationEvent::ADDITION)
1084 rootBox->AddTooltipSupport(aElement);
1088 #endif // MOZ_XUL
1090 if (primaryFrame) {
1091 // See if we have appearance information for a theme.
1092 const nsStyleDisplay* disp = primaryFrame->StyleDisplay();
1093 if (disp->mAppearance) {
1094 nsITheme *theme = mPresContext->GetTheme();
1095 if (theme && theme->ThemeSupportsWidget(mPresContext, primaryFrame, disp->mAppearance)) {
1096 bool repaint = false;
1097 theme->WidgetStateChanged(primaryFrame, disp->mAppearance, aAttribute, &repaint);
1098 if (repaint)
1099 NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
1103 // let the frame deal with it now, so we don't have to deal later
1104 primaryFrame->AttributeChanged(aNameSpaceID, aAttribute, aModType);
1105 // XXXwaterson should probably check for IB split siblings
1106 // here, and propagate the AttributeChanged notification to
1107 // them, as well. Currently, inline frames don't do anything on
1108 // this notification, so it's not that big a deal.
1111 // See if we can optimize away the style re-resolution -- must be called after
1112 // the frame's AttributeChanged() in case it does something that affects the style
1113 nsRestyleHint rshint =
1114 mPresContext->StyleSet()->HasAttributeDependentStyle(mPresContext,
1115 aElement,
1116 aAttribute,
1117 aModType,
1118 true);
1120 PostRestyleEvent(aElement, rshint, hint);
1123 void
1124 RestyleManager::RestyleForEmptyChange(Element* aContainer)
1126 // In some cases (:empty + E, :empty ~ E), a change if the content of
1127 // an element requires restyling its parent's siblings.
1128 nsRestyleHint hint = eRestyle_Subtree;
1129 nsIContent* grandparent = aContainer->GetParent();
1130 if (grandparent &&
1131 (grandparent->GetFlags() & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS)) {
1132 hint = nsRestyleHint(hint | eRestyle_LaterSiblings);
1134 PostRestyleEvent(aContainer, hint, NS_STYLE_HINT_NONE);
1137 void
1138 RestyleManager::RestyleForAppend(Element* aContainer,
1139 nsIContent* aFirstNewContent)
1141 NS_ASSERTION(aContainer, "must have container for append");
1142 #ifdef DEBUG
1144 for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) {
1145 NS_ASSERTION(!cur->IsRootOfAnonymousSubtree(),
1146 "anonymous nodes should not be in child lists");
1149 #endif
1150 uint32_t selectorFlags =
1151 aContainer->GetFlags() & (NODE_ALL_SELECTOR_FLAGS &
1152 ~NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS);
1153 if (selectorFlags == 0)
1154 return;
1156 if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) {
1157 // see whether we need to restyle the container
1158 bool wasEmpty = true; // :empty or :-moz-only-whitespace
1159 for (nsIContent* cur = aContainer->GetFirstChild();
1160 cur != aFirstNewContent;
1161 cur = cur->GetNextSibling()) {
1162 // We don't know whether we're testing :empty or :-moz-only-whitespace,
1163 // so be conservative and assume :-moz-only-whitespace (i.e., make
1164 // IsSignificantChild less likely to be true, and thus make us more
1165 // likely to restyle).
1166 if (nsStyleUtil::IsSignificantChild(cur, true, false)) {
1167 wasEmpty = false;
1168 break;
1171 if (wasEmpty) {
1172 RestyleForEmptyChange(aContainer);
1173 return;
1177 if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
1178 PostRestyleEvent(aContainer, eRestyle_Subtree, NS_STYLE_HINT_NONE);
1179 // Restyling the container is the most we can do here, so we're done.
1180 return;
1183 if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
1184 // restyle the last element child before this node
1185 for (nsIContent* cur = aFirstNewContent->GetPreviousSibling();
1186 cur;
1187 cur = cur->GetPreviousSibling()) {
1188 if (cur->IsElement()) {
1189 PostRestyleEvent(cur->AsElement(), eRestyle_Subtree, NS_STYLE_HINT_NONE);
1190 break;
1196 // Needed since we can't use PostRestyleEvent on non-elements (with
1197 // eRestyle_LaterSiblings or nsRestyleHint(eRestyle_Subtree |
1198 // eRestyle_LaterSiblings) as appropriate).
1199 static void
1200 RestyleSiblingsStartingWith(RestyleManager* aRestyleManager,
1201 nsIContent* aStartingSibling /* may be null */)
1203 for (nsIContent *sibling = aStartingSibling; sibling;
1204 sibling = sibling->GetNextSibling()) {
1205 if (sibling->IsElement()) {
1206 aRestyleManager->
1207 PostRestyleEvent(sibling->AsElement(),
1208 nsRestyleHint(eRestyle_Subtree | eRestyle_LaterSiblings),
1209 NS_STYLE_HINT_NONE);
1210 break;
1215 // Restyling for a ContentInserted or CharacterDataChanged notification.
1216 // This could be used for ContentRemoved as well if we got the
1217 // notification before the removal happened (and sometimes
1218 // CharacterDataChanged is more like a removal than an addition).
1219 // The comments are written and variables are named in terms of it being
1220 // a ContentInserted notification.
1221 void
1222 RestyleManager::RestyleForInsertOrChange(Element* aContainer,
1223 nsIContent* aChild)
1225 NS_ASSERTION(!aChild->IsRootOfAnonymousSubtree(),
1226 "anonymous nodes should not be in child lists");
1227 uint32_t selectorFlags =
1228 aContainer ? (aContainer->GetFlags() & NODE_ALL_SELECTOR_FLAGS) : 0;
1229 if (selectorFlags == 0)
1230 return;
1232 if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) {
1233 // see whether we need to restyle the container
1234 bool wasEmpty = true; // :empty or :-moz-only-whitespace
1235 for (nsIContent* child = aContainer->GetFirstChild();
1236 child;
1237 child = child->GetNextSibling()) {
1238 if (child == aChild)
1239 continue;
1240 // We don't know whether we're testing :empty or :-moz-only-whitespace,
1241 // so be conservative and assume :-moz-only-whitespace (i.e., make
1242 // IsSignificantChild less likely to be true, and thus make us more
1243 // likely to restyle).
1244 if (nsStyleUtil::IsSignificantChild(child, true, false)) {
1245 wasEmpty = false;
1246 break;
1249 if (wasEmpty) {
1250 RestyleForEmptyChange(aContainer);
1251 return;
1255 if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
1256 PostRestyleEvent(aContainer, eRestyle_Subtree, NS_STYLE_HINT_NONE);
1257 // Restyling the container is the most we can do here, so we're done.
1258 return;
1261 if (selectorFlags & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
1262 // Restyle all later siblings.
1263 RestyleSiblingsStartingWith(this, aChild->GetNextSibling());
1266 if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
1267 // restyle the previously-first element child if it is after this node
1268 bool passedChild = false;
1269 for (nsIContent* content = aContainer->GetFirstChild();
1270 content;
1271 content = content->GetNextSibling()) {
1272 if (content == aChild) {
1273 passedChild = true;
1274 continue;
1276 if (content->IsElement()) {
1277 if (passedChild) {
1278 PostRestyleEvent(content->AsElement(), eRestyle_Subtree,
1279 NS_STYLE_HINT_NONE);
1281 break;
1284 // restyle the previously-last element child if it is before this node
1285 passedChild = false;
1286 for (nsIContent* content = aContainer->GetLastChild();
1287 content;
1288 content = content->GetPreviousSibling()) {
1289 if (content == aChild) {
1290 passedChild = true;
1291 continue;
1293 if (content->IsElement()) {
1294 if (passedChild) {
1295 PostRestyleEvent(content->AsElement(), eRestyle_Subtree,
1296 NS_STYLE_HINT_NONE);
1298 break;
1304 void
1305 RestyleManager::RestyleForRemove(Element* aContainer,
1306 nsIContent* aOldChild,
1307 nsIContent* aFollowingSibling)
1309 if (aOldChild->IsRootOfAnonymousSubtree()) {
1310 // This should be an assert, but this is called incorrectly in
1311 // nsHTMLEditor::DeleteRefToAnonymousNode and the assertions were clogging
1312 // up the logs. Make it an assert again when that's fixed.
1313 NS_WARNING("anonymous nodes should not be in child lists (bug 439258)");
1315 uint32_t selectorFlags =
1316 aContainer ? (aContainer->GetFlags() & NODE_ALL_SELECTOR_FLAGS) : 0;
1317 if (selectorFlags == 0)
1318 return;
1320 if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) {
1321 // see whether we need to restyle the container
1322 bool isEmpty = true; // :empty or :-moz-only-whitespace
1323 for (nsIContent* child = aContainer->GetFirstChild();
1324 child;
1325 child = child->GetNextSibling()) {
1326 // We don't know whether we're testing :empty or :-moz-only-whitespace,
1327 // so be conservative and assume :-moz-only-whitespace (i.e., make
1328 // IsSignificantChild less likely to be true, and thus make us more
1329 // likely to restyle).
1330 if (nsStyleUtil::IsSignificantChild(child, true, false)) {
1331 isEmpty = false;
1332 break;
1335 if (isEmpty) {
1336 RestyleForEmptyChange(aContainer);
1337 return;
1341 if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
1342 PostRestyleEvent(aContainer, eRestyle_Subtree, NS_STYLE_HINT_NONE);
1343 // Restyling the container is the most we can do here, so we're done.
1344 return;
1347 if (selectorFlags & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
1348 // Restyle all later siblings.
1349 RestyleSiblingsStartingWith(this, aFollowingSibling);
1352 if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
1353 // restyle the now-first element child if it was after aOldChild
1354 bool reachedFollowingSibling = false;
1355 for (nsIContent* content = aContainer->GetFirstChild();
1356 content;
1357 content = content->GetNextSibling()) {
1358 if (content == aFollowingSibling) {
1359 reachedFollowingSibling = true;
1360 // do NOT continue here; we might want to restyle this node
1362 if (content->IsElement()) {
1363 if (reachedFollowingSibling) {
1364 PostRestyleEvent(content->AsElement(), eRestyle_Subtree,
1365 NS_STYLE_HINT_NONE);
1367 break;
1370 // restyle the now-last element child if it was before aOldChild
1371 reachedFollowingSibling = (aFollowingSibling == nullptr);
1372 for (nsIContent* content = aContainer->GetLastChild();
1373 content;
1374 content = content->GetPreviousSibling()) {
1375 if (content->IsElement()) {
1376 if (reachedFollowingSibling) {
1377 PostRestyleEvent(content->AsElement(), eRestyle_Subtree, NS_STYLE_HINT_NONE);
1379 break;
1381 if (content == aFollowingSibling) {
1382 reachedFollowingSibling = true;
1388 void
1389 RestyleManager::RebuildAllStyleData(nsChangeHint aExtraHint)
1391 NS_ASSERTION(!(aExtraHint & nsChangeHint_ReconstructFrame),
1392 "Should not reconstruct the root of the frame tree. "
1393 "Use ReconstructDocElementHierarchy instead.");
1395 mRebuildAllStyleData = false;
1396 NS_UpdateHint(aExtraHint, mRebuildAllExtraHint);
1397 mRebuildAllExtraHint = nsChangeHint(0);
1399 nsIPresShell* presShell = mPresContext->GetPresShell();
1400 if (!presShell || !presShell->GetRootFrame())
1401 return;
1403 // Make sure that the viewmanager will outlive the presshell
1404 nsRefPtr<nsViewManager> vm = presShell->GetViewManager();
1406 // Processing the style changes could cause a flush that propagates to
1407 // the parent frame and thus destroys the pres shell.
1408 nsCOMPtr<nsIPresShell> kungFuDeathGrip(presShell);
1410 // We may reconstruct frames below and hence process anything that is in the
1411 // tree. We don't want to get notified to process those items again after.
1412 presShell->GetDocument()->FlushPendingNotifications(Flush_ContentAndNotify);
1414 nsAutoScriptBlocker scriptBlocker;
1416 mPresContext->SetProcessingRestyles(true);
1418 // FIXME (bug 1047928): Many of the callers probably don't need
1419 // eRestyle_Subtree because they're changing things that affect data
1420 // computation rather than selector matching; we could have a restyle
1421 // hint passed in, and substantially improve the performance of things
1422 // like pref changes and the restyling that we do for downloadable
1423 // font loads.
1424 DoRebuildAllStyleData(mPendingRestyles, aExtraHint, eRestyle_Subtree);
1426 mPresContext->SetProcessingRestyles(false);
1428 // Make sure that we process any pending animation restyles from the
1429 // above style change. Note that we can *almost* implement the above
1430 // by just posting a style change -- except we really need to restyle
1431 // the root frame rather than the root element's primary frame.
1432 ProcessPendingRestyles();
1435 void
1436 RestyleManager::DoRebuildAllStyleData(RestyleTracker& aRestyleTracker,
1437 nsChangeHint aExtraHint,
1438 nsRestyleHint aRestyleHint)
1440 // Tell the style set to get the old rule tree out of the way
1441 // so we can recalculate while maintaining rule tree immutability
1442 nsresult rv = mPresContext->StyleSet()->BeginReconstruct();
1443 if (NS_FAILED(rv)) {
1444 return;
1447 if (aRestyleHint & ~eRestyle_Subtree) {
1448 // We want this hint to apply to the root node's primary frame
1449 // rather than the root frame, since it's the primary frame that has
1450 // the styles for the root element (rather than the ancestors of the
1451 // primary frame whose mContent is the root node but which have
1452 // different styles). If we use up the hint for one of the
1453 // ancestors that we hit first, then we'll fail to do the restyling
1454 // we need to do.
1455 Element* root = mPresContext->Document()->GetRootElement();
1456 if (root) {
1457 // If the root element is gone, dropping the hint on the floor
1458 // should be fine.
1459 aRestyleTracker.AddPendingRestyle(root, aRestyleHint, nsChangeHint(0));
1461 aRestyleHint = nsRestyleHint(0);
1464 // Recalculate all of the style contexts for the document
1465 // Note that we can ignore the return value of ComputeStyleChangeFor
1466 // because we never need to reframe the root frame
1467 // XXX This could be made faster by not rerunning rule matching
1468 // (but note that nsPresShell::SetPreferenceStyleRules currently depends
1469 // on us re-running rule matching here
1470 // XXX Does it matter that we're passing aExtraHint to the real root
1471 // frame and not the root node's primary frame? (We could do
1472 // roughly what we do for aRestyleHint above.)
1473 // Note: The restyle tracker we pass in here doesn't matter.
1474 ComputeAndProcessStyleChange(mPresContext->PresShell()->GetRootFrame(),
1475 aExtraHint, aRestyleTracker, aRestyleHint);
1476 FlushOverflowChangedTracker();
1478 // Tell the style set it's safe to destroy the old rule tree. We
1479 // must do this after the ProcessRestyledFrames call in case the
1480 // change list has frame reconstructs in it (since frames to be
1481 // reconstructed will still have their old style context pointers
1482 // until they are destroyed).
1483 mPresContext->StyleSet()->EndReconstruct();
1486 void
1487 RestyleManager::ProcessPendingRestyles()
1489 NS_PRECONDITION(mPresContext->Document(), "No document? Pshaw!");
1490 NS_PRECONDITION(!nsContentUtils::IsSafeToRunScript(),
1491 "Missing a script blocker!");
1493 if (mRebuildAllStyleData) {
1494 RebuildAllStyleData(nsChangeHint(0));
1495 MOZ_ASSERT(mPendingRestyles.Count() == 0);
1496 return;
1499 // First do any queued-up frame creation. (We should really
1500 // merge this into the rest of the process, though; see bug 827239.)
1501 mPresContext->FrameConstructor()->CreateNeededFrames();
1503 // Process non-animation restyles...
1504 NS_ABORT_IF_FALSE(!mPresContext->IsProcessingRestyles(),
1505 "Nesting calls to ProcessPendingRestyles?");
1506 mPresContext->SetProcessingRestyles(true);
1508 // Before we process any restyles, we need to ensure that style
1509 // resulting from any throttled animations (animations that we're
1510 // running entirely on the compositor thread) is up-to-date, so that
1511 // if any style changes we cause trigger transitions, we have the
1512 // correct old style for starting the transition.
1513 if (nsLayoutUtils::AreAsyncAnimationsEnabled() &&
1514 mPendingRestyles.Count() > 0) {
1515 ++mAnimationGeneration;
1516 UpdateOnlyAnimationStyles();
1519 mPendingRestyles.ProcessRestyles();
1521 #ifdef DEBUG
1522 uint32_t oldPendingRestyleCount = mPendingRestyles.Count();
1523 #endif
1525 // ...and then process animation restyles. This needs to happen
1526 // second because we need to start animations that resulted from the
1527 // first set of restyles (e.g., CSS transitions with negative
1528 // transition-delay), and because we need to immediately
1529 // restyle-with-animation any just-restyled elements that are
1530 // mid-transition (since processing the non-animation restyle ignores
1531 // the running transition so it can check for a new change on the same
1532 // property, and then posts an immediate animation style change).
1533 mPresContext->SetProcessingAnimationStyleChange(true);
1534 mPendingAnimationRestyles.ProcessRestyles();
1535 mPresContext->SetProcessingAnimationStyleChange(false);
1537 mPresContext->SetProcessingRestyles(false);
1538 NS_POSTCONDITION(mPendingRestyles.Count() == oldPendingRestyleCount,
1539 "We should not have posted new non-animation restyles while "
1540 "processing animation restyles");
1542 if (mRebuildAllStyleData) {
1543 // We probably wasted a lot of work up above, but this seems safest
1544 // and it should be rarely used.
1545 // This might add us as a refresh observer again; that's ok.
1546 RebuildAllStyleData(nsChangeHint(0));
1550 void
1551 RestyleManager::BeginProcessingRestyles()
1553 // Make sure to not rebuild quote or counter lists while we're
1554 // processing restyles
1555 mPresContext->FrameConstructor()->BeginUpdate();
1557 mInStyleRefresh = true;
1560 void
1561 RestyleManager::EndProcessingRestyles()
1563 FlushOverflowChangedTracker();
1565 // Set mInStyleRefresh to false now, since the EndUpdate call might
1566 // add more restyles.
1567 mInStyleRefresh = false;
1569 mPresContext->FrameConstructor()->EndUpdate();
1571 #ifdef DEBUG
1572 mPresContext->PresShell()->VerifyStyleTree();
1573 #endif
1576 void
1577 RestyleManager::UpdateOnlyAnimationStyles()
1579 TimeStamp now = mPresContext->RefreshDriver()->MostRecentRefresh();
1580 if (mLastUpdateForThrottledAnimations == now) {
1581 return;
1583 mLastUpdateForThrottledAnimations = now;
1585 nsTransitionManager* transitionManager = mPresContext->TransitionManager();
1586 nsAnimationManager* animationManager = mPresContext->AnimationManager();
1588 transitionManager->SetInAnimationOnlyStyleUpdate(true);
1590 RestyleTracker tracker(ELEMENT_HAS_PENDING_ANIMATION_ONLY_RESTYLE |
1591 ELEMENT_IS_POTENTIAL_ANIMATION_ONLY_RESTYLE_ROOT);
1592 tracker.Init(this);
1594 // FIXME: We should have the transition manager and animation manager
1595 // add only the elements for which animations are currently throttled
1596 // (i.e., animating on the compositor with main-thread style updates
1597 // suppressed).
1598 transitionManager->AddStyleUpdatesTo(tracker);
1599 animationManager->AddStyleUpdatesTo(tracker);
1601 tracker.ProcessRestyles();
1603 transitionManager->SetInAnimationOnlyStyleUpdate(false);
1606 void
1607 RestyleManager::PostRestyleEventCommon(Element* aElement,
1608 nsRestyleHint aRestyleHint,
1609 nsChangeHint aMinChangeHint,
1610 bool aForAnimation)
1612 if (MOZ_UNLIKELY(mPresContext->PresShell()->IsDestroying())) {
1613 return;
1616 if (aRestyleHint == 0 && !aMinChangeHint) {
1617 // Nothing to do here
1618 return;
1621 RestyleTracker& tracker =
1622 aForAnimation ? mPendingAnimationRestyles : mPendingRestyles;
1623 tracker.AddPendingRestyle(aElement, aRestyleHint, aMinChangeHint);
1625 PostRestyleEventInternal(false);
1628 void
1629 RestyleManager::PostRestyleEventInternal(bool aForLazyConstruction)
1631 // Make sure we're not in a style refresh; if we are, we still have
1632 // a call to ProcessPendingRestyles coming and there's no need to
1633 // add ourselves as a refresh observer until then.
1634 bool inRefresh = !aForLazyConstruction && mInStyleRefresh;
1635 nsIPresShell* presShell = mPresContext->PresShell();
1636 if (!mObservingRefreshDriver && !inRefresh) {
1637 mObservingRefreshDriver = mPresContext->RefreshDriver()->
1638 AddStyleFlushObserver(presShell);
1641 // Unconditionally flag our document as needing a flush. The other
1642 // option here would be a dedicated boolean to track whether we need
1643 // to do so (set here and unset in ProcessPendingRestyles).
1644 presShell->GetDocument()->SetNeedStyleFlush();
1647 void
1648 RestyleManager::PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint)
1650 NS_ASSERTION(!(aExtraHint & nsChangeHint_ReconstructFrame),
1651 "Should not reconstruct the root of the frame tree. "
1652 "Use ReconstructDocElementHierarchy instead.");
1654 mRebuildAllStyleData = true;
1655 NS_UpdateHint(mRebuildAllExtraHint, aExtraHint);
1657 // Get a restyle event posted if necessary
1658 PostRestyleEventInternal(false);
1661 #ifdef DEBUG
1662 static void
1663 DumpContext(nsIFrame* aFrame, nsStyleContext* aContext)
1665 if (aFrame) {
1666 fputs("frame: ", stdout);
1667 nsAutoString name;
1668 aFrame->GetFrameName(name);
1669 fputs(NS_LossyConvertUTF16toASCII(name).get(), stdout);
1670 fprintf(stdout, " (%p)", static_cast<void*>(aFrame));
1672 if (aContext) {
1673 fprintf(stdout, " style: %p ", static_cast<void*>(aContext));
1675 nsIAtom* pseudoTag = aContext->GetPseudo();
1676 if (pseudoTag) {
1677 nsAutoString buffer;
1678 pseudoTag->ToString(buffer);
1679 fputs(NS_LossyConvertUTF16toASCII(buffer).get(), stdout);
1680 fputs(" ", stdout);
1682 fputs("{}\n", stdout);
1686 static void
1687 VerifySameTree(nsStyleContext* aContext1, nsStyleContext* aContext2)
1689 nsStyleContext* top1 = aContext1;
1690 nsStyleContext* top2 = aContext2;
1691 nsStyleContext* parent;
1692 for (;;) {
1693 parent = top1->GetParent();
1694 if (!parent)
1695 break;
1696 top1 = parent;
1698 for (;;) {
1699 parent = top2->GetParent();
1700 if (!parent)
1701 break;
1702 top2 = parent;
1704 NS_ASSERTION(top1 == top2,
1705 "Style contexts are not in the same style context tree");
1708 static void
1709 VerifyContextParent(nsPresContext* aPresContext, nsIFrame* aFrame,
1710 nsStyleContext* aContext, nsStyleContext* aParentContext)
1712 // get the contexts not provided
1713 if (!aContext) {
1714 aContext = aFrame->StyleContext();
1717 if (!aParentContext) {
1718 // Get the correct parent context from the frame
1719 // - if the frame is a placeholder, we get the out of flow frame's context
1720 // as the parent context instead of asking the frame
1722 // get the parent context from the frame (indirectly)
1723 nsIFrame* providerFrame = aFrame->GetParentStyleContextFrame();
1724 if (providerFrame)
1725 aParentContext = providerFrame->StyleContext();
1726 // aParentContext could still be null
1729 NS_ASSERTION(aContext, "Failure to get required contexts");
1730 nsStyleContext* actualParentContext = aContext->GetParent();
1732 if (aParentContext) {
1733 if (aParentContext != actualParentContext) {
1734 DumpContext(aFrame, aContext);
1735 if (aContext == aParentContext) {
1736 NS_ERROR("Using parent's style context");
1738 else {
1739 NS_ERROR("Wrong parent style context");
1740 fputs("Wrong parent style context: ", stdout);
1741 DumpContext(nullptr, actualParentContext);
1742 fputs("should be using: ", stdout);
1743 DumpContext(nullptr, aParentContext);
1744 VerifySameTree(actualParentContext, aParentContext);
1745 fputs("\n", stdout);
1750 else {
1751 if (actualParentContext) {
1752 NS_ERROR("Have parent context and shouldn't");
1753 DumpContext(aFrame, aContext);
1754 fputs("Has parent context: ", stdout);
1755 DumpContext(nullptr, actualParentContext);
1756 fputs("Should be null\n\n", stdout);
1760 nsStyleContext* childStyleIfVisited = aContext->GetStyleIfVisited();
1761 // Either childStyleIfVisited has aContext->GetParent()->GetStyleIfVisited()
1762 // as the parent or it has a different rulenode from aContext _and_ has
1763 // aContext->GetParent() as the parent.
1764 if (childStyleIfVisited &&
1765 !((childStyleIfVisited->RuleNode() != aContext->RuleNode() &&
1766 childStyleIfVisited->GetParent() == aContext->GetParent()) ||
1767 childStyleIfVisited->GetParent() ==
1768 aContext->GetParent()->GetStyleIfVisited())) {
1769 NS_ERROR("Visited style has wrong parent");
1770 DumpContext(aFrame, aContext);
1771 fputs("\n", stdout);
1775 static void
1776 VerifyStyleTree(nsPresContext* aPresContext, nsIFrame* aFrame,
1777 nsStyleContext* aParentContext)
1779 nsStyleContext* context = aFrame->StyleContext();
1780 VerifyContextParent(aPresContext, aFrame, context, nullptr);
1782 nsIFrame::ChildListIterator lists(aFrame);
1783 for (; !lists.IsDone(); lists.Next()) {
1784 nsFrameList::Enumerator childFrames(lists.CurrentList());
1785 for (; !childFrames.AtEnd(); childFrames.Next()) {
1786 nsIFrame* child = childFrames.get();
1787 if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
1788 // only do frames that are in flow
1789 if (nsGkAtoms::placeholderFrame == child->GetType()) {
1790 // placeholder: first recurse and verify the out of flow frame,
1791 // then verify the placeholder's context
1792 nsIFrame* outOfFlowFrame =
1793 nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
1795 // recurse to out of flow frame, letting the parent context get resolved
1796 do {
1797 VerifyStyleTree(aPresContext, outOfFlowFrame, nullptr);
1798 } while ((outOfFlowFrame = outOfFlowFrame->GetNextContinuation()));
1800 // verify placeholder using the parent frame's context as
1801 // parent context
1802 VerifyContextParent(aPresContext, child, nullptr, nullptr);
1804 else { // regular frame
1805 VerifyStyleTree(aPresContext, child, nullptr);
1811 // do additional contexts
1812 int32_t contextIndex = 0;
1813 for (nsStyleContext* extraContext;
1814 (extraContext = aFrame->GetAdditionalStyleContext(contextIndex));
1815 ++contextIndex) {
1816 VerifyContextParent(aPresContext, aFrame, extraContext, context);
1820 void
1821 RestyleManager::DebugVerifyStyleTree(nsIFrame* aFrame)
1823 if (aFrame) {
1824 nsStyleContext* context = aFrame->StyleContext();
1825 nsStyleContext* parentContext = context->GetParent();
1826 VerifyStyleTree(mPresContext, aFrame, parentContext);
1830 #endif // DEBUG
1832 // aContent must be the content for the frame in question, which may be
1833 // :before/:after content
1834 /* static */ void
1835 RestyleManager::TryStartingTransition(nsPresContext* aPresContext,
1836 nsIContent* aContent,
1837 nsStyleContext* aOldStyleContext,
1838 nsRefPtr<nsStyleContext>*
1839 aNewStyleContext /* inout */)
1841 if (!aContent || !aContent->IsElement()) {
1842 return;
1845 // Notify the transition manager, and if it starts a transition,
1846 // it will give us back a transition-covering style rule which
1847 // we'll use to get *another* style context. We want to ignore
1848 // any already-running transitions, but cover up any that we're
1849 // currently starting with their start value so we don't start
1850 // them again for descendants that inherit that value.
1851 nsCOMPtr<nsIStyleRule> coverRule =
1852 aPresContext->TransitionManager()->StyleContextChanged(
1853 aContent->AsElement(), aOldStyleContext, *aNewStyleContext);
1854 if (coverRule) {
1855 nsCOMArray<nsIStyleRule> rules;
1856 rules.AppendObject(coverRule);
1857 *aNewStyleContext = aPresContext->StyleSet()->
1858 ResolveStyleByAddingRules(*aNewStyleContext, rules);
1862 static inline dom::Element*
1863 ElementForStyleContext(nsIContent* aParentContent,
1864 nsIFrame* aFrame,
1865 nsCSSPseudoElements::Type aPseudoType)
1867 // We don't expect XUL tree stuff here.
1868 NS_PRECONDITION(aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement ||
1869 aPseudoType == nsCSSPseudoElements::ePseudo_AnonBox ||
1870 aPseudoType < nsCSSPseudoElements::ePseudo_PseudoElementCount,
1871 "Unexpected pseudo");
1872 // XXX see the comments about the various element confusion in
1873 // ElementRestyler::Restyle.
1874 if (aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement) {
1875 return aFrame->GetContent()->AsElement();
1878 if (aPseudoType == nsCSSPseudoElements::ePseudo_AnonBox) {
1879 return nullptr;
1882 if (aPseudoType == nsCSSPseudoElements::ePseudo_firstLetter) {
1883 NS_ASSERTION(aFrame->GetType() == nsGkAtoms::letterFrame,
1884 "firstLetter pseudoTag without a nsFirstLetterFrame");
1885 nsBlockFrame* block = nsBlockFrame::GetNearestAncestorBlock(aFrame);
1886 return block->GetContent()->AsElement();
1889 if (aPseudoType == nsCSSPseudoElements::ePseudo_mozColorSwatch) {
1890 MOZ_ASSERT(aFrame->GetParent() &&
1891 aFrame->GetParent()->GetParent(),
1892 "Color swatch frame should have a parent & grandparent");
1894 nsIFrame* grandparentFrame = aFrame->GetParent()->GetParent();
1895 MOZ_ASSERT(grandparentFrame->GetType() == nsGkAtoms::colorControlFrame,
1896 "Color swatch's grandparent should be nsColorControlFrame");
1898 return grandparentFrame->GetContent()->AsElement();
1901 if (aPseudoType == nsCSSPseudoElements::ePseudo_mozNumberText ||
1902 aPseudoType == nsCSSPseudoElements::ePseudo_mozNumberWrapper ||
1903 aPseudoType == nsCSSPseudoElements::ePseudo_mozNumberSpinBox ||
1904 aPseudoType == nsCSSPseudoElements::ePseudo_mozNumberSpinUp ||
1905 aPseudoType == nsCSSPseudoElements::ePseudo_mozNumberSpinDown) {
1906 // Get content for nearest nsNumberControlFrame:
1907 nsIFrame* f = aFrame->GetParent();
1908 MOZ_ASSERT(f);
1909 while (f->GetType() != nsGkAtoms::numberControlFrame) {
1910 f = f->GetParent();
1911 MOZ_ASSERT(f);
1913 return f->GetContent()->AsElement();
1916 if (aParentContent) {
1917 return aParentContent->AsElement();
1920 MOZ_ASSERT(aFrame->GetContent()->GetParent(),
1921 "should not have got here for the root element");
1922 return aFrame->GetContent()->GetParent()->AsElement();
1926 * FIXME: Temporary. Should merge with following function.
1928 static nsIFrame*
1929 GetPrevContinuationWithPossiblySameStyle(nsIFrame* aFrame)
1931 // Account for {ib} splits when looking for "prevContinuation". In
1932 // particular, for the first-continuation of a part of an {ib} split
1933 // we want to use the previous ib-split sibling of the previous
1934 // ib-split sibling of aFrame, which should have the same style
1935 // context as aFrame itself. In particular, if aFrame is the first
1936 // continuation of an inline part of a block-in-inline split then its
1937 // previous ib-split sibling is a block, and the previous ib-split
1938 // sibling of _that_ is an inline, just like aFrame. Similarly, if
1939 // aFrame is the first continuation of a block part of an
1940 // block-in-inline split (a block-in-inline wrapper block), then its
1941 // previous ib-split sibling is an inline and the previous ib-split
1942 // sibling of that is either another block-in-inline wrapper block box
1943 // or null.
1944 nsIFrame *prevContinuation = aFrame->GetPrevContinuation();
1945 if (!prevContinuation &&
1946 (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
1947 // We're the first continuation, so we can just get the frame
1948 // property directly
1949 prevContinuation = static_cast<nsIFrame*>(
1950 aFrame->Properties().Get(nsIFrame::IBSplitPrevSibling()));
1951 if (prevContinuation) {
1952 prevContinuation = static_cast<nsIFrame*>(
1953 prevContinuation->Properties().Get(nsIFrame::IBSplitPrevSibling()));
1957 NS_ASSERTION(!prevContinuation ||
1958 prevContinuation->GetContent() == aFrame->GetContent(),
1959 "unexpected content mismatch");
1961 return prevContinuation;
1965 * Get the previous continuation or similar ib-split sibling (assuming
1966 * block/inline alternation), conditionally on it having the same style.
1967 * This assumes that we're not between resolving the two (i.e., that
1968 * they're both already resolved.
1970 static nsIFrame*
1971 GetPrevContinuationWithSameStyle(nsIFrame* aFrame)
1973 nsIFrame* prevContinuation = GetPrevContinuationWithPossiblySameStyle(aFrame);
1974 if (!prevContinuation) {
1975 return nullptr;
1978 nsStyleContext* prevStyle = prevContinuation->StyleContext();
1979 nsStyleContext* selfStyle = aFrame->StyleContext();
1980 if (prevStyle != selfStyle) {
1981 NS_ASSERTION(prevStyle->GetPseudo() != selfStyle->GetPseudo() ||
1982 prevStyle->GetParent() != selfStyle->GetParent(),
1983 "continuations should have the same style context");
1984 prevContinuation = nullptr;
1986 return prevContinuation;
1990 * Get the next continuation or similar ib-split sibling (assuming
1991 * block/inline alternation), conditionally on it having the same style.
1993 * Since this is used when deciding to copy the new style context, it
1994 * takes as an argument the old style context to check if the style is
1995 * the same. When it is used in other contexts (i.e., where the next
1996 * continuation would already have the new style context), the current
1997 * style context should be passed.
1999 static nsIFrame*
2000 GetNextContinuationWithSameStyle(nsIFrame* aFrame,
2001 nsStyleContext* aOldStyleContext,
2002 bool* aHaveMoreContinuations = nullptr)
2004 // See GetPrevContinuationWithSameStyle about {ib} splits.
2006 nsIFrame *nextContinuation = aFrame->GetNextContinuation();
2007 if (!nextContinuation &&
2008 (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
2009 // We're the last continuation, so we have to hop back to the first
2010 // before getting the frame property
2011 nextContinuation = static_cast<nsIFrame*>(aFrame->FirstContinuation()->
2012 Properties().Get(nsIFrame::IBSplitSibling()));
2013 if (nextContinuation) {
2014 nextContinuation = static_cast<nsIFrame*>(
2015 nextContinuation->Properties().Get(nsIFrame::IBSplitSibling()));
2019 if (!nextContinuation) {
2020 return nullptr;
2023 NS_ASSERTION(nextContinuation->GetContent() == aFrame->GetContent(),
2024 "unexpected content mismatch");
2026 nsStyleContext* nextStyle = nextContinuation->StyleContext();
2027 if (nextStyle != aOldStyleContext) {
2028 NS_ASSERTION(aOldStyleContext->GetPseudo() != nextStyle->GetPseudo() ||
2029 aOldStyleContext->GetParent() != nextStyle->GetParent(),
2030 "continuations should have the same style context");
2031 nextContinuation = nullptr;
2032 if (aHaveMoreContinuations) {
2033 *aHaveMoreContinuations = true;
2036 return nextContinuation;
2039 nsresult
2040 RestyleManager::ReparentStyleContext(nsIFrame* aFrame)
2042 if (nsGkAtoms::placeholderFrame == aFrame->GetType()) {
2043 // Also reparent the out-of-flow and all its continuations.
2044 nsIFrame* outOfFlow =
2045 nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame);
2046 NS_ASSERTION(outOfFlow, "no out-of-flow frame");
2047 do {
2048 ReparentStyleContext(outOfFlow);
2049 } while ((outOfFlow = outOfFlow->GetNextContinuation()));
2052 // DO NOT verify the style tree before reparenting. The frame
2053 // tree has already been changed, so this check would just fail.
2054 nsStyleContext* oldContext = aFrame->StyleContext();
2056 nsRefPtr<nsStyleContext> newContext;
2057 nsIFrame* providerFrame = aFrame->GetParentStyleContextFrame();
2058 bool isChild = providerFrame && providerFrame->GetParent() == aFrame;
2059 nsStyleContext* newParentContext = nullptr;
2060 nsIFrame* providerChild = nullptr;
2061 if (isChild) {
2062 ReparentStyleContext(providerFrame);
2063 newParentContext = providerFrame->StyleContext();
2064 providerChild = providerFrame;
2065 } else if (providerFrame) {
2066 newParentContext = providerFrame->StyleContext();
2067 } else {
2068 NS_NOTREACHED("Reparenting something that has no usable parent? "
2069 "Shouldn't happen!");
2071 // XXX need to do something here to produce the correct style context for
2072 // an IB split whose first inline part is inside a first-line frame.
2073 // Currently the first IB anonymous block's style context takes the first
2074 // part's style context as parent, which is wrong since first-line style
2075 // should not apply to the anonymous block.
2077 #ifdef DEBUG
2079 // Check that our assumption that continuations of the same
2080 // pseudo-type and with the same style context parent have the
2081 // same style context is valid before the reresolution. (We need
2082 // to check the pseudo-type and style context parent because of
2083 // :first-letter and :first-line, where we create styled and
2084 // unstyled letter/line frames distinguished by pseudo-type, and
2085 // then need to distinguish their descendants based on having
2086 // different parents.)
2087 nsIFrame *nextContinuation = aFrame->GetNextContinuation();
2088 if (nextContinuation) {
2089 nsStyleContext *nextContinuationContext =
2090 nextContinuation->StyleContext();
2091 NS_ASSERTION(oldContext == nextContinuationContext ||
2092 oldContext->GetPseudo() !=
2093 nextContinuationContext->GetPseudo() ||
2094 oldContext->GetParent() !=
2095 nextContinuationContext->GetParent(),
2096 "continuations should have the same style context");
2099 #endif
2101 nsIFrame *prevContinuation =
2102 GetPrevContinuationWithPossiblySameStyle(aFrame);
2103 nsStyleContext *prevContinuationContext;
2104 bool copyFromContinuation =
2105 prevContinuation &&
2106 (prevContinuationContext = prevContinuation->StyleContext())
2107 ->GetPseudo() == oldContext->GetPseudo() &&
2108 prevContinuationContext->GetParent() == newParentContext;
2109 if (copyFromContinuation) {
2110 // Just use the style context from the frame's previous
2111 // continuation (see assertion about aFrame->GetNextContinuation()
2112 // above, which we would have previously hit for aFrame's previous
2113 // continuation).
2114 newContext = prevContinuationContext;
2115 } else {
2116 nsIFrame* parentFrame = aFrame->GetParent();
2117 Element* element =
2118 ElementForStyleContext(parentFrame ? parentFrame->GetContent() : nullptr,
2119 aFrame,
2120 oldContext->GetPseudoType());
2121 nsIContent* pseudoElementContent = aFrame->GetContent();
2122 Element* pseudoElement =
2123 (pseudoElementContent && pseudoElementContent->IsElement())
2124 ? pseudoElementContent->AsElement() : nullptr;
2125 newContext = mPresContext->StyleSet()->
2126 ReparentStyleContext(oldContext, newParentContext, element,
2127 pseudoElement);
2130 if (newContext) {
2131 if (newContext != oldContext) {
2132 // We probably don't want to initiate transitions from
2133 // ReparentStyleContext, since we call it during frame
2134 // construction rather than in response to dynamic changes.
2135 // Also see the comment at the start of
2136 // nsTransitionManager::ConsiderStartingTransition.
2137 #if 0
2138 if (!copyFromContinuation) {
2139 TryStartingTransition(mPresContext, aFrame->GetContent(),
2140 oldContext, &newContext);
2142 #endif
2144 // Make sure to call CalcStyleDifference so that the new context ends
2145 // up resolving all the structs the old context resolved.
2146 if (!copyFromContinuation) {
2147 DebugOnly<nsChangeHint> styleChange =
2148 oldContext->CalcStyleDifference(newContext, nsChangeHint(0));
2149 // The style change is always 0 because we have the same rulenode and
2150 // CalcStyleDifference optimizes us away. That's OK, though:
2151 // reparenting should never trigger a frame reconstruct, and whenever
2152 // it's happening we already plan to reflow and repaint the frames.
2153 NS_ASSERTION(!(styleChange & nsChangeHint_ReconstructFrame),
2154 "Our frame tree is likely to be bogus!");
2157 aFrame->SetStyleContext(newContext);
2159 nsIFrame::ChildListIterator lists(aFrame);
2160 for (; !lists.IsDone(); lists.Next()) {
2161 nsFrameList::Enumerator childFrames(lists.CurrentList());
2162 for (; !childFrames.AtEnd(); childFrames.Next()) {
2163 nsIFrame* child = childFrames.get();
2164 // only do frames that are in flow
2165 if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
2166 child != providerChild) {
2167 #ifdef DEBUG
2168 if (nsGkAtoms::placeholderFrame == child->GetType()) {
2169 nsIFrame* outOfFlowFrame =
2170 nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
2171 NS_ASSERTION(outOfFlowFrame, "no out-of-flow frame");
2173 NS_ASSERTION(outOfFlowFrame != providerChild,
2174 "Out of flow provider?");
2176 #endif
2177 ReparentStyleContext(child);
2182 // If this frame is part of an IB split, then the style context of
2183 // the next part of the split might be a child of our style context.
2184 // Reparent its style context just in case one of our ancestors
2185 // (split or not) hasn't done so already). It's not a problem to
2186 // reparent the same frame twice because the "if (newContext !=
2187 // oldContext)" check will prevent us from redoing work.
2188 if ((aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) &&
2189 !aFrame->GetPrevContinuation()) {
2190 nsIFrame* sib = static_cast<nsIFrame*>
2191 (aFrame->Properties().Get(nsIFrame::IBSplitSibling()));
2192 if (sib) {
2193 ReparentStyleContext(sib);
2197 // do additional contexts
2198 int32_t contextIndex = 0;
2199 for (nsStyleContext* oldExtraContext;
2200 (oldExtraContext = aFrame->GetAdditionalStyleContext(contextIndex));
2201 ++contextIndex) {
2202 nsRefPtr<nsStyleContext> newExtraContext;
2203 newExtraContext = mPresContext->StyleSet()->
2204 ReparentStyleContext(oldExtraContext,
2205 newContext, nullptr, nullptr);
2206 if (newExtraContext) {
2207 if (newExtraContext != oldExtraContext) {
2208 // Make sure to call CalcStyleDifference so that the new
2209 // context ends up resolving all the structs the old context
2210 // resolved.
2211 DebugOnly<nsChangeHint> styleChange =
2212 oldExtraContext->CalcStyleDifference(newExtraContext,
2213 nsChangeHint(0));
2214 // The style change is always 0 because we have the same
2215 // rulenode and CalcStyleDifference optimizes us away. That's
2216 // OK, though: reparenting should never trigger a frame
2217 // reconstruct, and whenever it's happening we already plan to
2218 // reflow and repaint the frames.
2219 NS_ASSERTION(!(styleChange & nsChangeHint_ReconstructFrame),
2220 "Our frame tree is likely to be bogus!");
2223 aFrame->SetAdditionalStyleContext(contextIndex, newExtraContext);
2226 #ifdef DEBUG
2227 VerifyStyleTree(mPresContext, aFrame, newParentContext);
2228 #endif
2232 return NS_OK;
2235 ElementRestyler::ElementRestyler(nsPresContext* aPresContext,
2236 nsIFrame* aFrame,
2237 nsStyleChangeList* aChangeList,
2238 nsChangeHint aHintsHandledByAncestors,
2239 RestyleTracker& aRestyleTracker,
2240 TreeMatchContext& aTreeMatchContext,
2241 nsTArray<nsIContent*>&
2242 aVisibleKidsOfHiddenElement)
2243 : mPresContext(aPresContext)
2244 , mFrame(aFrame)
2245 , mParentContent(nullptr)
2246 // XXXldb Why does it make sense to use aParentContent? (See
2247 // comment above assertion at start of ElementRestyler::Restyle.)
2248 , mContent(mFrame->GetContent() ? mFrame->GetContent() : mParentContent)
2249 , mChangeList(aChangeList)
2250 , mHintsHandled(NS_SubtractHint(aHintsHandledByAncestors,
2251 NS_HintsNotHandledForDescendantsIn(aHintsHandledByAncestors)))
2252 , mParentFrameHintsNotHandledForDescendants(nsChangeHint(0))
2253 , mHintsNotHandledForDescendants(nsChangeHint(0))
2254 , mRestyleTracker(aRestyleTracker)
2255 , mTreeMatchContext(aTreeMatchContext)
2256 , mResolvedChild(nullptr)
2257 #ifdef ACCESSIBILITY
2258 , mDesiredA11yNotifications(eSendAllNotifications)
2259 , mKidsDesiredA11yNotifications(mDesiredA11yNotifications)
2260 , mOurA11yNotification(eDontNotify)
2261 , mVisibleKidsOfHiddenElement(aVisibleKidsOfHiddenElement)
2262 #endif
2266 ElementRestyler::ElementRestyler(const ElementRestyler& aParentRestyler,
2267 nsIFrame* aFrame,
2268 uint32_t aConstructorFlags)
2269 : mPresContext(aParentRestyler.mPresContext)
2270 , mFrame(aFrame)
2271 , mParentContent(aParentRestyler.mContent)
2272 // XXXldb Why does it make sense to use aParentContent? (See
2273 // comment above assertion at start of ElementRestyler::Restyle.)
2274 , mContent(mFrame->GetContent() ? mFrame->GetContent() : mParentContent)
2275 , mChangeList(aParentRestyler.mChangeList)
2276 , mHintsHandled(NS_SubtractHint(aParentRestyler.mHintsHandled,
2277 NS_HintsNotHandledForDescendantsIn(aParentRestyler.mHintsHandled)))
2278 , mParentFrameHintsNotHandledForDescendants(
2279 aParentRestyler.mHintsNotHandledForDescendants)
2280 , mHintsNotHandledForDescendants(nsChangeHint(0))
2281 , mRestyleTracker(aParentRestyler.mRestyleTracker)
2282 , mTreeMatchContext(aParentRestyler.mTreeMatchContext)
2283 , mResolvedChild(nullptr)
2284 #ifdef ACCESSIBILITY
2285 , mDesiredA11yNotifications(aParentRestyler.mKidsDesiredA11yNotifications)
2286 , mKidsDesiredA11yNotifications(mDesiredA11yNotifications)
2287 , mOurA11yNotification(eDontNotify)
2288 , mVisibleKidsOfHiddenElement(aParentRestyler.mVisibleKidsOfHiddenElement)
2289 #endif
2291 if (aConstructorFlags & FOR_OUT_OF_FLOW_CHILD) {
2292 // Note that the out-of-flow may not be a geometric descendant of
2293 // the frame where we started the reresolve. Therefore, even if
2294 // mHintsHandled already includes nsChangeHint_AllReflowHints we
2295 // don't want to pass that on to the out-of-flow reresolve, since
2296 // that can lead to the out-of-flow not getting reflowed when it
2297 // should be (eg a reresolve starting at <body> that involves
2298 // reflowing the <body> would miss reflowing fixed-pos nodes that
2299 // also need reflow). In the cases when the out-of-flow _is_ a
2300 // geometric descendant of a frame we already have a reflow hint
2301 // for, reflow coalescing should keep us from doing the work twice.
2302 mHintsHandled = NS_SubtractHint(mHintsHandled, nsChangeHint_AllReflowHints);
2306 ElementRestyler::ElementRestyler(ParentContextFromChildFrame,
2307 const ElementRestyler& aParentRestyler,
2308 nsIFrame* aFrame)
2309 : mPresContext(aParentRestyler.mPresContext)
2310 , mFrame(aFrame)
2311 , mParentContent(aParentRestyler.mParentContent)
2312 // XXXldb Why does it make sense to use aParentContent? (See
2313 // comment above assertion at start of ElementRestyler::Restyle.)
2314 , mContent(mFrame->GetContent() ? mFrame->GetContent() : mParentContent)
2315 , mChangeList(aParentRestyler.mChangeList)
2316 , mHintsHandled(NS_SubtractHint(aParentRestyler.mHintsHandled,
2317 NS_HintsNotHandledForDescendantsIn(aParentRestyler.mHintsHandled)))
2318 , mParentFrameHintsNotHandledForDescendants(
2319 // assume the worst
2320 nsChangeHint_Hints_NotHandledForDescendants)
2321 , mHintsNotHandledForDescendants(nsChangeHint(0))
2322 , mRestyleTracker(aParentRestyler.mRestyleTracker)
2323 , mTreeMatchContext(aParentRestyler.mTreeMatchContext)
2324 , mResolvedChild(nullptr)
2325 #ifdef ACCESSIBILITY
2326 , mDesiredA11yNotifications(aParentRestyler.mDesiredA11yNotifications)
2327 , mKidsDesiredA11yNotifications(mDesiredA11yNotifications)
2328 , mOurA11yNotification(eDontNotify)
2329 , mVisibleKidsOfHiddenElement(aParentRestyler.mVisibleKidsOfHiddenElement)
2330 #endif
2334 void
2335 ElementRestyler::CaptureChange(nsStyleContext* aOldContext,
2336 nsStyleContext* aNewContext,
2337 nsChangeHint aChangeToAssume)
2339 // Check some invariants about replacing one style context with another.
2340 NS_ASSERTION(aOldContext->GetPseudo() == aNewContext->GetPseudo(),
2341 "old and new style contexts should have the same pseudo");
2342 NS_ASSERTION(aOldContext->GetPseudoType() == aNewContext->GetPseudoType(),
2343 "old and new style contexts should have the same pseudo");
2345 nsChangeHint ourChange = aOldContext->CalcStyleDifference(aNewContext,
2346 mParentFrameHintsNotHandledForDescendants);
2347 NS_ASSERTION(!(ourChange & nsChangeHint_AllReflowHints) ||
2348 (ourChange & nsChangeHint_NeedReflow),
2349 "Reflow hint bits set without actually asking for a reflow");
2351 // nsChangeHint_UpdateEffects is inherited, but it can be set due to changes
2352 // in inherited properties (fill and stroke). Avoid propagating it into
2353 // text nodes.
2354 if ((ourChange & nsChangeHint_UpdateEffects) &&
2355 mContent && !mContent->IsElement()) {
2356 ourChange = NS_SubtractHint(ourChange, nsChangeHint_UpdateEffects);
2359 NS_UpdateHint(ourChange, aChangeToAssume);
2360 if (NS_UpdateHint(mHintsHandled, ourChange)) {
2361 if (!(ourChange & nsChangeHint_ReconstructFrame) || mContent) {
2362 mChangeList->AppendChange(mFrame, mContent, ourChange);
2365 NS_UpdateHint(mHintsNotHandledForDescendants,
2366 NS_HintsNotHandledForDescendantsIn(ourChange));
2370 * Recompute style for mFrame (which should not have a prev continuation
2371 * with the same style), all of its next continuations with the same
2372 * style, and all ib-split siblings of the same type (either block or
2373 * inline, skipping the intermediates of the other type) and accumulate
2374 * changes into mChangeList given that mHintsHandled is already accumulated
2375 * for an ancestor.
2376 * mParentContent is the content node used to resolve the parent style
2377 * context. This means that, for pseudo-elements, it is the content
2378 * that should be used for selector matching (rather than the fake
2379 * content node attached to the frame).
2381 void
2382 ElementRestyler::Restyle(nsRestyleHint aRestyleHint)
2384 // It would be nice if we could make stronger assertions here; they
2385 // would let us simplify the ?: expressions below setting |content|
2386 // and |pseudoContent| in sensible ways as well as making what
2387 // |content| and |pseudoContent| mean, and their relationship to
2388 // |mFrame->GetContent()|, make more sense. However, we can't,
2389 // because of frame trees like the one in
2390 // https://bugzilla.mozilla.org/show_bug.cgi?id=472353#c14 . Once we
2391 // fix bug 242277 we should be able to make this make more sense.
2392 NS_ASSERTION(mFrame->GetContent() || !mParentContent ||
2393 !mParentContent->GetParent(),
2394 "frame must have content (unless at the top of the tree)");
2396 NS_ASSERTION(!GetPrevContinuationWithSameStyle(mFrame),
2397 "should not be trying to restyle this frame separately");
2399 MOZ_ASSERT(!(aRestyleHint & eRestyle_LaterSiblings),
2400 "eRestyle_LaterSiblings must not be part of aRestyleHint");
2402 nsRestyleHint hintToRestore = nsRestyleHint(0);
2403 if (mContent && mContent->IsElement() &&
2404 // If we're we're resolving from the root of the frame tree (which
2405 // we do in DoRebuildAllStyleData), we need to avoid getting the
2406 // root's restyle data until we get to its primary frame, since
2407 // it's the primary frame that has the styles for the root element
2408 // (rather than the ancestors of the primary frame whose mContent
2409 // is the root node but which have different styles). If we use
2410 // up the hint for one of the ancestors that we hit first, then
2411 // we'll fail to do the restyling we need to do.
2412 (mContent->GetParent() || mContent->GetPrimaryFrame() == mFrame)) {
2413 mContent->OwnerDoc()->FlushPendingLinkUpdates();
2414 RestyleTracker::RestyleData restyleData;
2415 if (mRestyleTracker.GetRestyleData(mContent->AsElement(), &restyleData)) {
2416 if (NS_UpdateHint(mHintsHandled, restyleData.mChangeHint)) {
2417 mChangeList->AppendChange(mFrame, mContent, restyleData.mChangeHint);
2419 hintToRestore = restyleData.mRestyleHint;
2420 aRestyleHint = nsRestyleHint(aRestyleHint | restyleData.mRestyleHint);
2424 nsRestyleHint childRestyleHint = nsRestyleHint(aRestyleHint & eRestyle_Subtree);
2427 nsRefPtr<nsStyleContext> oldContext = mFrame->StyleContext();
2429 // TEMPORARY (until bug 918064): Call RestyleSelf for each
2430 // continuation or block-in-inline sibling.
2432 bool haveMoreContinuations = false;
2433 for (nsIFrame* f = mFrame; f;
2434 f = GetNextContinuationWithSameStyle(f, oldContext,
2435 &haveMoreContinuations)) {
2436 RestyleSelf(f, aRestyleHint);
2439 if (haveMoreContinuations && hintToRestore) {
2440 // If we have more continuations with different style (e.g., because
2441 // we're inside a ::first-letter or ::first-line), put the restyle
2442 // hint back.
2443 mRestyleTracker.AddPendingRestyleToTable(mContent->AsElement(),
2444 hintToRestore, nsChangeHint(0));
2448 RestyleChildren(childRestyleHint);
2451 void
2452 ElementRestyler::RestyleSelf(nsIFrame* aSelf, nsRestyleHint aRestyleHint)
2454 MOZ_ASSERT(!(aRestyleHint & eRestyle_LaterSiblings),
2455 "eRestyle_LaterSiblings must not be part of aRestyleHint");
2457 // XXXldb get new context from prev-in-flow if possible, to avoid
2458 // duplication. (Or should we just let |GetContext| handle that?)
2459 // Getting the hint would be nice too, but that's harder.
2461 // XXXbryner we may be able to avoid some of the refcounting goop here.
2462 // We do need a reference to oldContext for the lifetime of this function, and it's possible
2463 // that the frame has the last reference to it, so AddRef it here.
2465 nsChangeHint assumeDifferenceHint = NS_STYLE_HINT_NONE;
2466 nsRefPtr<nsStyleContext> oldContext = aSelf->StyleContext();
2467 nsStyleSet* styleSet = mPresContext->StyleSet();
2469 #ifdef ACCESSIBILITY
2470 mWasFrameVisible = nsIPresShell::IsAccessibilityActive() ?
2471 oldContext->StyleVisibility()->IsVisible() : false;
2472 #endif
2474 nsIAtom* const pseudoTag = oldContext->GetPseudo();
2475 const nsCSSPseudoElements::Type pseudoType = oldContext->GetPseudoType();
2477 nsStyleContext* parentContext;
2478 // Get the frame providing the parent style context. If it is a
2479 // child, then resolve the provider first.
2480 nsIFrame* providerFrame = aSelf->GetParentStyleContextFrame();
2481 bool isChild = providerFrame && providerFrame->GetParent() == aSelf;
2482 if (!isChild) {
2483 if (providerFrame)
2484 parentContext = providerFrame->StyleContext();
2485 else
2486 parentContext = nullptr;
2488 else {
2489 MOZ_ASSERT(providerFrame->GetContent() == aSelf->GetContent(),
2490 "Postcondition for GetParentStyleContextFrame() violated. "
2491 "That means we need to add the current element to the "
2492 "ancestor filter.");
2494 // resolve the provider here (before aSelf below).
2496 // assumeDifferenceHint forces the parent's change to be also
2497 // applied to this frame, no matter what
2498 // nsStyleContext::CalcStyleDifference says. CalcStyleDifference
2499 // can't be trusted because it assumes any changes to the parent
2500 // style context provider will be automatically propagated to
2501 // the frame(s) with child style contexts.
2503 ElementRestyler providerRestyler(PARENT_CONTEXT_FROM_CHILD_FRAME,
2504 *this, providerFrame);
2505 providerRestyler.Restyle(aRestyleHint);
2506 assumeDifferenceHint = providerRestyler.HintsHandledForFrame();
2508 // The provider's new context becomes the parent context of
2509 // aSelf's context.
2510 parentContext = providerFrame->StyleContext();
2511 // Set |mResolvedChild| so we don't bother resolving the
2512 // provider again.
2513 mResolvedChild = providerFrame;
2516 if (providerFrame != aSelf->GetParent()) {
2517 // We don't actually know what the parent style context's
2518 // non-inherited hints were, so assume the worst.
2519 mParentFrameHintsNotHandledForDescendants =
2520 nsChangeHint_Hints_NotHandledForDescendants;
2523 // do primary context
2524 nsRefPtr<nsStyleContext> newContext;
2525 nsIFrame *prevContinuation =
2526 GetPrevContinuationWithPossiblySameStyle(aSelf);
2527 nsStyleContext *prevContinuationContext;
2528 bool copyFromContinuation =
2529 prevContinuation &&
2530 (prevContinuationContext = prevContinuation->StyleContext())
2531 ->GetPseudo() == oldContext->GetPseudo() &&
2532 prevContinuationContext->GetParent() == parentContext;
2533 if (copyFromContinuation) {
2534 // Just use the style context from the frame's previous
2535 // continuation.
2536 newContext = prevContinuationContext;
2538 else if (pseudoTag == nsCSSAnonBoxes::mozNonElement) {
2539 NS_ASSERTION(aSelf->GetContent(),
2540 "non pseudo-element frame without content node");
2541 newContext = styleSet->ResolveStyleForNonElement(parentContext);
2543 else if (!(aRestyleHint & (eRestyle_Self | eRestyle_Subtree))) {
2544 Element* element = ElementForStyleContext(mParentContent, aSelf, pseudoType);
2545 if (aRestyleHint == nsRestyleHint(0) &&
2546 !styleSet->IsInRuleTreeReconstruct()) {
2547 nsIContent* pseudoElementContent = aSelf->GetContent();
2548 Element* pseudoElement =
2549 (pseudoElementContent && pseudoElementContent->IsElement())
2550 ? pseudoElementContent->AsElement() : nullptr;
2551 newContext =
2552 styleSet->ReparentStyleContext(oldContext, parentContext, element,
2553 pseudoElement);
2554 } else {
2555 // Use ResolveStyleWithReplacement either for actual replacements
2556 // or, with no replacements, as a substitute for
2557 // ReparentStyleContext that rebuilds the path in the rule tree
2558 // rather than reusing the rule node, as we need to do during a
2559 // rule tree reconstruct.
2560 newContext =
2561 styleSet->ResolveStyleWithReplacement(element, parentContext, oldContext,
2562 aRestyleHint);
2564 } else if (pseudoType == nsCSSPseudoElements::ePseudo_AnonBox) {
2565 newContext = styleSet->ResolveAnonymousBoxStyle(pseudoTag,
2566 parentContext);
2568 else {
2569 Element* element = ElementForStyleContext(mParentContent, aSelf, pseudoType);
2570 if (pseudoTag) {
2571 if (pseudoTag == nsCSSPseudoElements::before ||
2572 pseudoTag == nsCSSPseudoElements::after) {
2573 // XXX what other pseudos do we need to treat like this?
2574 newContext = styleSet->ProbePseudoElementStyle(element,
2575 pseudoType,
2576 parentContext,
2577 mTreeMatchContext);
2578 if (!newContext) {
2579 // This pseudo should no longer exist; gotta reframe
2580 NS_UpdateHint(mHintsHandled, nsChangeHint_ReconstructFrame);
2581 mChangeList->AppendChange(aSelf, element,
2582 nsChangeHint_ReconstructFrame);
2583 // We're reframing anyway; just keep the same context
2584 newContext = oldContext;
2586 } else {
2587 // Don't expect XUL tree stuff here, since it needs a comparator and
2588 // all.
2589 NS_ASSERTION(pseudoType <
2590 nsCSSPseudoElements::ePseudo_PseudoElementCount,
2591 "Unexpected pseudo type");
2592 Element* pseudoElement =
2593 nsCSSPseudoElements::PseudoElementSupportsStyleAttribute(pseudoType) ||
2594 nsCSSPseudoElements::PseudoElementSupportsUserActionState(pseudoType) ?
2595 aSelf->GetContent()->AsElement() : nullptr;
2596 MOZ_ASSERT(element != pseudoElement);
2597 newContext = styleSet->ResolvePseudoElementStyle(element,
2598 pseudoType,
2599 parentContext,
2600 pseudoElement);
2603 else {
2604 NS_ASSERTION(aSelf->GetContent(),
2605 "non pseudo-element frame without content node");
2606 // Skip parent display based style fixup for anonymous subtrees:
2607 TreeMatchContext::AutoParentDisplayBasedStyleFixupSkipper
2608 parentDisplayBasedFixupSkipper(mTreeMatchContext,
2609 element->IsRootOfNativeAnonymousSubtree());
2610 newContext = styleSet->ResolveStyleFor(element, parentContext,
2611 mTreeMatchContext);
2615 MOZ_ASSERT(newContext);
2617 if (!parentContext) {
2618 if (oldContext->RuleNode() == newContext->RuleNode() &&
2619 oldContext->IsLinkContext() == newContext->IsLinkContext() &&
2620 oldContext->RelevantLinkVisited() ==
2621 newContext->RelevantLinkVisited()) {
2622 // We're the root of the style context tree and the new style
2623 // context returned has the same rule node. This means that
2624 // we can use FindChildWithRules to keep a lot of the old
2625 // style contexts around. However, we need to start from the
2626 // same root.
2627 newContext = oldContext;
2631 if (newContext != oldContext) {
2632 if (!copyFromContinuation) {
2633 RestyleManager::TryStartingTransition(mPresContext, aSelf->GetContent(),
2634 oldContext, &newContext);
2636 CaptureChange(oldContext, newContext, assumeDifferenceHint);
2639 if (!(mHintsHandled & nsChangeHint_ReconstructFrame)) {
2640 // If the frame gets regenerated, let it keep its old context,
2641 // which is important to maintain various invariants about
2642 // frame types matching their style contexts.
2643 // Note that this check even makes sense if we didn't call
2644 // CaptureChange because of copyFromContinuation being true,
2645 // since we'll have copied the existing context from the
2646 // previous continuation, so newContext == oldContext.
2647 aSelf->SetStyleContext(newContext);
2650 oldContext = nullptr;
2652 // do additional contexts
2653 // XXXbz might be able to avoid selector matching here in some
2654 // cases; won't worry about it for now.
2655 int32_t contextIndex = 0;
2656 for (nsStyleContext* oldExtraContext;
2657 (oldExtraContext = aSelf->GetAdditionalStyleContext(contextIndex));
2658 ++contextIndex) {
2659 nsRefPtr<nsStyleContext> newExtraContext;
2660 nsIAtom* const extraPseudoTag = oldExtraContext->GetPseudo();
2661 const nsCSSPseudoElements::Type extraPseudoType =
2662 oldExtraContext->GetPseudoType();
2663 NS_ASSERTION(extraPseudoTag &&
2664 extraPseudoTag != nsCSSAnonBoxes::mozNonElement,
2665 "extra style context is not pseudo element");
2666 if (!(aRestyleHint & (eRestyle_Self | eRestyle_Subtree))) {
2667 Element* element = extraPseudoType != nsCSSPseudoElements::ePseudo_AnonBox
2668 ? mContent->AsElement() : nullptr;
2669 if (styleSet->IsInRuleTreeReconstruct()) {
2670 // Use ResolveStyleWithReplacement as a substitute for
2671 // ReparentStyleContext that rebuilds the path in the rule tree
2672 // rather than reusing the rule node, as we need to do during a
2673 // rule tree reconstruct.
2674 newExtraContext =
2675 styleSet->ResolveStyleWithReplacement(element, newContext,
2676 oldExtraContext,
2677 nsRestyleHint(0));
2678 } else {
2679 nsIContent* pseudoElementContent = aSelf->GetContent();
2680 Element* pseudoElement =
2681 (pseudoElementContent && pseudoElementContent->IsElement())
2682 ? pseudoElementContent->AsElement() : nullptr;
2683 newExtraContext =
2684 styleSet->ReparentStyleContext(oldExtraContext, newContext, element,
2685 pseudoElement);
2687 } else if (extraPseudoType == nsCSSPseudoElements::ePseudo_AnonBox) {
2688 newExtraContext = styleSet->ResolveAnonymousBoxStyle(extraPseudoTag,
2689 newContext);
2690 } else {
2691 // Don't expect XUL tree stuff here, since it needs a comparator and
2692 // all.
2693 NS_ASSERTION(extraPseudoType <
2694 nsCSSPseudoElements::ePseudo_PseudoElementCount,
2695 "Unexpected type");
2696 newExtraContext = styleSet->ResolvePseudoElementStyle(mContent->AsElement(),
2697 extraPseudoType,
2698 newContext,
2699 nullptr);
2702 MOZ_ASSERT(newExtraContext);
2704 if (oldExtraContext != newExtraContext) {
2705 CaptureChange(oldExtraContext, newExtraContext, assumeDifferenceHint);
2706 if (!(mHintsHandled & nsChangeHint_ReconstructFrame)) {
2707 aSelf->SetAdditionalStyleContext(contextIndex, newExtraContext);
2713 void
2714 ElementRestyler::RestyleChildren(nsRestyleHint aChildRestyleHint)
2716 // We'd like style resolution to be exact in the sense that an
2717 // animation-only style flush flushes only the styles it requests
2718 // flushing and doesn't update any other styles. This means avoiding
2719 // constructing new frames during such a flush.
2721 // For a ::before or ::after, we'll do an eRestyle_Subtree due to
2722 // RestyleHintForOp in nsCSSRuleProcessor.cpp (via its
2723 // HasAttributeDependentStyle or HasStateDependentStyle), given that
2724 // we store pseudo-elements in selectors like they were children.
2726 // Also, it's faster to skip the work we do on undisplayed children
2727 // and pseudo-elements when we can skip it.
2728 bool mightReframePseudos = aChildRestyleHint & eRestyle_Subtree;
2730 RestyleUndisplayedChildren(aChildRestyleHint);
2732 // Check whether we might need to create a new ::before frame.
2733 // There's no need to do this if we're planning to reframe already
2734 // or if we're not forcing restyles on kids.
2735 // It's also important to check mHintsHandled since we use
2736 // mFrame->StyleContext(), which is out of date if mHintsHandled has a
2737 // ReconstructFrame hint. Using an out of date style context could
2738 // trigger assertions about mismatched rule trees.
2739 if (!(mHintsHandled & nsChangeHint_ReconstructFrame) &&
2740 mightReframePseudos) {
2741 MaybeReframeForBeforePseudo();
2744 // There is no need to waste time crawling into a frame's children
2745 // on a frame change. The act of reconstructing frames will force
2746 // new style contexts to be resolved on all of this frame's
2747 // descendants anyway, so we want to avoid wasting time processing
2748 // style contexts that we're just going to throw away anyway. - dwh
2749 // It's also important to check mHintsHandled since reresolving the
2750 // kids would use mFrame->StyleContext(), which is out of date if
2751 // mHintsHandled has a ReconstructFrame hint; doing this could trigger
2752 // assertions about mismatched rule trees.
2753 nsIFrame *lastContinuation;
2754 if (!(mHintsHandled & nsChangeHint_ReconstructFrame)) {
2755 InitializeAccessibilityNotifications();
2757 for (nsIFrame* f = mFrame; f;
2758 f = GetNextContinuationWithSameStyle(f, f->StyleContext())) {
2759 lastContinuation = f;
2760 RestyleContentChildren(f, aChildRestyleHint);
2763 SendAccessibilityNotifications();
2766 // Check whether we might need to create a new ::after frame.
2767 // See comments above regarding :before.
2768 if (!(mHintsHandled & nsChangeHint_ReconstructFrame) &&
2769 mightReframePseudos) {
2770 MaybeReframeForAfterPseudo(lastContinuation);
2774 void
2775 ElementRestyler::RestyleUndisplayedChildren(nsRestyleHint aChildRestyleHint)
2777 // When the root element is display:none, we still construct *some*
2778 // frames that have the root element as their mContent, down to the
2779 // DocElementContainingBlock.
2780 bool checkUndisplayed;
2781 nsIContent* undisplayedParent;
2782 nsCSSFrameConstructor* frameConstructor = mPresContext->FrameConstructor();
2783 if (mFrame->StyleContext()->GetPseudo()) {
2784 checkUndisplayed = mFrame == frameConstructor->
2785 GetDocElementContainingBlock();
2786 undisplayedParent = nullptr;
2787 } else {
2788 checkUndisplayed = !!mFrame->GetContent();
2789 undisplayedParent = mFrame->GetContent();
2791 if (checkUndisplayed &&
2792 // No need to do this if we're planning to reframe already.
2793 // It's also important to check mHintsHandled since we use
2794 // mFrame->StyleContext(), which is out of date if mHintsHandled
2795 // has a ReconstructFrame hint. Using an out of date style
2796 // context could trigger assertions about mismatched rule trees.
2797 !(mHintsHandled & nsChangeHint_ReconstructFrame)) {
2798 UndisplayedNode* undisplayed =
2799 frameConstructor->GetAllUndisplayedContentIn(undisplayedParent);
2800 TreeMatchContext::AutoAncestorPusher pusher(mTreeMatchContext);
2801 if (undisplayed) {
2802 pusher.PushAncestorAndStyleScope(undisplayedParent);
2804 for (; undisplayed; undisplayed = undisplayed->mNext) {
2805 NS_ASSERTION(undisplayedParent ||
2806 undisplayed->mContent ==
2807 mPresContext->Document()->GetRootElement(),
2808 "undisplayed node child of null must be root");
2809 NS_ASSERTION(!undisplayed->mStyle->GetPseudo(),
2810 "Shouldn't have random pseudo style contexts in the "
2811 "undisplayed map");
2813 // Get the parent of the undisplayed content and check if it is a XBL
2814 // children element. Push the children element as an ancestor here because it does
2815 // not have a frame and would not otherwise be pushed as an ancestor.
2816 nsIContent* parent = undisplayed->mContent->GetParent();
2817 TreeMatchContext::AutoAncestorPusher insertionPointPusher(mTreeMatchContext);
2818 if (parent && nsContentUtils::IsContentInsertionPoint(parent)) {
2819 insertionPointPusher.PushAncestorAndStyleScope(parent);
2822 nsRestyleHint thisChildHint = aChildRestyleHint;
2823 RestyleTracker::RestyleData undisplayedRestyleData;
2824 Element* element = undisplayed->mContent->AsElement();
2825 if (mRestyleTracker.GetRestyleData(element,
2826 &undisplayedRestyleData)) {
2827 thisChildHint =
2828 nsRestyleHint(thisChildHint | undisplayedRestyleData.mRestyleHint);
2830 nsRefPtr<nsStyleContext> undisplayedContext;
2831 nsStyleSet* styleSet = mPresContext->StyleSet();
2832 if (thisChildHint & (eRestyle_Self | eRestyle_Subtree)) {
2833 undisplayedContext =
2834 styleSet->ResolveStyleFor(element,
2835 mFrame->StyleContext(),
2836 mTreeMatchContext);
2837 } else if (thisChildHint ||
2838 styleSet->IsInRuleTreeReconstruct()) {
2839 // Use ResolveStyleWithReplacement either for actual
2840 // replacements, or as a substitute for ReparentStyleContext
2841 // that rebuilds the path in the rule tree rather than reusing
2842 // the rule node, as we need to do during a rule tree
2843 // reconstruct.
2844 undisplayedContext =
2845 styleSet->ResolveStyleWithReplacement(element,
2846 mFrame->StyleContext(),
2847 undisplayed->mStyle,
2848 thisChildHint);
2849 } else {
2850 undisplayedContext =
2851 styleSet->ReparentStyleContext(undisplayed->mStyle,
2852 mFrame->StyleContext(),
2853 element, element);
2855 const nsStyleDisplay* display = undisplayedContext->StyleDisplay();
2856 if (display->mDisplay != NS_STYLE_DISPLAY_NONE) {
2857 NS_ASSERTION(undisplayed->mContent,
2858 "Must have undisplayed content");
2859 mChangeList->AppendChange(nullptr, undisplayed->mContent,
2860 NS_STYLE_HINT_FRAMECHANGE);
2861 // The node should be removed from the undisplayed map when
2862 // we reframe it.
2863 } else {
2864 // update the undisplayed node with the new context
2865 undisplayed->mStyle = undisplayedContext;
2871 void
2872 ElementRestyler::MaybeReframeForBeforePseudo()
2874 // Make sure not to do this for pseudo-frames or frames that
2875 // can't have generated content.
2876 nsContainerFrame* cif;
2877 if (!mFrame->StyleContext()->GetPseudo() &&
2878 ((mFrame->GetStateBits() & NS_FRAME_MAY_HAVE_GENERATED_CONTENT) ||
2879 // Our content insertion frame might have gotten flagged
2880 ((cif = mFrame->GetContentInsertionFrame()) &&
2881 (cif->GetStateBits() & NS_FRAME_MAY_HAVE_GENERATED_CONTENT)))) {
2882 // Check for a new :before pseudo and an existing :before
2883 // frame, but only if the frame is the first continuation.
2884 nsIFrame* prevContinuation = mFrame->GetPrevContinuation();
2885 if (!prevContinuation) {
2886 // Checking for a :before frame is cheaper than getting the
2887 // :before style context.
2888 if (!nsLayoutUtils::GetBeforeFrame(mFrame) &&
2889 nsLayoutUtils::HasPseudoStyle(mFrame->GetContent(),
2890 mFrame->StyleContext(),
2891 nsCSSPseudoElements::ePseudo_before,
2892 mPresContext)) {
2893 // Have to create the new :before frame
2894 NS_UpdateHint(mHintsHandled, nsChangeHint_ReconstructFrame);
2895 mChangeList->AppendChange(mFrame, mContent,
2896 nsChangeHint_ReconstructFrame);
2903 * aFrame is the last continuation or block-in-inline sibling that this
2904 * ElementRestyler is restyling.
2906 void
2907 ElementRestyler::MaybeReframeForAfterPseudo(nsIFrame* aFrame)
2909 // Make sure not to do this for pseudo-frames or frames that
2910 // can't have generated content.
2911 nsContainerFrame* cif;
2912 if (!aFrame->StyleContext()->GetPseudo() &&
2913 ((aFrame->GetStateBits() & NS_FRAME_MAY_HAVE_GENERATED_CONTENT) ||
2914 // Our content insertion frame might have gotten flagged
2915 ((cif = aFrame->GetContentInsertionFrame()) &&
2916 (cif->GetStateBits() & NS_FRAME_MAY_HAVE_GENERATED_CONTENT)))) {
2917 // Check for new :after content, but only if the frame is the
2918 // last continuation.
2919 nsIFrame* nextContinuation = aFrame->GetNextContinuation();
2921 if (!nextContinuation) {
2922 // Getting the :after frame is more expensive than getting the pseudo
2923 // context, so get the pseudo context first.
2924 if (nsLayoutUtils::HasPseudoStyle(aFrame->GetContent(),
2925 aFrame->StyleContext(),
2926 nsCSSPseudoElements::ePseudo_after,
2927 mPresContext) &&
2928 !nsLayoutUtils::GetAfterFrame(aFrame)) {
2929 // have to create the new :after frame
2930 NS_UpdateHint(mHintsHandled, nsChangeHint_ReconstructFrame);
2931 mChangeList->AppendChange(aFrame, mContent,
2932 nsChangeHint_ReconstructFrame);
2938 void
2939 ElementRestyler::InitializeAccessibilityNotifications()
2941 #ifdef ACCESSIBILITY
2942 // Notify a11y for primary frame only if it's a root frame of visibility
2943 // changes or its parent frame was hidden while it stays visible and
2944 // it is not inside a {ib} split or is the first frame of {ib} split.
2945 if (nsIPresShell::IsAccessibilityActive() &&
2946 !mFrame->GetPrevContinuation() &&
2947 !mFrame->FrameIsNonFirstInIBSplit()) {
2948 if (mDesiredA11yNotifications == eSendAllNotifications) {
2949 bool isFrameVisible = mFrame->StyleVisibility()->IsVisible();
2950 if (isFrameVisible != mWasFrameVisible) {
2951 if (isFrameVisible) {
2952 // Notify a11y the element (perhaps with its children) was shown.
2953 // We don't fall into this case if this element gets or stays shown
2954 // while its parent becomes hidden.
2955 mKidsDesiredA11yNotifications = eSkipNotifications;
2956 mOurA11yNotification = eNotifyShown;
2957 } else {
2958 // The element is being hidden; its children may stay visible, or
2959 // become visible after being hidden previously. If we'll find
2960 // visible children then we should notify a11y about that as if
2961 // they were inserted into tree. Notify a11y this element was
2962 // hidden.
2963 mKidsDesiredA11yNotifications = eNotifyIfShown;
2964 mOurA11yNotification = eNotifyHidden;
2967 } else if (mDesiredA11yNotifications == eNotifyIfShown &&
2968 mFrame->StyleVisibility()->IsVisible()) {
2969 // Notify a11y that element stayed visible while its parent was
2970 // hidden.
2971 mVisibleKidsOfHiddenElement.AppendElement(mFrame->GetContent());
2972 mKidsDesiredA11yNotifications = eSkipNotifications;
2975 #endif
2978 void
2979 ElementRestyler::RestyleContentChildren(nsIFrame* aParent,
2980 nsRestyleHint aChildRestyleHint)
2982 nsIFrame::ChildListIterator lists(aParent);
2983 TreeMatchContext::AutoAncestorPusher ancestorPusher(mTreeMatchContext);
2984 if (!lists.IsDone()) {
2985 ancestorPusher.PushAncestorAndStyleScope(mContent);
2987 for (; !lists.IsDone(); lists.Next()) {
2988 nsFrameList::Enumerator childFrames(lists.CurrentList());
2989 for (; !childFrames.AtEnd(); childFrames.Next()) {
2990 nsIFrame* child = childFrames.get();
2991 // Out-of-flows are reached through their placeholders. Continuations
2992 // and block-in-inline splits are reached through those chains.
2993 if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
2994 !GetPrevContinuationWithSameStyle(child)) {
2995 // Get the parent of the child frame's content and check if it
2996 // is a XBL children element. Push the children element as an
2997 // ancestor here because it does not have a frame and would not
2998 // otherwise be pushed as an ancestor.
3000 // Check if the frame has a content because |child| may be a
3001 // nsPageFrame that does not have a content.
3002 nsIContent* parent = child->GetContent() ? child->GetContent()->GetParent() : nullptr;
3003 TreeMatchContext::AutoAncestorPusher insertionPointPusher(mTreeMatchContext);
3004 if (parent && nsContentUtils::IsContentInsertionPoint(parent)) {
3005 insertionPointPusher.PushAncestorAndStyleScope(parent);
3008 // only do frames that are in flow
3009 if (nsGkAtoms::placeholderFrame == child->GetType()) { // placeholder
3010 // get out of flow frame and recur there
3011 nsIFrame* outOfFlowFrame =
3012 nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
3013 NS_ASSERTION(outOfFlowFrame, "no out-of-flow frame");
3014 NS_ASSERTION(outOfFlowFrame != mResolvedChild,
3015 "out-of-flow frame not a true descendant");
3017 // |nsFrame::GetParentStyleContextFrame| checks being out
3018 // of flow so that this works correctly.
3019 do {
3020 if (GetPrevContinuationWithSameStyle(outOfFlowFrame)) {
3021 // Later continuations are likely restyled as a result of
3022 // the restyling of the previous continuation.
3023 // (Currently that's always true, but it's likely to
3024 // change if we implement overflow:fragments or similar.)
3025 continue;
3027 ElementRestyler oofRestyler(*this, outOfFlowFrame,
3028 FOR_OUT_OF_FLOW_CHILD);
3029 oofRestyler.Restyle(aChildRestyleHint);
3030 } while ((outOfFlowFrame = outOfFlowFrame->GetNextContinuation()));
3032 // reresolve placeholder's context under the same parent
3033 // as the out-of-flow frame
3034 ElementRestyler phRestyler(*this, child, 0);
3035 phRestyler.Restyle(aChildRestyleHint);
3037 else { // regular child frame
3038 if (child != mResolvedChild) {
3039 ElementRestyler childRestyler(*this, child, 0);
3040 childRestyler.Restyle(aChildRestyleHint);
3046 // XXX need to do overflow frames???
3049 void
3050 ElementRestyler::SendAccessibilityNotifications()
3052 #ifdef ACCESSIBILITY
3053 // Send notifications about visibility changes.
3054 if (mOurA11yNotification == eNotifyShown) {
3055 nsAccessibilityService* accService = nsIPresShell::AccService();
3056 if (accService) {
3057 nsIPresShell* presShell = mFrame->PresContext()->GetPresShell();
3058 nsIContent* content = mFrame->GetContent();
3060 accService->ContentRangeInserted(presShell, content->GetParent(),
3061 content,
3062 content->GetNextSibling());
3064 } else if (mOurA11yNotification == eNotifyHidden) {
3065 nsAccessibilityService* accService = nsIPresShell::AccService();
3066 if (accService) {
3067 nsIPresShell* presShell = mFrame->PresContext()->GetPresShell();
3068 nsIContent* content = mFrame->GetContent();
3069 accService->ContentRemoved(presShell, content->GetParent(), content);
3071 // Process children staying shown.
3072 uint32_t visibleContentCount = mVisibleKidsOfHiddenElement.Length();
3073 for (uint32_t idx = 0; idx < visibleContentCount; idx++) {
3074 nsIContent* childContent = mVisibleKidsOfHiddenElement[idx];
3075 accService->ContentRangeInserted(presShell, childContent->GetParent(),
3076 childContent,
3077 childContent->GetNextSibling());
3079 mVisibleKidsOfHiddenElement.Clear();
3082 #endif
3085 static inline nsIFrame*
3086 GetNextBlockInInlineSibling(FramePropertyTable* aPropTable, nsIFrame* aFrame)
3088 NS_ASSERTION(!aFrame->GetPrevContinuation(),
3089 "must start with the first continuation");
3090 // Might we have ib-split siblings?
3091 if (!(aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
3092 // nothing more to do here
3093 return nullptr;
3096 return static_cast<nsIFrame*>
3097 (aPropTable->Get(aFrame, nsIFrame::IBSplitSibling()));
3100 void
3101 RestyleManager::ComputeAndProcessStyleChange(nsIFrame* aFrame,
3102 nsChangeHint aMinChange,
3103 RestyleTracker& aRestyleTracker,
3104 nsRestyleHint aRestyleHint)
3106 // Create a ReframingStyleContexts struct on the stack and put it in
3107 // our mReframingStyleContexts for the scope of this function.
3108 MOZ_ASSERT(!mReframingStyleContexts, "shouldn't call recursively");
3109 AutoRestore<ReframingStyleContexts*> ar(mReframingStyleContexts);
3110 ReframingStyleContexts reframingStyleContexts;
3111 mReframingStyleContexts = &reframingStyleContexts;
3113 nsStyleChangeList changeList;
3114 ComputeStyleChangeFor(aFrame, &changeList, aMinChange,
3115 aRestyleTracker, aRestyleHint);
3116 ProcessRestyledFrames(changeList);
3119 void
3120 RestyleManager::ComputeStyleChangeFor(nsIFrame* aFrame,
3121 nsStyleChangeList* aChangeList,
3122 nsChangeHint aMinChange,
3123 RestyleTracker& aRestyleTracker,
3124 nsRestyleHint aRestyleHint)
3126 PROFILER_LABEL("RestyleManager", "ComputeStyleChangeFor",
3127 js::ProfileEntry::Category::CSS);
3129 nsIContent *content = aFrame->GetContent();
3130 if (aMinChange) {
3131 aChangeList->AppendChange(aFrame, content, aMinChange);
3134 NS_ASSERTION(!aFrame->GetPrevContinuation(),
3135 "must start with the first continuation");
3137 // We want to start with this frame and walk all its next-in-flows,
3138 // as well as all its ib-split siblings and their next-in-flows,
3139 // reresolving style on all the frames we encounter in this walk that
3140 // we didn't reach already. In the normal case, this will mean only
3141 // restyling the first two block-in-inline splits and no
3142 // continuations, and skipping everything else. However, when we have
3143 // a style change targeted at an element inside a context where styles
3144 // vary between continuations (e.g., a style change on an element that
3145 // extends from inside a styled ::first-line to outside of that first
3146 // line), we might restyle more than that.
3148 FramePropertyTable* propTable = mPresContext->PropertyTable();
3150 TreeMatchContext treeMatchContext(true,
3151 nsRuleWalker::eRelevantLinkUnvisited,
3152 mPresContext->Document());
3153 Element* parent =
3154 content ? content->GetParentElementCrossingShadowRoot() : nullptr;
3155 treeMatchContext.InitAncestors(parent);
3156 nsTArray<nsIContent*> visibleKidsOfHiddenElement;
3157 for (nsIFrame* ibSibling = aFrame; ibSibling;
3158 ibSibling = GetNextBlockInInlineSibling(propTable, ibSibling)) {
3159 // Outer loop over ib-split siblings
3160 for (nsIFrame* cont = ibSibling; cont; cont = cont->GetNextContinuation()) {
3161 if (GetPrevContinuationWithSameStyle(cont)) {
3162 // We already handled this element when dealing with its earlier
3163 // continuation.
3164 continue;
3167 // Inner loop over next-in-flows of the current frame
3168 ElementRestyler restyler(mPresContext, cont, aChangeList,
3169 aMinChange, aRestyleTracker,
3170 treeMatchContext,
3171 visibleKidsOfHiddenElement);
3173 restyler.Restyle(aRestyleHint);
3175 if (restyler.HintsHandledForFrame() & nsChangeHint_ReconstructFrame) {
3176 // If it's going to cause a framechange, then don't bother
3177 // with the continuations or ib-split siblings since they'll be
3178 // clobbered by the frame reconstruct anyway.
3179 NS_ASSERTION(!cont->GetPrevContinuation(),
3180 "continuing frame had more severe impact than first-in-flow");
3181 return;
3187 } // namespace mozilla