Bug 1364361 - Make AllChildrenIterator find NAC created by non-primary frames for content. r?bholley,bz draft
authorCameron McCormack <cam@mcc.id.au>
Fri, 19 May 2017 17:39:13 +0800
changeset 581880 ba658af1dd26b166cffdd6be81bb050a278dc8b8
parent 581473 8d60d0f825110cfb646ac31dc16dc011708bcf34
child 581881 fde88c38def5807394b8a58d90461dfdb63be00b
child 581904 710492f48c6c2242916754baed86eb39f2850b40
push id59901
push userbmo:cam@mcc.id.au
push dateSat, 20 May 2017 01:01:41 +0000
reviewersbholley, bz
bugs1364361
milestone55.0a1
Bug 1364361 - Make AllChildrenIterator find NAC created by non-primary frames for content. r?bholley,bz MozReview-Commit-ID: GjvKOxYmKvR
dom/base/ChildIterator.cpp
dom/base/ChildIterator.h
dom/base/nsContentUtils.cpp
--- a/dom/base/ChildIterator.cpp
+++ b/dom/base/ChildIterator.cpp
@@ -1,23 +1,28 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ChildIterator.h"
+#include "nsContainerFrame.h"
 #include "nsContentUtils.h"
 #include "mozilla/dom/XBLChildrenElement.h"
 #include "mozilla/dom/HTMLContentElement.h"
 #include "mozilla/dom/HTMLShadowElement.h"
 #include "mozilla/dom/ShadowRoot.h"
 #include "nsIAnonymousContentCreator.h"
-#include "nsIFrame.h"
 #include "nsCSSAnonBoxes.h"
