--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -106,934 +106,16 @@ RestyleManager::RestyleManager(nsPresCon
}
void
RestyleManager::NotifyDestroyingFrame(nsIFrame* aFrame)
{
mOverflowChangedTracker.RemoveFrame(aFrame);
}
-#ifdef DEBUG
- // To ensure that the functions below are only called within
- // |ApplyRenderingChangeToTree|.
-static bool gInApplyRenderingChangeToTree = false;
-#endif
-
-static inline nsIFrame*
-GetNextBlockInInlineSibling(FramePropertyTable* aPropTable, nsIFrame* aFrame)
-{
- NS_ASSERTION(!aFrame->GetPrevContinuation(),
- "must start with the first continuation");
- // Might we have ib-split siblings?
- if (!(aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
- // nothing more to do here
- return nullptr;
- }
-
- return static_cast<nsIFrame*>
- (aPropTable->Get(aFrame, nsIFrame::IBSplitSibling()));
-}
-
-static nsIFrame*
-GetNearestAncestorFrame(nsIContent* aContent)
-{
- nsIFrame* ancestorFrame = nullptr;
- for (nsIContent* ancestor = aContent->GetParent();
- ancestor && !ancestorFrame;
- ancestor = ancestor->GetParent()) {
- ancestorFrame = ancestor->GetPrimaryFrame();
- }
- return ancestorFrame;
-}
-
-static void
-DoApplyRenderingChangeToTree(nsIFrame* aFrame,
- nsChangeHint aChange);
-
-/**
- * Sync views on aFrame and all of aFrame's descendants (following placeholders),
- * if aChange has nsChangeHint_SyncFrameView.
- * Calls DoApplyRenderingChangeToTree on all aFrame's out-of-flow descendants
- * (following placeholders), if aChange has nsChangeHint_RepaintFrame.
- * aFrame should be some combination of nsChangeHint_SyncFrameView,
- * nsChangeHint_RepaintFrame, nsChangeHint_UpdateOpacityLayer and
- * nsChangeHint_SchedulePaint, nothing else.
-*/
-static void
-SyncViewsAndInvalidateDescendants(nsIFrame* aFrame,
- nsChangeHint aChange)
-{
- NS_PRECONDITION(gInApplyRenderingChangeToTree,
- "should only be called within ApplyRenderingChangeToTree");
- NS_ASSERTION(nsChangeHint_size_t(aChange) ==
- (aChange & (nsChangeHint_RepaintFrame |
- nsChangeHint_SyncFrameView |
- nsChangeHint_UpdateOpacityLayer |
- nsChangeHint_SchedulePaint)),
- "Invalid change flag");
-
- nsView* view = aFrame->GetView();
- if (view) {
- if (aChange & nsChangeHint_SyncFrameView) {
- nsContainerFrame::SyncFrameViewProperties(aFrame->PresContext(),
- aFrame, nullptr, view);
- }
- }
-
- nsIFrame::ChildListIterator lists(aFrame);
- for (; !lists.IsDone(); lists.Next()) {
- for (nsIFrame* child : lists.CurrentList()) {
- if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
- // only do frames that don't have placeholders
- if (nsGkAtoms::placeholderFrame == child->GetType()) {
- // do the out-of-flow frame and its continuations
- nsIFrame* outOfFlowFrame =
- nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
- DoApplyRenderingChangeToTree(outOfFlowFrame, aChange);
- } else if (lists.CurrentID() == nsIFrame::kPopupList) {
- DoApplyRenderingChangeToTree(child, aChange);
- } else { // regular frame
- SyncViewsAndInvalidateDescendants(child, aChange);
- }
- }
- }
- }
-}
-
-/**
- * To handle nsChangeHint_ChildrenOnlyTransform we must iterate over the child
- * frames of the SVG frame concerned. This helper function is used to find that
- * SVG frame when we encounter nsChangeHint_ChildrenOnlyTransform to ensure
- * that we iterate over the intended children, since sometimes we end up
- * handling that hint while processing hints for one of the SVG frame's
- * ancestor frames.
- *
- * The reason that we sometimes end up trying to process the hint for an
- * ancestor of the SVG frame that the hint is intended for is due to the way we
- * process restyle events. ApplyRenderingChangeToTree adjusts the frame from
- * the restyled element's principle frame to one of its ancestor frames based
- * on what nsCSSRendering::FindBackground returns, since the background style
- * may have been propagated up to an ancestor frame. Processing hints using an
- * ancestor frame is fine in general, but nsChangeHint_ChildrenOnlyTransform is
- * a special case since it is intended to update the children of a specific
- * frame.
- */
-static nsIFrame*
-GetFrameForChildrenOnlyTransformHint(nsIFrame* aFrame)
-{
- if (aFrame->GetType() == nsGkAtoms::viewportFrame) {
- // This happens if the root-<svg> is fixed positioned, in which case we
- // can't use aFrame->GetContent() to find the primary frame, since
- // GetContent() returns nullptr for ViewportFrame.
- aFrame = aFrame->PrincipalChildList().FirstChild();
- }
- // For an nsHTMLScrollFrame, this will get the SVG frame that has the
- // children-only transforms:
- aFrame = aFrame->GetContent()->GetPrimaryFrame();
- if (aFrame->GetType() == nsGkAtoms::svgOuterSVGFrame) {
- aFrame = aFrame->PrincipalChildList().FirstChild();
- MOZ_ASSERT(aFrame->GetType() == nsGkAtoms::svgOuterSVGAnonChildFrame,
- "Where is the nsSVGOuterSVGFrame's anon child??");
- }
- MOZ_ASSERT(aFrame->IsFrameOfType(nsIFrame::eSVG |
- nsIFrame::eSVGContainer),
- "Children-only transforms only expected on SVG frames");
- return aFrame;
-}
-
-static void
-DoApplyRenderingChangeToTree(nsIFrame* aFrame,
- nsChangeHint aChange)
-{
- NS_PRECONDITION(gInApplyRenderingChangeToTree,
- "should only be called within ApplyRenderingChangeToTree");
-
- for ( ; aFrame; aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame)) {
- // Invalidate and sync views on all descendant frames, following placeholders.
- // We don't need to update transforms in SyncViewsAndInvalidateDescendants, because
- // there can't be any out-of-flows or popups that need to be transformed;
- // all out-of-flow descendants of the transformed element must also be
- // descendants of the transformed frame.
- SyncViewsAndInvalidateDescendants(aFrame,
- nsChangeHint(aChange & (nsChangeHint_RepaintFrame |
- nsChangeHint_SyncFrameView |
- nsChangeHint_UpdateOpacityLayer |
- nsChangeHint_SchedulePaint)));
- // This must be set to true if the rendering change needs to
- // invalidate content. If it's false, a composite-only paint
- // (empty transaction) will be scheduled.
- bool needInvalidatingPaint = false;
-
- // if frame has view, will already be invalidated
- if (aChange & nsChangeHint_RepaintFrame) {
- // Note that this whole block will be skipped when painting is suppressed
- // (due to our caller ApplyRendingChangeToTree() discarding the
- // nsChangeHint_RepaintFrame hint). If you add handling for any other
- // hints within this block, be sure that they too should be ignored when
- // painting is suppressed.
- needInvalidatingPaint = true;
- aFrame->InvalidateFrameSubtree();
- if ((aChange & nsChangeHint_UpdateEffects) &&
- aFrame->IsFrameOfType(nsIFrame::eSVG) &&
- !(aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)) {
- // Need to update our overflow rects:
- nsSVGUtils::ScheduleReflowSVG(aFrame);
- }
- }
- if (aChange & nsChangeHint_UpdateTextPath) {
- if (aFrame->IsSVGText()) {
- // Invalidate and reflow the entire SVGTextFrame:
- NS_ASSERTION(aFrame->GetContent()->IsSVGElement(nsGkAtoms::textPath),
- "expected frame for a <textPath> element");
- nsIFrame* text = nsLayoutUtils::GetClosestFrameOfType(
- aFrame,
- nsGkAtoms::svgTextFrame);
- NS_ASSERTION(text, "expected to find an ancestor SVGTextFrame");
- static_cast<SVGTextFrame*>(text)->NotifyGlyphMetricsChange();
- } else {
- MOZ_ASSERT(false, "unexpected frame got nsChangeHint_UpdateTextPath");
- }
- }
- if (aChange & nsChangeHint_UpdateOpacityLayer) {
- // FIXME/bug 796697: we can get away with empty transactions for
- // opacity updates in many cases.
- needInvalidatingPaint = true;
-
- ActiveLayerTracker::NotifyRestyle(aFrame, eCSSProperty_opacity);
- if (nsSVGIntegrationUtils::UsingEffectsForFrame(aFrame)) {
- // SVG effects paints the opacity without using
- // nsDisplayOpacity. We need to invalidate manually.
- aFrame->InvalidateFrameSubtree();
- }
- }
- if ((aChange & nsChangeHint_UpdateTransformLayer) &&
- aFrame->IsTransformed()) {
- ActiveLayerTracker::NotifyRestyle(aFrame, eCSSProperty_transform);
- // If we're not already going to do an invalidating paint, see
- // if we can get away with only updating the transform on a
- // layer for this frame, and not scheduling an invalidating
- // paint.
- if (!needInvalidatingPaint) {
- Layer* layer;
- needInvalidatingPaint |= !aFrame->TryUpdateTransformOnly(&layer);
-
- if (!needInvalidatingPaint) {
- // Since we're not going to paint, we need to resend animation
- // data to the layer.
- MOZ_ASSERT(layer, "this can't happen if there's no layer");
- nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(layer,
- nullptr, nullptr, aFrame, eCSSProperty_transform);
- }
- }
- }
- if (aChange & nsChangeHint_ChildrenOnlyTransform) {
- needInvalidatingPaint = true;
- nsIFrame* childFrame =
- GetFrameForChildrenOnlyTransformHint(aFrame)->PrincipalChildList().FirstChild();
- for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
- ActiveLayerTracker::NotifyRestyle(childFrame, eCSSProperty_transform);
- }
- }
- if (aChange & nsChangeHint_SchedulePaint) {
- needInvalidatingPaint = true;
- }
- aFrame->SchedulePaint(needInvalidatingPaint ?
- nsIFrame::PAINT_DEFAULT :
- nsIFrame::PAINT_COMPOSITE_ONLY);
- }
-}
-
-static void
-ApplyRenderingChangeToTree(nsPresContext* aPresContext,
- nsIFrame* aFrame,
- nsChangeHint aChange)
-{
- // We check StyleDisplay()->HasTransformStyle() in addition to checking
- // IsTransformed() since we can get here for some frames that don't support
- // CSS transforms.
- NS_ASSERTION(!(aChange & nsChangeHint_UpdateTransformLayer) ||
- aFrame->IsTransformed() ||
- aFrame->StyleDisplay()->HasTransformStyle(),
- "Unexpected UpdateTransformLayer hint");
-
- nsIPresShell* shell = aPresContext->PresShell();
- if (shell->IsPaintingSuppressed()) {
- // Don't allow synchronous rendering changes when painting is turned off.
- aChange &= ~nsChangeHint_RepaintFrame;
- if (!aChange) {
- return;
- }
- }
-
- // Trigger rendering updates by damaging this frame and any
- // continuations of this frame.
-#ifdef DEBUG
- gInApplyRenderingChangeToTree = true;
-#endif
- if (aChange & nsChangeHint_RepaintFrame) {
- // If the frame's background is propagated to an ancestor, walk up to
- // that ancestor and apply the RepaintFrame change hint to it.
- nsStyleContext* bgSC;
- nsIFrame* propagatedFrame = aFrame;
- while (!nsCSSRendering::FindBackground(propagatedFrame, &bgSC)) {
- propagatedFrame = propagatedFrame->GetParent();
- NS_ASSERTION(aFrame, "root frame must paint");
- }
-
- if (propagatedFrame != aFrame) {
- DoApplyRenderingChangeToTree(propagatedFrame, nsChangeHint_RepaintFrame);
- aChange &= ~nsChangeHint_RepaintFrame;
- if (!aChange) {
- return;
- }
- }
- }
- DoApplyRenderingChangeToTree(aFrame, aChange);
-#ifdef DEBUG
- gInApplyRenderingChangeToTree = false;
-#endif
-}
-
-bool
-RestyleManager::RecomputePosition(nsIFrame* aFrame)
-{
- // Don't process position changes on table frames, since we already handle
- // the dynamic position change on the table wrapper frame, and the
- // reflow-based fallback code path also ignores positions on inner table
- // frames.
- if (aFrame->GetType() == nsGkAtoms::tableFrame) {
- return true;
- }
-
- const nsStyleDisplay* display = aFrame->StyleDisplay();
- // Changes to the offsets of a non-positioned element can safely be ignored.
- if (display->mPosition == NS_STYLE_POSITION_STATIC) {
- return true;
- }
-
- // Don't process position changes on frames which have views or the ones which
- // have a view somewhere in their descendants, because the corresponding view
- // needs to be repositioned properly as well.
- if (aFrame->HasView() ||
- (aFrame->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW)) {
- StyleChangeReflow(aFrame, nsChangeHint_NeedReflow);
- return false;
- }
-
- aFrame->SchedulePaint();
-
- // For relative positioning, we can simply update the frame rect
- if (display->IsRelativelyPositionedStyle()) {
- // Move the frame
- if (display->mPosition == NS_STYLE_POSITION_STICKY) {
- if (display->IsInnerTableStyle()) {
- // We don't currently support sticky positioning of inner table
- // elements (bug 975644). Bail.
- //
- // When this is fixed, remove the null-check for the computed
- // offsets in nsTableRowFrame::ReflowChildren.
- return true;
- }
-
- // Update sticky positioning for an entire element at once, starting with
- // the first continuation or ib-split sibling.
- // It's rare that the frame we already have isn't already the first
- // continuation or ib-split sibling, but it can happen when styles differ
- // across continuations such as ::first-line or ::first-letter, and in
- // those cases we will generally (but maybe not always) do the work twice.
- nsIFrame* firstContinuation =
- nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
-
- StickyScrollContainer::ComputeStickyOffsets(firstContinuation);
- StickyScrollContainer* ssc =
- StickyScrollContainer::GetStickyScrollContainerForFrame(firstContinuation);
- if (ssc) {
- ssc->PositionContinuations(firstContinuation);
- }
- } else {
- MOZ_ASSERT(NS_STYLE_POSITION_RELATIVE == display->mPosition,
- "Unexpected type of positioning");
- for (nsIFrame* cont = aFrame; cont;
- cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
- nsIFrame* cb = cont->GetContainingBlock();
- nsMargin newOffsets;
- WritingMode wm = cb->GetWritingMode();
- const LogicalSize size(wm, cb->GetContentRectRelativeToSelf().Size());
-
- ReflowInput::ComputeRelativeOffsets(wm, cont, size, newOffsets);
- NS_ASSERTION(newOffsets.left == -newOffsets.right &&
- newOffsets.top == -newOffsets.bottom,
- "ComputeRelativeOffsets should return valid results");
-
- // ReflowInput::ApplyRelativePositioning would work here, but
- // since we've already checked mPosition and aren't changing the frame's
- // normal position, go ahead and add the offsets directly.
- cont->SetPosition(cont->GetNormalPosition() +
- nsPoint(newOffsets.left, newOffsets.top));
- }
- }
-
- return true;
- }
-
- // For the absolute positioning case, set up a fake HTML reflow state for
- // the frame, and then get the offsets and size from it. If the frame's size
- // doesn't need to change, we can simply update the frame position. Otherwise
- // we fall back to a reflow.
- nsRenderingContext rc(
- aFrame->PresContext()->PresShell()->CreateReferenceRenderingContext());
-
- // Construct a bogus parent reflow state so that there's a usable
- // containing block reflow state.
- nsIFrame* parentFrame = aFrame->GetParent();
- WritingMode parentWM = parentFrame->GetWritingMode();
- WritingMode frameWM = aFrame->GetWritingMode();
- LogicalSize parentSize = parentFrame->GetLogicalSize();
-
- nsFrameState savedState = parentFrame->GetStateBits();
- ReflowInput parentReflowInput(aFrame->PresContext(), parentFrame,
- &rc, parentSize);
- parentFrame->RemoveStateBits(~nsFrameState(0));
- parentFrame->AddStateBits(savedState);
-
- // The bogus parent state here was created with no parent state of its own,
- // and therefore it won't have an mCBReflowInput set up.
- // But we may need one (for InitCBReflowInput in a child state), so let's
- // try to create one here for the cases where it will be needed.
- Maybe<ReflowInput> cbReflowInput;
- nsIFrame* cbFrame = parentFrame->GetContainingBlock();
- if (cbFrame && (aFrame->GetContainingBlock() != parentFrame ||
- parentFrame->GetType() == nsGkAtoms::tableFrame)) {
- LogicalSize cbSize = cbFrame->GetLogicalSize();
- cbReflowInput.emplace(cbFrame->PresContext(), cbFrame, &rc, cbSize);
- cbReflowInput->ComputedPhysicalMargin() = cbFrame->GetUsedMargin();
- cbReflowInput->ComputedPhysicalPadding() = cbFrame->GetUsedPadding();
- cbReflowInput->ComputedPhysicalBorderPadding() =
- cbFrame->GetUsedBorderAndPadding();
- parentReflowInput.mCBReflowInput = cbReflowInput.ptr();
- }
-
- NS_WARN_IF_FALSE(parentSize.ISize(parentWM) != NS_INTRINSICSIZE &&
- parentSize.BSize(parentWM) != NS_INTRINSICSIZE,
- "parentSize should be valid");
- parentReflowInput.SetComputedISize(std::max(parentSize.ISize(parentWM), 0));
- parentReflowInput.SetComputedBSize(std::max(parentSize.BSize(parentWM), 0));
- parentReflowInput.ComputedPhysicalMargin().SizeTo(0, 0, 0, 0);
-
- parentReflowInput.ComputedPhysicalPadding() = parentFrame->GetUsedPadding();
- parentReflowInput.ComputedPhysicalBorderPadding() =
- parentFrame->GetUsedBorderAndPadding();
- LogicalSize availSize = parentSize.ConvertTo(frameWM, parentWM);
- availSize.BSize(frameWM) = NS_INTRINSICSIZE;
-
- ViewportFrame* viewport = do_QueryFrame(parentFrame);
- nsSize cbSize = viewport ?
- viewport->AdjustReflowInputAsContainingBlock(&parentReflowInput).Size()
- : aFrame->GetContainingBlock()->GetSize();
- const nsMargin& parentBorder =
- parentReflowInput.mStyleBorder->GetComputedBorder();
- cbSize -= nsSize(parentBorder.LeftRight(), parentBorder.TopBottom());
- LogicalSize lcbSize(frameWM, cbSize);
- ReflowInput reflowInput(aFrame->PresContext(), parentReflowInput,
- aFrame, availSize, &lcbSize);
- nsSize computedSize(reflowInput.ComputedWidth(), reflowInput.ComputedHeight());
- computedSize.width += reflowInput.ComputedPhysicalBorderPadding().LeftRight();
- if (computedSize.height != NS_INTRINSICSIZE) {
- computedSize.height += reflowInput.ComputedPhysicalBorderPadding().TopBottom();
- }
- nsSize size = aFrame->GetSize();
- // The RecomputePosition hint is not used if any offset changed between auto
- // and non-auto. If computedSize.height == NS_INTRINSICSIZE then the new
- // element height will be its intrinsic height, and since 'top' and 'bottom''s
- // auto-ness hasn't changed, the old height must also be its intrinsic
- // height, which we can assume hasn't changed (or reflow would have
- // been triggered).
- if (computedSize.width == size.width &&
- (computedSize.height == NS_INTRINSICSIZE || computedSize.height == size.height)) {
- // If we're solving for 'left' or 'top', then compute it here, in order to
- // match the reflow code path.
- if (NS_AUTOOFFSET == reflowInput.ComputedPhysicalOffsets().left) {
- reflowInput.ComputedPhysicalOffsets().left = cbSize.width -
- reflowInput.ComputedPhysicalOffsets().right -
- reflowInput.ComputedPhysicalMargin().right -
- size.width -
- reflowInput.ComputedPhysicalMargin().left;
- }
-
- if (NS_AUTOOFFSET == reflowInput.ComputedPhysicalOffsets().top) {
- reflowInput.ComputedPhysicalOffsets().top = cbSize.height -
- reflowInput.ComputedPhysicalOffsets().bottom -
- reflowInput.ComputedPhysicalMargin().bottom -
- size.height -
- reflowInput.ComputedPhysicalMargin().top;
- }
-
- // Move the frame
- nsPoint pos(parentBorder.left + reflowInput.ComputedPhysicalOffsets().left +
- reflowInput.ComputedPhysicalMargin().left,
- parentBorder.top + reflowInput.ComputedPhysicalOffsets().top +
- reflowInput.ComputedPhysicalMargin().top);
- aFrame->SetPosition(pos);
-
- return true;
- }
-
- // Fall back to a reflow
- StyleChangeReflow(aFrame, nsChangeHint_NeedReflow);
- return false;
-}
-
-static bool
-HasBoxAncestor(nsIFrame* aFrame)
-{
- for (nsIFrame* f = aFrame; f; f = f->GetParent()) {
- if (f->IsXULBoxFrame()) {
- return true;
- }
- }
- return false;
-}
-
-void
-RestyleManager::StyleChangeReflow(nsIFrame* aFrame, nsChangeHint aHint)
-{
- nsIPresShell::IntrinsicDirty dirtyType;
- if (aHint & nsChangeHint_ClearDescendantIntrinsics) {
- NS_ASSERTION(aHint & nsChangeHint_ClearAncestorIntrinsics,
- "Please read the comments in nsChangeHint.h");
- NS_ASSERTION(aHint & nsChangeHint_NeedDirtyReflow,
- "ClearDescendantIntrinsics requires NeedDirtyReflow");
- dirtyType = nsIPresShell::eStyleChange;
- } else if ((aHint & nsChangeHint_UpdateComputedBSize) &&
- aFrame->HasAnyStateBits(NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE)) {
- dirtyType = nsIPresShell::eStyleChange;
- } else if (aHint & nsChangeHint_ClearAncestorIntrinsics) {
- dirtyType = nsIPresShell::eTreeChange;
- } else if ((aHint & nsChangeHint_UpdateComputedBSize) &&
- HasBoxAncestor(aFrame)) {
- // The frame's computed BSize is changing, and we have a box ancestor
- // whose cached intrinsic height may need to be updated.
- dirtyType = nsIPresShell::eTreeChange;
- } else {
- dirtyType = nsIPresShell::eResize;
- }
-
- nsFrameState dirtyBits;
- if (aFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
- dirtyBits = nsFrameState(0);
- } else if ((aHint & nsChangeHint_NeedDirtyReflow) ||
- dirtyType == nsIPresShell::eStyleChange) {
- dirtyBits = NS_FRAME_IS_DIRTY;
- } else {
- dirtyBits = NS_FRAME_HAS_DIRTY_CHILDREN;
- }
-
- // If we're not going to clear any intrinsic sizes on the frames, and
- // there are no dirty bits to set, then there's nothing to do.
- if (dirtyType == nsIPresShell::eResize && !dirtyBits)
- return;
-
- nsIPresShell::ReflowRootHandling rootHandling;
- if (aHint & nsChangeHint_ReflowChangesSizeOrPosition) {
- rootHandling = nsIPresShell::ePositionOrSizeChange;
- } else {
- rootHandling = nsIPresShell::eNoPositionOrSizeChange;
- }
-
- do {
- PresContext()->PresShell()->FrameNeedsReflow(aFrame, dirtyType, dirtyBits,
- rootHandling);
- aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame);
- } while (aFrame);
-}
-
-void
-RestyleManager::AddSubtreeToOverflowTracker(nsIFrame* aFrame)
-{
- if (aFrame->FrameMaintainsOverflow()) {
- mOverflowChangedTracker.AddFrame(aFrame,
- OverflowChangedTracker::CHILDREN_CHANGED);
- }
- nsIFrame::ChildListIterator lists(aFrame);
- for (; !lists.IsDone(); lists.Next()) {
- for (nsIFrame* child : lists.CurrentList()) {
- AddSubtreeToOverflowTracker(child);
- }
- }
-}
-
-NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(ChangeListProperty, bool)
-
-/**
- * Return true if aFrame's subtree has placeholders for out-of-flow content
- * whose 'position' style's bit in aPositionMask is set.
- */
-static bool
-FrameHasPositionedPlaceholderDescendants(nsIFrame* aFrame, uint32_t aPositionMask)
-{
- const nsIFrame::ChildListIDs skip(nsIFrame::kAbsoluteList |
- nsIFrame::kFixedList);
- for (nsIFrame::ChildListIterator lists(aFrame); !lists.IsDone(); lists.Next()) {
- if (!skip.Contains(lists.CurrentID())) {
- for (nsIFrame* f : lists.CurrentList()) {
- if (f->GetType() == nsGkAtoms::placeholderFrame) {
- nsIFrame* outOfFlow = nsPlaceholderFrame::GetRealFrameForPlaceholder(f);
- // If SVG text frames could appear here, they could confuse us since
- // they ignore their position style ... but they can't.
- NS_ASSERTION(!outOfFlow->IsSVGText(),
- "SVG text frames can't be out of flow");
- if (aPositionMask & (1 << outOfFlow->StyleDisplay()->mPosition)) {
- return true;
- }
- }
- if (FrameHasPositionedPlaceholderDescendants(f, aPositionMask)) {
- return true;
- }
- }
- }
- }
- return false;
-}
-
-static bool
-NeedToReframeForAddingOrRemovingTransform(nsIFrame* aFrame)
-{
- static_assert(0 <= NS_STYLE_POSITION_ABSOLUTE &&
- NS_STYLE_POSITION_ABSOLUTE < 32, "Style constant out of range");
- static_assert(0 <= NS_STYLE_POSITION_FIXED &&
- NS_STYLE_POSITION_FIXED < 32, "Style constant out of range");
-
- uint32_t positionMask;
- // Don't call aFrame->IsPositioned here, since that returns true if
- // the frame already has a transform, and we want to ignore that here
- if (aFrame->IsAbsolutelyPositioned() ||
- aFrame->IsRelativelyPositioned()) {
- // This frame is a container for abs-pos descendants whether or not it
- // has a transform.
- // So abs-pos descendants are no problem; we only need to reframe if
- // we have fixed-pos descendants.
- positionMask = 1 << NS_STYLE_POSITION_FIXED;
- } else {
- // This frame may not be a container for abs-pos descendants already.
- // So reframe if we have abs-pos or fixed-pos descendants.
- positionMask = (1 << NS_STYLE_POSITION_FIXED) |
- (1 << NS_STYLE_POSITION_ABSOLUTE);
- }
- for (nsIFrame* f = aFrame; f;
- f = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(f)) {
- if (FrameHasPositionedPlaceholderDescendants(f, positionMask)) {
- return true;
- }
- }
- return false;
-}
-
-nsresult
-RestyleManager::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
-{
- NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
- "Someone forgot a script blocker");
- int32_t count = aChangeList.Count();
- if (!count)
- return NS_OK;
-
- PROFILER_LABEL("RestyleManager", "ProcessRestyledFrames",
- js::ProfileEntry::Category::CSS);
-
- // Make sure to not rebuild quote or counter lists while we're
- // processing restyles
- FrameConstructor()->BeginUpdate();
-
- FramePropertyTable* propTable = PresContext()->PropertyTable();
-
- // Mark frames so that we skip frames that die along the way, bug 123049.
- // A frame can be in the list multiple times with different hints. Further
- // optmization is possible if nsStyleChangeList::AppendChange could coalesce
- int32_t index = count;
-
- while (0 <= --index) {
- const nsStyleChangeData* changeData;
- aChangeList.ChangeAt(index, &changeData);
- if (changeData->mFrame) {
- propTable->Set(changeData->mFrame, ChangeListProperty(), true);
- }
- }
-
- index = count;
-
- bool didUpdateCursor = false;
-
- while (0 <= --index) {
- nsIFrame* frame;
- nsIContent* content;
- bool didReflowThisFrame = false;
- nsChangeHint hint;
- aChangeList.ChangeAt(index, frame, content, hint);
-
- NS_ASSERTION(!(hint & nsChangeHint_AllReflowHints) ||
- (hint & nsChangeHint_NeedReflow),
- "Reflow hint bits set without actually asking for a reflow");
-
- // skip any frame that has been destroyed due to a ripple effect
- if (frame && !propTable->Get(frame, ChangeListProperty())) {
- continue;
- }
-
- if (frame && frame->GetContent() != content) {
- // XXXbz this is due to image maps messing with the primary frame of
- // <area>s. See bug 135040. Remove this block once that's fixed.
- frame = nullptr;
- if (!(hint & nsChangeHint_ReconstructFrame)) {
- continue;
- }
- }
-
- if ((hint & nsChangeHint_UpdateContainingBlock) && frame &&
- !(hint & nsChangeHint_ReconstructFrame)) {
- if (NeedToReframeForAddingOrRemovingTransform(frame) ||
- frame->GetType() == nsGkAtoms::fieldSetFrame ||
- frame->GetContentInsertionFrame() != frame) {
- // The frame has positioned children that need to be reparented, or
- // it can't easily be converted to/from being an abs-pos container correctly.
- hint |= nsChangeHint_ReconstructFrame;
- } else {
- for (nsIFrame* cont = frame; cont;
- cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
- // Normally frame construction would set state bits as needed,
- // but we're not going to reconstruct the frame so we need to set them.
- // It's because we need to set this state on each affected frame
- // that we can't coalesce nsChangeHint_UpdateContainingBlock hints up
- // to ancestors (i.e. it can't be an inherited change hint).
- if (cont->IsAbsPosContaininingBlock()) {
- if (cont->StyleDisplay()->HasTransform(cont)) {
- cont->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
- }
- if (!cont->IsAbsoluteContainer() &&
- (cont->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)) {
- cont->MarkAsAbsoluteContainingBlock();
- }
- } else {
- // Don't remove NS_FRAME_MAY_BE_TRANSFORMED since it may still by
- // transformed by other means. It's OK to have the bit even if it's
- // not needed.
- if (cont->IsAbsoluteContainer()) {
- cont->MarkAsNotAbsoluteContainingBlock();
- }
- }
- }
- }
- }
- if (hint & nsChangeHint_ReconstructFrame) {
- // If we ever start passing true here, be careful of restyles
- // that involve a reframe and animations. In particular, if the
- // restyle we're processing here is an animation restyle, but
- // the style resolution we will do for the frame construction
- // happens async when we're not in an animation restyle already,
- // problems could arise.
- // We could also have problems with triggering of CSS transitions
- // on elements whose frames are reconstructed, since we depend on
- // the reconstruction happening synchronously.
- FrameConstructor()->RecreateFramesForContent(content, false,
- nsCSSFrameConstructor::REMOVE_FOR_RECONSTRUCTION, nullptr);
- } else {
- NS_ASSERTION(frame, "This shouldn't happen");
-
- if (!frame->FrameMaintainsOverflow()) {
- // frame does not maintain overflow rects, so avoid calling
- // FinishAndStoreOverflow on it:
- hint &= ~(nsChangeHint_UpdateOverflow |
- nsChangeHint_ChildrenOnlyTransform |
- nsChangeHint_UpdatePostTransformOverflow |
- nsChangeHint_UpdateParentOverflow);
- }
-
- if (!(frame->GetStateBits() & NS_FRAME_MAY_BE_TRANSFORMED)) {
- // Frame can not be transformed, and thus a change in transform will
- // have no effect and we should not use the
- // nsChangeHint_UpdatePostTransformOverflow hint.
- hint &= ~nsChangeHint_UpdatePostTransformOverflow;
- }
-
- if (hint & nsChangeHint_UpdateEffects) {
- for (nsIFrame* cont = frame; cont;
- cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
- nsSVGEffects::UpdateEffects(cont);
- }
- }
- if ((hint & nsChangeHint_InvalidateRenderingObservers) ||
- ((hint & nsChangeHint_UpdateOpacityLayer) &&
- frame->IsFrameOfType(nsIFrame::eSVG) &&
- !(frame->GetStateBits() & NS_STATE_IS_OUTER_SVG))) {
- nsSVGEffects::InvalidateRenderingObservers(frame);
- }
- if (hint & nsChangeHint_NeedReflow) {
- StyleChangeReflow(frame, hint);
- didReflowThisFrame = true;
- }
-
- if ((hint & nsChangeHint_UpdateUsesOpacity) &&
- frame->IsFrameOfType(nsIFrame::eTablePart)) {
- NS_ASSERTION(hint & nsChangeHint_UpdateOpacityLayer,
- "should only return UpdateUsesOpacity hint "
- "when also returning UpdateOpacityLayer hint");
- // When an internal table part (including cells) changes between
- // having opacity 1 and non-1, it changes whether its
- // backgrounds (and those of table parts inside of it) are
- // painted as part of the table's nsDisplayTableBorderBackground
- // display item, or part of its own display item. That requires
- // invalidation, so change UpdateOpacityLayer to RepaintFrame.
- hint &= ~nsChangeHint_UpdateOpacityLayer;
- hint |= nsChangeHint_RepaintFrame;
- }
-
- if (hint & nsChangeHint_UpdateBackgroundPosition) {
- // For most frame types, DLBI can detect background position changes,
- // so we only need to schedule a paint.
- hint |= nsChangeHint_SchedulePaint;
- if (frame->IsFrameOfType(nsIFrame::eTablePart) ||
- frame->IsFrameOfType(nsIFrame::eMathML)) {
- // Table parts and MathML frames don't build display items for their
- // backgrounds, so DLBI can't detect background-position changes for
- // these frames. Repaint the whole frame.
- hint |= nsChangeHint_RepaintFrame;
- }
- }
-
- if (hint & (nsChangeHint_RepaintFrame | nsChangeHint_SyncFrameView |
- nsChangeHint_UpdateOpacityLayer | nsChangeHint_UpdateTransformLayer |
- nsChangeHint_ChildrenOnlyTransform | nsChangeHint_SchedulePaint)) {
- ApplyRenderingChangeToTree(PresContext(), frame, hint);
- }
- if ((hint & nsChangeHint_RecomputePosition) && !didReflowThisFrame) {
- ActiveLayerTracker::NotifyOffsetRestyle(frame);
- // It is possible for this to fall back to a reflow
- if (!RecomputePosition(frame)) {
- didReflowThisFrame = true;
- }
- }
- NS_ASSERTION(!(hint & nsChangeHint_ChildrenOnlyTransform) ||
- (hint & nsChangeHint_UpdateOverflow),
- "nsChangeHint_UpdateOverflow should be passed too");
- if (!didReflowThisFrame &&
- (hint & (nsChangeHint_UpdateOverflow |
- nsChangeHint_UpdatePostTransformOverflow |
- nsChangeHint_UpdateParentOverflow |
- nsChangeHint_UpdateSubtreeOverflow))) {
- if (hint & nsChangeHint_UpdateSubtreeOverflow) {
- for (nsIFrame* cont = frame; cont; cont =
- nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
- AddSubtreeToOverflowTracker(cont);
- }
- // The work we just did in AddSubtreeToOverflowTracker
- // subsumes some of the other hints:
- hint &= ~(nsChangeHint_UpdateOverflow |
- nsChangeHint_UpdatePostTransformOverflow);
- }
- if (hint & nsChangeHint_ChildrenOnlyTransform) {
- // The overflow areas of the child frames need to be updated:
- nsIFrame* hintFrame = GetFrameForChildrenOnlyTransformHint(frame);
- nsIFrame* childFrame = hintFrame->PrincipalChildList().FirstChild();
- NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(frame),
- "SVG frames should not have continuations "
- "or ib-split siblings");
- NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(hintFrame),
- "SVG frames should not have continuations "
- "or ib-split siblings");
- for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
- MOZ_ASSERT(childFrame->IsFrameOfType(nsIFrame::eSVG),
- "Not expecting non-SVG children");
- // If |childFrame| is dirty or has dirty children, we don't bother
- // updating overflows since that will happen when it's reflowed.
- if (!(childFrame->GetStateBits() &
- (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN))) {
- mOverflowChangedTracker.AddFrame(childFrame,
- OverflowChangedTracker::CHILDREN_CHANGED);
- }
- NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(childFrame),
- "SVG frames should not have continuations "
- "or ib-split siblings");
- NS_ASSERTION(childFrame->GetParent() == hintFrame,
- "SVG child frame not expected to have different parent");
- }
- }
- // If |frame| is dirty or has dirty children, we don't bother updating
- // overflows since that will happen when it's reflowed.
- if (!(frame->GetStateBits() &
- (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN))) {
- if (hint & (nsChangeHint_UpdateOverflow |
- nsChangeHint_UpdatePostTransformOverflow)) {
- OverflowChangedTracker::ChangeKind changeKind;
- // If we have both nsChangeHint_UpdateOverflow and
- // nsChangeHint_UpdatePostTransformOverflow,
- // CHILDREN_CHANGED is selected as it is
- // strictly stronger.
- if (hint & nsChangeHint_UpdateOverflow) {
- changeKind = OverflowChangedTracker::CHILDREN_CHANGED;
- } else {
- changeKind = OverflowChangedTracker::TRANSFORM_CHANGED;
- }
- for (nsIFrame* cont = frame; cont; cont =
- nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
- mOverflowChangedTracker.AddFrame(cont, changeKind);
- }
- }
- // UpdateParentOverflow hints need to be processed in addition
- // to the above, since if the processing of the above hints
- // yields no change, the update will not propagate to the
- // parent.
- if (hint & nsChangeHint_UpdateParentOverflow) {
- MOZ_ASSERT(frame->GetParent(),
- "shouldn't get style hints for the root frame");
- for (nsIFrame* cont = frame; cont; cont =
- nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
- mOverflowChangedTracker.AddFrame(cont->GetParent(),
- OverflowChangedTracker::CHILDREN_CHANGED);
- }
- }
- }
- }
- if ((hint & nsChangeHint_UpdateCursor) && !didUpdateCursor) {
- PresContext()->PresShell()->SynthesizeMouseMove(false);
- didUpdateCursor = true;
- }
- }
- }
-
- FrameConstructor()->EndUpdate();
-
- // cleanup references and verify the style tree. Note that the latter needs
- // to happen once we've processed the whole list, since until then the tree
- // is not in fact in a consistent state.
- index = count;
- while (0 <= --index) {
- const nsStyleChangeData* changeData;
- aChangeList.ChangeAt(index, &changeData);
- if (changeData->mFrame) {
- propTable->Delete(changeData->mFrame, ChangeListProperty());
- }
-
-#ifdef DEBUG
- // reget frame from content since it may have been regenerated...
- if (changeData->mContent) {
- nsIFrame* frame = changeData->mContent->GetPrimaryFrame();
- if (frame) {
- DebugVerifyStyleTree(frame);
- }
- } else if (!changeData->mFrame ||
- changeData->mFrame->GetType() != nsGkAtoms::viewportFrame) {
- NS_WARNING("Unable to test style tree integrity -- no content node "
- "(and not a viewport frame)");
- }
-#endif
- }
-
- aChangeList.Clear();
- return NS_OK;
-}
-
void
RestyleManager::RestyleElement(Element* aElement,
nsIFrame* aPrimaryFrame,
nsChangeHint aMinHint,
RestyleTracker& aRestyleTracker,
nsRestyleHint aRestyleHint,
const RestyleHintData& aRestyleHintData)
{
@@ -1907,176 +989,16 @@ RestyleManager::PostRebuildAllStyleDataE
mDoRebuildAllStyleData = true;
mRebuildAllExtraHint |= aExtraHint;
mRebuildAllRestyleHint |= aRestyleHint;
// Get a restyle event posted if necessary
PostRestyleEventInternal(false);
}
-#ifdef DEBUG
-static void
-DumpContext(nsIFrame* aFrame, nsStyleContext* aContext)
-{
- if (aFrame) {
- fputs("frame: ", stdout);
- nsAutoString name;
- aFrame->GetFrameName(name);
- fputs(NS_LossyConvertUTF16toASCII(name).get(), stdout);
- fprintf(stdout, " (%p)", static_cast<void*>(aFrame));
- }
- if (aContext) {
- fprintf(stdout, " style: %p ", static_cast<void*>(aContext));
-
- nsIAtom* pseudoTag = aContext->GetPseudo();
- if (pseudoTag) {
- nsAutoString buffer;
- pseudoTag->ToString(buffer);
- fputs(NS_LossyConvertUTF16toASCII(buffer).get(), stdout);
- fputs(" ", stdout);
- }
- fputs("{}\n", stdout);
- }
-}
-
-static void
-VerifySameTree(nsStyleContext* aContext1, nsStyleContext* aContext2)
-{
- nsStyleContext* top1 = aContext1;
- nsStyleContext* top2 = aContext2;
- nsStyleContext* parent;
- for (;;) {
- parent = top1->GetParent();
- if (!parent)
- break;
- top1 = parent;
- }
- for (;;) {
- parent = top2->GetParent();
- if (!parent)
- break;
- top2 = parent;
- }
- NS_ASSERTION(top1 == top2,
- "Style contexts are not in the same style context tree");
-}
-
-static void
-VerifyContextParent(nsIFrame* aFrame, nsStyleContext* aContext,
- nsStyleContext* aParentContext)
-{
- // get the contexts not provided
- if (!aContext) {
- aContext = aFrame->StyleContext();
- }
-
- if (!aParentContext) {
- nsIFrame* providerFrame;
- aParentContext = aFrame->GetParentStyleContext(&providerFrame);
- // aParentContext could still be null
- }
-
- NS_ASSERTION(aContext, "Failure to get required contexts");
- nsStyleContext* actualParentContext = aContext->GetParent();
-
- if (aParentContext) {
- if (aParentContext != actualParentContext) {
- DumpContext(aFrame, aContext);
- if (aContext == aParentContext) {
- NS_ERROR("Using parent's style context");
- }
- else {
- NS_ERROR("Wrong parent style context");
- fputs("Wrong parent style context: ", stdout);
- DumpContext(nullptr, actualParentContext);
- fputs("should be using: ", stdout);
- DumpContext(nullptr, aParentContext);
- VerifySameTree(actualParentContext, aParentContext);
- fputs("\n", stdout);
- }
- }
-
- }
- else {
- if (actualParentContext) {
- NS_ERROR("Have parent context and shouldn't");
- DumpContext(aFrame, aContext);
- fputs("Has parent context: ", stdout);
- DumpContext(nullptr, actualParentContext);
- fputs("Should be null\n\n", stdout);
- }
- }
-
- nsStyleContext* childStyleIfVisited = aContext->GetStyleIfVisited();
- // Either childStyleIfVisited has aContext->GetParent()->GetStyleIfVisited()
- // as the parent or it has a different rulenode from aContext _and_ has
- // aContext->GetParent() as the parent.
- if (childStyleIfVisited &&
- !((childStyleIfVisited->RuleNode() != aContext->RuleNode() &&
- childStyleIfVisited->GetParent() == aContext->GetParent()) ||
- childStyleIfVisited->GetParent() ==
- aContext->GetParent()->GetStyleIfVisited())) {
- NS_ERROR("Visited style has wrong parent");
- DumpContext(aFrame, aContext);
- fputs("\n", stdout);
- }
-}
-
-static void
-VerifyStyleTree(nsIFrame* aFrame)
-{
- nsStyleContext* context = aFrame->StyleContext();
- VerifyContextParent(aFrame, context, nullptr);
-
- nsIFrame::ChildListIterator lists(aFrame);
- for (; !lists.IsDone(); lists.Next()) {
- for (nsIFrame* child : lists.CurrentList()) {
- if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
- // only do frames that are in flow
- if (nsGkAtoms::placeholderFrame == child->GetType()) {
- // placeholder: first recurse and verify the out of flow frame,
- // then verify the placeholder's context
- nsIFrame* outOfFlowFrame =
- nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
-
- // recurse to out of flow frame, letting the parent context get resolved
- do {
- VerifyStyleTree(outOfFlowFrame);
- } while ((outOfFlowFrame = outOfFlowFrame->GetNextContinuation()));
-
- // verify placeholder using the parent frame's context as
- // parent context
- VerifyContextParent(child, nullptr, nullptr);
- }
- else { // regular frame
- VerifyStyleTree(child);
- }
- }
- }
- }
-
- // do additional contexts
- int32_t contextIndex = 0;
- for (nsStyleContext* extraContext;
- (extraContext = aFrame->GetAdditionalStyleContext(contextIndex));
- ++contextIndex) {
- VerifyContextParent(aFrame, extraContext, context);
- }
-}
-
-void
-RestyleManager::DebugVerifyStyleTree(nsIFrame* aFrame)
-{
- if (aFrame) {
- VerifyStyleTree(aFrame);
- }
-}
-
-#endif // DEBUG
-
// aContent must be the content for the frame in question, which may be
// :before/:after content
/* static */ bool
RestyleManager::TryStartingTransition(nsPresContext* aPresContext,
nsIContent* aContent,
nsStyleContext* aOldStyleContext,
RefPtr<nsStyleContext>*
aNewStyleContext /* inout */)
@@ -2484,17 +1406,17 @@ RestyleManager::ReparentStyleContext(nsI
NS_ASSERTION(!(styleChange & nsChangeHint_ReconstructFrame),
"Our frame tree is likely to be bogus!");
}
aFrame->SetAdditionalStyleContext(contextIndex, newExtraContext);
}
}
#ifdef DEBUG
- VerifyStyleTree(aFrame);
+ DebugVerifyStyleTree(aFrame);
#endif
}
}
return NS_OK;
}
ElementRestyler::ElementRestyler(nsPresContext* aPresContext,
@@ -4493,17 +3415,17 @@ ElementRestyler::ComputeStyleChangeFor(n
content ? content->GetParentElementCrossingShadowRoot() : nullptr;
treeMatchContext.InitAncestors(parent);
nsTArray<nsCSSSelector*> selectorsForDescendants;
selectorsForDescendants.AppendElements(
aRestyleHintData.mSelectorsForDescendants);
nsTArray<nsIContent*> visibleKidsOfHiddenElement;
nsIFrame* nextIBSibling;
for (nsIFrame* ibSibling = aFrame; ibSibling; ibSibling = nextIBSibling) {
- nextIBSibling = GetNextBlockInInlineSibling(propTable, ibSibling);
+ nextIBSibling = RestyleManager::GetNextBlockInInlineSibling(propTable, ibSibling);
if (nextIBSibling) {
// Don't allow some ib-split siblings to be processed with
// RestyleResult::eStopWithStyleChange and others not.
aRestyleHint |= eRestyle_Force;
}
// Outer loop over ib-split siblings
--- a/layout/base/RestyleManager.h
+++ b/layout/base/RestyleManager.h
@@ -32,17 +32,20 @@ namespace mozilla {
namespace dom {
class Element;
} // namespace dom
class RestyleManager final : public RestyleManagerBase
{
public:
+ typedef RestyleManagerBase base_type;
+
friend class RestyleTracker;
+ friend class ElementRestyler;
explicit RestyleManager(nsPresContext* aPresContext);
private:
// Private destructor, to discourage deletion outside of Release():
~RestyleManager()
{
MOZ_ASSERT(!mReframingStyleContexts,
@@ -132,29 +135,26 @@ private:
Element* aElement,
nsChangeHint aMinChange,
RestyleTracker& aRestyleTracker,
nsRestyleHint aRestyleHint,
const RestyleHintData& aRestyleHintData);
public:
-#ifdef DEBUG
- /**
- * DEBUG ONLY method to verify integrity of style tree versus frame tree
- */
- void DebugVerifyStyleTree(nsIFrame* aFrame);
-#endif
-
// Note: It's the caller's responsibility to make sure to wrap a
// ProcessRestyledFrames call in a view update batch and a script blocker.
// This function does not call ProcessAttachedQueue() on the binding manager.
// If the caller wants that to happen synchronously, it needs to handle that
// itself.
- nsresult ProcessRestyledFrames(nsStyleChangeList& aRestyleArray);
+ nsresult ProcessRestyledFrames(nsStyleChangeList& aChangeList) {
+ return base_type::ProcessRestyledFrames(aChangeList,
+ *PresContext(),
+ mOverflowChangedTracker);
+ }
/**
* In order to start CSS transitions on elements that are being
* reframed, we need to stash their style contexts somewhere during
* the reframing process.
*
* In all cases, the content node in the hash table is the real
* content node, not the anonymous content node we create for ::before
@@ -488,21 +488,16 @@ private:
void StartRebuildAllStyleData(RestyleTracker& aRestyleTracker);
void FinishRebuildAllStyleData();
void StyleChangeReflow(nsIFrame* aFrame, nsChangeHint aHint);
// Recursively add all the given frame and all children to the tracker.
void AddSubtreeToOverflowTracker(nsIFrame* aFrame);
- // Returns true if this function managed to successfully move a frame, and
- // false if it could not process the position change, and a reflow should
- // be performed instead.
- bool RecomputePosition(nsIFrame* aFrame);
-
bool ShouldStartRebuildAllFor(RestyleTracker& aRestyleTracker) {
// When we process our primary restyle tracker and we have a pending
// rebuild-all, we need to process it.
return mDoRebuildAllStyleData &&
&aRestyleTracker == &mPendingRestyles;
}
void ProcessRestyles(RestyleTracker& aRestyleTracker) {
--- a/layout/base/RestyleManagerBase.cpp
+++ b/layout/base/RestyleManagerBase.cpp
@@ -129,10 +129,1095 @@ RestyleManagerBase::RestyleHintToString(
} else {
if (!any) {
result.AppendLiteral("0");
}
}
return result;
}
+/**
+ * Frame construction helpers follow.
+ */
+#ifdef DEBUG
+static bool gInApplyRenderingChangeToTree = false;
+#endif
+
+#ifdef DEBUG
+static void
+DumpContext(nsIFrame* aFrame, nsStyleContext* aContext)
+{
+ if (aFrame) {
+ fputs("frame: ", stdout);
+ nsAutoString name;
+ aFrame->GetFrameName(name);
+ fputs(NS_LossyConvertUTF16toASCII(name).get(), stdout);
+ fprintf(stdout, " (%p)", static_cast<void*>(aFrame));
+ }
+ if (aContext) {
+ fprintf(stdout, " style: %p ", static_cast<void*>(aContext));
+
+ nsIAtom* pseudoTag = aContext->GetPseudo();
+ if (pseudoTag) {
+ nsAutoString buffer;
+ pseudoTag->ToString(buffer);
+ fputs(NS_LossyConvertUTF16toASCII(buffer).get(), stdout);
+ fputs(" ", stdout);
+ }
+ fputs("{}\n", stdout);
+ }
+}
+
+static void
+VerifySameTree(nsStyleContext* aContext1, nsStyleContext* aContext2)
+{
+ nsStyleContext* top1 = aContext1;
+ nsStyleContext* top2 = aContext2;
+ nsStyleContext* parent;
+ for (;;) {
+ parent = top1->GetParent();
+ if (!parent)
+ break;
+ top1 = parent;
+ }
+ for (;;) {
+ parent = top2->GetParent();
+ if (!parent)
+ break;
+ top2 = parent;
+ }
+ NS_ASSERTION(top1 == top2,
+ "Style contexts are not in the same style context tree");
+}
+
+static void
+VerifyContextParent(nsIFrame* aFrame, nsStyleContext* aContext,
+ nsStyleContext* aParentContext)
+{
+ // get the contexts not provided
+ if (!aContext) {
+ aContext = aFrame->StyleContext();
+ }
+
+ if (!aParentContext) {
+ nsIFrame* providerFrame;
+ aParentContext = aFrame->GetParentStyleContext(&providerFrame);
+ // aParentContext could still be null
+ }
+
+ NS_ASSERTION(aContext, "Failure to get required contexts");
+ nsStyleContext* actualParentContext = aContext->GetParent();
+
+ if (aParentContext) {
+ if (aParentContext != actualParentContext) {
+ DumpContext(aFrame, aContext);
+ if (aContext == aParentContext) {
+ NS_ERROR("Using parent's style context");
+ }
+ else {
+ NS_ERROR("Wrong parent style context");
+ fputs("Wrong parent style context: ", stdout);
+ DumpContext(nullptr, actualParentContext);
+ fputs("should be using: ", stdout);
+ DumpContext(nullptr, aParentContext);
+ VerifySameTree(actualParentContext, aParentContext);
+ fputs("\n", stdout);
+ }
+ }
+
+ }
+ else {
+ if (actualParentContext) {
+ NS_ERROR("Have parent context and shouldn't");
+ DumpContext(aFrame, aContext);
+ fputs("Has parent context: ", stdout);
+ DumpContext(nullptr, actualParentContext);
+ fputs("Should be null\n\n", stdout);
+ }
+ }
+
+ nsStyleContext* childStyleIfVisited = aContext->GetStyleIfVisited();
+ // Either childStyleIfVisited has aContext->GetParent()->GetStyleIfVisited()
+ // as the parent or it has a different rulenode from aContext _and_ has
+ // aContext->GetParent() as the parent.
+ if (childStyleIfVisited &&
+ !((childStyleIfVisited->RuleNode() != aContext->RuleNode() &&
+ childStyleIfVisited->GetParent() == aContext->GetParent()) ||
+ childStyleIfVisited->GetParent() ==
+ aContext->GetParent()->GetStyleIfVisited())) {
+ NS_ERROR("Visited style has wrong parent");
+ DumpContext(aFrame, aContext);
+ fputs("\n", stdout);
+ }
+}
+
+static void
+VerifyStyleTree(nsIFrame* aFrame)
+{
+ nsStyleContext* context = aFrame->StyleContext();
+ VerifyContextParent(aFrame, context, nullptr);
+
+ nsIFrame::ChildListIterator lists(aFrame);
+ for (; !lists.IsDone(); lists.Next()) {
+ for (nsIFrame* child : lists.CurrentList()) {
+ if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
+ // only do frames that are in flow
+ if (nsGkAtoms::placeholderFrame == child->GetType()) {
+ // placeholder: first recurse and verify the out of flow frame,
+ // then verify the placeholder's context
+ nsIFrame* outOfFlowFrame =
+ nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
+
+ // recurse to out of flow frame, letting the parent context get resolved
+ do {
+ VerifyStyleTree(outOfFlowFrame);
+ } while ((outOfFlowFrame = outOfFlowFrame->GetNextContinuation()));
+
+ // verify placeholder using the parent frame's context as
+ // parent context
+ VerifyContextParent(child, nullptr, nullptr);
+ } else { // regular frame
+ VerifyStyleTree(child);
+ }
+ }
+ }
+ }
+
+ // do additional contexts
+ int32_t contextIndex = 0;
+ for (nsStyleContext* extraContext;
+ (extraContext = aFrame->GetAdditionalStyleContext(contextIndex));
+ ++contextIndex) {
+ VerifyContextParent(aFrame, extraContext, context);
+ }
+}
+
+void
+RestyleManagerBase::DebugVerifyStyleTree(nsIFrame* aFrame)
+{
+ if (aFrame) {
+ VerifyStyleTree(aFrame);
+ }
+}
+
+#endif // DEBUG
+
+
+NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(ChangeListProperty, bool)
+
+/**
+ * Sync views on aFrame and all of aFrame's descendants (following placeholders),
+ * if aChange has nsChangeHint_SyncFrameView.
+ * Calls DoApplyRenderingChangeToTree on all aFrame's out-of-flow descendants
+ * (following placeholders), if aChange has nsChangeHint_RepaintFrame.
+ * aFrame should be some combination of nsChangeHint_SyncFrameView,
+ * nsChangeHint_RepaintFrame, nsChangeHint_UpdateOpacityLayer and
+ * nsChangeHint_SchedulePaint, nothing else.
+*/
+static void
+SyncViewsAndInvalidateDescendants(nsIFrame* aFrame, nsChangeHint aChange);
+
+static void
+StyleChangeReflow(nsIFrame* aFrame, nsChangeHint aHint);
+
+/**
+ * To handle nsChangeHint_ChildrenOnlyTransform we must iterate over the child
+ * frames of the SVG frame concerned. This helper function is used to find that
+ * SVG frame when we encounter nsChangeHint_ChildrenOnlyTransform to ensure
+ * that we iterate over the intended children, since sometimes we end up
+ * handling that hint while processing hints for one of the SVG frame's
+ * ancestor frames.
+ *
+ * The reason that we sometimes end up trying to process the hint for an
+ * ancestor of the SVG frame that the hint is intended for is due to the way we
+ * process restyle events. ApplyRenderingChangeToTree adjusts the frame from
+ * the restyled element's principle frame to one of its ancestor frames based
+ * on what nsCSSRendering::FindBackground returns, since the background style
+ * may have been propagated up to an ancestor frame. Processing hints using an
+ * ancestor frame is fine in general, but nsChangeHint_ChildrenOnlyTransform is
+ * a special case since it is intended to update the children of a specific
+ * frame.
+ */
+static nsIFrame*
+GetFrameForChildrenOnlyTransformHint(nsIFrame* aFrame)
+{
+ if (aFrame->GetType() == nsGkAtoms::viewportFrame) {
+ // This happens if the root-<svg> is fixed positioned, in which case we
+ // can't use aFrame->GetContent() to find the primary frame, since
+ // GetContent() returns nullptr for ViewportFrame.
+ aFrame = aFrame->PrincipalChildList().FirstChild();
+ }
+ // For an nsHTMLScrollFrame, this will get the SVG frame that has the
+ // children-only transforms:
+ aFrame = aFrame->GetContent()->GetPrimaryFrame();
+ if (aFrame->GetType() == nsGkAtoms::svgOuterSVGFrame) {
+ aFrame = aFrame->PrincipalChildList().FirstChild();
+ MOZ_ASSERT(aFrame->GetType() == nsGkAtoms::svgOuterSVGAnonChildFrame,
+ "Where is the nsSVGOuterSVGFrame's anon child??");
+ }
+ MOZ_ASSERT(aFrame->IsFrameOfType(nsIFrame::eSVG |
+ nsIFrame::eSVGContainer),
+ "Children-only transforms only expected on SVG frames");
+ return aFrame;
+}
+
+
+// Returns true if this function managed to successfully move a frame, and
+// false if it could not process the position change, and a reflow should
+// be performed instead.
+bool
+RecomputePosition(nsIFrame* aFrame)
+{
+ // Don't process position changes on table frames, since we already handle
+ // the dynamic position change on the table wrapper frame, and the
+ // reflow-based fallback code path also ignores positions on inner table
+ // frames.
+ if (aFrame->GetType() == nsGkAtoms::tableFrame) {
+ return true;
+ }
+
+ const nsStyleDisplay* display = aFrame->StyleDisplay();
+ // Changes to the offsets of a non-positioned element can safely be ignored.
+ if (display->mPosition == NS_STYLE_POSITION_STATIC) {
+ return true;
+ }
+
+ // Don't process position changes on frames which have views or the ones which
+ // have a view somewhere in their descendants, because the corresponding view
+ // needs to be repositioned properly as well.
+ if (aFrame->HasView() ||
+ (aFrame->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW)) {
+ StyleChangeReflow(aFrame, nsChangeHint_NeedReflow);
+ return false;
+ }
+
+ aFrame->SchedulePaint();
+
+ // For relative positioning, we can simply update the frame rect
+ if (display->IsRelativelyPositionedStyle()) {
+ // Move the frame
+ if (display->mPosition == NS_STYLE_POSITION_STICKY) {
+ if (display->IsInnerTableStyle()) {
+ // We don't currently support sticky positioning of inner table
+ // elements (bug 975644). Bail.
+ //
+ // When this is fixed, remove the null-check for the computed
+ // offsets in nsTableRowFrame::ReflowChildren.
+ return true;
+ }
+
+ // Update sticky positioning for an entire element at once, starting with
+ // the first continuation or ib-split sibling.
+ // It's rare that the frame we already have isn't already the first
+ // continuation or ib-split sibling, but it can happen when styles differ
+ // across continuations such as ::first-line or ::first-letter, and in
+ // those cases we will generally (but maybe not always) do the work twice.
+ nsIFrame* firstContinuation =
+ nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
+
+ StickyScrollContainer::ComputeStickyOffsets(firstContinuation);
+ StickyScrollContainer* ssc =
+ StickyScrollContainer::GetStickyScrollContainerForFrame(firstContinuation);
+ if (ssc) {
+ ssc->PositionContinuations(firstContinuation);
+ }
+ } else {
+ MOZ_ASSERT(NS_STYLE_POSITION_RELATIVE == display->mPosition,
+ "Unexpected type of positioning");
+ for (nsIFrame* cont = aFrame; cont;
+ cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
+ nsIFrame* cb = cont->GetContainingBlock();
+ nsMargin newOffsets;
+ WritingMode wm = cb->GetWritingMode();
+ const LogicalSize size(wm, cb->GetContentRectRelativeToSelf().Size());
+
+ ReflowInput::ComputeRelativeOffsets(wm, cont, size, newOffsets);
+ NS_ASSERTION(newOffsets.left == -newOffsets.right &&
+ newOffsets.top == -newOffsets.bottom,
+ "ComputeRelativeOffsets should return valid results");
+
+ // ReflowInput::ApplyRelativePositioning would work here, but
+ // since we've already checked mPosition and aren't changing the frame's
+ // normal position, go ahead and add the offsets directly.
+ cont->SetPosition(cont->GetNormalPosition() +
+ nsPoint(newOffsets.left, newOffsets.top));
+ }
+ }
+
+ return true;
+ }
+
+ // For the absolute positioning case, set up a fake HTML reflow state for
+ // the frame, and then get the offsets and size from it. If the frame's size
+ // doesn't need to change, we can simply update the frame position. Otherwise
+ // we fall back to a reflow.
+ nsRenderingContext rc(
+ aFrame->PresContext()->PresShell()->CreateReferenceRenderingContext());
+
+ // Construct a bogus parent reflow state so that there's a usable
+ // containing block reflow state.
+ nsIFrame* parentFrame = aFrame->GetParent();
+ WritingMode parentWM = parentFrame->GetWritingMode();
+ WritingMode frameWM = aFrame->GetWritingMode();
+ LogicalSize parentSize = parentFrame->GetLogicalSize();
+
+ nsFrameState savedState = parentFrame->GetStateBits();
+ ReflowInput parentReflowInput(aFrame->PresContext(), parentFrame,
+ &rc, parentSize);
+ parentFrame->RemoveStateBits(~nsFrameState(0));
+ parentFrame->AddStateBits(savedState);
+
+ // The bogus parent state here was created with no parent state of its own,
+ // and therefore it won't have an mCBReflowInput set up.
+ // But we may need one (for InitCBReflowInput in a child state), so let's
+ // try to create one here for the cases where it will be needed.
+ Maybe<ReflowInput> cbReflowInput;
+ nsIFrame* cbFrame = parentFrame->GetContainingBlock();
+ if (cbFrame && (aFrame->GetContainingBlock() != parentFrame ||
+ parentFrame->GetType() == nsGkAtoms::tableFrame)) {
+ LogicalSize cbSize = cbFrame->GetLogicalSize();
+ cbReflowInput.emplace(cbFrame->PresContext(), cbFrame, &rc, cbSize);
+ cbReflowInput->ComputedPhysicalMargin() = cbFrame->GetUsedMargin();
+ cbReflowInput->ComputedPhysicalPadding() = cbFrame->GetUsedPadding();
+ cbReflowInput->ComputedPhysicalBorderPadding() =
+ cbFrame->GetUsedBorderAndPadding();
+ parentReflowInput.mCBReflowInput = cbReflowInput.ptr();
+ }
+
+ NS_WARN_IF_FALSE(parentSize.ISize(parentWM) != NS_INTRINSICSIZE &&
+ parentSize.BSize(parentWM) != NS_INTRINSICSIZE,
+ "parentSize should be valid");
+ parentReflowInput.SetComputedISize(std::max(parentSize.ISize(parentWM), 0));
+ parentReflowInput.SetComputedBSize(std::max(parentSize.BSize(parentWM), 0));
+ parentReflowInput.ComputedPhysicalMargin().SizeTo(0, 0, 0, 0);
+
+ parentReflowInput.ComputedPhysicalPadding() = parentFrame->GetUsedPadding();
+ parentReflowInput.ComputedPhysicalBorderPadding() =
+ parentFrame->GetUsedBorderAndPadding();
+ LogicalSize availSize = parentSize.ConvertTo(frameWM, parentWM);
+ availSize.BSize(frameWM) = NS_INTRINSICSIZE;
+
+ ViewportFrame* viewport = do_QueryFrame(parentFrame);
+ nsSize cbSize = viewport ?
+ viewport->AdjustReflowInputAsContainingBlock(&parentReflowInput).Size()
+ : aFrame->GetContainingBlock()->GetSize();
+ const nsMargin& parentBorder =
+ parentReflowInput.mStyleBorder->GetComputedBorder();
+ cbSize -= nsSize(parentBorder.LeftRight(), parentBorder.TopBottom());
+ LogicalSize lcbSize(frameWM, cbSize);
+ ReflowInput reflowInput(aFrame->PresContext(), parentReflowInput,
+ aFrame, availSize, &lcbSize);
+ nsSize computedSize(reflowInput.ComputedWidth(), reflowInput.ComputedHeight());
+ computedSize.width += reflowInput.ComputedPhysicalBorderPadding().LeftRight();
+ if (computedSize.height != NS_INTRINSICSIZE) {
+ computedSize.height += reflowInput.ComputedPhysicalBorderPadding().TopBottom();
+ }
+ nsSize size = aFrame->GetSize();
+ // The RecomputePosition hint is not used if any offset changed between auto
+ // and non-auto. If computedSize.height == NS_INTRINSICSIZE then the new
+ // element height will be its intrinsic height, and since 'top' and 'bottom''s
+ // auto-ness hasn't changed, the old height must also be its intrinsic
+ // height, which we can assume hasn't changed (or reflow would have
+ // been triggered).
+ if (computedSize.width == size.width &&
+ (computedSize.height == NS_INTRINSICSIZE || computedSize.height == size.height)) {
+ // If we're solving for 'left' or 'top', then compute it here, in order to
+ // match the reflow code path.
+ if (NS_AUTOOFFSET == reflowInput.ComputedPhysicalOffsets().left) {
+ reflowInput.ComputedPhysicalOffsets().left = cbSize.width -
+ reflowInput.ComputedPhysicalOffsets().right -
+ reflowInput.ComputedPhysicalMargin().right -
+ size.width -
+ reflowInput.ComputedPhysicalMargin().left;
+ }
+
+ if (NS_AUTOOFFSET == reflowInput.ComputedPhysicalOffsets().top) {
+ reflowInput.ComputedPhysicalOffsets().top = cbSize.height -
+ reflowInput.ComputedPhysicalOffsets().bottom -
+ reflowInput.ComputedPhysicalMargin().bottom -
+ size.height -
+ reflowInput.ComputedPhysicalMargin().top;
+ }
+
+ // Move the frame
+ nsPoint pos(parentBorder.left + reflowInput.ComputedPhysicalOffsets().left +
+ reflowInput.ComputedPhysicalMargin().left,
+ parentBorder.top + reflowInput.ComputedPhysicalOffsets().top +
+ reflowInput.ComputedPhysicalMargin().top);
+ aFrame->SetPosition(pos);
+
+ return true;
+ }
+
+ // Fall back to a reflow
+ StyleChangeReflow(aFrame, nsChangeHint_NeedReflow);
+ return false;
+}
+
+static bool
+HasBoxAncestor(nsIFrame* aFrame)
+{
+ for (nsIFrame* f = aFrame; f; f = f->GetParent()) {
+ if (f->IsXULBoxFrame()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Return true if aFrame's subtree has placeholders for out-of-flow content
+ * whose 'position' style's bit in aPositionMask is set.
+ */
+static bool
+FrameHasPositionedPlaceholderDescendants(nsIFrame* aFrame, uint32_t aPositionMask)
+{
+ const nsIFrame::ChildListIDs skip(nsIFrame::kAbsoluteList |
+ nsIFrame::kFixedList);
+ for (nsIFrame::ChildListIterator lists(aFrame); !lists.IsDone(); lists.Next()) {
+ if (!skip.Contains(lists.CurrentID())) {
+ for (nsIFrame* f : lists.CurrentList()) {
+ if (f->GetType() == nsGkAtoms::placeholderFrame) {
+ nsIFrame* outOfFlow = nsPlaceholderFrame::GetRealFrameForPlaceholder(f);
+ // If SVG text frames could appear here, they could confuse us since
+ // they ignore their position style ... but they can't.
+ NS_ASSERTION(!outOfFlow->IsSVGText(),
+ "SVG text frames can't be out of flow");
+ if (aPositionMask & (1 << outOfFlow->StyleDisplay()->mPosition)) {
+ return true;
+ }
+ }
+ if (FrameHasPositionedPlaceholderDescendants(f, aPositionMask)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+static bool
+NeedToReframeForAddingOrRemovingTransform(nsIFrame* aFrame)
+{
+ static_assert(0 <= NS_STYLE_POSITION_ABSOLUTE &&
+ NS_STYLE_POSITION_ABSOLUTE < 32, "Style constant out of range");
+ static_assert(0 <= NS_STYLE_POSITION_FIXED &&
+ NS_STYLE_POSITION_FIXED < 32, "Style constant out of range");
+
+ uint32_t positionMask;
+ // Don't call aFrame->IsPositioned here, since that returns true if
+ // the frame already has a transform, and we want to ignore that here
+ if (aFrame->IsAbsolutelyPositioned() ||
+ aFrame->IsRelativelyPositioned()) {
+ // This frame is a container for abs-pos descendants whether or not it
+ // has a transform.
+ // So abs-pos descendants are no problem; we only need to reframe if
+ // we have fixed-pos descendants.
+ positionMask = 1 << NS_STYLE_POSITION_FIXED;
+ } else {
+ // This frame may not be a container for abs-pos descendants already.
+ // So reframe if we have abs-pos or fixed-pos descendants.
+ positionMask = (1 << NS_STYLE_POSITION_FIXED) |
+ (1 << NS_STYLE_POSITION_ABSOLUTE);
+ }
+ for (nsIFrame* f = aFrame; f;
+ f = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(f)) {
+ if (FrameHasPositionedPlaceholderDescendants(f, positionMask)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/* static */ nsIFrame*
+RestyleManagerBase::GetNearestAncestorFrame(nsIContent* aContent)
+{
+ nsIFrame* ancestorFrame = nullptr;
+ for (nsIContent* ancestor = aContent->GetParent();
+ ancestor && !ancestorFrame;
+ ancestor = ancestor->GetParent()) {
+ ancestorFrame = ancestor->GetPrimaryFrame();
+ }
+ return ancestorFrame;
+}
+
+/* static */ nsIFrame*
+RestyleManagerBase::GetNextBlockInInlineSibling(FramePropertyTable* aPropTable, nsIFrame* aFrame)
+{
+ NS_ASSERTION(!aFrame->GetPrevContinuation(),
+ "must start with the first continuation");
+ // Might we have ib-split siblings?
+ if (!(aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
+ // nothing more to do here
+ return nullptr;
+ }
+
+ return static_cast<nsIFrame*>
+ (aPropTable->Get(aFrame, nsIFrame::IBSplitSibling()));
+}
+
+static void
+DoApplyRenderingChangeToTree(nsIFrame* aFrame,
+ nsChangeHint aChange)
+{
+ NS_PRECONDITION(gInApplyRenderingChangeToTree,
+ "should only be called within ApplyRenderingChangeToTree");
+
+ for ( ; aFrame; aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame)) {
+ // Invalidate and sync views on all descendant frames, following placeholders.
+ // We don't need to update transforms in SyncViewsAndInvalidateDescendants, because
+ // there can't be any out-of-flows or popups that need to be transformed;
+ // all out-of-flow descendants of the transformed element must also be
+ // descendants of the transformed frame.
+ SyncViewsAndInvalidateDescendants(aFrame,
+ nsChangeHint(aChange & (nsChangeHint_RepaintFrame |
+ nsChangeHint_SyncFrameView |
+ nsChangeHint_UpdateOpacityLayer |
+ nsChangeHint_SchedulePaint)));
+ // This must be set to true if the rendering change needs to
+ // invalidate content. If it's false, a composite-only paint
+ // (empty transaction) will be scheduled.
+ bool needInvalidatingPaint = false;
+
+ // if frame has view, will already be invalidated
+ if (aChange & nsChangeHint_RepaintFrame) {
+ // Note that this whole block will be skipped when painting is suppressed
+ // (due to our caller ApplyRendingChangeToTree() discarding the
+ // nsChangeHint_RepaintFrame hint). If you add handling for any other
+ // hints within this block, be sure that they too should be ignored when
+ // painting is suppressed.
+ needInvalidatingPaint = true;
+ aFrame->InvalidateFrameSubtree();
+ if ((aChange & nsChangeHint_UpdateEffects) &&
+ aFrame->IsFrameOfType(nsIFrame::eSVG) &&
+ !(aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)) {
+ // Need to update our overflow rects:
+ nsSVGUtils::ScheduleReflowSVG(aFrame);
+ }
+ }
+ if (aChange & nsChangeHint_UpdateTextPath) {
+ if (aFrame->IsSVGText()) {
+ // Invalidate and reflow the entire SVGTextFrame:
+ NS_ASSERTION(aFrame->GetContent()->IsSVGElement(nsGkAtoms::textPath),
+ "expected frame for a <textPath> element");
+ nsIFrame* text = nsLayoutUtils::GetClosestFrameOfType(
+ aFrame,
+ nsGkAtoms::svgTextFrame);
+ NS_ASSERTION(text, "expected to find an ancestor SVGTextFrame");
+ static_cast<SVGTextFrame*>(text)->NotifyGlyphMetricsChange();
+ } else {
+ MOZ_ASSERT(false, "unexpected frame got nsChangeHint_UpdateTextPath");
+ }
+ }
+ if (aChange & nsChangeHint_UpdateOpacityLayer) {
+ // FIXME/bug 796697: we can get away with empty transactions for
+ // opacity updates in many cases.
+ needInvalidatingPaint = true;
+
+ ActiveLayerTracker::NotifyRestyle(aFrame, eCSSProperty_opacity);
+ if (nsSVGIntegrationUtils::UsingEffectsForFrame(aFrame)) {
+ // SVG effects paints the opacity without using
+ // nsDisplayOpacity. We need to invalidate manually.
+ aFrame->InvalidateFrameSubtree();
+ }
+ }
+ if ((aChange & nsChangeHint_UpdateTransformLayer) &&
+ aFrame->IsTransformed()) {
+ ActiveLayerTracker::NotifyRestyle(aFrame, eCSSProperty_transform);
+ // If we're not already going to do an invalidating paint, see
+ // if we can get away with only updating the transform on a
+ // layer for this frame, and not scheduling an invalidating
+ // paint.
+ if (!needInvalidatingPaint) {
+ Layer* layer;
+ needInvalidatingPaint |= !aFrame->TryUpdateTransformOnly(&layer);
+
+ if (!needInvalidatingPaint) {
+ // Since we're not going to paint, we need to resend animation
+ // data to the layer.
+ MOZ_ASSERT(layer, "this can't happen if there's no layer");
+ nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(layer,
+ nullptr, nullptr, aFrame, eCSSProperty_transform);
+ }
+ }
+ }
+ if (aChange & nsChangeHint_ChildrenOnlyTransform) {
+ needInvalidatingPaint = true;
+ nsIFrame* childFrame =
+ GetFrameForChildrenOnlyTransformHint(aFrame)->PrincipalChildList().FirstChild();
+ for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
+ ActiveLayerTracker::NotifyRestyle(childFrame, eCSSProperty_transform);
+ }
+ }
+ if (aChange & nsChangeHint_SchedulePaint) {
+ needInvalidatingPaint = true;
+ }
+ aFrame->SchedulePaint(needInvalidatingPaint ?
+ nsIFrame::PAINT_DEFAULT :
+ nsIFrame::PAINT_COMPOSITE_ONLY);
+ }
+}
+
+/* static */ void
+SyncViewsAndInvalidateDescendants(nsIFrame* aFrame,
+ nsChangeHint aChange)
+{
+ NS_ASSERTION(nsChangeHint_size_t(aChange) ==
+ (aChange & (nsChangeHint_RepaintFrame |
+ nsChangeHint_SyncFrameView |
+ nsChangeHint_UpdateOpacityLayer |
+ nsChangeHint_SchedulePaint)),
+ "Invalid change flag");
+
+ nsView* view = aFrame->GetView();
+ if (view) {
+ if (aChange & nsChangeHint_SyncFrameView) {
+ nsContainerFrame::SyncFrameViewProperties(aFrame->PresContext(),
+ aFrame, nullptr, view);
+ }
+ }
+
+ nsIFrame::ChildListIterator lists(aFrame);
+ for (; !lists.IsDone(); lists.Next()) {
+ for (nsIFrame* child : lists.CurrentList()) {
+ if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
+ // only do frames that don't have placeholders
+ if (nsGkAtoms::placeholderFrame == child->GetType()) {
+ // do the out-of-flow frame and its continuations
+ nsIFrame* outOfFlowFrame =
+ nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
+ DoApplyRenderingChangeToTree(outOfFlowFrame, aChange);
+ } else if (lists.CurrentID() == nsIFrame::kPopupList) {
+ DoApplyRenderingChangeToTree(child, aChange);
+ } else { // regular frame
+ SyncViewsAndInvalidateDescendants(child, aChange);
+ }
+ }
+ }
+ }
+}
+
+void
+ApplyRenderingChangeToTree(nsIPresShell* aPresShell,
+ nsIFrame* aFrame,
+ nsChangeHint aChange)
+{
+ // We check StyleDisplay()->HasTransformStyle() in addition to checking
+ // IsTransformed() since we can get here for some frames that don't support
+ // CSS transforms.
+ NS_ASSERTION(!(aChange & nsChangeHint_UpdateTransformLayer) ||
+ aFrame->IsTransformed() ||
+ aFrame->StyleDisplay()->HasTransformStyle(),
+ "Unexpected UpdateTransformLayer hint");
+
+ if (aPresShell->IsPaintingSuppressed()) {
+ // Don't allow synchronous rendering changes when painting is turned off.
+ aChange &= ~nsChangeHint_RepaintFrame;
+ if (!aChange) {
+ return;
+ }
+ }
+
+ // Trigger rendering updates by damaging this frame and any
+ // continuations of this frame.
+#ifdef DEBUG
+ gInApplyRenderingChangeToTree = true;
+#endif
+ if (aChange & nsChangeHint_RepaintFrame) {
+ // If the frame's background is propagated to an ancestor, walk up to
+ // that ancestor and apply the RepaintFrame change hint to it.
+ nsStyleContext* bgSC;
+ nsIFrame* propagatedFrame = aFrame;
+ while (!nsCSSRendering::FindBackground(propagatedFrame, &bgSC)) {
+ propagatedFrame = propagatedFrame->GetParent();
+ NS_ASSERTION(aFrame, "root frame must paint");
+ }
+
+ if (propagatedFrame != aFrame) {
+ DoApplyRenderingChangeToTree(propagatedFrame, nsChangeHint_RepaintFrame);
+ aChange &= ~nsChangeHint_RepaintFrame;
+ if (!aChange) {
+ return;
+ }
+ }
+ }
+ DoApplyRenderingChangeToTree(aFrame, aChange);
+#ifdef DEBUG
+ gInApplyRenderingChangeToTree = false;
+#endif
+}
+
+static void
+AddSubtreeToOverflowTracker(nsIFrame* aFrame,
+ OverflowChangedTracker& aOverflowChangedTracker)
+{
+ if (aFrame->FrameMaintainsOverflow()) {
+ aOverflowChangedTracker.AddFrame(aFrame,
+ OverflowChangedTracker::CHILDREN_CHANGED);
+ }
+ nsIFrame::ChildListIterator lists(aFrame);
+ for (; !lists.IsDone(); lists.Next()) {
+ for (nsIFrame* child : lists.CurrentList()) {
+ AddSubtreeToOverflowTracker(child, aOverflowChangedTracker);
+ }
+ }
+}
+
+static void
+StyleChangeReflow(nsIFrame* aFrame, nsChangeHint aHint)
+{
+ nsIPresShell::IntrinsicDirty dirtyType;
+ if (aHint & nsChangeHint_ClearDescendantIntrinsics) {
+ NS_ASSERTION(aHint & nsChangeHint_ClearAncestorIntrinsics,
+ "Please read the comments in nsChangeHint.h");
+ NS_ASSERTION(aHint & nsChangeHint_NeedDirtyReflow,
+ "ClearDescendantIntrinsics requires NeedDirtyReflow");
+ dirtyType = nsIPresShell::eStyleChange;
+ } else if ((aHint & nsChangeHint_UpdateComputedBSize) &&
+ aFrame->HasAnyStateBits(NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE)) {
+ dirtyType = nsIPresShell::eStyleChange;
+ } else if (aHint & nsChangeHint_ClearAncestorIntrinsics) {
+ dirtyType = nsIPresShell::eTreeChange;
+ } else if ((aHint & nsChangeHint_UpdateComputedBSize) &&
+ HasBoxAncestor(aFrame)) {
+ // The frame's computed BSize is changing, and we have a box ancestor
+ // whose cached intrinsic height may need to be updated.
+ dirtyType = nsIPresShell::eTreeChange;
+ } else {
+ dirtyType = nsIPresShell::eResize;
+ }
+
+ nsFrameState dirtyBits;
+ if (aFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
+ dirtyBits = nsFrameState(0);
+ } else if ((aHint & nsChangeHint_NeedDirtyReflow) ||
+ dirtyType == nsIPresShell::eStyleChange) {
+ dirtyBits = NS_FRAME_IS_DIRTY;
+ } else {
+ dirtyBits = NS_FRAME_HAS_DIRTY_CHILDREN;
+ }
+
+ // If we're not going to clear any intrinsic sizes on the frames, and
+ // there are no dirty bits to set, then there's nothing to do.
+ if (dirtyType == nsIPresShell::eResize && !dirtyBits)
+ return;
+
+ nsIPresShell::ReflowRootHandling rootHandling;
+ if (aHint & nsChangeHint_ReflowChangesSizeOrPosition) {
+ rootHandling = nsIPresShell::ePositionOrSizeChange;
+ } else {
+ rootHandling = nsIPresShell::eNoPositionOrSizeChange;
+ }
+
+ do {
+ aFrame->PresContext()->PresShell()->FrameNeedsReflow(aFrame, dirtyType, dirtyBits,
+ rootHandling);
+ aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame);
+ } while (aFrame);
+}
+
+/* static */ nsresult
+RestyleManagerBase::ProcessRestyledFrames(nsStyleChangeList& aChangeList,
+ nsPresContext& aPresContext,
+ OverflowChangedTracker& aOverflowChangedTracker)
+{
+ NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
+ "Someone forgot a script blocker");
+ int32_t count = aChangeList.Count();
+ if (!count)
+ return NS_OK;
+
+ PROFILER_LABEL("RestyleManager", "ProcessRestyledFrames",
+ js::ProfileEntry::Category::CSS);
+
+ FramePropertyTable* propTable = aPresContext.PropertyTable();
+ nsCSSFrameConstructor* frameConstructor = aPresContext.FrameConstructor();
+
+ // Make sure to not rebuild quote or counter lists while we're
+ // processing restyles
+ frameConstructor->BeginUpdate();
+
+ // Mark frames so that we skip frames that die along the way, bug 123049.
+ // A frame can be in the list multiple times with different hints. Further
+ // optmization is possible if nsStyleChangeList::AppendChange could coalesce
+ int32_t index = count;
+
+ while (0 <= --index) {
+ const nsStyleChangeData* changeData;
+ aChangeList.ChangeAt(index, &changeData);
+ if (changeData->mFrame) {
+ propTable->Set(changeData->mFrame, ChangeListProperty(), true);
+ }
+ }
+
+ index = count;
+
+ bool didUpdateCursor = false;
+
+ while (0 <= --index) {
+ nsIFrame* frame;
+ nsIContent* content;
+ bool didReflowThisFrame = false;
+ nsChangeHint hint;
+ aChangeList.ChangeAt(index, frame, content, hint);
+
+ NS_ASSERTION(!(hint & nsChangeHint_AllReflowHints) ||
+ (hint & nsChangeHint_NeedReflow),
+ "Reflow hint bits set without actually asking for a reflow");
+
+ // skip any frame that has been destroyed due to a ripple effect
+ if (frame && !propTable->Get(frame, ChangeListProperty())) {
+ continue;
+ }
+
+ if (frame && frame->GetContent() != content) {
+ // XXXbz this is due to image maps messing with the primary frame of
+ // <area>s. See bug 135040. Remove this block once that's fixed.
+ frame = nullptr;
+ if (!(hint & nsChangeHint_ReconstructFrame)) {
+ continue;
+ }
+ }
+
+ if ((hint & nsChangeHint_UpdateContainingBlock) && frame &&
+ !(hint & nsChangeHint_ReconstructFrame)) {
+ if (NeedToReframeForAddingOrRemovingTransform(frame) ||
+ frame->GetType() == nsGkAtoms::fieldSetFrame ||
+ frame->GetContentInsertionFrame() != frame) {
+ // The frame has positioned children that need to be reparented, or
+ // it can't easily be converted to/from being an abs-pos container correctly.
+ hint |= nsChangeHint_ReconstructFrame;
+ } else {
+ for (nsIFrame* cont = frame; cont;
+ cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
+ // Normally frame construction would set state bits as needed,
+ // but we're not going to reconstruct the frame so we need to set them.
+ // It's because we need to set this state on each affected frame
+ // that we can't coalesce nsChangeHint_UpdateContainingBlock hints up
+ // to ancestors (i.e. it can't be an inherited change hint).
+ if (cont->IsAbsPosContaininingBlock()) {
+ if (cont->StyleDisplay()->HasTransform(cont)) {
+ cont->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
+ }
+ if (!cont->IsAbsoluteContainer() &&
+ (cont->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)) {
+ cont->MarkAsAbsoluteContainingBlock();
+ }
+ } else {
+ // Don't remove NS_FRAME_MAY_BE_TRANSFORMED since it may still by
+ // transformed by other means. It's OK to have the bit even if it's
+ // not needed.
+ if (cont->IsAbsoluteContainer()) {
+ cont->MarkAsNotAbsoluteContainingBlock();
+ }
+ }
+ }
+ }
+ }
+ if (hint & nsChangeHint_ReconstructFrame) {
+ // If we ever start passing true here, be careful of restyles
+ // that involve a reframe and animations. In particular, if the
+ // restyle we're processing here is an animation restyle, but
+ // the style resolution we will do for the frame construction
+ // happens async when we're not in an animation restyle already,
+ // problems could arise.
+ // We could also have problems with triggering of CSS transitions
+ // on elements whose frames are reconstructed, since we depend on
+ // the reconstruction happening synchronously.
+ frameConstructor->RecreateFramesForContent(content, false,
+ nsCSSFrameConstructor::REMOVE_FOR_RECONSTRUCTION, nullptr);
+ } else {
+ NS_ASSERTION(frame, "This shouldn't happen");
+
+ if (!frame->FrameMaintainsOverflow()) {
+ // frame does not maintain overflow rects, so avoid calling
+ // FinishAndStoreOverflow on it:
+ hint &= ~(nsChangeHint_UpdateOverflow |
+ nsChangeHint_ChildrenOnlyTransform |
+ nsChangeHint_UpdatePostTransformOverflow |
+ nsChangeHint_UpdateParentOverflow);
+ }
+
+ if (!(frame->GetStateBits() & NS_FRAME_MAY_BE_TRANSFORMED)) {
+ // Frame can not be transformed, and thus a change in transform will
+ // have no effect and we should not use the
+ // nsChangeHint_UpdatePostTransformOverflow hint.
+ hint &= ~nsChangeHint_UpdatePostTransformOverflow;
+ }
+
+ if (hint & nsChangeHint_UpdateEffects) {
+ for (nsIFrame* cont = frame; cont;
+ cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
+ nsSVGEffects::UpdateEffects(cont);
+ }
+ }
+ if ((hint & nsChangeHint_InvalidateRenderingObservers) ||
+ ((hint & nsChangeHint_UpdateOpacityLayer) &&
+ frame->IsFrameOfType(nsIFrame::eSVG) &&
+ !(frame->GetStateBits() & NS_STATE_IS_OUTER_SVG))) {
+ nsSVGEffects::InvalidateRenderingObservers(frame);
+ }
+ if (hint & nsChangeHint_NeedReflow) {
+ StyleChangeReflow(frame, hint);
+ didReflowThisFrame = true;
+ }
+
+ if ((hint & nsChangeHint_UpdateUsesOpacity) &&
+ frame->IsFrameOfType(nsIFrame::eTablePart)) {
+ NS_ASSERTION(hint & nsChangeHint_UpdateOpacityLayer,
+ "should only return UpdateUsesOpacity hint "
+ "when also returning UpdateOpacityLayer hint");
+ // When an internal table part (including cells) changes between
+ // having opacity 1 and non-1, it changes whether its
+ // backgrounds (and those of table parts inside of it) are
+ // painted as part of the table's nsDisplayTableBorderBackground
+ // display item, or part of its own display item. That requires
+ // invalidation, so change UpdateOpacityLayer to RepaintFrame.
+ hint &= ~nsChangeHint_UpdateOpacityLayer;
+ hint |= nsChangeHint_RepaintFrame;
+ }
+
+ if (hint & nsChangeHint_UpdateBackgroundPosition) {
+ // For most frame types, DLBI can detect background position changes,
+ // so we only need to schedule a paint.
+ hint |= nsChangeHint_SchedulePaint;
+ if (frame->IsFrameOfType(nsIFrame::eTablePart) ||
+ frame->IsFrameOfType(nsIFrame::eMathML)) {
+ // Table parts and MathML frames don't build display items for their
+ // backgrounds, so DLBI can't detect background-position changes for
+ // these frames. Repaint the whole frame.
+ hint |= nsChangeHint_RepaintFrame;
+ }
+ }
+
+ if (hint & (nsChangeHint_RepaintFrame | nsChangeHint_SyncFrameView |
+ nsChangeHint_UpdateOpacityLayer | nsChangeHint_UpdateTransformLayer |
+ nsChangeHint_ChildrenOnlyTransform | nsChangeHint_SchedulePaint)) {
+ ApplyRenderingChangeToTree(aPresContext.PresShell(), frame, hint);
+ }
+ if ((hint & nsChangeHint_RecomputePosition) && !didReflowThisFrame) {
+ ActiveLayerTracker::NotifyOffsetRestyle(frame);
+ // It is possible for this to fall back to a reflow
+ if (!RecomputePosition(frame)) {
+ didReflowThisFrame = true;
+ }
+ }
+ NS_ASSERTION(!(hint & nsChangeHint_ChildrenOnlyTransform) ||
+ (hint & nsChangeHint_UpdateOverflow),
+ "nsChangeHint_UpdateOverflow should be passed too");
+ if (!didReflowThisFrame &&
+ (hint & (nsChangeHint_UpdateOverflow |
+ nsChangeHint_UpdatePostTransformOverflow |
+ nsChangeHint_UpdateParentOverflow |
+ nsChangeHint_UpdateSubtreeOverflow))) {
+ if (hint & nsChangeHint_UpdateSubtreeOverflow) {
+ for (nsIFrame* cont = frame; cont; cont =
+ nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
+ AddSubtreeToOverflowTracker(cont, aOverflowChangedTracker);
+ }
+ // The work we just did in AddSubtreeToOverflowTracker
+ // subsumes some of the other hints:
+ hint &= ~(nsChangeHint_UpdateOverflow |
+ nsChangeHint_UpdatePostTransformOverflow);
+ }
+ if (hint & nsChangeHint_ChildrenOnlyTransform) {
+ // The overflow areas of the child frames need to be updated:
+ nsIFrame* hintFrame = GetFrameForChildrenOnlyTransformHint(frame);
+ nsIFrame* childFrame = hintFrame->PrincipalChildList().FirstChild();
+ NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(frame),
+ "SVG frames should not have continuations "
+ "or ib-split siblings");
+ NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(hintFrame),
+ "SVG frames should not have continuations "
+ "or ib-split siblings");
+ for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
+ MOZ_ASSERT(childFrame->IsFrameOfType(nsIFrame::eSVG),
+ "Not expecting non-SVG children");
+ // If |childFrame| is dirty or has dirty children, we don't bother
+ // updating overflows since that will happen when it's reflowed.
+ if (!(childFrame->GetStateBits() &
+ (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN))) {
+ aOverflowChangedTracker.AddFrame(childFrame,
+ OverflowChangedTracker::CHILDREN_CHANGED);
+ }
+ NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(childFrame),
+ "SVG frames should not have continuations "
+ "or ib-split siblings");
+ NS_ASSERTION(childFrame->GetParent() == hintFrame,
+ "SVG child frame not expected to have different parent");
+ }
+ }
+ // If |frame| is dirty or has dirty children, we don't bother updating
+ // overflows since that will happen when it's reflowed.
+ if (!(frame->GetStateBits() &
+ (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN))) {
+ if (hint & (nsChangeHint_UpdateOverflow |
+ nsChangeHint_UpdatePostTransformOverflow)) {
+ OverflowChangedTracker::ChangeKind changeKind;
+ // If we have both nsChangeHint_UpdateOverflow and
+ // nsChangeHint_UpdatePostTransformOverflow,
+ // CHILDREN_CHANGED is selected as it is
+ // strictly stronger.
+ if (hint & nsChangeHint_UpdateOverflow) {
+ changeKind = OverflowChangedTracker::CHILDREN_CHANGED;
+ } else {
+ changeKind = OverflowChangedTracker::TRANSFORM_CHANGED;
+ }
+ for (nsIFrame* cont = frame; cont; cont =
+ nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
+ aOverflowChangedTracker.AddFrame(cont, changeKind);
+ }
+ }
+ // UpdateParentOverflow hints need to be processed in addition
+ // to the above, since if the processing of the above hints
+ // yields no change, the update will not propagate to the
+ // parent.
+ if (hint & nsChangeHint_UpdateParentOverflow) {
+ MOZ_ASSERT(frame->GetParent(),
+ "shouldn't get style hints for the root frame");
+ for (nsIFrame* cont = frame; cont; cont =
+ nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
+ aOverflowChangedTracker.AddFrame(cont->GetParent(),
+ OverflowChangedTracker::CHILDREN_CHANGED);
+ }
+ }
+ }
+ }
+ if ((hint & nsChangeHint_UpdateCursor) && !didUpdateCursor) {
+ aPresContext.PresShell()->SynthesizeMouseMove(false);
+ didUpdateCursor = true;
+ }
+ }
+ }
+
+ frameConstructor->EndUpdate();
+
+ // cleanup references and verify the style tree. Note that the latter needs
+ // to happen once we've processed the whole list, since until then the tree
+ // is not in fact in a consistent state.
+ index = count;
+ while (0 <= --index) {
+ const nsStyleChangeData* changeData;
+ aChangeList.ChangeAt(index, &changeData);
+ if (changeData->mFrame) {
+ propTable->Delete(changeData->mFrame, ChangeListProperty());
+ }
+
+#ifdef DEBUG
+ // reget frame from content since it may have been regenerated...
+ if (changeData->mContent) {
+ nsIFrame* frame = changeData->mContent->GetPrimaryFrame();
+ if (frame) {
+ DebugVerifyStyleTree(frame);
+ }
+ } else if (!changeData->mFrame ||
+ changeData->mFrame->GetType() != nsGkAtoms::viewportFrame) {
+ NS_WARNING("Unable to test style tree integrity -- no content node "
+ "(and not a viewport frame)");
+ }
+#endif
+ }
+
+ aChangeList.Clear();
+ return NS_OK;
+}
} // namespace mozilla
--- a/layout/base/RestyleManagerBase.h
+++ b/layout/base/RestyleManagerBase.h
@@ -4,18 +4,21 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_RestyleManagerBase_h
#define mozilla_RestyleManagerBase_h
#include "nsChangeHint.h"
+class nsStyleChangeList;
+
namespace mozilla {
+class OverflowChangedTracker;
class ServoRestyleManager;
class RestyleManager;
/**
* Class for sharing data and logic common to both RestyleManager and
* ServoRestyleManager.
*/
class RestyleManagerBase
@@ -39,16 +42,23 @@ public:
void SetObservingRefreshDriver(bool aObserving) {
mObservingRefreshDriver = aObserving;
}
void Disconnect() { mPresContext = nullptr; }
static nsCString RestyleHintToString(nsRestyleHint aHint);
+#ifdef DEBUG
+ /**
+ * DEBUG ONLY method to verify integrity of style tree versus frame tree
+ */
+ static void DebugVerifyStyleTree(nsIFrame* aFrame);
+#endif
+
protected:
void ContentStateChangedInternal(Element* aElement,
EventStates aStateMask,
nsChangeHint* aOutChangeHint,
nsRestyleHint* aOutRestyleHint);
bool IsDisconnected() { return mPresContext == nullptr; }
@@ -87,13 +97,29 @@ protected:
}
private:
nsPresContext* mPresContext; // weak, can be null after Disconnect().
uint32_t mRestyleGeneration;
uint32_t mHoverGeneration;
// True if we're already waiting for a refresh notification.
bool mObservingRefreshDriver;
+
+ /**
+ * These are protected static methods that help with the frame construction
+ * bits of the restyle managers.
+ */
+protected:
+ static nsIFrame*
+ GetNearestAncestorFrame(nsIContent* aContent);
+
+ static nsIFrame*
+ GetNextBlockInInlineSibling(FramePropertyTable* aPropTable, nsIFrame* aFrame);
+
+ static nsresult
+ ProcessRestyledFrames(nsStyleChangeList& aChangeList,
+ nsPresContext& aPresContext,
+ OverflowChangedTracker& aOverflowChangedTracker);
};
} // namespace mozilla
#endif
--- a/layout/base/RestyleManagerHandle.h
+++ b/layout/base/RestyleManagerHandle.h
@@ -20,16 +20,17 @@ class ServoRestyleManager;
namespace dom {
class Element;
} // namespace dom
} // namespace mozilla
class nsAttrValue;
class nsIAtom;
class nsIContent;
class nsIFrame;
+class nsStyleChangeList;
namespace mozilla {
#define SERVO_BIT 0x1
/**
* Smart pointer class that can hold a pointer to either a RestyleManager
* or a ServoRestyleManager.
@@ -132,16 +133,18 @@ public:
nsIAtom* aAttribute,
int32_t aModType,
const nsAttrValue* aOldValue);
inline nsresult ReparentStyleContext(nsIFrame* aFrame);
inline bool HasPendingRestyles();
inline uint64_t GetRestyleGeneration() const;
inline uint32_t GetHoverGeneration() const;
inline void SetObservingRefreshDriver(bool aObserving);
+ inline nsresult ProcessRestyledFrames(nsStyleChangeList& aChangeList);
+ inline void FlushOverflowChangedTracker();
private:
// Stores a pointer to an RestyleManager or a ServoRestyleManager. The least
// significant bit is 0 for the former, 1 for the latter. This is
// valid as the least significant bit will never be used for a pointer
// value on platforms we care about.
uintptr_t mValue;
};
--- a/layout/base/RestyleManagerHandleInlines.h
+++ b/layout/base/RestyleManagerHandleInlines.h
@@ -69,16 +69,28 @@ RestyleManagerHandle::Ptr::PostRebuildAl
}
void
RestyleManagerHandle::Ptr::ProcessPendingRestyles()
{
FORWARD(ProcessPendingRestyles, ());
}
+nsresult
+RestyleManagerHandle::Ptr::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
+{
+ FORWARD(ProcessRestyledFrames, (aChangeList));
+}
+
+void
+RestyleManagerHandle::Ptr::FlushOverflowChangedTracker()
+{
+ FORWARD(FlushOverflowChangedTracker, ());
+}
+
void
RestyleManagerHandle::Ptr::RestyleForInsertOrChange(dom::Element* aContainer,
nsIContent* aChild)
{
FORWARD(RestyleForInsertOrChange, (aContainer, aChild));
}
void
--- a/layout/base/ServoRestyleManager.cpp
+++ b/layout/base/ServoRestyleManager.cpp
@@ -306,9 +306,23 @@ ServoRestyleManager::ReparentStyleContex
ServoElementSnapshot*
ServoRestyleManager::SnapshotForElement(Element* aElement)
{
// NB: aElement is the argument for the construction of the snapshot in the
// not found case.
return mModifiedElements.LookupOrAdd(aElement, aElement);
}
+nsresult
+ServoRestyleManager::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
+{
+ MOZ_CRASH("stylo: ServoRestyleManager::ProcessRestyledFrames not implemented "
+ "for Servo-backed style system");
+}
+
+void
+ServoRestyleManager::FlushOverflowChangedTracker()
+{
+ MOZ_CRASH("stylo: ServoRestyleManager::FlushOverflowChangedTracker "
+ "not implemented for Servo-backed style system");
+}
+
} // namespace mozilla
--- a/layout/base/ServoRestyleManager.h
+++ b/layout/base/ServoRestyleManager.h
@@ -20,16 +20,17 @@ namespace mozilla {
namespace dom {
class Element;
} // namespace dom
} // namespace mozilla
class nsAttrValue;
class nsIAtom;
class nsIContent;
class nsIFrame;
+class nsStyleChangeList;
namespace mozilla {
/**
* Restyle manager for a Servo-backed style system.
*/
class ServoRestyleManager : public RestyleManagerBase
{
@@ -64,16 +65,18 @@ public:
const nsAttrValue* aNewValue);
// XXXbholley: We should assert that the element is already snapshotted.
void AttributeChanged(dom::Element* aElement, int32_t aNameSpaceID,
nsIAtom* aAttribute, int32_t aModType,
const nsAttrValue* aOldValue) {}
nsresult ReparentStyleContext(nsIFrame* aFrame);
+ nsresult ProcessRestyledFrames(nsStyleChangeList& aChangeList);
+ void FlushOverflowChangedTracker();
bool HasPendingRestyles() { return !mModifiedElements.IsEmpty(); }
protected:
~ServoRestyleManager() {}
private:
ServoElementSnapshot* SnapshotForElement(Element* aElement);
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -7,16 +7,17 @@
* construction of a frame tree that is nearly isomorphic to the content
* tree and updating of that tree in response to dynamic changes
*/
#ifndef nsCSSFrameConstructor_h___
#define nsCSSFrameConstructor_h___
#include "mozilla/Attributes.h"
+#include "mozilla/RestyleManagerBase.h"
#include "mozilla/RestyleManagerHandle.h"
#include "nsCOMPtr.h"
#include "nsILayoutHistoryState.h"
#include "nsQuoteList.h"
#include "nsCounterManager.h"
#include "nsIAnonymousContentCreator.h"
#include "nsFrameManager.h"
@@ -48,16 +49,17 @@ class FlattenedChildIterator;
class nsCSSFrameConstructor : public nsFrameManager
{
public:
typedef mozilla::CSSPseudoElementType CSSPseudoElementType;
typedef mozilla::dom::Element Element;
friend class mozilla::RestyleManager;
+ friend class mozilla::RestyleManagerBase;
nsCSSFrameConstructor(nsIDocument* aDocument, nsIPresShell* aPresShell);
~nsCSSFrameConstructor(void) {
NS_ASSERTION(mUpdateCount == 0, "Dying in the middle of our own update?");
}
// get the alternate text for a content node
static void GetAlternateTextFor(nsIContent* aContent,
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -52,16 +52,17 @@
#include "GeckoProfiler.h"
#include "nsCSSRenderingBorders.h"
#include "mozilla/css/ImageLoader.h"
#include "ImageContainer.h"
#include "mozilla/Telemetry.h"
#include "gfxUtils.h"
#include "gfxGradientCache.h"
#include "nsInlineFrame.h"
+#include "nsRubyTextContainerFrame.h"
#include <algorithm>
using namespace mozilla;
using namespace mozilla::css;
using namespace mozilla::gfx;
using namespace mozilla::image;
using mozilla::CSSSizeOrRatio;
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -2894,22 +2894,18 @@ PresShell::RecreateFramesFor(nsIContent*
nsAutoScriptBlocker scriptBlocker;
nsStyleChangeList changeList;
changeList.AppendChange(nullptr, aContent, nsChangeHint_ReconstructFrame);
// Mark ourselves as not safe to flush while we're doing frame construction.
++mChangeNestCount;
RestyleManagerHandle restyleManager = mPresContext->RestyleManager();
- if (restyleManager->IsServo()) {
- MOZ_CRASH("stylo: PresShell::RecreateFramesFor not implemented for Servo-"
- "backed style system");
- }
- nsresult rv = restyleManager->AsGecko()->ProcessRestyledFrames(changeList);
- restyleManager->AsGecko()->FlushOverflowChangedTracker();
+ nsresult rv = restyleManager->ProcessRestyledFrames(changeList);
+ restyleManager->FlushOverflowChangedTracker();
--mChangeNestCount;
return rv;
}
void
nsIPresShell::PostRecreateFramesFor(Element* aElement)
{