diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index 102708c546bc..c10438b5bd26 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -1223,13 +1223,10 @@ already_AddRefed Element::AttachShadowWithoutNameChecks( // // This is a minor optimization, but also works around nasty stuff like // bug 1397876. - if (HasChildren()) { - if (Document* doc = GetComposedDoc()) { - if (PresShell* presShell = doc->GetPresShell()) { - presShell->DestroyFramesForAndRestyle(this); - } + if (Document* doc = GetComposedDoc()) { + if (PresShell* presShell = doc->GetPresShell()) { + presShell->ShadowRootWillBeAttached(*this); } - MOZ_ASSERT(!GetPrimaryFrame()); } /** diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp index 72231b61b8c6..ed9e74457965 100644 --- a/layout/base/PresShell.cpp +++ b/layout/base/PresShell.cpp @@ -2978,51 +2978,86 @@ void PresShell::SlotAssignmentWillChange(Element& aElement, } #ifdef DEBUG -static void AssertNoFramesInSubtree(nsIContent* aContent) { - for (nsINode* node : ShadowIncludingTreeIterator(*aContent)) { +static void AssertNoFramesOrStyleDataInDescendants(Element& aElement) { + for (nsINode* node : ShadowIncludingTreeIterator(aElement)) { nsIContent* c = nsIContent::FromNode(node); + if (c == &aElement) { + continue; + } MOZ_ASSERT(!c->GetPrimaryFrame()); + MOZ_ASSERT(!c->IsElement() || !c->AsElement()->HasServoData()); } } #endif void PresShell::DestroyFramesForAndRestyle(Element* aElement) { #ifdef DEBUG - auto postCondition = - mozilla::MakeScopeExit([&]() { AssertNoFramesInSubtree(aElement); }); + auto postCondition = MakeScopeExit([&]() { + MOZ_ASSERT(!aElement->GetPrimaryFrame()); + AssertNoFramesOrStyleDataInDescendants(*aElement); + }); #endif MOZ_ASSERT(aElement); - if (MOZ_UNLIKELY(!mDidInitialize)) { + if (!aElement->HasServoData()) { + // Nothing to do here, the element already is out of the flat tree or is not + // styled. return; } - if (!aElement->GetFlattenedTreeParentNode()) { - // Nothing to do here, the element already is out of the frame tree. - return; - } - - nsAutoScriptBlocker scriptBlocker; - // Mark ourselves as not safe to flush while we're doing frame destruction. + nsAutoScriptBlocker scriptBlocker; ++mChangeNestCount; const bool didReconstruct = FrameConstructor()->DestroyFramesFor(aElement); - // Clear the style data from all the flattened tree descendants, but _not_ // from us, since otherwise we wouldn't see the reframe. RestyleManager::ClearServoDataFromSubtree(aElement, RestyleManager::IncludeRoot::No); - auto changeHint = didReconstruct ? nsChangeHint(0) : nsChangeHint_ReconstructFrame; - mPresContext->RestyleManager()->PostRestyleEvent( aElement, RestyleHint::RestyleSubtree(), changeHint); --mChangeNestCount; } +void PresShell::ShadowRootWillBeAttached(Element& aElement) { +#ifdef DEBUG + auto postCondition = MakeScopeExit( + [&]() { AssertNoFramesOrStyleDataInDescendants(aElement); }); +#endif + + if (!aElement.HasServoData()) { + // Nothing to do here, the element already is out of the flat tree or is not + // styled. + return; + } + + if (!aElement.HasChildren()) { + // The element has no children, just avoid the work. + return; + } + + // Mark ourselves as not safe to flush while we're doing frame destruction. + nsAutoScriptBlocker scriptBlocker; + ++mChangeNestCount; + + // NOTE(emilio): We use FlattenedChildIterator intentionally here (rather than + // StyleChildrenIterator), since we don't want to remove ::before / ::after + // content. + FlattenedChildIterator iter(&aElement); + nsCSSFrameConstructor* fc = FrameConstructor(); + for (nsIContent* c = iter.GetNextChild(); c; c = iter.GetNextChild()) { + fc->DestroyFramesFor(c); + if (c->IsElement()) { + RestyleManager::ClearServoDataFromSubtree(c->AsElement()); + } + } + + --mChangeNestCount; +} + void PresShell::PostRecreateFramesFor(Element* aElement) { if (MOZ_UNLIKELY(!mDidInitialize)) { // Nothing to do here. In fact, if we proceed and aElement is the root, we diff --git a/layout/base/PresShell.h b/layout/base/PresShell.h index e9710ee7488d..73d1aca4c3f3 100644 --- a/layout/base/PresShell.h +++ b/layout/base/PresShell.h @@ -485,10 +485,17 @@ class PresShell final : public nsStubDocumentObserver, * Destroy the frames for aElement, and reconstruct them asynchronously if * needed. * - * Note that this may destroy frames for an ancestor instead. + * Note that this may destroy frames for an arbitrary ancestor, depending on + * the frame tree structure. */ void DestroyFramesForAndRestyle(Element* aElement); + /** + * Called when a ShadowRoot will be attached to an element (and thus the flat + * tree children will go away). + */ + void ShadowRootWillBeAttached(Element& aElement); + /** * Handles all the layout stuff needed when the slot assignment for an element * is about to change. diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index d897aac38abe..68c4e3f9dbe8 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -8686,14 +8686,14 @@ void nsCSSFrameConstructor::RecreateFramesForContent( } } -bool nsCSSFrameConstructor::DestroyFramesFor(Element* aElement) { - MOZ_ASSERT(aElement && aElement->GetParentNode()); +bool nsCSSFrameConstructor::DestroyFramesFor(nsIContent* aContent) { + MOZ_ASSERT(aContent && aContent->GetParentNode()); - nsIContent* nextSibling = aElement->IsRootOfNativeAnonymousSubtree() + nsIContent* nextSibling = aContent->IsRootOfNativeAnonymousSubtree() ? nullptr - : aElement->GetNextSibling(); + : aContent->GetNextSibling(); - return ContentRemoved(aElement, nextSibling, REMOVE_FOR_RECONSTRUCTION); + return ContentRemoved(aContent, nextSibling, REMOVE_FOR_RECONSTRUCTION); } ////////////////////////////////////////////////////////////////////// diff --git a/layout/base/nsCSSFrameConstructor.h b/layout/base/nsCSSFrameConstructor.h index fd7750f745f6..f66701a3baf6 100644 --- a/layout/base/nsCSSFrameConstructor.h +++ b/layout/base/nsCSSFrameConstructor.h @@ -297,7 +297,7 @@ class nsCSSFrameConstructor final : public nsFrameManager { * * Returns whether a reconstruct was posted for any ancestor. */ - bool DestroyFramesFor(Element* aElement); + bool DestroyFramesFor(nsIContent* aContent); // Request to create a continuing frame. This method never returns null. nsIFrame* CreateContinuingFrame(nsIFrame* aFrame,