Bug 1596445 - Add some supporting code to nsINode to deal with NAC and shadow DOM separately. r=bzbarsky
authorEmilio Cobos Álvarez <emilio@crisal.io>
Fri, 15 Nov 2019 15:10:45 +0000
changeset 502182 52a8e787a8642a2c5ad6d8cb8ffee4770f742758
parent 502181 a3cf1224284f9177fe2d8a86daab3fd40aa4578b
child 502183 f22a913450cb3f5fd722b7864429fb12554a41bd
push id114172
push userdluca@mozilla.com
push dateTue, 19 Nov 2019 11:31:10 +0000
treeherdermozilla-inbound@b5c5ba07d3db [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky
bugs1596445
milestone72.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1596445 - Add some supporting code to nsINode to deal with NAC and shadow DOM separately. r=bzbarsky We'll use these to remove GetBindingParent. Differential Revision: https://phabricator.services.mozilla.com/D53029
dom/base/FragmentOrElement.cpp
dom/base/nsIContent.h
dom/base/nsINode.cpp
dom/base/nsINode.h
layout/base/PresShell.cpp
toolkit/components/find/nsFind.cpp
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -1051,23 +1051,16 @@ bool nsIContent::IsFocusableInternal(int
   return false;
 }
 
 bool FragmentOrElement::IsLink(nsIURI** aURI) const {
   *aURI = nullptr;
   return false;
 }
 
-nsIContent* nsIContent::GetContainingShadowHost() const {
-  if (mozilla::dom::ShadowRoot* shadow = GetContainingShadow()) {
-    return shadow->GetHost();
-  }
-  return nullptr;
-}
-
 void nsIContent::SetAssignedSlot(HTMLSlotElement* aSlot) {
   MOZ_ASSERT(aSlot || GetExistingExtendedContentSlots());
   ExtendedContentSlots()->mAssignedSlot = aSlot;
 }
 
 #ifdef DEBUG
 void nsIContent::AssertAnonymousSubtreeRelatedInvariants() const {
   MOZ_ASSERT(!IsRootOfNativeAnonymousSubtree() ||
--- a/dom/base/nsIContent.h
+++ b/dom/base/nsIContent.h
@@ -390,35 +390,25 @@ class nsIContent : public nsINode {
    * Gets the ShadowRoot binding for this element.
    *
    * @return The ShadowRoot currently bound to this element.
    */
   inline mozilla::dom::ShadowRoot* GetShadowRoot() const;
 
   /**
    * Gets the root of the node tree for this content if it is in a shadow tree.
-   * This method is called |GetContainingShadow| instead of |GetRootShadowRoot|
-   * to avoid confusion with |GetShadowRoot|.
    *
    * @return The ShadowRoot that is the root of the node tree.
    */
   mozilla::dom::ShadowRoot* GetContainingShadow() const {
     const nsExtendedContentSlots* slots = GetExistingExtendedContentSlots();
     return slots ? slots->mContainingShadow.get() : nullptr;
   }
 
   /**
-   * Gets the shadow host if this content is in a shadow tree. That is, the host
-   * of |GetContainingShadow|, if its not null.
-   *
-   * @return The shadow host, if this is in shadow tree, or null.
-   */
-  nsIContent* GetContainingShadowHost() const;
-
-  /**
    * Gets the assigned slot associated with this content.
    *
    * @return The assigned slot element or null.
    */
   mozilla::dom::HTMLSlotElement* GetAssignedSlot() const {
     const nsExtendedContentSlots* slots = GetExistingExtendedContentSlots();
     return slots ? slots->mAssignedSlot.get() : nullptr;
   }
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -575,19 +575,33 @@ std::ostream& operator<<(std::ostream& a
 
     curr = curr->GetParentNode();
   }
 
   NS_ConvertUTF16toUTF8 str(elemDesc);
   return aStream << str.get();
 }
 
+ShadowRoot* nsINode::GetContainingShadow() const {
+  if (!IsInShadowTree()) {
+    return nullptr;
+  }
+  return AsContent()->GetContainingShadow();
+}
+
+nsIContent* nsINode::GetContainingShadowHost() const {
+  if (ShadowRoot* shadow = GetContainingShadow()) {
+    return shadow->GetHost();
+  }
+  return nullptr;
+}
+
 SVGUseElement* nsINode::DoGetContainingSVGUseShadowHost() const {
   MOZ_ASSERT(IsInShadowTree());
-  return SVGUseElement::FromNodeOrNull(AsContent()->GetContainingShadowHost());
+  return SVGUseElement::FromNodeOrNull(GetContainingShadowHost());
 }
 
 void nsINode::GetNodeValueInternal(nsAString& aNodeValue) {
   SetDOMStringToNull(aNodeValue);
 }
 
 nsINode* nsINode::RemoveChild(nsINode& aOldChild, ErrorResult& aError) {
   if (!aOldChild.IsContent()) {
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -86,16 +86,17 @@ class DOMRectReadOnly;
 class Element;
 class EventHandlerNonNull;
 class MutationObservers;
 template <typename T>
 class Optional;
 class OwningNodeOrString;
 template <typename>
 class Sequence;
+class ShadowRoot;
 class SVGUseElement;
 class Text;
 class TextOrElementOrDocument;
 struct DOMPointInit;
 struct GetRootNodeOptions;
 enum class CallerType : uint32_t;
 }  // namespace dom
 }  // namespace mozilla
@@ -1255,20 +1256,68 @@ class nsINode : public mozilla::dom::Eve
   /**
    * Returns true if there is NOT a path through child lists
    * from the top of this node's parent chain back to this node or
    * if the node is in native anonymous subtree without a parent.
    *
    * TODO(emilio):: Remove this function, and use just
    * IsInNativeAnonymousSubtree, or something?
    */
-  bool IsInAnonymousSubtree() const {
-    return IsInNativeAnonymousSubtree();
+  bool IsInAnonymousSubtree() const { return IsInNativeAnonymousSubtree(); }
+
+  /**
+   * If |this| or any ancestor is native anonymous, return the root of the
+   * native anonymous subtree. Note that in case of nested native anonymous
+   * content, this returns the innermost root, not the outermost.
+   */
+  nsIContent* GetClosestNativeAnonymousSubtreeRoot() const {
+    if (!IsInNativeAnonymousSubtree()) {
+      return nullptr;
+    }
+    MOZ_ASSERT(IsContent(), "How did non-content end up in NAC?");
+    for (const nsINode* node = this; node; node = node->GetParentNode()) {
+      if (node->IsRootOfNativeAnonymousSubtree()) {
+        return const_cast<nsINode*>(node)->AsContent();
+      }
+    }
+    // FIXME(emilio): This should not happen, usually, but editor removes nodes
+    // in native anonymous subtrees, and we don't clean nodes from the current
+    // event content stack from ContentRemoved, so it can actually happen, see
+    // bug 1510208.
+    NS_WARNING("GetClosestNativeAnonymousSubtreeRoot on disconnected NAC!");
+    return nullptr;
   }
 
+  /**
+   * If |this| or any ancestor is native anonymous, return the parent of the
+   * native anonymous subtree. Note that in case of nested native anonymous
+   * content, this returns the parent of the innermost root, not the outermost.
+   */
+  nsIContent* GetClosestNativeAnonymousSubtreeRootParent() const {
+    const nsIContent* root = GetClosestNativeAnonymousSubtreeRoot();
+    if (!root) {
+      return nullptr;
+    }
+    // We could put this in nsIContentInlines.h or such to avoid this
+    // reinterpret_cast, but it doesn't seem worth it.
+    return reinterpret_cast<const nsINode*>(root)->GetParent();
+  }
+
+  /**
+   * Gets the root of the node tree for this content if it is in a shadow tree.
+   */
+  mozilla::dom::ShadowRoot* GetContainingShadow() const;
+  /**
+   * Gets the shadow host if this content is in a shadow tree. That is, the host
+   * of |GetContainingShadow|, if its not null.
+   *
+   * @return The shadow host, if this is in shadow tree, or null.
+   */
+  nsIContent* GetContainingShadowHost() const;
+
   bool IsInSVGUseShadowTree() const {
     return !!GetContainingSVGUseShadowHost();
   }
 
   mozilla::dom::SVGUseElement* GetContainingSVGUseShadowHost() const {
     if (!IsInShadowTree()) {
       return nullptr;
     }
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -2002,28 +2002,20 @@ void PresShell::FireResizeEvent() {
   nsEventStatus status = nsEventStatus_eIgnore;
 
   if (nsPIDOMWindowOuter* window = mDocument->GetWindow()) {
     EventDispatcher::Dispatch(window, mPresContext, &event, nullptr, &status);
   }
 }
 
 static nsIContent* GetNativeAnonymousSubtreeRoot(nsIContent* aContent) {
-  if (!aContent || !aContent->IsInNativeAnonymousSubtree()) {
+  if (!aContent) {
     return nullptr;
   }
-  auto* current = aContent;
-  // FIXME(emilio): This should not need to worry about current being null, but
-  // editor removes nodes in native anonymous subtrees, and we don't clean nodes
-  // from the current event content stack from ContentRemoved, so it can
-  // actually happen, see bug 1510208.
-  while (current && !current->IsRootOfNativeAnonymousSubtree()) {
-    current = current->GetFlattenedTreeParent();
-  }
-  return current;
+  return aContent->GetClosestNativeAnonymousSubtreeRoot();
 }
 
 void PresShell::NativeAnonymousContentRemoved(nsIContent* aAnonContent) {
   MOZ_ASSERT(aAnonContent->IsRootOfNativeAnonymousSubtree());
   if (nsIContent* root = GetNativeAnonymousSubtreeRoot(mCurrentEventContent)) {
     if (aAnonContent == root) {
       mCurrentEventContent = aAnonContent->GetFlattenedTreeParent();
       mCurrentEventFrame = nullptr;
--- a/toolkit/components/find/nsFind.cpp
+++ b/toolkit/components/find/nsFind.cpp
@@ -68,23 +68,17 @@ nsFind::~nsFind() = default;
 #ifdef DEBUG_FIND
 #  define DEBUG_FIND_PRINTF(...) printf(__VA_ARGS__)
 #else
 #  define DEBUG_FIND_PRINTF(...) /* nothing */
 #endif
 
 static nsIContent& AnonymousSubtreeRootParent(const nsINode& aNode) {
   MOZ_ASSERT(aNode.IsInNativeAnonymousSubtree());
-
-  nsIContent* current = aNode.GetParent();
-  while (current->IsInNativeAnonymousSubtree()) {
-    current = current->GetParent();
-    MOZ_ASSERT(current, "huh?");
-  }
-  return *current;
+  return *aNode.GetClosestNativeAnonymousSubtreeRootParent();
 }
 
 static void DumpNode(const nsINode* aNode) {
 #ifdef DEBUG_FIND
   if (!aNode) {
     printf(">>>> Node: NULL\n");
     return;
   }