diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index 7b1ecd4abdea..ed7c8d74dcca 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -69,6 +69,7 @@ #include "mozilla/EventStates.h" #include "mozilla/InternalMutationEvent.h" #include "mozilla/MouseEvents.h" +#include "mozilla/ServoRestyleManager.h" #include "mozilla/SizeOfState.h" #include "mozilla/TextEditor.h" #include "mozilla/TextEvents.h" @@ -1925,6 +1926,15 @@ Element::UnbindFromTree(bool aDeep, bool aNullParent) // Fully exit full-screen. nsIDocument::ExitFullscreenInDocTree(OwnerDoc()); } + + if (HasServoData()) { + MOZ_ASSERT(IsInAnonymousSubtree()); + MOZ_ASSERT(document && document->IsStyledByServo()); + ClearServoData(document); + } else if (document && document->GetServoRestyleRoot() == this) { + document->ClearServoRestyleRoot(); + } + if (aNullParent) { if (GetParent() && GetParent()->IsInUncomposedDoc()) { // Update the editable descendant count in the ancestors before we @@ -1992,19 +2002,6 @@ Element::UnbindFromTree(bool aDeep, bool aNullParent) } } - // Computed style data isn't useful for detached nodes, and we'll need to - // recompute it anyway if we ever insert the nodes back into a document. - if (IsStyledByServo()) { - if (document) { - ClearServoData(document); - } else { - MOZ_ASSERT(!HasServoData()); - MOZ_ASSERT(!HasAnyOfFlags(kAllServoDescendantBits | NODE_NEEDS_FRAME)); - } - } else { - MOZ_ASSERT(!HasServoData()); - } - // Editable descendant count only counts descendants that // are in the uncomposed document. ResetEditableDescendantCount(); @@ -2108,24 +2105,8 @@ Element::UnbindFromTree(bool aDeep, bool aNullParent) shadowRoot->SetIsComposedDocParticipant(false); } - // Unbinding of children is the only point in time where we don't enforce the - // "child has style data implies parent has it too" invariant. - // - // As such, the restyle root tracking may incorrectly end up setting dirty - // bits on the parent chain when moving from a not yet unbound root with - // already unbound parents to a root higher up in the tree, so we clear those - // (again, since they're also cleared in ClearServoData) here. - // - // This can happen when the element changes the state of some ancestor up in - // the tree, for example. - // - // Note that clearing the data itself here would have its own set of problems, - // since the invariant we'd be breaking in that case is "HasServoData() - // implies InComposedDoc()", which we rely on in various places. - UnsetFlags(kAllServoDescendantBits); - if (document && document->GetServoRestyleRoot() == this) { - document->ClearServoRestyleRoot(); - } + MOZ_ASSERT_IF(IsStyledByServo(), !HasAnyOfFlags(kAllServoDescendantBits)); + MOZ_ASSERT(!document || document->GetServoRestyleRoot() != this); } nsDOMCSSAttributeDeclaration* @@ -4516,7 +4497,6 @@ BitsArePropagated(const Element* aElement, uint32_t aBits, nsINode* aRestyleRoot nsINode* parentNode = curr->GetParentNode(); curr = curr->GetFlattenedTreeParentElementForStyle(); MOZ_ASSERT_IF(!curr, - !parentNode || // can only happen mid-unbind. parentNode == aElement->OwnerDoc() || parentNode == parentNode->OwnerDoc()->GetRootElement()); } @@ -4532,24 +4512,6 @@ AssertNoBitsPropagatedFrom(nsINode* aRoot) return; } - // If we are in the middle of unbinding a subtree, then the bits on elements - // in that subtree (and which we haven't yet unbound) could be dirty. - // Just skip asserting the absence of bits on those element that are in - // the unbinding subtree. (The incorrect bits will only cause us to - // incorrectly choose a new restyle root if the newly dirty element is - // also within the unbinding subtree, so it is OK to leave them there.) - for (nsINode* n = aRoot; n; n = n->GetFlattenedTreeParentElementForStyle()) { - if (!n->IsInComposedDoc()) { - // Find the top-most element that is marked as no longer in the document, - // so we can start checking bits from its parent. - do { - aRoot = n; - n = n->GetFlattenedTreeParentElementForStyle(); - } while (n && !n->IsInComposedDoc()); - break; - } - } - auto* element = aRoot->GetFlattenedTreeParentElementForStyle(); while (element) { MOZ_ASSERT(!element->HasAnyOfFlags(Element::kAllServoDescendantBits)); diff --git a/dom/base/FragmentOrElement.cpp b/dom/base/FragmentOrElement.cpp index 764d077b1b9f..075305cf508f 100644 --- a/dom/base/FragmentOrElement.cpp +++ b/dom/base/FragmentOrElement.cpp @@ -1193,38 +1193,21 @@ void nsIContent::SetAssignedSlot(HTMLSlotElement* aSlot) { MOZ_ASSERT(aSlot || GetExistingExtendedContentSlots()); - nsExtendedContentSlots* slots = ExtendedContentSlots(); - - RefPtr oldSlot = slots->mAssignedSlot.forget(); - slots->mAssignedSlot = aSlot; - - if (oldSlot != aSlot && IsElement() && AsElement()->HasServoData()) { - ServoRestyleManager::ClearServoDataFromSubtree(AsElement()); - } + ExtendedContentSlots()->mAssignedSlot = aSlot; } void nsIContent::SetXBLInsertionPoint(nsIContent* aContent) { - nsCOMPtr oldInsertionPoint = nullptr; if (aContent) { nsExtendedContentSlots* slots = ExtendedContentSlots(); SetFlags(NODE_MAY_BE_IN_BINDING_MNGR); - oldInsertionPoint = slots->mXBLInsertionPoint.forget(); slots->mXBLInsertionPoint = aContent; } else { if (nsExtendedContentSlots* slots = GetExistingExtendedContentSlots()) { - oldInsertionPoint = slots->mXBLInsertionPoint.forget(); slots->mXBLInsertionPoint = nullptr; } } - - // We just changed the flattened tree, so any Servo style data is now invalid. - // We rely on nsXBLService::LoadBindings to re-traverse the subtree afterwards. - if (oldInsertionPoint != aContent && IsElement() && - AsElement()->HasServoData()) { - ServoRestyleManager::ClearServoDataFromSubtree(AsElement()); - } } nsresult diff --git a/dom/base/nsIDocumentInlines.h b/dom/base/nsIDocumentInlines.h index bb3e8d2454b6..d7ec62e3c6e0 100644 --- a/dom/base/nsIDocumentInlines.h +++ b/dom/base/nsIDocumentInlines.h @@ -63,27 +63,8 @@ nsIDocument::SetServoRestyleRoot(nsINode* aRoot, uint32_t aDirtyBits) { MOZ_ASSERT(aRoot); - // NOTE(emilio): The !aRoot->IsElement() check allows us to handle cases where - // we change the restyle root during unbinding of a subtree where the root is - // not unbound yet (and thus hasn't cleared the restyle root yet). - // - // In that case the tree can be in a somewhat inconsistent state (with the - // document no longer being the subtree root of the current root, but the root - // not having being unbound first). - // - // In that case, given there's no common ancestor, aRoot should be the - // document, and we allow that, provided that the previous root will - // eventually be unbound and the dirty bits will be cleared. - // - // If we want to enforce calling into this method with the tree in a - // consistent state, we'd need to move all the state changes that happen on - // content unbinding for parents, like fieldset validity stuff and ancestor - // direction changes off script runners or, alternatively, nulling out the - // document and parent node _after_ nulling out the children's, and then - // remove that line. MOZ_ASSERT(!mServoRestyleRoot || mServoRestyleRoot == aRoot || - !aRoot->IsElement() || nsContentUtils::ContentIsFlattenedTreeDescendantOfForStyle(mServoRestyleRoot, aRoot)); MOZ_ASSERT(aRoot == aRoot->OwnerDocAsNode() || aRoot->IsElement()); mServoRestyleRoot = aRoot; diff --git a/layout/base/RestyleManager.cpp b/layout/base/RestyleManager.cpp index 73ac4e02a084..0acb44db4370 100644 --- a/layout/base/RestyleManager.cpp +++ b/layout/base/RestyleManager.cpp @@ -252,6 +252,12 @@ RestyleManager::ContentRemoved(nsINode* aContainer, nsIContent* aOldChild, nsIContent* aFollowingSibling) { + // Computed style data isn't useful for detached nodes, and we'll need to + // recompute it anyway if we ever insert the nodes back into a document. + if (IsServo() && aOldChild->IsElement()) { + ServoRestyleManager::ClearServoDataFromSubtree(aOldChild->AsElement()); + } + // The container might be a document or a ShadowRoot. if (!aContainer->IsElement()) { return;