Bug 1453206: Fix IsInAnonymousSubtree to account for XBL in Shadow DOM. r=smaug
authorEmilio Cobos Álvarez <emilio@crisal.io>
Fri, 13 Apr 2018 20:13:10 +0200
changeset 466896 b8c5eb9fb1e6b4979d566ca78a1aab85e558bfb0
parent 466890 37b8862d354e0014a72715462dd2102dd5b599cc
child 466897 6ac542a489afe6dde28b2b43b01336667506b0f7
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1453206
milestone61.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 1453206: Fix IsInAnonymousSubtree to account for XBL in Shadow DOM. r=smaug MozReview-Commit-ID: B2aYury8K7i
dom/base/nsIContent.h
dom/base/nsIContentInlines.h
dom/events/Event.cpp
dom/events/EventDispatcher.cpp
dom/events/WheelHandlingHelper.cpp
dom/html/nsGenericHTMLElement.cpp
dom/html/nsGenericHTMLElement.h
extensions/spellcheck/src/mozInlineSpellChecker.cpp
layout/base/RestyleManager.cpp
layout/style/crashtests/1453206.html
layout/style/crashtests/crashtests.list
servo/components/style/gecko/wrapper.rs
--- a/dom/base/nsIContent.h
+++ b/dom/base/nsIContent.h
@@ -235,25 +235,17 @@ public:
     return HasFlag(NODE_IS_ANONYMOUS_ROOT);
   }
 
   /**
    * 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.
    */
-  bool IsInAnonymousSubtree() const
-  {
-    NS_ASSERTION(!IsInNativeAnonymousSubtree() || GetBindingParent() ||
-                 (!IsInUncomposedDoc() &&
-                  static_cast<nsIContent*>(SubtreeRoot())->IsInNativeAnonymousSubtree()),
-                 "Must have binding parent when in native anonymous subtree which is in document.\n"
-                 "Native anonymous subtree which is not in document must have native anonymous root.");
-    return IsInNativeAnonymousSubtree() || (!IsInShadowTree() && GetBindingParent() != nullptr);
-  }
+  inline bool IsInAnonymousSubtree() const;
 
   /**
    * Return true iff this node is in an HTML document (in the HTML5 sense of
    * the term, i.e. not in an XHTML/XML document).
    */
   inline bool IsInHTMLDocument() const;
 
 
--- a/dom/base/nsIContentInlines.h
+++ b/dom/base/nsIContentInlines.h
@@ -174,9 +174,30 @@ nsIContent::IsActiveChildrenElement() co
   nsIContent* bindingParent = GetBindingParent();
   if (!bindingParent) {
     return false;
   }
 
   return !bindingParent->GetShadowRoot();
 }
 
+inline bool
+nsIContent::IsInAnonymousSubtree() const
+{
+  NS_ASSERTION(!IsInNativeAnonymousSubtree() || GetBindingParent() ||
+               (!IsInUncomposedDoc() &&
+                static_cast<nsIContent*>(SubtreeRoot())->IsInNativeAnonymousSubtree()),
+               "Must have binding parent when in native anonymous subtree which is in document.\n"
+               "Native anonymous subtree which is not in document must have native anonymous root.");
+
+  if (IsInNativeAnonymousSubtree()) {
+    return true;
+  }
+
+  nsIContent* bindingParent = GetBindingParent();
+  if (!bindingParent) {
+    return false;
+  }
+
+  return !bindingParent->GetShadowRoot();
+}
+
 #endif // nsIContentInlines_h
--- a/dom/events/Event.cpp
+++ b/dom/events/Event.cpp
@@ -22,16 +22,17 @@
 #include "mozilla/TouchEvents.h"
 #include "nsContentUtils.h"
 #include "nsCOMPtr.h"
 #include "nsDeviceContext.h"
 #include "nsError.h"
 #include "nsGlobalWindow.h"
 #include "nsIFrame.h"
 #include "nsIContent.h"
+#include "nsIContentInlines.h"
 #include "nsIDocument.h"
 #include "nsIPresShell.h"
 #include "nsIScrollableFrame.h"
 #include "nsJSEnvironment.h"
 #include "nsLayoutUtils.h"
 #include "nsPIWindowRoot.h"
 #include "nsRFPService.h"
 
--- a/dom/events/EventDispatcher.cpp
+++ b/dom/events/EventDispatcher.cpp
@@ -4,16 +4,17 @@
  * 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 "nsPresContext.h"
 #include "nsContentUtils.h"
 #include "nsError.h"
 #include <new>
 #include "nsIContent.h"
+#include "nsIContentInlines.h"
 #include "nsIDocument.h"
 #include "nsINode.h"
 #include "nsPIDOMWindow.h"
 #include "AnimationEvent.h"
 #include "BeforeUnloadEvent.h"
 #include "ClipboardEvent.h"
 #include "CommandEvent.h"
 #include "CompositionEvent.h"
