diff options
Diffstat (limited to 'layout/base/RestyleManager.cpp')
-rw-r--r-- | layout/base/RestyleManager.cpp | 142 |
1 files changed, 98 insertions, 44 deletions
diff --git a/layout/base/RestyleManager.cpp b/layout/base/RestyleManager.cpp index 8c07512093..e00493467f 100644 --- a/layout/base/RestyleManager.cpp +++ b/layout/base/RestyleManager.cpp @@ -2087,6 +2087,28 @@ void RestyleManager::AnimationsWithDestroyedFrame ::StopAnimationsWithoutFrame( } } +// When using handled hints by an ancestor, we need to make sure that our +// ancestor in the DOM tree is actually our ancestor in the flat tree. +// Otherwise, we can't guarantee that e.g. a repaint from an ancestor in the DOM +// will really end up repainting us. +static bool CanUseHandledHintsFromAncestors(const nsIFrame* aFrame) { + if (aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) { + // An out of flow can be parented in other part of the tree. + return false; + } + if (aFrame->IsColumnSpanInMulticolSubtree()) { + // Any column-spanner's parent frame is not its DOM parent's primary frame. + return false; + } + if (aFrame->IsTableCaption()) { + // This one is more subtle. captions are in-flow children of the table + // frame. But they are parented to the table wrapper. So hints handled for + // the inner table might not be applicable to us. + return false; + } + return true; +} + #ifdef DEBUG static bool IsAnonBox(const nsIFrame* aFrame) { return aFrame->Style()->IsAnonBox(); @@ -2185,8 +2207,7 @@ static bool IsInReplicatedFixedPosTree(const nsIFrame* aFrame) { void ServoRestyleState::AssertOwner(const ServoRestyleState& aParent) const { MOZ_ASSERT(mOwner); - MOZ_ASSERT(!mOwner->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)); - MOZ_ASSERT(!mOwner->IsColumnSpanInMulticolSubtree()); + MOZ_ASSERT(CanUseHandledHintsFromAncestors(mOwner)); // We allow aParent.mOwner to be null, for cases when we're not starting at // the root of the tree. We also allow aParent.mOwner to be somewhere up our // expected owner chain not our immediate owner, which allows us creating long @@ -2309,7 +2330,7 @@ size_t ServoRestyleState::ProcessMaybeNestedWrapperRestyle(nsIFrame* aParent, nsLayoutUtils::FirstContinuationOrIBSplitSibling(parent); if (parentForRestyle != aParent) { parentRestyleState.emplace(*parentForRestyle, *this, nsChangeHint_Empty, - Type::InFlow); + CanUseHandledHints::Yes); } ServoRestyleState& curRestyleState = parentRestyleState ? *parentRestyleState : *this; @@ -2332,7 +2353,7 @@ size_t ServoRestyleState::ProcessMaybeNestedWrapperRestyle(nsIFrame* aParent, // the other hand, presumably our mChangesHandled already has the bits // we really want here so in practice it doesn't matter. ServoRestyleState childState(*cur, curRestyleState, nsChangeHint_Empty, - Type::InFlow, + CanUseHandledHints::Yes, /* aAssertWrapperRestyleLength = */ false); numProcessed += childState.ProcessMaybeNestedWrapperRestyle(cur, aIndex + 1); @@ -2652,8 +2673,7 @@ static void UpdateOneAdditionalComputedStyle(nsIFrame* aFrame, uint32_t aIndex, uint32_t equalStructs; // Not used, actually. nsChangeHint childHint = aOldContext.CalcStyleDifference(*newStyle, &equalStructs); - if (!aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) && - !aFrame->IsColumnSpanInMulticolSubtree()) { + if (CanUseHandledHintsFromAncestors(aFrame)) { childHint = NS_RemoveSubsumedHints(childHint, aRestyleState.ChangesHandledFor(aFrame)); } @@ -2812,11 +2832,8 @@ bool RestyleManager::ProcessPostTraversal(Element* aElement, const bool isOutOfFlow = primaryFrame && primaryFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW); - // We need this because any column-spanner's parent frame is not its DOM - // parent's primary frame. We need some special check similar to out-of-flow - // frames. - const bool isColumnSpan = - primaryFrame && primaryFrame->IsColumnSpanInMulticolSubtree(); + const bool canUseHandledHints = + primaryFrame && CanUseHandledHintsFromAncestors(primaryFrame); // Grab the change hint from Servo. bool wasRestyled = false; @@ -2848,11 +2865,12 @@ bool RestyleManager::ProcessPostTraversal(Element* aElement, maybeAnonBoxChild = primaryFrame->GetPlaceholderFrame(); } else { maybeAnonBoxChild = primaryFrame; - // Do not subsume change hints for the column-spanner. - if (!isColumnSpan) { - changeHint = NS_RemoveSubsumedHints( - changeHint, aRestyleState.ChangesHandledFor(styleFrame)); - } + } + + // Do not subsume change hints for the column-spanner. + if (canUseHandledHints) { + changeHint = NS_RemoveSubsumedHints( + changeHint, aRestyleState.ChangesHandledFor(styleFrame)); } // If the parent wasn't restyled, the styles of our anon box parents won't @@ -2944,10 +2962,9 @@ bool RestyleManager::ProcessPostTraversal(Element* aElement, Maybe<ServoRestyleState> thisFrameRestyleState; if (styleFrame) { - auto type = isOutOfFlow || isColumnSpan ? ServoRestyleState::Type::OutOfFlow - : ServoRestyleState::Type::InFlow; - - thisFrameRestyleState.emplace(*styleFrame, aRestyleState, changeHint, type); + thisFrameRestyleState.emplace( + *styleFrame, aRestyleState, changeHint, + ServoRestyleState::CanUseHandledHints(canUseHandledHints)); } // We can't really assume as used changes from display: contents elements (or @@ -3229,6 +3246,12 @@ void RestyleManager::DoProcessPendingRestyles(ServoTraversalFlags aFlags) { // construction). nsTArray<RefPtr<Element>> anchorsToSuppress; + // Unfortunately, the frame constructor and ProcessPostTraversal can + // generate new change hints while processing existing ones. We redirect + // those into a secondary queue and iterate until there's nothing left. + ReentrantChangeList newChanges; + mReentrantChanges = &newChanges; + { DocumentStyleRootIterator iter(doc->GetServoRestyleRoot()); while (Element* root = iter.GetNextStyleRoot()) { @@ -3254,34 +3277,26 @@ void RestyleManager::DoProcessPendingRestyles(ServoTraversalFlags aFlags) { doc->ClearServoRestyleRoot(); ClearSnapshots(); - // Process the change hints. - // - // Unfortunately, the frame constructor can generate new change hints while - // processing existing ones. We redirect those into a secondary queue and - // iterate until there's nothing left. - { - ReentrantChangeList newChanges; - mReentrantChanges = &newChanges; - while (!currentChanges.IsEmpty()) { - ProcessRestyledFrames(currentChanges); - MOZ_ASSERT(currentChanges.IsEmpty()); - for (ReentrantChange& change : newChanges) { - if (!(change.mHint & nsChangeHint_ReconstructFrame) && - !change.mContent->GetPrimaryFrame()) { - // SVG Elements post change hints without ensuring that the primary - // frame will be there after that (see bug 1366142). - // - // Just ignore those, since we can't really process them. - continue; - } - currentChanges.AppendChange(change.mContent->GetPrimaryFrame(), - change.mContent, change.mHint); + while (!currentChanges.IsEmpty()) { + ProcessRestyledFrames(currentChanges); + MOZ_ASSERT(currentChanges.IsEmpty()); + for (ReentrantChange& change : newChanges) { + if (!(change.mHint & nsChangeHint_ReconstructFrame) && + !change.mContent->GetPrimaryFrame()) { + // SVG Elements post change hints without ensuring that the primary + // frame will be there after that (see bug 1366142). + // + // Just ignore those, since we can't really process them. + continue; } - newChanges.Clear(); + currentChanges.AppendChange(change.mContent->GetPrimaryFrame(), + change.mContent, change.mHint); } - mReentrantChanges = nullptr; + newChanges.Clear(); } + mReentrantChanges = nullptr; + // Suppress adjustments in the after-change scroll anchors if needed, now // that we're done reframing everything. for (Element* element : anchorsToSuppress) { @@ -3435,6 +3450,45 @@ void RestyleManager::ElementStateChanged(Element* aElement, MaybeRestyleForRelativeSelectorState(styleSet, aElement, aChangedBits); } +void RestyleManager::CustomStatesWillChange(Element& aElement) { + MOZ_DIAGNOSTIC_ASSERT(!mInStyleRefresh); + + IncrementUndisplayedRestyleGeneration(); + + // Relative selector invalidation travels ancestor and earlier sibling + // direction, so it's very possible that it invalidates a styled element. + if (!aElement.HasServoData() && + !(aElement.GetSelectorFlags() & + NodeSelectorFlags::RelativeSelectorSearchDirectionAncestorSibling)) { + return; + } + + ServoElementSnapshot& snapshot = SnapshotFor(aElement); + snapshot.AddCustomStates(aElement); +} + +void RestyleManager::CustomStateChanged(Element& aElement, nsAtom* aState) { + ServoStyleSet& styleSet = *StyleSet(); + MaybeRestyleForNthOfCustomState(styleSet, aElement, aState); + styleSet.MaybeInvalidateRelativeSelectorCustomStateDependency( + aElement, aState, Snapshots()); +} + +void RestyleManager::MaybeRestyleForNthOfCustomState(ServoStyleSet& aStyleSet, + Element& aChild, + nsAtom* aState) { + const auto* parentNode = aChild.GetParentNode(); + MOZ_ASSERT(parentNode); + const auto parentFlags = parentNode->GetSelectorFlags(); + if (!(parentFlags & NodeSelectorFlags::HasSlowSelectorNthOf)) { + return; + } + + if (aStyleSet.HasNthOfCustomStateDependency(aChild, aState)) { + RestyleSiblingsForNthOf(&aChild, parentFlags); + } +} + void RestyleManager::MaybeRestyleForNthOfState(ServoStyleSet& aStyleSet, Element* aChild, ElementState aChangedBits) { |