summaryrefslogtreecommitdiffstats
path: root/layout/base/RestyleManagerBase.cpp
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /layout/base/RestyleManagerBase.cpp
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'layout/base/RestyleManagerBase.cpp')
-rw-r--r--layout/base/RestyleManagerBase.cpp1378
1 files changed, 1378 insertions, 0 deletions
diff --git a/layout/base/RestyleManagerBase.cpp b/layout/base/RestyleManagerBase.cpp
new file mode 100644
index 000000000..9a5ce43eb
--- /dev/null
+++ b/layout/base/RestyleManagerBase.cpp
@@ -0,0 +1,1378 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#include "mozilla/RestyleManagerBase.h"
+#include "mozilla/StyleSetHandle.h"
+#include "nsIFrame.h"
+
+namespace mozilla {
+
+RestyleManagerBase::RestyleManagerBase(nsPresContext* aPresContext)
+ : mPresContext(aPresContext)
+ , mRestyleGeneration(1)
+ , mHoverGeneration(0)
+ , mObservingRefreshDriver(false)
+ , mInStyleRefresh(false)
+{
+ MOZ_ASSERT(mPresContext);
+}
+
+/**
+ * Calculates the change hint and the restyle hint for a given content state
+ * change.
+ *
+ * This is called from both Restyle managers.
+ */
+void
+RestyleManagerBase::ContentStateChangedInternal(Element* aElement,
+ EventStates aStateMask,
+ nsChangeHint* aOutChangeHint,
+ nsRestyleHint* aOutRestyleHint)
+{
+ MOZ_ASSERT(aOutChangeHint);
+ MOZ_ASSERT(aOutRestyleHint);
+
+ StyleSetHandle styleSet = PresContext()->StyleSet();
+ NS_ASSERTION(styleSet, "couldn't get style set");
+
+ *aOutChangeHint = nsChangeHint(0);
+ // Any change to a content state that affects which frames we construct
+ // must lead to a frame reconstruct here if we already have a frame.
+ // Note that we never decide through non-CSS means to not create frames
+ // based on content states, so if we already don't have a frame we don't
+ // need to force a reframe -- if it's needed, the HasStateDependentStyle
+ // call will handle things.
+ nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
+ CSSPseudoElementType pseudoType = CSSPseudoElementType::NotPseudo;
+ if (primaryFrame) {
+ // If it's generated content, ignore LOADING/etc state changes on it.
+ if (!primaryFrame->IsGeneratedContentFrame() &&
+ aStateMask.HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN |
+ NS_EVENT_STATE_USERDISABLED |
+ NS_EVENT_STATE_SUPPRESSED |
+ NS_EVENT_STATE_LOADING)) {
+ *aOutChangeHint = nsChangeHint_ReconstructFrame;
+ } else {
+ uint8_t app = primaryFrame->StyleDisplay()->mAppearance;
+ if (app) {
+ nsITheme* theme = PresContext()->GetTheme();
+ if (theme &&
+ theme->ThemeSupportsWidget(PresContext(), primaryFrame, app)) {
+ bool repaint = false;
+ theme->WidgetStateChanged(primaryFrame, app, nullptr, &repaint,
+ nullptr);
+ if (repaint) {
+ *aOutChangeHint |= nsChangeHint_RepaintFrame;
+ }
+ }
+ }
+ }
+
+ pseudoType = primaryFrame->StyleContext()->GetPseudoType();
+
+ primaryFrame->ContentStatesChanged(aStateMask);
+ }
+
+ if (pseudoType >= CSSPseudoElementType::Count) {
+ *aOutRestyleHint = styleSet->HasStateDependentStyle(aElement, aStateMask);
+ } else if (nsCSSPseudoElements::PseudoElementSupportsUserActionState(
+ pseudoType)) {
+ // If aElement is a pseudo-element, we want to check to see whether there
+ // are any state-dependent rules applying to that pseudo.
+ Element* ancestor =
+ ElementForStyleContext(nullptr, primaryFrame, pseudoType);
+ *aOutRestyleHint = styleSet->HasStateDependentStyle(ancestor, pseudoType,
+ aElement, aStateMask);
+ } else {
+ *aOutRestyleHint = nsRestyleHint(0);
+ }
+
+ if (aStateMask.HasState(NS_EVENT_STATE_HOVER) && *aOutRestyleHint != 0) {
+ IncrementHoverGeneration();
+ }
+
+ if (aStateMask.HasState(NS_EVENT_STATE_VISITED)) {
+ // Exposing information to the page about whether the link is
+ // visited or not isn't really something we can worry about here.
+ // FIXME: We could probably do this a bit better.
+ *aOutChangeHint |= nsChangeHint_RepaintFrame;
+ }
+}
+
+/* static */ nsCString
+RestyleManagerBase::RestyleHintToString(nsRestyleHint aHint)
+{
+ nsCString result;
+ bool any = false;
+ const char* names[] = {
+ "Self", "SomeDescendants", "Subtree", "LaterSiblings", "CSSTransitions",
+ "CSSAnimations", "SVGAttrAnimations", "StyleAttribute",
+ "StyleAttribute_Animations", "Force", "ForceDescendants"
+ };
+ uint32_t hint = aHint & ((1 << ArrayLength(names)) - 1);
+ uint32_t rest = aHint & ~((1 << ArrayLength(names)) - 1);
+ for (uint32_t i = 0; i < ArrayLength(names); i++) {
+ if (hint & (1 << i)) {
+ if (any) {
+ result.AppendLiteral(" | ");
+ }
+ result.AppendPrintf("eRestyle_%s", names[i]);
+ any = true;
+ }
+ }
+ if (rest) {
+ if (any) {
+ result.AppendLiteral(" | ");
+ }
+ result.AppendPrintf("0x%0x", rest);
+ } else {
+ if (!any) {
+ result.AppendLiteral("0");
+ }
+ }
+ return result;
+}
+
+#ifdef DEBUG
+/* static */ nsCString
+RestyleManagerBase::ChangeHintToString(nsChangeHint aHint)
+{
+ nsCString result;
+ bool any = false;
+ const char* names[] = {
+ "RepaintFrame", "NeedReflow", "ClearAncestorIntrinsics",
+ "ClearDescendantIntrinsics", "NeedDirtyReflow", "SyncFrameView",
+ "UpdateCursor", "UpdateEffects", "UpdateOpacityLayer",
+ "UpdateTransformLayer", "ReconstructFrame", "UpdateOverflow",
+ "UpdateSubtreeOverflow", "UpdatePostTransformOverflow",
+ "UpdateParentOverflow",
+ "ChildrenOnlyTransform", "RecomputePosition", "AddOrRemoveTransform",
+ "BorderStyleNoneChange", "UpdateTextPath", "SchedulePaint",
+ "NeutralChange", "InvalidateRenderingObservers",
+ "ReflowChangesSizeOrPosition", "UpdateComputedBSize",
+ "UpdateUsesOpacity", "UpdateBackgroundPosition",
+ "AddOrRemoveTransform"
+ };
+ static_assert(nsChangeHint_AllHints == (1 << ArrayLength(names)) - 1,
+ "Name list doesn't match change hints.");
+ uint32_t hint = aHint & ((1 << ArrayLength(names)) - 1);
+ uint32_t rest = aHint & ~((1 << ArrayLength(names)) - 1);
+ if (hint == nsChangeHint_Hints_NotHandledForDescendants) {
+ result.AppendLiteral("nsChangeHint_Hints_NotHandledForDescendants");
+ hint = 0;
+ any = true;
+ } else {
+ if ((hint & NS_STYLE_HINT_REFLOW) == NS_STYLE_HINT_REFLOW) {
+ result.AppendLiteral("NS_STYLE_HINT_REFLOW");
+ hint = hint & ~NS_STYLE_HINT_REFLOW;
+ any = true;
+ } else if ((hint & nsChangeHint_AllReflowHints) == nsChangeHint_AllReflowHints) {
+ result.AppendLiteral("nsChangeHint_AllReflowHints");
+ hint = hint & ~nsChangeHint_AllReflowHints;
+ any = true;
+ } else if ((hint & NS_STYLE_HINT_VISUAL) == NS_STYLE_HINT_VISUAL) {
+ result.AppendLiteral("NS_STYLE_HINT_VISUAL");
+ hint = hint & ~NS_STYLE_HINT_VISUAL;
+ any = true;
+ }
+ }
+ for (uint32_t i = 0; i < ArrayLength(names); i++) {
+ if (hint & (1 << i)) {
+ if (any) {
+ result.AppendLiteral(" | ");
+ }
+ result.AppendPrintf("nsChangeHint_%s", names[i]);
+ any = true;
+ }
+ }
+ if (rest) {
+ if (any) {
+ result.AppendLiteral(" | ");
+ }
+ result.AppendPrintf("0x%0x", rest);
+ } else {
+ if (!any) {
+ result.AppendLiteral("nsChangeHint(0)");
+ }
+ }
+ return result;
+}
+#endif
+
+void
+RestyleManagerBase::PostRestyleEventInternal(bool aForLazyConstruction)
+{
+ // Make sure we're not in a style refresh; if we are, we still have
+ // a call to ProcessPendingRestyles coming and there's no need to
+ // add ourselves as a refresh observer until then.
+ bool inRefresh = !aForLazyConstruction && mInStyleRefresh;
+ nsIPresShell* presShell = PresContext()->PresShell();
+ if (!ObservingRefreshDriver() && !inRefresh) {
+ SetObservingRefreshDriver(PresContext()->RefreshDriver()->
+ AddStyleFlushObserver(presShell));
+ }
+
+ // Unconditionally flag our document as needing a flush. The other
+ // option here would be a dedicated boolean to track whether we need
+ // to do so (set here and unset in ProcessPendingRestyles).
+ presShell->GetDocument()->SetNeedStyleFlush();
+}
+
+/**
+ * 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.
+ // First, we need to ensure that the normal position is stored though.
+ nsPoint normalPosition = cont->GetNormalPosition();
+ auto props = cont->Properties();
+ const auto& prop = nsIFrame::NormalPositionProperty();
+ if (!props.Get(prop)) {
+ props.Set(prop, new nsPoint(normalPosition));
+ }
+ cont->SetPosition(normalPosition +
+ 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_WARNING_ASSERTION(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)
+{
+ MOZ_ASSERT(aPositionMask & (1 << NS_STYLE_POSITION_FIXED));
+
+ for (nsIFrame::ChildListIterator lists(aFrame); !lists.IsDone(); lists.Next()) {
+ 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;
+ }
+ }
+ uint32_t positionMask = aPositionMask;
+ // NOTE: It's tempting to check f->IsAbsPosContainingBlock() or
+ // f->IsFixedPosContainingBlock() here. However, that would only
+ // be testing the *new* style of the frame, which might exclude
+ // descendants that currently have this frame as an abs-pos
+ // containing block. Taking the codepath where we don't reframe
+ // could lead to an unsafe call to
+ // cont->MarkAsNotAbsoluteContainingBlock() before we've reframed
+ // the descendant and taken it off the absolute list.
+ if (FrameHasPositionedPlaceholderDescendants(f, positionMask)) {
+ 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_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);
+ }
+ }
+ }
+ }
+}
+
+static 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 */ nsIFrame*
+RestyleManagerBase::GetNextContinuationWithSameStyle(
+ nsIFrame* aFrame, nsStyleContext* aOldStyleContext,
+ bool* aHaveMoreContinuations)
+{
+ // See GetPrevContinuationWithSameStyle about {ib} splits.
+
+ nsIFrame* nextContinuation = aFrame->GetNextContinuation();
+ if (!nextContinuation &&
+ (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
+ // We're the last continuation, so we have to hop back to the first
+ // before getting the frame property
+ nextContinuation =
+ aFrame->FirstContinuation()->Properties().Get(nsIFrame::IBSplitSibling());
+ if (nextContinuation) {
+ nextContinuation =
+ nextContinuation->Properties().Get(nsIFrame::IBSplitSibling());
+ }
+ }
+
+ if (!nextContinuation) {
+ return nullptr;
+ }
+
+ NS_ASSERTION(nextContinuation->GetContent() == aFrame->GetContent(),
+ "unexpected content mismatch");
+
+ nsStyleContext* nextStyle = nextContinuation->StyleContext();
+ if (nextStyle != aOldStyleContext) {
+ NS_ASSERTION(aOldStyleContext->GetPseudo() != nextStyle->GetPseudo() ||
+ aOldStyleContext->GetParent() != nextStyle->GetParent(),
+ "continuations should have the same style context");
+ nextContinuation = nullptr;
+ if (aHaveMoreContinuations) {
+ *aHaveMoreContinuations = true;
+ }
+ }
+ return nextContinuation;
+}
+
+nsresult
+RestyleManagerBase::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
+{
+ NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
+ "Someone forgot a script blocker");
+ if (aChangeList.IsEmpty())
+ return NS_OK;
+
+ PROFILER_LABEL("RestyleManager", "ProcessRestyledFrames",
+ js::ProfileEntry::Category::CSS);
+
+ nsPresContext* presContext = PresContext();
+ FramePropertyTable* propTable = presContext->PropertyTable();
+ nsCSSFrameConstructor* frameConstructor = presContext->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
+ for (const nsStyleChangeData& data : aChangeList) {
+ if (data.mFrame) {
+ propTable->Set(data.mFrame, ChangeListProperty(), true);
+ }
+ }
+
+ bool didUpdateCursor = false;
+
+ for (const nsStyleChangeData& data : aChangeList) {
+ nsIFrame* frame = data.mFrame;
+ nsIContent* content = data.mContent;
+ nsChangeHint hint = data.mHint;
+ bool didReflowThisFrame = false;
+
+ 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 change hint that is handled for
+ // descendants).
+ if (cont->IsAbsPosContainingBlock()) {
+ if (!cont->IsAbsoluteContainer() &&
+ (cont->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)) {
+ cont->MarkAsAbsoluteContainingBlock();
+ }
+ } else {
+ if (cont->IsAbsoluteContainer()) {
+ if (cont->HasAbsolutelyPositionedChildren()) {
+ // If |cont| still has absolutely positioned children,
+ // we can't call MarkAsNotAbsoluteContainingBlock. This
+ // will remove a frame list that still has children in
+ // it that we need to keep track of.
+ // The optimization of removing it isn't particularly
+ // important, although it does mean we skip some tests.
+ NS_WARNING("skipping removal of absolute containing block");
+ } else {
+ cont->MarkAsNotAbsoluteContainingBlock();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ((hint & nsChangeHint_AddOrRemoveTransform) && frame &&
+ !(hint & nsChangeHint_ReconstructFrame)) {
+ for (nsIFrame* cont = frame; cont;
+ cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
+ if (cont->StyleDisplay()->HasTransform(cont)) {
+ cont->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
+ }
+ // Don't remove NS_FRAME_MAY_BE_TRANSFORMED since it may still be
+ // transformed by other means. It's OK to have the bit even if it's
+ // not needed.
+ }
+ }
+
+ 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;
+ }
+
+ // Opacity disables preserve-3d, so if we toggle it, then we also need
+ // to update the overflow areas of all potentially affected frames.
+ if ((hint & nsChangeHint_UpdateUsesOpacity) &&
+ frame->StyleDisplay()->mTransformStyle == NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D) {
+ hint |= nsChangeHint_UpdateSubtreeOverflow;
+ }
+
+ 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->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, mOverflowChangedTracker);
+ }
+ // 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.
+ for (const nsStyleChangeData& data : aChangeList) {
+ if (data.mFrame) {
+ propTable->Delete(data.mFrame, ChangeListProperty());
+ }
+
+#ifdef DEBUG
+ // reget frame from content since it may have been regenerated...
+ if (data.mContent) {
+ nsIFrame* frame = data.mContent->GetPrimaryFrame();
+ if (frame) {
+ DebugVerifyStyleTree(frame);
+ }
+ } else if (!data.mFrame ||
+ data.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