Bug 1427677: Get rid of nsContentUtils::HasDistributedChildren. r?bz draft
authorEmilio Cobos Álvarez <emilio@crisal.io>
Tue, 02 Jan 2018 15:04:38 +0100
changeset 716648 b28374b590f1d888c9d2dc3952885b4aab7b9ef1
parent 716647 44e5280a2e05323579acf1bc2fc2bf94c4e89d80
child 716649 0b5396f3f027690263487a920b5631bf32cb8693
push id94475
push userbmo:emilio@crisal.io
push dateSat, 06 Jan 2018 01:13:40 +0000
reviewersbz
bugs1427677, 1427511
milestone59.0a1
Bug 1427677: Get rid of nsContentUtils::HasDistributedChildren. r?bz The whole function doesn't have much sense. I killed its only DOM use in bug 1427511. Now it only has two callers in nsCSSFrameConstructor, which basically only want to know whether the children of the same node can have different flattened tree parents. So let's check that directly instead (checking whether the element has a binding or a shadow root), and simplify a bit other surrounding code while at it. Leave the XUL popup / menubar code doing the broken thing they were doing beforehand, because it doesn't look to me like it's trivial to fix... They're effectively assuming that the children of the menupopup end up in a single insertion point, which is true, but doesn't need to be. Maybe they should walk the DOM tree? Don't want to dig into that right now, since XUL insertion points can be reordered and all that... Not fun. MozReview-Commit-ID: L4lspkxKENr
dom/base/nsContentUtils.cpp
dom/base/nsContentUtils.h
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsCSSFrameConstructor.h
layout/xul/nsMenuBarFrame.cpp
layout/xul/nsMenuPopupFrame.cpp
layout/xul/nsXULPopupManager.cpp
layout/xul/nsXULPopupManager.h
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -7502,40 +7502,16 @@ nsContentUtils::GetHTMLEditor(nsPresCont
       NS_FAILED(docShell->GetEditable(&isEditable)) || !isEditable)
     return nullptr;
 
   return docShell->GetHTMLEditor();
 }
 
 // static
 bool