+#include "nsComboboxControlFrame.h"
+#include "nsFieldSetFrame.h"
+#include "nsMenuFrame.h"
+#include "nsMenuPopupFrame.h"
+#include "nsPopupSetFrame.h"
 
 namespace mozilla {
 namespace dom {
 
 class MatchedNodes {
 public:
   explicit MatchedNodes(HTMLContentElement* aInsertionPoint)
     : mIsContentElement(true), mContentElement(aInsertionPoint) {}
@@ -362,20 +367,132 @@ AllChildrenIterator::Seek(nsIContent* aC
   nsIContent* child = nullptr;
   do {
     child = GetNextChild();
   } while (child && child != aChildToFind);
 
   return child == aChildToFind;
 }
 
+#ifdef DEBUG
+static void
+AssertNoOtherNACInSameContentFrames(nsIFrame* aFrame,
+                                    const nsIContent* aContent,
+                                    const nsTArray<nsIFrame*>& aHandledFrames)
+{
+  MOZ_ASSERT(aFrame->GetContent() == aContent);
+
+  nsIAnonymousContentCreator* ac = do_QueryFrame(aFrame);
+  MOZ_ASSERT(!ac ||
+             aContent->GetPrimaryFrame() == aFrame ||
+             aHandledFrames.Contains(aFrame),
+             "found frame that could create NAC that we won't iterate");
+
+  for (nsIFrame::ChildListIterator iter(aFrame); !iter.IsDone(); iter.Next()) {
+    for (nsIFrame* f : iter.CurrentList()) {
+      if (f->GetContent() == aContent && !f->GetPrevContinuation()) {
+        AssertNoOtherNACInSameContentFrames(f, aContent, aHandledFrames);
+      }
+    }
+  }
+}
+#endif
+
+static void
+CollectSameContentNACCreatingFrames(nsIFrame* aFrame,
+                                    nsTArray<nsIFrame*>& aChildren)
+{
+  switch (aFrame->Type()) {
+    case LayoutFrameType::ComboboxControl: {
+      // A <select> element's popup frame.
+      auto frame = static_cast<nsComboboxControlFrame*>(aFrame);
+      const nsFrameList& popups =
+        frame->GetChildList(nsIFrame::kSelectPopupList);
+      for (nsIFrame* child : popups) {
+        aChildren.AppendElement(child);
+      }
+      break;
+    }
+    case LayoutFrameType::FieldSet: {
+      // A <fieldset> element's inner frame.
+      auto frame = static_cast<nsFieldSetFrame*>(aFrame);
+      // The inner frame is sometimes a scroll frame (which does create NAC)
+      // and sometimes a regular block frame (which doesn't).
+      nsIFrame* inner = frame->GetInner();
+      if (inner->QueryFrame(nsIAnonymousContentCreator::kFrameIID)) {
+        aChildren.AppendElement(inner);
+      }
+      break;
+    }
+    case LayoutFrameType::Scroll: {
+      // A scrollable <details> element's DetailsFrame.  (The outer frame
+      // is the scroll frame.)
+      nsIScrollableFrame* frame = do_QueryFrame(aFrame);
+      MOZ_ASSERT(frame);
+      nsIFrame* inner = frame->GetScrolledFrame();
+      MOZ_ASSERT(inner);
+      if (inner->Type() == LayoutFrameType::Details) {
+        aChildren.AppendElement(inner);
+      }
+      break;
+    }
+    case LayoutFrameType::ColumnSet: {
+      // Any multicol element.  nsColumnSetFrames only contain a single frame
+      // and its continuations, so just traverse down to the first principal
+      // child.
+      nsIFrame* inner = aFrame->PrincipalChildList().FirstChild();
+      if (inner->QueryFrame(nsIAnonymousContentCreator::kFrameIID)) {
+        aChildren.AppendElement(inner);
+      }
+      CollectSameContentNACCreatingFrames(inner, aChildren);
+      break;
+    }
+    default:
+      break;
+  }
+}
+
 void
 AllChildrenIterator::AppendNativeAnonymousChildren()
 {
-  AppendNativeAnonymousChildrenFromFrame(mOriginalContent->GetPrimaryFrame());
+  if (nsIFrame* primaryFrame = mOriginalContent->GetPrimaryFrame()) {
+    AppendNativeAnonymousChildrenFromFrame(primaryFrame);
+
+    // Sometimes we have other, non-primary frames created for some content,
+    // which can also create NAC, which we wouldn't otherwise find in our
+    // content tree traversal.  We just handle these explicitly here, and assert
+    // below that there aren't any other NAC-creating frames lying around that
+    // we need to traverse.
+    nsTArray<nsIFrame*> children;
+    CollectSameContentNACCreatingFrames(primaryFrame, children);
+
+    for (nsIFrame* f : children) {
+      MOZ_ASSERT(f->GetContent() == mOriginalContent);
+      MOZ_ASSERT(f->QueryFrame(nsIAnonymousContentCreator::kFrameIID));
+      AppendNativeAnonymousChildrenFromFrame(f);
+    }
+
+#ifdef DEBUG
+    // Find the top-most frame for this content.  Stop before getting to
+    // the root frames, since their NAC is handled specially below.
+    // (And skip this check for <area> elements, since they're weird.)
+    if (!mOriginalContent->IsHTMLElement(nsGkAtoms::area)) {
+      MOZ_ASSERT(primaryFrame->GetContent() == mOriginalContent);
+
+      nsIFrame* root = primaryFrame;
+      while (root->GetParent() &&
+             root->GetParent()->GetContent() == mOriginalContent &&
+             root->GetParent()->Type() != LayoutFrameType::Canvas &&
+             root->GetParent()->Type() != LayoutFrameType::Root) {
+        root = root->GetParent();
+      }
+      AssertNoOtherNACInSameContentFrames(root, mOriginalContent, children);
+    }
+#endif
+  }
 
   // The root scroll frame is not the primary frame of the root element.
   // Detect and handle this case.
   if (!(mFlags & nsIContent::eSkipDocumentLevelNativeAnonymousContent) &&
       mOriginalContent == mOriginalContent->OwnerDoc()->GetRootElement()) {
     nsContentUtils::AppendDocumentLevelNativeAnonymousContentTo(
         mOriginalContent->OwnerDoc(), mAnonKids);
   }
--- a/dom/base/ChildIterator.h
+++ b/dom/base/ChildIterator.h
@@ -227,16 +227,17 @@ public:
     eAtEnd
   };
   IteratorPhase Phase() const { return mPhase; }
 
 private:
   // Helpers.
   void AppendNativeAnonymousChildren();
   void AppendNativeAnonymousChildrenFromFrame(nsIFrame* aFrame);
+  void AppendNativeAnonymousChildrenFromFramesForContent(nsIFrame* aFrame);
 
   const nsIContent* mOriginalContent;
 
   // mAnonKids is an array of native anonymous children, mAnonKidsIdx is index
   // in the array. If mAnonKidsIdx < mAnonKids.Length() and mPhase is
   // eAtAnonKids then the iterator points at a child at mAnonKidsIdx index. If
   // mAnonKidsIdx == mAnonKids.Length() then the iterator is somewhere after
   // the last native anon child. If mAnonKidsIdx == UINT32_MAX then the iterator
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -216,17 +216,16 @@
 #include "mozilla/EnumSet.h"
 #include "mozilla/BloomFilter.h"
 #include "TabChild.h"
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/TabGroup.h"
 #include "nsIWebNavigationInfo.h"
 #include "nsPluginHost.h"
 #include "mozilla/HangAnnotations.h"
-#include "mozilla/ServoRestyleManager.h"
 
 #include "nsIBidiKeyboard.h"
 
 #if defined(XP_WIN)
 // Undefine LoadImage to prevent naming conflict with Windows.
 #undef LoadImage
 #endif
 
@@ -5302,37 +5301,25 @@ private:
   nsCOMPtr<nsIContent> mParent;
 };
 
 /* static */
 void
 nsContentUtils::DestroyAnonymousContent(nsCOMPtr<nsIContent>* aContent)
 {
   if (*aContent) {
-    // Don't wait until UnbindFromTree to clear ServoElementData, since
-    // leak checking at shutdown can run before the AnonymousContentDestroyer
-    // runs.
-    if ((*aContent)->IsStyledByServo() && (*aContent)->IsElement()) {
-      ServoRestyleManager::ClearServoDataFromSubtree((*aContent)->AsElement());
-    }
     AddScriptRunner(new AnonymousContentDestroyer(aContent));
   }
 }
 
 /* static */
 void
 nsContentUtils::DestroyAnonymousContent(nsCOMPtr<Element>* aElement)
 {
   if (*aElement) {
-    // Don't wait until UnbindFromTree to clear ServoElementData, since
-    // leak checking at shutdown can run before the AnonymousContentDestroyer
-    // runs.
-    if ((*aElement)->IsStyledByServo()) {
-      ServoRestyleManager::ClearServoDataFromSubtree(*aElement);
-    }
     AddScriptRunner(new AnonymousContentDestroyer(aElement));
   }
 }
 
 /* static */
 void
 nsContentUtils::NotifyInstalledMenuKeyboardListener(bool aInstalling)
 {