Bug 1454157: Tell layout about default content going away. r?smaug draft
authorEmilio Cobos Álvarez <emilio@crisal.io>
Sat, 14 Apr 2018 15:04:53 +0200
changeset 782176 d8c2311fb053551e9782a00a2273f7f90da3ad19
parent 782175 fbf598fc41e9cb3ad709bf2563abc6944865cc36
push id106488
push userbmo:emilio@crisal.io
push dateSat, 14 Apr 2018 13:09:28 +0000
reviewerssmaug
bugs1454157
milestone61.0a1
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
dom/xbl/XBLChildrenElement.cpp
dom/xbl/XBLChildrenElement.h
dom/xbl/nsBindingManager.cpp
dom/xbl/nsXBLBinding.cpp
--- 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