--- a/dom/events/WheelHandlingHelper.cpp
+++ b/dom/events/WheelHandlingHelper.cpp
@@ -11,16 +11,18 @@
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/WheelEventBinding.h"
 #include "nsCOMPtr.h"
 #include "nsContentUtils.h"
 #include "nsIContent.h"
+#include "nsIContentInlines.h"
+#include "nsIDocument.h"
 #include "nsIDocumentInlines.h"         // for nsIDocument and HTMLBodyElement
 #include "nsIPresShell.h"
 #include "nsIScrollableFrame.h"
 #include "nsITextControlElement.h"
 #include "nsITimer.h"
 #include "nsPluginFrame.h"
 #include "nsPresContext.h"
 #include "prtime.h"
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -168,16 +168,37 @@ nsGenericHTMLElement::CopyInnerTo(Elemen
 static const nsAttrValue::EnumTable kDirTable[] = {
   { "ltr", eDir_LTR },
   { "rtl", eDir_RTL },
   { "auto", eDir_Auto },
   { nullptr, 0 }
 };
 
 void
+nsGenericHTMLElement::AddToNameTable(nsAtom* aName)
+{
+  MOZ_ASSERT(HasName(), "Node doesn't have name?");
+  nsIDocument* doc = GetUncomposedDoc();
+  if (doc && !IsInAnonymousSubtree()) {
+    doc->AddToNameTable(this, aName);
+  }
+}
+
+void
+nsGenericHTMLElement::RemoveFromNameTable()
+{
+  if (HasName() && CanHaveName(NodeInfo()->NameAtom())) {
+    if (nsIDocument* doc = GetUncomposedDoc()) {
+      doc->RemoveFromNameTable(this,
+                               GetParsedAttr(nsGkAtoms::name)->GetAtomValue());
+    }
+  }
+}
+
+void
 nsGenericHTMLElement::GetAccessKeyLabel(nsString& aLabel)
 {
   nsAutoString suffix;
   GetAccessKey(suffix);
   if (!suffix.IsEmpty()) {
     EventStateManager::GetAccessKeyLabelPrefix(this, aLabel);
     aLabel.Append(suffix);
   }
--- a/dom/html/nsGenericHTMLElement.h
+++ b/dom/html/nsGenericHTMLElement.h
@@ -693,32 +693,18 @@ public:
     // HasName() is true precisely when name is nonempty.
     return aElement->IsHTMLElement(nsGkAtoms::img) && aElement->HasName();
   }
 
 protected:
   /**
    * Add/remove this element to the documents name cache
    */
-  void AddToNameTable(nsAtom* aName) {
-    NS_ASSERTION(HasName(), "Node doesn't have name?");
-    nsIDocument* doc = GetUncomposedDoc();
-    if (doc && !IsInAnonymousSubtree()) {
-      doc->AddToNameTable(this, aName);
-    }
-  }
-  void RemoveFromNameTable() {
-    if (HasName() && CanHaveName(NodeInfo()->NameAtom())) {
-      nsIDocument* doc = GetUncomposedDoc();
-      if (doc) {
-        doc->RemoveFromNameTable(this, GetParsedAttr(nsGkAtoms::name)->
-                                         GetAtomValue());
-      }
-    }
-  }
+  void AddToNameTable(nsAtom* aName);
+  void RemoveFromNameTable();
 
   /**
    * Register or unregister an access key to this element based on the
    * accesskey attribute.
    */
   void RegAccessKey()
   {
     if (HasFlag(NODE_HAS_ACCESSKEY)) {
--- a/extensions/spellcheck/src/mozInlineSpellChecker.cpp
+++ b/extensions/spellcheck/src/mozInlineSpellChecker.cpp
@@ -64,16 +64,17 @@
 #include "nsISelectionPrivate.h"
 #include "nsISelectionController.h"
 #include "nsIServiceManager.h"
 #include "nsITextServicesFilter.h"
 #include "nsString.h"
 #include "nsThreadUtils.h"
 #include "nsUnicharUtils.h"
 #include "nsIContent.h"
+#include "nsIContentInlines.h"
 #include "nsRange.h"
 #include "nsContentUtils.h"
 #include "nsIObserverService.h"
 #include "nsITextControlElement.h"
 #include "prtime.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -3051,16 +3051,17 @@ static void
 VerifyFlatTree(const nsIContent& aContent)
 {
   StyleChildrenIterator iter(&aContent);
 
   for (auto* content = iter.GetNextChild();
        content;
        content = iter.GetNextChild()) {
     MOZ_ASSERT(content->GetFlattenedTreeParentNodeForStyle() == &aContent);
+    MOZ_ASSERT(!content->IsActiveChildrenElement());
     VerifyFlatTree(*content);
   }
 }
 #endif
 
 void
 RestyleManager::ProcessPendingRestyles()
 {
new file mode 100644
--- /dev/null
+++ b/layout/style/crashtests/1453206.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<div id="host"></div>
+<script>
+document.addEventListener('DOMContentLoaded', () => {
+  let shadow = host.attachShadow({ mode: 'open' });
+  shadow.appendChild(document.createElement('marquee'));
+});
+</script>
--- a/layout/style/crashtests/crashtests.list
+++ b/layout/style/crashtests/crashtests.list
@@ -266,8 +266,9 @@ pref(dom.webcomponents.shadowdom.enabled
 load 1418059.html
 test-pref(dom.animations-api.core.enabled,true) load 1418867.html
 pref(dom.webcomponents.shadowdom.enabled,true) load 1419554.html
 load 1426312.html
 load 1439793.html
 load 1409183.html
 pref(dom.webcomponents.shadowdom.enabled,true) load 1445682.html
 load 1450691.html
+pref(dom.webcomponents.shadowdom.enabled,true) load 1453206.html
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -632,20 +632,21 @@ impl<'le> GeckoElement<'le> {
         self.xbl_binding().and_then(|b| b.binding_with_content())
     }
 
     #[inline]
     fn has_xbl_binding_with_content(&self) -> bool {
         !self.xbl_binding_with_content().is_none()
     }
 
-    /// This and has_xbl_binding_parent duplicate the logic in Gecko's virtual
-    /// nsINode::GetBindingParent function, which only has two implementations:
-    /// one for XUL elements, and one for other elements.  We just hard code in
-    /// our knowledge of those two implementations here.
+    /// This duplicates the logic in Gecko's virtual nsINode::GetBindingParent
+    /// function, which only has two implementations: one for XUL elements, and
+    /// one for other elements.
+    ///
+    /// We just hard code in our knowledge of those two implementations here.
     fn xbl_binding_parent(&self) -> Option<Self> {
         if self.is_xul_element() {
             // FIXME(heycam): Having trouble with bindgen on nsXULElement,
             // where the binding parent is stored in a member variable
             // rather than in slots.  So just get it through FFI for now.
             unsafe { bindings::Gecko_GetBindingParent(self.0).map(GeckoElement) }
         } else {
             let binding_parent = unsafe { self.non_xul_xbl_binding_parent_raw_content().as_ref() }
@@ -662,27 +663,16 @@ impl<'le> GeckoElement<'le> {
     }
 
     fn non_xul_xbl_binding_parent_raw_content(&self) -> *mut nsIContent {
         debug_assert!(!self.is_xul_element());
         self.extended_slots()
             .map_or(ptr::null_mut(), |slots| slots._base.mBindingParent)
     }
 
-    fn has_xbl_binding_parent(&self) -> bool {
-        if self.is_xul_element() {
-            // FIXME(heycam): Having trouble with bindgen on nsXULElement,
-            // where the binding parent is stored in a member variable
-            // rather than in slots.  So just get it through FFI for now.
-            unsafe { bindings::Gecko_GetBindingParent(self.0).is_some() }
-        } else {
-            !self.non_xul_xbl_binding_parent_raw_content().is_null()
-        }
-    }
-
     #[inline]
     fn namespace_id(&self) -> i32 {
         self.as_node().node_info().mInner.mNamespaceID
     }
 
     #[inline]
     fn is_xul_element(&self) -> bool {
         self.namespace_id() == (structs::root::kNameSpaceID_XUL as i32)
@@ -810,18 +800,26 @@ impl<'le> GeckoElement<'le> {
     fn is_in_native_anonymous_subtree(&self) -> bool {
         use gecko_bindings::structs::NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE;
         self.flags() & (NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE as u32) != 0
     }
 
     /// This logic is duplicated in Gecko's nsIContent::IsInAnonymousSubtree.
     #[inline]
     fn is_in_anonymous_subtree(&self) -> bool {
-        self.is_in_native_anonymous_subtree() ||
-            (!self.as_node().is_in_shadow_tree() && self.has_xbl_binding_parent())
+        if self.is_in_native_anonymous_subtree() {
+            return true;
+        }
+
+        let binding_parent = match self.xbl_binding_parent() {
+            Some(p) => p,
+            None => return false,
+        };
+
+        binding_parent.shadow_root().is_none()
     }
 
     /// Returns true if this node is the shadow root of an use-element shadow tree.
     #[inline]
     fn is_root_of_use_element_shadow_tree(&self) -> bool {
         if !self.is_root_of_anonymous_subtree() {
             return false;
         }