-nsContentUtils::HasDistributedChildren(nsIContent* aContent)
-{
-  if (!aContent || !nsDocument::IsWebComponentsEnabled(aContent)) {
-    return false;
-  }
-
-  if (aContent->GetShadowRoot()) {
-    // Children of a shadow root host are distributed
-    // to content insertion points in the shadow root.
-    return true;
-  }
-
-  HTMLSlotElement* slotEl = HTMLSlotElement::FromContent(aContent);
-  if (slotEl && slotEl->GetContainingShadow()) {
-    // Children of a slot are rendered if the slot does not have any assigned
-    // nodes (fallback content).
-    return slotEl->AssignedNodes().IsEmpty();
-  }
-
-  return false;
-}
-
-// static
-bool
 nsContentUtils::IsForbiddenRequestHeader(const nsACString& aHeader)
 {
   if (IsForbiddenSystemRequestHeader(aHeader)) {
     return true;
   }
 
   return StringBeginsWith(aHeader, NS_LITERAL_CSTRING("proxy-"),
                           nsCaseInsensitiveCStringComparator()) ||
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -2708,22 +2708,16 @@ public:
   /**
    * Returns a LogModule that dump calls from content script are logged to.
    * This can be enabled with the 'Dump' module, and is useful for synchronizing
    * content JS to other logging modules.
    */
   static mozilla::LogModule* DOMDumpLog();
 
   /**
-   * Returns whether the children of the provided content are
-   * nodes that are distributed to Shadow DOM insertion points.
-   */
-  static bool HasDistributedChildren(nsIContent* aContent);
-
-  /**
    * Returns whether a given header is forbidden for an XHR or fetch
    * request.
    */
   static bool IsForbiddenRequestHeader(const nsACString& aHeader);
 
   /**
    * Returns whether a given header is forbidden for a system XHR
    * request.
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -7389,46 +7389,66 @@ nsCSSFrameConstructor::IssueSingleInsert
       continue;
     }
     // Call ContentRangeInserted with this node.
     ContentRangeInserted(aContainer, child, child->GetNextSibling(),
                          mTempFrameTreeState, aInsertionKind, nullptr);
   }
 }
 
+bool
+nsCSSFrameConstructor::InsertionPoint::IsMultiple() const
+{
+  if (!mParentFrame) {
+    return false;
+  }
+
+  // Fieldset frames have multiple normal flow child frame lists so handle it
+  // the same as if it had multiple content insertion points.
+  if (mParentFrame->IsFieldSetFrame()) {
+    return true;
+  }
+
+  // A details frame moves the first summary frame to be its first child, so we
+  // treat it as if it has multiple content insertion points.
+  if (mParentFrame->IsDetailsFrame()) {
+    return true;
+  }
+
+  return false;
+}
+
 nsCSSFrameConstructor::InsertionPoint
 nsCSSFrameConstructor::GetRangeInsertionPoint(nsIContent* aContainer,
                                               nsIContent* aStartChild,
                                               nsIContent* aEndChild,
                                               InsertionKind aInsertionKind)
 {
-  // See if we have an XBL insertion point. If so, then that's our
-  // real parent frame; if not, then the frame hasn't been built yet
-  // and we just bail.
-  InsertionPoint insertionPoint = GetInsertionPoint(aContainer, nullptr);
-  if (!insertionPoint.mParentFrame && !insertionPoint.mMultiple) {
-    return insertionPoint; // Don't build the frames.
-  }
-
-  if (insertionPoint.mMultiple || aStartChild->GetXBLInsertionPoint()) {
-    // If we have multiple insertion points or if we have an insertion point
-    // and the operation is not a true append or if the insertion point already
-    // has explicit children, then we must fall back.
-    if (insertionPoint.mMultiple || aEndChild ||
-        insertionPoint.mParentFrame->GetContent()->HasChildren()) {
-      // Now comes the fun part.  For each inserted child, make a
-      // ContentInserted call as if it had just gotten inserted and
-      // let ContentInserted handle the mess.
+  MOZ_ASSERT(aStartChild);
+
+  // If the children of the container may be distributed to different insertion
+  // points, insert them separately and bail out, letting ContentInserted handle
+  // the mess.
+  if (aContainer->GetShadowRoot() || aContainer->GetXBLBinding()) {
       IssueSingleInsertNofications(aContainer, aStartChild, aEndChild,
                                    aInsertionKind);
-      insertionPoint.mParentFrame = nullptr;
-    }
-  }
-
-  return insertionPoint;
+      return { };
+  }
+
+  // Now the flattened tree parent of all the siblings is the same, just use the
+  // same insertion point and take the fast path, unless it's a multiple
+  // insertion point.
+  InsertionPoint ip = GetInsertionPoint(aStartChild);
+  if (ip.IsMultiple()) {
+    IssueSingleInsertNofications(aContainer, aStartChild, aEndChild,
+                                 aInsertionKind);
+    return { };
+  }
+
+  return ip;
 }
 
 bool
 nsCSSFrameConstructor::MaybeRecreateForFrameset(nsIFrame* aParentFrame,
                                                 nsIContent* aStartChild,
                                                 nsIContent* aEndChild)
 {
   if (aParentFrame->IsFrameSetFrame()) {
@@ -8059,17 +8079,17 @@ nsCSSFrameConstructor::ContentRangeInser
   // if needed.
   styleNewChildRangeEagerly();
 
   InsertionPoint insertion;
   if (isSingleInsert) {
     // See if we have an XBL insertion point. If so, then that's our
     // real parent frame; if not, then the frame hasn't been built yet
     // and we just bail.
-    insertion = GetInsertionPoint(aContainer, aStartChild);
+    insertion = GetInsertionPoint(aStartChild);
   } else {
     // Get our insertion point. If we need to issue single ContentInserted's
     // GetRangeInsertionPoint will take care of that for us.
     LAYOUT_PHASE_TEMP_EXIT();
     insertion = GetRangeInsertionPoint(aContainer, aStartChild, aEndChild,
                                        aInsertionKind);
     LAYOUT_PHASE_TEMP_REENTER();
   }
@@ -9319,88 +9339,27 @@ nsCSSFrameConstructor::ReplicateFixedFra
   // broken auto-positioning. Oh, well.
   NS_ASSERTION(!canvasFrame->PrincipalChildList().FirstChild(),
                "leaking frames; doc root continuation must be empty");
   canvasFrame->SetInitialChildList(kPrincipalList, fixedPlaceholders);
   return NS_OK;
 }
 
 nsCSSFrameConstructor::InsertionPoint
-nsCSSFrameConstructor::GetInsertionPoint(nsIContent* aContainer,
-                                         nsIContent* aChild)
-{
-  nsBindingManager* bindingManager = mDocument->BindingManager();
-
-  nsIContent* insertionElement;
-  if (aChild) {
-    // We've got an explicit insertion child. Check to see if it's
-    // anonymous.
-    if (aChild->GetBindingParent() == aContainer) {
-      // This child content is anonymous. Don't use the insertion
-      // point, since that's only for the explicit kids.
-      return InsertionPoint(GetContentInsertionFrameFor(aContainer), aContainer);
-    }
-
-    if (nsContentUtils::HasDistributedChildren(aContainer) ||
-        aContainer->IsShadowRoot()) {
-      // The container distributes nodes or is a shadow root, use the frame of
-      // the flattened tree parent.
-      //
-      // It may be the case that the node is distributed but not matched to any
-      // insertion points, so there is no flattened parent.
-      //
-      // FIXME(emilio): We should be able to use this path all the time.
-      nsIContent* flattenedParent = aChild->GetFlattenedTreeParent();
-      if (flattenedParent) {
-        return InsertionPoint(GetContentInsertionFrameFor(flattenedParent),
-                              flattenedParent);
-      }
-      return InsertionPoint();
-    }
-
-    insertionElement = bindingManager->FindNestedInsertionPoint(aContainer, aChild);
-  } else {
-    if (nsContentUtils::HasDistributedChildren(aContainer)) {
-      // The container distributes nodes to shadow DOM insertion points.
-      // Return with aMultiple set to true to induce callers to insert children
-      // individually into the node's flattened tree parent.
-      return InsertionPoint(nullptr, nullptr, true);
-    }
-
-    bool multiple;
-    insertionElement = bindingManager->FindNestedSingleInsertionPoint(aContainer, &multiple);
-    if (multiple) {
-      return InsertionPoint(nullptr, nullptr, true);
-    }
-  }
-
+nsCSSFrameConstructor::GetInsertionPoint(nsIContent* aChild)
+{
+  MOZ_ASSERT(aChild);
+  nsIContent* insertionElement = aChild->GetFlattenedTreeParent();
   if (!insertionElement) {
-    // The FindNested{,Single}InsertionPoint methods return null in the case
-    // that there is a binding with anonymous content but no insertion point.
-    // In that case the element doesn't belong in the flattened tree, and we
-    // don't want to render it.
-    return InsertionPoint();
-  }
-
-  InsertionPoint insertion(GetContentInsertionFrameFor(insertionElement),
-                           insertionElement);
-
-  // Fieldset frames have multiple normal flow child frame lists so handle it
-  // the same as if it had multiple content insertion points.
-  if (insertion.mParentFrame && insertion.mParentFrame->IsFieldSetFrame()) {
-    insertion.mMultiple = true;
-  }
-
-  // A details frame moves the first summary frame to be its first child, so we
-  // treat it as if it has multiple content insertion points.
-  if (insertion.mParentFrame && insertion.mParentFrame->IsDetailsFrame()) {
-    insertion.mMultiple = true;
-  }
-
-  return insertion;
+    // The element doesn't belong in the flattened tree, and thus we don't want
+    // to render it.
+    return { };
+  }
+
+  return { GetContentInsertionFrameFor(insertionElement), insertionElement };
 }
 
 // Capture state for the frame tree rooted at the frame associated with the
 // content object, aContent
 void
 nsCSSFrameConstructor::CaptureStateForFramesOf(nsIContent* aContent,
                                                nsILayoutHistoryState* aHistoryState)
 {
@@ -11096,17 +11055,17 @@ nsCSSFrameConstructor::ProcessChildren(n
       // for default content). Push the children element as an ancestor here because
       // it does not have a frame and would not otherwise be pushed as an ancestor.
       insertion.mContainer = aContent;
       nsIContent* parent = child->GetParent();
       MOZ_ASSERT(parent, "Parent must be non-null because we are iterating children.");
       TreeMatchContext::AutoAncestorPusher ancestorPusher(aState.mTreeMatchContext);
       if (parent != aContent && parent->IsElement()) {
         insertion.mContainer = child->GetFlattenedTreeParent();
-        MOZ_ASSERT(insertion.mContainer == GetInsertionPoint(parent, child).mContainer);
+        MOZ_ASSERT(insertion.mContainer == GetInsertionPoint(child).mContainer);
         if (aState.HasAncestorFilter()) {
           ancestorPusher.PushAncestorAndStyleScope(parent->AsElement());
         } else {
           ancestorPusher.PushStyleScope(parent->AsElement());
         }
       }
 
       // Frame construction item construction should not post
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -136,41 +136,47 @@ private:
                                     InsertionKind);
 
   /**
    * Data that represents an insertion point for some child content.
    */
   struct InsertionPoint
   {
     InsertionPoint()
-      : mParentFrame(nullptr), mContainer(nullptr), mMultiple(false) {}
-    InsertionPoint(nsContainerFrame* aParentFrame, nsIContent* aContainer,
-                   bool aMultiple = false)
-      : mParentFrame(aParentFrame), mContainer(aContainer),
-        mMultiple(aMultiple) {}
+      : mParentFrame(nullptr)
+      , mContainer(nullptr)
+    {}
+
+    InsertionPoint(nsContainerFrame* aParentFrame, nsIContent* aContainer)
+      : mParentFrame(aParentFrame)
+      , mContainer(aContainer)
+    {}
+
     /**
      * The parent frame to use if the inserted children needs to create
      * frame(s).  May be null, which signals that  we shouldn't try to
      * create frames for the inserted children; either because there are
      * no parent frame or because there are multiple insertion points and
      * we will call IssueSingleInsertNofications for each child instead.
      * mContainer should not be used when mParentFrame is null.
      */
     nsContainerFrame* mParentFrame;
     /**
      * The flattened tree parent for the inserted children.
      * It's undefined if mParentFrame is null.
      */
     nsIContent* mContainer;
+
     /**
-     * If true then there are multiple insertion points, which means consumers
-     * should insert children individually into the node's flattened tree parent.
+     * Whether it is required to insert children one-by-one instead of as a
+     * range.
      */
-    bool mMultiple;
+    bool IsMultiple() const;
   };
+
   /**
    * Checks if the children of aContainer in the range [aStartChild, aEndChild)
    * can be inserted/appended to one insertion point together. If so, returns
    * that insertion point. If not, returns with InsertionPoint.mFrame == nullptr
    * and issues single ContentInserted calls for each child.
    * aEndChild = nullptr indicates that we are dealing with an append.
    */
   InsertionPoint GetRangeInsertionPoint(nsIContent* aContainer,
@@ -351,19 +357,25 @@ public:
                                   nsIFrame*         aFrame,
                                   nsContainerFrame* aParentFrame,
                                   bool              aIsFluid = true);
 
   // Copy over fixed frames from aParentFrame's prev-in-flow
   nsresult ReplicateFixedFrames(nsPageContentFrame* aParentFrame);
 
   /**
-   * Get the XBL insertion point for aChild in aContainer.
+   * Get the insertion point for aChild.
    */
-  InsertionPoint GetInsertionPoint(nsIContent* aContainer, nsIContent* aChild);
+  InsertionPoint GetInsertionPoint(nsIContent* aChild);
+
+  /**
+   * Return the insertion frame of the primary frame of aContent, or its nearest
+   * ancestor that isn't display:contents.
+   */
+  nsContainerFrame* GetContentInsertionFrameFor(nsIContent* aContent);
 
   void CreateListBoxContent(nsContainerFrame* aParentFrame,
                             nsIFrame*         aPrevFrame,
                             nsIContent*       aChild,
                             nsIFrame**        aResult,
                             bool              aIsAppend);
 
   // GetInitialContainingBlock() is deprecated in favor of GetRootElementFrame();
@@ -2174,22 +2186,16 @@ private:
   // insertion points.
   nsIFrame* GetInsertionPrevSibling(InsertionPoint* aInsertion, // inout
                                     nsIContent* aChild,
                                     bool* aIsAppend,
                                     bool* aIsRangeInsertSafe,
                                     nsIContent* aStartSkipChild = nullptr,
                                     nsIContent *aEndSkipChild = nullptr);
 
-  /**
-   * Return the insertion frame of the primary frame of aContent, or its nearest
-   * ancestor that isn't display:contents.
-   */
-  nsContainerFrame* GetContentInsertionFrameFor(nsIContent* aContent);
-
   // see if aContent and aSibling are legitimate siblings due to restrictions
   // imposed by table columns
   // XXXbz this code is generally wrong, since the frame for aContent
   // may be constructed based on tag, not based on aDisplay!
   bool IsValidSibling(nsIFrame*              aSibling,
                       nsIContent*            aContent,
                       mozilla::StyleDisplay& aDisplay);
 
--- a/layout/xul/nsMenuBarFrame.cpp
+++ b/layout/xul/nsMenuBarFrame.cpp
@@ -157,21 +157,18 @@ nsMenuBarFrame::FindMenuWithShortcut(nsI
   }
   if (accessKeys.IsEmpty() && charCode)
     accessKeys.AppendElement(charCode);
 
   if (accessKeys.IsEmpty())
     return nullptr; // no character was pressed so just return
 
   // Enumerate over our list of frames.
-  auto insertion = PresShell()->FrameConstructor()->
-    GetInsertionPoint(GetContent(), nullptr);
-  nsContainerFrame* immediateParent = insertion.mParentFrame;
-  if (!immediateParent)
-    immediateParent = this;
+  nsContainerFrame* immediateParent =
+    nsXULPopupManager::ImmediateParentFrame(this);
 
   // Find a most preferred accesskey which should be returned.
   nsIFrame* foundMenu = nullptr;
   size_t foundIndex = accessKeys.NoIndex;
   nsIFrame* currFrame = immediateParent->PrincipalChildList().FirstChild();
 
   while (currFrame) {
     nsIContent* current = currFrame->GetContent();
--- a/layout/xul/nsMenuPopupFrame.cpp
+++ b/layout/xul/nsMenuPopupFrame.cpp
@@ -2085,22 +2085,18 @@ nsMenuPopupFrame::FindMenuWithShortcut(n
 {
   uint32_t charCode, keyCode;
   aKeyEvent->GetCharCode(&charCode);
   aKeyEvent->GetKeyCode(&keyCode);
 
   doAction = false;
 
   // Enumerate over our list of frames.
-  auto insertion = PresShell()->
-    FrameConstructor()->GetInsertionPoint(GetContent(), nullptr);
-  nsContainerFrame* immediateParent = insertion.mParentFrame;
-  if (!immediateParent)
-    immediateParent = this;
-
+  nsContainerFrame* immediateParent =
+    nsXULPopupManager::ImmediateParentFrame(this);
   uint32_t matchCount = 0, matchShortcutCount = 0;
   bool foundActive = false;
   nsMenuFrame* frameBefore = nullptr;
   nsMenuFrame* frameAfter = nullptr;
   nsMenuFrame* frameShortcut = nullptr;
 
   nsIContent* parentContent = mContent->GetParent();
 
--- a/layout/xul/nsXULPopupManager.cpp
+++ b/layout/xul/nsXULPopupManager.cpp
@@ -2428,29 +2428,42 @@ nsXULPopupManager::HandleKeyboardEventWi
   if (consume) {
     aKeyEvent->AsEvent()->StopPropagation();
     aKeyEvent->AsEvent()->StopCrossProcessForwarding();
     aKeyEvent->AsEvent()->PreventDefault();
   }
   return true;
 }
 
+nsContainerFrame*
+nsXULPopupManager::ImmediateParentFrame(nsContainerFrame* aFrame)
+{
+  MOZ_ASSERT(aFrame && aFrame->GetContent());
+
+  bool multiple = false; // Unused
+  nsIContent* insertionPoint =
+    aFrame->GetContent()->OwnerDoc()->BindingManager()->
+      FindNestedSingleInsertionPoint(aFrame->GetContent(), &multiple);
+
+  nsCSSFrameConstructor* fc = aFrame->PresContext()->FrameConstructor();
+  nsContainerFrame* insertionFrame =
+    insertionPoint
+      ? fc->GetContentInsertionFrameFor(insertionPoint)
+      : nullptr;
+
+  return insertionFrame ? insertionFrame : aFrame;
+}
+
 nsMenuFrame*
 nsXULPopupManager::GetNextMenuItem(nsContainerFrame* aParent,
                                    nsMenuFrame* aStart,
                                    bool aIsPopup,
                                    bool aWrap)
 {
-  nsPresContext* presContext = aParent->PresContext();
-  auto insertion = presContext->PresShell()->
-    FrameConstructor()->GetInsertionPoint(aParent->GetContent(), nullptr);
-  nsContainerFrame* immediateParent = insertion.mParentFrame;
-  if (!immediateParent)
-    immediateParent = aParent;
-
+  nsContainerFrame* immediateParent = ImmediateParentFrame(aParent);
   nsIFrame* currFrame = nullptr;
   if (aStart) {
     if (aStart->GetNextSibling())
       currFrame = aStart->GetNextSibling();
     else if (aStart->GetParent()->GetContent()->IsXULElement(nsGkAtoms::menugroup))
       currFrame = aStart->GetParent()->GetNextSibling();
   }
   else
@@ -2500,23 +2513,17 @@ nsXULPopupManager::GetNextMenuItem(nsCon
 }
 
 nsMenuFrame*
 nsXULPopupManager::GetPreviousMenuItem(nsContainerFrame* aParent,
                                        nsMenuFrame* aStart,
                                        bool aIsPopup,
                                        bool aWrap)
 {
-  nsPresContext* presContext = aParent->PresContext();
-  auto insertion = presContext->PresShell()->
-    FrameConstructor()->GetInsertionPoint(aParent->GetContent(), nullptr);
-  nsContainerFrame* immediateParent = insertion.mParentFrame;
-  if (!immediateParent)
-    immediateParent = aParent;
-
+  nsContainerFrame* immediateParent = ImmediateParentFrame(aParent);
   const nsFrameList& frames(immediateParent->PrincipalChildList());
 
   nsIFrame* currFrame = nullptr;
   if (aStart) {
     if (aStart->GetPrevSibling())
       currFrame = aStart->GetPrevSibling();
     else if (aStart->GetParent()->GetContent()->IsXULElement(nsGkAtoms::menugroup))
       currFrame = aStart->GetParent()->GetPrevSibling();
--- a/layout/xul/nsXULPopupManager.h
+++ b/layout/xul/nsXULPopupManager.h
@@ -358,16 +358,23 @@ public:
   // initialize and shutdown methods called by nsLayoutStatics
   static nsresult Init();
   static void Shutdown();
 
   // returns a weak reference to the popup manager instance, could return null
   // if a popup manager could not be allocated
   static nsXULPopupManager* GetInstance();
 
+  // Returns the immediate parent frame of inserted children of aFrame's
+  // content.
+  //
+  // FIXME(emilio): Or something like that, because this is kind of broken in a
+  // variety of situations like multiple insertion points.
+  static nsContainerFrame* ImmediateParentFrame(nsContainerFrame* aFrame);
+
   // This should be called when a window is moved or resized to adjust the
   // popups accordingly.
   void AdjustPopupsOnWindowChange(nsPIDOMWindowOuter* aWindow);
   void AdjustPopupsOnWindowChange(nsIPresShell* aPresShell);
 
   // given a menu frame, find the prevous or next menu frame. If aPopup is
   // true then navigate a menupopup, from one item on the menu to the previous
   // or next one. This is used for cursor navigation between items in a popup