Bug 1454157: Tell layout about default content going away. r?smaug
Pretty much the same way we do for Shadow DOM.
There are a couple more places broken, but provided our fronted folks don't do
weird stuff nothing bad should happen, and the assertion that this allows me to
add should catch those if they do, so I'm punting on it for now.
MozReview-Commit-ID: Bgr41C4zGgn
--- a/dom/xbl/XBLChildrenElement.cpp
+++ b/dom/xbl/XBLChildrenElement.cpp
@@ -39,16 +39,44 @@ XBLChildrenElement::BeforeSetAttr(int32_
}
}
}
}
return nsXMLElement::BeforeSetAttr(aNamespaceID, aName, aValue, aNotify);
}
+
+void
+XBLChildrenElement::DoRemoveDefaultContent(bool aNotify)
+{
+ // Default content is going away, need to tell layout about it first.
+ MOZ_ASSERT(HasChildren(), "Why bothering?");
+ MOZ_ASSERT(GetParentElement());
+
+ // We don't want to do this from frame construction while setting up the
+ // binding initially.
+ if (aNotify) {
+ Element* parent = GetParentElement();
+ if (nsIDocument* doc = parent->GetComposedDoc()) {
+ if (nsIPresShell* shell = doc->GetShell()) {
+ shell->DestroyFramesForAndRestyle(parent);
+ }
+ }
+ }
+
+ for (nsIContent* child = static_cast<nsINode*>(this)->GetFirstChild();
+ child;
+ child = child->GetNextSibling()) {
+ MOZ_ASSERT(!child->GetPrimaryFrame());
+ MOZ_ASSERT(!child->IsElement() || !child->AsElement()->HasServoData());
+ child->SetXBLInsertionPoint(nullptr);
+ }
+}
+
} // namespace dom
} // namespace mozilla
using namespace mozilla::dom;
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsAnonymousContentList, mParent)
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsAnonymousContentList)
--- a/dom/xbl/XBLChildrenElement.h
+++ b/dom/xbl/XBLChildrenElement.h
@@ -33,80 +33,82 @@ public:
NS_DECL_ISUPPORTS_INHERITED
// nsINode interface methods
virtual nsresult Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult,
bool aPreallocateChildren) const override;
virtual nsIDOMNode* AsDOMNode() override { return this; }
- void AppendInsertedChild(nsIContent* aChild)
+ void AppendInsertedChild(nsIContent* aChild, bool aNotify)
{
// Appending an inserted child causes the inserted
// children to be projected instead of default content.
- MaybeRemoveDefaultContent();
+ MaybeRemoveDefaultContent(aNotify);
mInsertedChildren.AppendElement(aChild);
aChild->SetXBLInsertionPoint(this);
}
void InsertInsertedChildAt(nsIContent* aChild, uint32_t aIndex)
{
// Inserting an inserted child causes the inserted
// children to be projected instead of default content.
- MaybeRemoveDefaultContent();
+ MaybeRemoveDefaultContent(true);
mInsertedChildren.InsertElementAt(aIndex, aChild);
aChild->SetXBLInsertionPoint(this);
}
void RemoveInsertedChild(nsIContent* aChild)
{
// Can't use this assertion as we cheat for dynamic insertions and
// only insert in the innermost insertion point.
//NS_ASSERTION(mInsertedChildren.Contains(aChild),
// "Removing child that's not there");
mInsertedChildren.RemoveElement(aChild);
// After removing the inserted child, default content
// may be projected into this insertion point.
+ //
+ // FIXME: Layout should be told about this before clearing
+ // mInsertedChildren, this leaves stale styles and frames in the frame tree.
MaybeSetupDefaultContent();
}
void ClearInsertedChildren()
{
for (auto* child : mInsertedChildren) {
child->SetXBLInsertionPoint(nullptr);
}
mInsertedChildren.Clear();
// After clearing inserted children, default content
// will be projected into this insertion point.
+ //
+ // FIXME: Layout should be told about this before clearing
+ // mInsertedChildren, this leaves stale styles and frames in the frame tree.
MaybeSetupDefaultContent();
}
void MaybeSetupDefaultContent()
{
if (!HasInsertedChildren()) {
for (nsIContent* child = static_cast<nsINode*>(this)->GetFirstChild();
child;
child = child->GetNextSibling()) {
child->SetXBLInsertionPoint(this);
}
}
}
- void MaybeRemoveDefaultContent()
+ void MaybeRemoveDefaultContent(bool aNotify)
{
- if (!HasInsertedChildren()) {
- for (nsIContent* child = static_cast<nsINode*>(this)->GetFirstChild();
- child;
- child = child->GetNextSibling()) {
- child->SetXBLInsertionPoint(nullptr);
- }
+ if (!HasInsertedChildren() && HasChildren()) {
+ DoRemoveDefaultContent(aNotify);
}
}
uint32_t InsertedChildrenLength()
{
return mInsertedChildren.Length();
}
@@ -138,16 +140,18 @@ public:
}
protected:
~XBLChildrenElement();
virtual nsresult BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName,
const nsAttrValueOrString* aValue,
bool aNotify) override;
+ void DoRemoveDefaultContent(bool aNotify);
+
private:
nsTArray<nsIContent*> mInsertedChildren; // WEAK
nsTArray<RefPtr<nsAtom> > mIncludes;
};
} // namespace dom
} // namespace mozilla
--- a/dom/xbl/nsBindingManager.cpp
+++ b/dom/xbl/nsBindingManager.cpp
@@ -782,17 +782,17 @@ nsBindingManager::ContentAppended(nsICon
// Even though we're in ContentAppended, nested insertion points force us
// to deal with this append as an insertion except in the outermost
// binding.
if (first) {
first = false;
for (nsIContent* child = aFirstNewContent; child;
child = child->GetNextSibling()) {
- point->AppendInsertedChild(child);
+ point->AppendInsertedChild(child, true);
}
} else {
InsertAppendedContent(point, aFirstNewContent);
}
nsIContent* newParent = point->GetParent();
if (newParent == parent) {
break;
--- a/dom/xbl/nsXBLBinding.cpp
+++ b/dom/xbl/nsXBLBinding.cpp
@@ -342,27 +342,27 @@ nsXBLBinding::GenerateAnonymousContent()
// pointer which would make the GetNextNode call above fail
BindAnonymousContent(mContent, mBoundElement,
mPrototypeBinding->ChromeOnlyContent());
// Insert explicit children into insertion points
if (mDefaultInsertionPoint && mInsertionPoints.IsEmpty()) {
ExplicitChildIterator iter(mBoundElement);
for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
- mDefaultInsertionPoint->AppendInsertedChild(child);
+ mDefaultInsertionPoint->AppendInsertedChild(child, false);
}
} else {
// It is odd to come into this code if mInsertionPoints is not empty, but
// we need to make sure to do the compatibility hack below if the bound
// node has any non <xul:template> or <xul:observes> children.
ExplicitChildIterator iter(mBoundElement);
for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
XBLChildrenElement* point = FindInsertionPointForInternal(child);
if (point) {
- point->AppendInsertedChild(child);
+ point->AppendInsertedChild(child, false);
} else {
NodeInfo *ni = child->NodeInfo();
if (ni->NamespaceID() != kNameSpaceID_XUL ||
(!ni->Equals(nsGkAtoms::_template) &&
!ni->Equals(nsGkAtoms::observes))) {
// Compatibility hack. For some reason the original XBL
// implementation dropped the content of a binding if any child of
// the bound element didn't match any of the <children> in the