/* static */ void ServoRestyleManager::ClearRestyleStateFromSubtree(Element* aElement) { if (aElement->HasDirtyDescendantsForServo()) { StyleChildrenIterator it(aElement); for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) { if (n->IsElement()) { ClearRestyleStateFromSubtree(n->AsElement()); } } } Unused << Servo_TakeChangeHint(aElement); aElement->UnsetHasDirtyDescendantsForServo(); aElement->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES); }
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); }
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(); }