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(); }