void ServoRestyleManager::ProcessPostTraversalForText( nsIContent* aTextNode, nsStyleChangeList& aChangeList, TextPostTraversalState& aPostTraversalState) { // Handle lazy frame construction. if (aTextNode->HasFlag(NODE_NEEDS_FRAME)) { aChangeList.AppendChange(nullptr, aTextNode, nsChangeHint_ReconstructFrame); return; } // Handle restyle. nsIFrame* primaryFrame = aTextNode->GetPrimaryFrame(); if (primaryFrame) { RefPtr<nsStyleContext> oldStyleContext = primaryFrame->StyleContext(); nsStyleContext& newContext = aPostTraversalState.ComputeStyle(aTextNode); aPostTraversalState.ComputeHintIfNeeded( aTextNode, primaryFrame, newContext, aChangeList); for (nsIFrame* f = primaryFrame; f; f = GetNextContinuationWithSameStyle(f, oldStyleContext)) { f->SetStyleContext(&newContext); } } }
void ComputeHintIfNeeded(nsIContent* aContent, nsIFrame* aTextFrame, nsStyleContext& aNewContext, nsStyleChangeList& aChangeList) { MOZ_ASSERT(aTextFrame); MOZ_ASSERT(aNewContext.GetPseudo() == nsCSSAnonBoxes::mozText); if (MOZ_LIKELY(!mShouldPostHints)) { return; } nsStyleContext* oldContext = aTextFrame->StyleContext(); MOZ_ASSERT(oldContext->GetPseudo() == nsCSSAnonBoxes::mozText); // We rely on the fact that all the text children for the same element share // style to avoid recomputing style differences for all of them. // // TODO(emilio): The above may not be true for ::first-{line,letter}, but // we'll cross that bridge when we support those in stylo. if (mShouldComputeHints) { mShouldComputeHints = false; uint32_t equalStructs, samePointerStructs; mComputedHint = oldContext->CalcStyleDifference(&aNewContext, &equalStructs, &samePointerStructs); } if (mComputedHint) { aChangeList.AppendChange(aTextFrame, aContent, mComputedHint); } }
void ServoRestyleManager::ProcessPostTraversal(Element* aElement, nsStyleContext* aParentContext, ServoStyleSet* aStyleSet, nsStyleChangeList& aChangeList) { nsIFrame* styleFrame = nsLayoutUtils::GetStyleFrame(aElement); // Grab the change hint from Servo. nsChangeHint changeHint = Servo_TakeChangeHint(aElement); // Handle lazy frame construction by posting a reconstruct for any lazily- // constructed roots. if (aElement->HasFlag(NODE_NEEDS_FRAME)) { changeHint |= nsChangeHint_ReconstructFrame; // The only time the primary frame is non-null is when image maps do hacky // SetPrimaryFrame calls. MOZ_ASSERT_IF(styleFrame, styleFrame->IsImageFrame()); styleFrame = nullptr; } // Although we shouldn't generate non-ReconstructFrame hints for elements with // no frames, we can still get them here if they were explicitly posted by // PostRestyleEvent, such as a RepaintFrame hint when a :link changes to be // :visited. Skip processing these hints if there is no frame. if ((styleFrame || (changeHint & nsChangeHint_ReconstructFrame)) && changeHint) { aChangeList.AppendChange(styleFrame, aElement, changeHint); } // If our change hint is reconstruct, we delegate to the frame constructor, // which consumes the new style and expects the old style to be on the frame. // // XXXbholley: We should teach the frame constructor how to clear the dirty // descendants bit to avoid the traversal here. if (changeHint & nsChangeHint_ReconstructFrame) { ClearRestyleStateFromSubtree(aElement); return; } // TODO(emilio): We could avoid some refcount traffic here, specially in the // ServoComputedValues case, which uses atomic refcounting. // // Hold the old style context alive, because it could become a dangling // pointer during the replacement. In practice it's not a huge deal (on // GetNextContinuationWithSameStyle the pointer is not dereferenced, only // compared), but better not playing with dangling pointers if not needed. RefPtr<nsStyleContext> oldStyleContext = styleFrame ? styleFrame->StyleContext() : nullptr; UndisplayedNode* displayContentsNode = nullptr; // FIXME(emilio, bug 1303605): This can be simpler for Servo. // Note that we intentionally don't check for display: none content. if (!oldStyleContext) { displayContentsNode = PresContext()->FrameConstructor()->GetDisplayContentsNodeFor(aElement); if (displayContentsNode) { oldStyleContext = displayContentsNode->mStyle; } } RefPtr<ServoComputedValues> computedValues = aStyleSet->ResolveServoStyle(aElement); // Note that we rely in the fact that we don't cascade pseudo-element styles // separately right now (that is, if a pseudo style changes, the normal style // changes too). // // Otherwise we should probably encode that information somehow to avoid // expensive checks in the common case. // // Also, we're going to need to check for pseudos of display: contents // elements, though that is buggy right now even in non-stylo mode, see // bug 1251799. const bool recreateContext = oldStyleContext && oldStyleContext->StyleSource().AsServoComputedValues() != computedValues; RefPtr<nsStyleContext> newContext = nullptr; if (recreateContext) { MOZ_ASSERT(styleFrame || displayContentsNode); auto pseudo = aElement->GetPseudoElementType(); nsIAtom* pseudoTag = pseudo == CSSPseudoElementType::NotPseudo ? nullptr : nsCSSPseudoElements::GetPseudoAtom(pseudo); newContext = aStyleSet->GetContext(computedValues.forget(), aParentContext, pseudoTag, pseudo, aElement); newContext->EnsureSameStructsCached(oldStyleContext); // XXX This could not always work as expected: there are kinds of content // with the first split and the last sharing style, but others not. We // should handle those properly. // XXXbz I think the UpdateStyleOfOwnedAnonBoxes call below handles _that_ // right, but not other cases where we happen to have different styles on // different continuations... (e.g. first-line). for (nsIFrame* f = styleFrame; f; f = GetNextContinuationWithSameStyle(f, oldStyleContext)) { f->SetStyleContext(newContext); } if (MOZ_UNLIKELY(displayContentsNode)) { MOZ_ASSERT(!styleFrame); displayContentsNode->mStyle = newContext; } if (styleFrame) { styleFrame->UpdateStyleOfOwnedAnonBoxes(*aStyleSet, aChangeList, changeHint); } } const bool descendantsNeedFrames = aElement->HasFlag(NODE_DESCENDANTS_NEED_FRAMES); const bool traverseElementChildren = aElement->HasDirtyDescendantsForServo() || descendantsNeedFrames; const bool traverseTextChildren = recreateContext || descendantsNeedFrames; if (traverseElementChildren || traverseTextChildren) { nsStyleContext* upToDateContext = recreateContext ? newContext : oldStyleContext; StyleChildrenIterator it(aElement); TextPostTraversalState textState( *upToDateContext, *aStyleSet, displayContentsNode && recreateContext); for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) { if (traverseElementChildren && n->IsElement()) { ProcessPostTraversal(n->AsElement(), upToDateContext, aStyleSet, aChangeList); } else if (traverseTextChildren && n->IsNodeOfType(nsINode::eTEXT)) { ProcessPostTraversalForText(n, aChangeList, textState); } } } aElement->UnsetHasDirtyDescendantsForServo(); aElement->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES); }
nsresult RestyleManagerBase::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); 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 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->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. 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 ServoRestyleManager::RecreateStyleContexts(nsIContent* aContent, nsStyleContext* aParentContext, ServoStyleSet* aStyleSet, nsStyleChangeList& aChangeListToProcess) { MOZ_ASSERT(aContent->IsElement() || aContent->IsNodeOfType(nsINode::eTEXT)); nsIFrame* primaryFrame = aContent->GetPrimaryFrame(); if (!primaryFrame && !aContent->IsDirtyForServo()) { // This happens when, for example, a display: none child of a // HAS_DIRTY_DESCENDANTS content is reached as part of the traversal. return; } // Work on text before. if (!aContent->IsElement()) { if (primaryFrame) { RefPtr<nsStyleContext> oldStyleContext = primaryFrame->StyleContext(); RefPtr<nsStyleContext> newContext = aStyleSet->ResolveStyleForText(aContent, aParentContext); for (nsIFrame* f = primaryFrame; f; f = GetNextContinuationWithSameStyle(f, oldStyleContext)) { f->SetStyleContext(newContext); } } aContent->UnsetIsDirtyForServo(); return; } Element* element = aContent->AsElement(); if (element->IsDirtyForServo()) { RefPtr<ServoComputedValues> computedValues = Servo_ComputedValues_Get(aContent).Consume(); MOZ_ASSERT(computedValues); nsChangeHint changeHint = nsChangeHint(0); // Add an explicit change hint if appropriate. ServoElementSnapshot* snapshot; if (mModifiedElements.Get(element, &snapshot)) { changeHint |= snapshot->ExplicitChangeHint(); } // Add the stored change hint if there's a frame. If there isn't a frame, // generate a ReconstructFrame change hint if the new display value // (which we can get from the ComputedValues stored on the node) is not // none. if (primaryFrame) { changeHint |= primaryFrame->StyleContext()->ConsumeStoredChangeHint(); } else { const nsStyleDisplay* currentDisplay = Servo_GetStyleDisplay(computedValues); if (currentDisplay->mDisplay != StyleDisplay::None) { changeHint |= nsChangeHint_ReconstructFrame; } } // Add the new change hint to the list of elements to process if // we need to do any work. if (changeHint) { aChangeListToProcess.AppendChange(primaryFrame, element, changeHint); } // The frame reconstruction step (if needed) will ask for the descendants' // style correctly. If not needed, we're done too. if (!primaryFrame) { aContent->UnsetIsDirtyForServo(); return; } // Hold the old style context alive, because it could become a dangling // pointer during the replacement. In practice it's not a huge deal (on // GetNextContinuationWithSameStyle the pointer is not dereferenced, only // compared), but better not playing with dangling pointers if not needed. RefPtr<nsStyleContext> oldStyleContext = primaryFrame->StyleContext(); MOZ_ASSERT(oldStyleContext); RefPtr<nsStyleContext> newContext = aStyleSet->GetContext(computedValues.forget(), aParentContext, nullptr, CSSPseudoElementType::NotPseudo); // XXX This could not always work as expected: there are kinds of content // with the first split and the last sharing style, but others not. We // should handle those properly. for (nsIFrame* f = primaryFrame; f; f = GetNextContinuationWithSameStyle(f, oldStyleContext)) { f->SetStyleContext(newContext); } // Update pseudo-elements state if appropriate. const static CSSPseudoElementType pseudosToRestyle[] = { CSSPseudoElementType::before, CSSPseudoElementType::after, }; for (CSSPseudoElementType pseudoType : pseudosToRestyle) { nsIAtom* pseudoTag = nsCSSPseudoElements::GetPseudoAtom(pseudoType); if (nsIFrame* pseudoFrame = FrameForPseudoElement(element, pseudoTag)) { // TODO: we could maybe make this more performant via calling into // Servo just once to know which pseudo-elements we've got to restyle? RefPtr<nsStyleContext> pseudoContext = aStyleSet->ProbePseudoElementStyle(element, pseudoType, newContext); // If pseudoContext is null here, it means the frame is going away, so // our change hint computation should have already indicated we need // to reframe. MOZ_ASSERT_IF(!pseudoContext, changeHint & nsChangeHint_ReconstructFrame); if (pseudoContext) { pseudoFrame->SetStyleContext(pseudoContext); // We only care restyling text nodes, since other type of nodes // (images), are still not supported. If that eventually changes, we // may have to write more code here... Or not, I don't think too // many inherited properties can affect those other frames. StyleChildrenIterator it(pseudoFrame->GetContent()); for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) { if (n->IsNodeOfType(nsINode::eTEXT)) { RefPtr<nsStyleContext> childContext = aStyleSet->ResolveStyleForText(n, pseudoContext); MOZ_ASSERT(n->GetPrimaryFrame(), "How? This node is created at FC time!"); n->GetPrimaryFrame()->SetStyleContext(childContext); } } } } } aContent->UnsetIsDirtyForServo(); } if (aContent->HasDirtyDescendantsForServo()) { MOZ_ASSERT(primaryFrame, "Frame construction should be scheduled, and it takes the " "correct style for the children, so no need to be here."); StyleChildrenIterator it(aContent); for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) { if (n->IsElement() || n->IsNodeOfType(nsINode::eTEXT)) { RecreateStyleContexts(n, primaryFrame->StyleContext(), aStyleSet, aChangeListToProcess); } } aContent->UnsetHasDirtyDescendantsForServo(); } }
void ServoRestyleManager::RecreateStyleContexts(Element* aElement, nsStyleContext* aParentContext, ServoStyleSet* aStyleSet, nsStyleChangeList& aChangeListToProcess) { nsIFrame* primaryFrame = aElement->GetPrimaryFrame(); // FIXME(bholley): Once we transfer ownership of the styles to the frame, we // can fast-reject without the FFI call by checking mServoData for null. nsChangeHint changeHint = Servo_TakeChangeHint(aElement); if (changeHint) { aChangeListToProcess.AppendChange(primaryFrame, aElement, changeHint); } // If our change hint is reconstruct, we delegate to the frame constructor, // which consumes the new style and expects the old style to be on the frame. // // XXXbholley: We should teach the frame constructor how to clear the dirty // descendants bit to avoid the traversal here. if (changeHint & nsChangeHint_ReconstructFrame) { ClearDirtyDescendantsFromSubtree(aElement); return; } // If we have a frame and a non-zero + non-reconstruct change hint, we need to // attach a new style context. bool recreateContext = primaryFrame && changeHint; if (recreateContext) { RefPtr<ServoComputedValues> computedValues = aStyleSet->ResolveServoStyle(aElement); // Hold the old style context alive, because it could become a dangling // pointer during the replacement. In practice it's not a huge deal (on // GetNextContinuationWithSameStyle the pointer is not dereferenced, only // compared), but better not playing with dangling pointers if not needed. RefPtr<nsStyleContext> oldStyleContext = primaryFrame->StyleContext(); MOZ_ASSERT(oldStyleContext); RefPtr<nsStyleContext> newContext = aStyleSet->GetContext(computedValues.forget(), aParentContext, nullptr, CSSPseudoElementType::NotPseudo); // XXX This could not always work as expected: there are kinds of content // with the first split and the last sharing style, but others not. We // should handle those properly. for (nsIFrame* f = primaryFrame; f; f = GetNextContinuationWithSameStyle(f, oldStyleContext)) { f->SetStyleContext(newContext); } // Update pseudo-elements state if appropriate. const static CSSPseudoElementType pseudosToRestyle[] = { CSSPseudoElementType::before, CSSPseudoElementType::after, }; for (CSSPseudoElementType pseudoType : pseudosToRestyle) { nsIAtom* pseudoTag = nsCSSPseudoElements::GetPseudoAtom(pseudoType); if (nsIFrame* pseudoFrame = FrameForPseudoElement(aElement, pseudoTag)) { // TODO: we could maybe make this more performant via calling into // Servo just once to know which pseudo-elements we've got to restyle? RefPtr<nsStyleContext> pseudoContext = aStyleSet->ProbePseudoElementStyle(aElement, pseudoType, newContext); MOZ_ASSERT(pseudoContext, "should have taken the ReconstructFrame path above"); pseudoFrame->SetStyleContext(pseudoContext); // We only care restyling text nodes, since other type of nodes // (images), are still not supported. If that eventually changes, we // may have to write more code here... Or not, I don't think too // many inherited properties can affect those other frames. StyleChildrenIterator it(pseudoFrame->GetContent()); for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) { if (n->IsNodeOfType(nsINode::eTEXT)) { RefPtr<nsStyleContext> childContext = aStyleSet->ResolveStyleForText(n, pseudoContext); MOZ_ASSERT(n->GetPrimaryFrame(), "How? This node is created at FC time!"); n->GetPrimaryFrame()->SetStyleContext(childContext); } } } } } bool traverseElementChildren = aElement->HasDirtyDescendantsForServo(); bool traverseTextChildren = recreateContext; if (traverseElementChildren || traverseTextChildren) { StyleChildrenIterator it(aElement); for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) { if (traverseElementChildren && n->IsElement()) { if (!primaryFrame) { // The frame constructor presumably decided to suppress frame // construction on this subtree. Just clear the dirty descendants // bit from the subtree, since there's no point in harvesting the // change hints. MOZ_ASSERT(!n->AsElement()->GetPrimaryFrame(), "Only display:contents should do this, and we don't handle that yet"); ClearDirtyDescendantsFromSubtree(n->AsElement()); } else { RecreateStyleContexts(n->AsElement(), primaryFrame->StyleContext(), aStyleSet, aChangeListToProcess); } } else if (traverseTextChildren && n->IsNodeOfType(nsINode::eTEXT)) { RecreateStyleContextsForText(n, primaryFrame->StyleContext(), aStyleSet); } } } aElement->UnsetHasDirtyDescendantsForServo(); }